diff options
author | Bart Visscher <bartv@thisnet.nl> | 2014-05-07 17:54:38 +0200 |
---|---|---|
committer | Bart Visscher <bartv@thisnet.nl> | 2014-05-07 17:54:38 +0200 |
commit | f569c721a64486d0e7c7e307ed77ac0caed2dc2d (patch) | |
tree | df7b3399a858ffdae6bd6e66616746efbcee24bc /lib | |
parent | 47d70da2f5cb55ad47023b061b68062dd8b8d8e2 (diff) | |
parent | 254fa5eb22efa5ba572702064377a6ad9eec9a53 (diff) | |
download | nextcloud-server-f569c721a64486d0e7c7e307ed77ac0caed2dc2d.tar.gz nextcloud-server-f569c721a64486d0e7c7e307ed77ac0caed2dc2d.zip |
Merge branch 'master' into optimize-startup-queries
Conflicts:
apps/files_sharing/lib/sharedstorage.php
tests/lib/group/manager.php
removed hasFilesSharedWith from lib/public/share.php and
sharedstorage.php to fix merge
Diffstat (limited to 'lib')
206 files changed, 6532 insertions, 4651 deletions
diff --git a/lib/base.php b/lib/base.php index 525d290931f..1f7d0c0da65 100644 --- a/lib/base.php +++ b/lib/base.php @@ -74,11 +74,6 @@ class OC { public static $CLI = false; /** - * @var OC_Router - */ - protected static $router = null; - - /** * @var \OC\Session\Session */ public static $session = null; @@ -103,7 +98,9 @@ class OC { get_include_path() ); - if(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) { + 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/'; } else { self::$configDir = OC::$SERVERROOT . '/config/'; @@ -188,7 +185,6 @@ class OC { if (file_exists(self::$configDir . "/config.php") and !is_writable(self::$configDir . "/config.php") ) { - $defaults = new OC_Defaults(); if (self::$CLI) { echo "Can't write into config directory!\n"; echo "This can usually be fixed by giving the webserver write access to the config directory\n"; @@ -216,6 +212,34 @@ class OC { } } + /* + * 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() { + 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 + + // iFrame Restriction Policy + $xFramePolicy = OC_Config::getValue('xframe_restriction', true); + if($xFramePolicy) { + header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains + } + + // Content Security Policy + // If you change the standard policy, please also change it in config.sample.php + $policy = OC_Config::getValue('custom_csp_policy', + 'default-src \'self\'; ' + .'script-src \'self\' \'unsafe-eval\'; ' + .'style-src \'self\' \'unsafe-inline\'; ' + .'frame-src *; ' + .'img-src *; ' + .'font-src \'self\' data:; ' + .'media-src *'); + header('Content-Security-Policy:'.$policy); + } + public static function checkSSL() { // redirect to https site if configured if (OC_Config::getValue("forcessl", false)) { @@ -280,6 +304,11 @@ class OC { } } + /** + * 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 (self::needUpgrade()) { if ($showTemplate && !OC_Config::getValue('maintenance', false)) { @@ -308,6 +337,7 @@ class OC { OC_Util::addScript("jquery.placeholder"); OC_Util::addScript("jquery-tipsy"); OC_Util::addScript("compatibility"); + OC_Util::addScript("underscore"); OC_Util::addScript("jquery.ocdialog"); OC_Util::addScript("oc-dialogs"); OC_Util::addScript("js"); @@ -316,7 +346,6 @@ class OC { OC_Util::addScript("config"); //OC_Util::addScript( "multiselect" ); OC_Util::addScript('search', 'result'); - OC_Util::addScript('router'); OC_Util::addScript("oc-requesttoken"); // avatars @@ -388,19 +417,6 @@ class OC { return OC_Config::getValue('session_lifetime', 60 * 60 * 24); } - /** - * @return OC_Router - */ - public static function getRouter() { - if (!isset(OC::$router)) { - OC::$router = new OC_Router(); - OC::$router->loadRoutes(); - } - - return OC::$router; - } - - public static function loadAppClassPaths() { foreach (OC_APP::getEnabledApps() as $app) { $file = OC_App::getAppPath($app) . '/appinfo/classpath.php'; @@ -420,6 +436,7 @@ class OC { self::$loader->registerPrefix('Symfony\\Component\\Routing', 'symfony/routing'); self::$loader->registerPrefix('Symfony\\Component\\Console', 'symfony/console'); self::$loader->registerPrefix('Patchwork', '3rdparty'); + self::$loader->registerPrefix('Pimple', '3rdparty/Pimple'); spl_autoload_register(array(self::$loader, 'load')); // set some stuff @@ -528,6 +545,7 @@ class OC { self::checkConfig(); self::checkInstalled(); self::checkSSL(); + self::addSecurityHeaders(); $errors = OC_Util::checkServer(); if (count($errors) > 0) { @@ -537,6 +555,7 @@ class OC { echo $error['hint'] . "\n\n"; } } else { + OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE); OC_Template::printGuestPage('', 'error', array('errors' => $errors)); } exit; @@ -554,26 +573,10 @@ class OC { OC_User::useBackend(new OC_User_Database()); OC_Group::useBackend(new OC_Group_Database()); - if (isset($_SERVER['PHP_AUTH_USER']) && self::$session->exists('loginname') - && $_SERVER['PHP_AUTH_USER'] !== self::$session->get('loginname')) { - $sessionUser = self::$session->get('loginname'); - $serverUser = $_SERVER['PHP_AUTH_USER']; - OC_Log::write('core', - "Session loginname ($sessionUser) doesn't match SERVER[PHP_AUTH_USER] ($serverUser).", - OC_Log::WARN); - OC_User::logout(); - } - - // Load Apps - // This includes plugins for users and filesystems as well - global $RUNTIME_NOAPPS; - global $RUNTIME_APPTYPES; - if (!$RUNTIME_NOAPPS && !self::checkUpgrade(false)) { - if ($RUNTIME_APPTYPES) { - OC_App::loadApps($RUNTIME_APPTYPES); - } else { - OC_App::loadApps(); - } + // Load minimum set of apps - which is filesystem, authentication and logging + if (!self::checkUpgrade(false)) { + OC_App::loadApps(array('authentication')); + OC_App::loadApps(array('filesystem', 'logging')); } //setup extra user backends @@ -661,7 +664,10 @@ class OC { */ public static function registerPreviewHooks() { OC_Hook::connect('OC_Filesystem', 'post_write', 'OC\Preview', 'post_write'); - OC_Hook::connect('OC_Filesystem', 'delete', 'OC\Preview', 'post_delete'); + OC_Hook::connect('OC_Filesystem', 'preDelete', 'OC\Preview', 'prepare_delete_files'); + OC_Hook::connect('\OCP\Versions', 'preDelete', 'OC\Preview', 'prepare_delete'); + OC_Hook::connect('\OCP\Trashbin', 'preDelete', 'OC\Preview', 'prepare_delete'); + OC_Hook::connect('OC_Filesystem', 'delete', 'OC\Preview', 'post_delete_files'); OC_Hook::connect('\OCP\Versions', 'delete', 'OC\Preview', 'post_delete'); OC_Hook::connect('\OCP\Trashbin', 'delete', 'OC\Preview', 'post_delete'); } @@ -671,10 +677,10 @@ class OC { */ public static function registerShareHooks() { if (\OC_Config::getValue('installed')) { - OC_Hook::connect('OC_User', 'post_deleteUser', 'OCP\Share', 'post_deleteUser'); - OC_Hook::connect('OC_User', 'post_addToGroup', 'OCP\Share', 'post_addToGroup'); - OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OCP\Share', 'post_removeFromGroup'); - OC_Hook::connect('OC_User', 'post_deleteGroup', 'OCP\Share', 'post_deleteGroup'); + OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share\Hooks', 'post_deleteUser'); + OC_Hook::connect('OC_User', 'post_addToGroup', 'OC\Share\Hooks', 'post_addToGroup'); + OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OC\Share\Hooks', 'post_removeFromGroup'); + OC_Hook::connect('OC_User', 'post_deleteGroup', 'OC\Share\Hooks', 'post_deleteGroup'); } } @@ -682,6 +688,7 @@ class OC { * @brief Handle the request */ public static function handleRequest() { + $l = \OC_L10N::get('lib'); // load all the classpaths from the enabled apps so they are available // in the routing files of each app OC::loadAppClassPaths(); @@ -693,14 +700,32 @@ class OC { exit(); } + $host = OC_Request::insecureServerHost(); + // if the host passed in headers isn't trusted + if (!OC::$CLI + // overwritehost is always trusted + && OC_Request::getOverwriteHost() === null + && !OC_Request::isTrustedDomain($host)) { + + header('HTTP/1.1 400 Bad Request'); + header('Status: 400 Bad Request'); + OC_Template::printErrorPage( + $l->t('You are accessing the server from an untrusted domain.'), + $l->t('Please contact your administrator. If you are an administrator of this instance, configure the "trusted_domain" setting in config/config.php. An example configuration is provided in config/config.sample.php.') + ); + return; + } + $request = OC_Request::getPathInfo(); if (substr($request, -3) !== '.js') { // we need these files during the upgrade self::checkMaintenanceMode(); self::checkUpgrade(); } - // Test it the user is already authenticated using Apaches AuthType Basic... very usable in combination with LDAP - OC::tryBasicAuthLogin(); + if (!OC_User::isLoggedIn()) { + // Test it the user is already authenticated using Apaches AuthType Basic... very usable in combination with LDAP + OC::tryBasicAuthLogin(); + } if (!self::$CLI and (!isset($_GET["logout"]) or ($_GET["logout"] !== 'true'))) { try { @@ -708,7 +733,7 @@ class OC { OC_App::loadApps(); } self::checkSingleUserMode(); - OC::getRouter()->match(OC_Request::getRawPathInfo()); + OC::$server->getRouter()->match(OC_Request::getRawPathInfo()); return; } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { //header('HTTP/1.0 404 Not Found'); @@ -751,8 +776,18 @@ class OC { if (isset($_COOKIE['oc_token'])) { OC_Preferences::deleteKey(OC_User::getUser(), 'login_token', $_COOKIE['oc_token']); } + if (isset($_SERVER['PHP_AUTH_USER'])) { + if (isset($_COOKIE['oc_ignore_php_auth_user'])) { + // Ignore HTTP Authentication for 5 more mintues. + setcookie('oc_ignore_php_auth_user', $_SERVER['PHP_AUTH_USER'], time() + 300, OC::$WEBROOT.(empty(OC::$WEBROOT) ? '/' : '')); + } elseif ($_SERVER['PHP_AUTH_USER'] === self::$session->get('loginname')) { + // Ignore HTTP Authentication to allow a different user to log in. + setcookie('oc_ignore_php_auth_user', $_SERVER['PHP_AUTH_USER'], 0, OC::$WEBROOT.(empty(OC::$WEBROOT) ? '/' : '')); + } + } OC_User::logout(); - header("Location: " . OC::$WEBROOT . '/'); + // redirect to webroot and add slash if webroot is empty + header("Location: " . OC::$WEBROOT.(empty(OC::$WEBROOT) ? '/' : '')); } else { if (is_null($file)) { $param['file'] = 'index.php'; @@ -770,6 +805,11 @@ class OC { self::handleLogin(); } + /** + * Load a PHP file belonging to the specified application + * @param array $param The application and file to load + * @return bool Whether the file has been found (will return 404 and false if not) + */ public static function loadAppScriptFile($param) { OC_App::loadApps(); $app = $param['app']; @@ -812,6 +852,10 @@ class OC { OC_Util::displayLoginPage(array_unique($error)); } + /** + * Remove outdated and therefore invalid tokens for a user + * @param string $user + */ protected static function cleanupLoginTokens($user) { $cutoff = time() - OC_Config::getValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); $tokens = OC_Preferences::getKeys($user, 'login_token'); @@ -823,6 +867,10 @@ class OC { } } + /** + * Try to login a user via HTTP authentication + * @return bool|void + */ protected static function tryApacheAuth() { $return = OC_User::handleApacheAuth(); @@ -837,6 +885,10 @@ class OC { return is_null($return) ? false : true; } + /** + * Try to login a user using the remember me cookie. + * @return bool Whether the provided cookie was valid + */ protected static function tryRememberLogin() { if (!isset($_COOKIE["oc_remember_login"]) || !isset($_COOKIE["oc_token"]) @@ -846,7 +898,7 @@ class OC { ) { return false; } - OC_App::loadApps(array('authentication')); + if (defined("DEBUG") && DEBUG) { OC_Log::write('core', 'Trying to login from cookie', OC_Log::DEBUG); } @@ -878,6 +930,10 @@ class OC { return true; } + /** + * Tries to login a user using the formbased authentication + * @return bool|void + */ protected static function tryFormLogin() { if (!isset($_POST["user"]) || !isset($_POST['password'])) { return false; @@ -912,13 +968,18 @@ class OC { return true; } + /** + * Try to login a user using HTTP authentication. + * @return bool + */ protected static function tryBasicAuthLogin() { if (!isset($_SERVER["PHP_AUTH_USER"]) || !isset($_SERVER["PHP_AUTH_PW"]) + || (isset($_COOKIE['oc_ignore_php_auth_user']) && $_COOKIE['oc_ignore_php_auth_user'] === $_SERVER['PHP_AUTH_USER']) ) { return false; } - OC_App::loadApps(array('authentication')); + if (OC_User::login($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])) { //OC_Log::write('core',"Logged in with HTTP Authentication", OC_Log::DEBUG); OC_User::unsetMagicInCookie(); @@ -929,12 +990,11 @@ class OC { } -// define runtime variables - unless this already has been done -if (!isset($RUNTIME_NOAPPS)) { - $RUNTIME_NOAPPS = false; -} - if (!function_exists('get_temp_dir')) { + /** + * Get the temporary dir to store uploaded data + * @return null|string Path to the temporary directory or null + */ function get_temp_dir() { if ($temp = ini_get('upload_tmp_dir')) return $temp; if ($temp = getenv('TMP')) return $temp; @@ -952,4 +1012,3 @@ if (!function_exists('get_temp_dir')) { } OC::init(); - diff --git a/lib/l10n/af_ZA.php b/lib/l10n/af_ZA.php index d6bf5771e8d..34d750ab65d 100644 --- a/lib/l10n/af_ZA.php +++ b/lib/l10n/af_ZA.php @@ -5,10 +5,11 @@ $TRANSLATIONS = array( "Settings" => "Instellings", "Users" => "Gebruikers", "Admin" => "Admin", +"Unknown filetype" => "Onbekende leertipe", "web services under your control" => "webdienste onder jou beheer", "_%n minute ago_::_%n minutes ago_" => array("",""), -"_%n hour ago_::_%n hours ago_" => array("",""), -"_%n day go_::_%n days ago_" => array("",""), -"_%n month ago_::_%n months ago_" => array("","") +"_%n hour ago_::_%n hours ago_" => array("","%n ure gelede"), +"_%n day go_::_%n days ago_" => array("","%n dae gelede"), +"_%n month ago_::_%n months ago_" => array("","%n maande gelede") ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/am_ET.php b/lib/l10n/am_ET.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/am_ET.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/ar.php b/lib/l10n/ar.php index 6870c549940..c8b51a3bb7e 100644 --- a/lib/l10n/ar.php +++ b/lib/l10n/ar.php @@ -1,18 +1,29 @@ <?php $TRANSLATIONS = array( +"App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "برنامج \"%s\" لا يمكن تثبيته بسبب انه لا يتناسب مع الاصدار الخاص بـ ownCloud.", +"No app name specified" => "لا يوجد برنامج بهذا الاسم", "Help" => "المساعدة", "Personal" => "شخصي", "Settings" => "إعدادات", "Users" => "المستخدمين", "Admin" => "المدير", +"Failed to upgrade \"%s\"." => "خطا في ترقية \"%s\".", +"Unknown filetype" => "نوع الملف غير معروف", +"Invalid image" => "الصورة غير صالحة", "web services under your control" => "خدمات الشبكة تحت سيطرتك", "ZIP download is turned off." => "تحميل ملفات ZIP متوقف", "Files need to be downloaded one by one." => "الملفات بحاجة الى ان يتم تحميلها واحد تلو الاخر", "Back to Files" => "العودة الى الملفات", "Selected files too large to generate zip file." => "الملفات المحددة كبيرة جدا ليتم ضغطها في ملف zip", +"No source specified when installing app" => "لم يتم تحديد المصدر عن تثبيت البرنامج", +"Archives of type %s are not supported" => "الأرشيفات من نوع %s غير مدعومة", +"App does not provide an info.xml file" => "التطبيق لا يتوفر على ملف info.xml", +"App directory already exists" => "مجلد التطبيق موجود مسبقا", +"Can't create app folder. Please fix permissions. %s" => "لا يمكن إنشاء مجلد التطبيق. يرجى تعديل الصلاحيات. %s", "Application is not enabled" => "التطبيق غير مفعّل", "Authentication error" => "لم يتم التأكد من الشخصية بنجاح", "Token expired. Please reload page." => "انتهت صلاحية الكلمة , يرجى اعادة تحميل الصفحة", +"Unknown user" => "المستخدم غير معروف", "Files" => "الملفات", "Text" => "معلومات إضافية", "Images" => "صور", @@ -21,13 +32,12 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات", "MS SQL username and/or password not valid: %s" => "اسم المستخدم و/أو كلمة المرور لنظام MS SQL غير صحيح : %s", "You need to enter either an existing account or the administrator." => "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", -"MySQL username and/or password not valid" => "اسم المستخدم و/أو كلمة المرور لنظام MySQL غير صحيح", +"MySQL/MariaDB username and/or password not valid" => "اسم مستخدم أو كلمة مرور MySQL/MariaDB غير صحيحين", "DB Error: \"%s\"" => "خطأ في قواعد البيانات : \"%s\"", "Offending command was: \"%s\"" => "الأمر المخالف كان : \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "أسم المستخدم '%s'@'localhost' الخاص بـ MySQL موجود مسبقا", -"Drop this user from MySQL" => "احذف اسم المستخدم هذا من الـ MySQL", -"MySQL user '%s'@'%%' already exists" => "أسم المستخدم '%s'@'%%' الخاص بـ MySQL موجود مسبقا", -"Drop this user from MySQL." => "احذف اسم المستخدم هذا من الـ MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "مستخدم MySQL/MariaDB '%s'@'localhost' موجود مسبقا", +"Drop this user from MySQL/MariaDB." => "حذف هذا المستخدم من MySQL/MariaDB", +"Oracle connection could not be established" => "لم تنجح محاولة اتصال Oracle", "Oracle username and/or password not valid" => "اسم المستخدم و/أو كلمة المرور لنظام Oracle غير صحيح", "Offending command was: \"%s\", name: %s, password: %s" => "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s", "PostgreSQL username and/or password not valid" => "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة", @@ -35,6 +45,7 @@ $TRANSLATIONS = array( "Set an admin password." => "اعداد كلمة مرور للمدير", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "اعدادات خادمك غير صحيحة بشكل تسمح لك بمزامنة ملفاتك وذلك بسبب أن واجهة WebDAV تبدو معطلة", "Please double check the <a href='%s'>installation guides</a>." => "الرجاء التحقق من <a href='%s'>دليل التنصيب</a>.", +"%s shared »%s« with you" => "%s شارك »%s« معك", "Could not find category \"%s\"" => "تعذر العثور على المجلد \"%s\"", "seconds ago" => "منذ ثواني", "_%n minute ago_::_%n minutes ago_" => array("","","","","",""), @@ -45,6 +56,8 @@ $TRANSLATIONS = array( "last month" => "الشهر الماضي", "_%n month ago_::_%n months ago_" => array("","","","","",""), "last year" => "السنةالماضية", -"years ago" => "سنة مضت" +"years ago" => "سنة مضت", +"A valid username must be provided" => "يجب ادخال اسم مستخدم صحيح", +"A valid password must be provided" => "يجب ادخال كلمة مرور صحيحة" ); $PLURAL_FORMS = "nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"; diff --git a/lib/l10n/ast.php b/lib/l10n/ast.php new file mode 100644 index 00000000000..219317c19b9 --- /dev/null +++ b/lib/l10n/ast.php @@ -0,0 +1,30 @@ +<?php +$TRANSLATIONS = array( +"Personal" => "Personal", +"Settings" => "Axustes", +"Users" => "Usuarios", +"Failed to upgrade \"%s\"." => "Fallu al anovar \"%s\".", +"Invalid image" => "Imaxe inválida", +"Application is not enabled" => "L'aplicación nun ta habilitada", +"Authentication error" => "Fallu d'autenticación", +"Files" => "Ficheros", +"Text" => "Testu", +"Images" => "Imaxes", +"Set an admin username." => "Afitar nome d'usuariu p'almin", +"Set an admin password." => "Afitar contraseña p'almin", +"Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "El sirvidor web entá nun ta configurado pa permitir la sincronización de ficheros yá que la interface WebDAV paez nun tar funcionando.", +"Could not find category \"%s\"" => "Nun pudo alcontrase la estaya \"%s.\"", +"seconds ago" => "fai segundos", +"_%n minute ago_::_%n minutes ago_" => array("","fai %n minutos"), +"_%n hour ago_::_%n hours ago_" => array("","fai %n hores"), +"today" => "güei", +"yesterday" => "ayeri", +"_%n day go_::_%n days ago_" => array("","fai %n díes"), +"last month" => "mes caberu", +"_%n month ago_::_%n months ago_" => array("","fai %n meses"), +"last year" => "añu caberu", +"years ago" => "fai años", +"A valid username must be provided" => "Tien d'apurrise un nome d'usuariu válidu", +"A valid password must be provided" => "Tien d'apurrise una contraseña válida" +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/az.php b/lib/l10n/az.php index e7b09649a24..15f78e0bce6 100644 --- a/lib/l10n/az.php +++ b/lib/l10n/az.php @@ -1,8 +1,8 @@ <?php $TRANSLATIONS = array( -"_%n minute ago_::_%n minutes ago_" => array(""), -"_%n hour ago_::_%n hours ago_" => array(""), -"_%n day go_::_%n days ago_" => array(""), -"_%n month ago_::_%n months ago_" => array("") +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") ); -$PLURAL_FORMS = "nplurals=1; plural=0;"; +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/bg_BG.php b/lib/l10n/bg_BG.php index c9de3d64d89..f29120e60e9 100644 --- a/lib/l10n/bg_BG.php +++ b/lib/l10n/bg_BG.php @@ -21,13 +21,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s, не можете да ползвате точки в името на базата от данни", "MS SQL username and/or password not valid: %s" => "Невалидно MS SQL потребителско име и/или парола: %s", "You need to enter either an existing account or the administrator." => "Необходимо е да влезете в всъществуващ акаунт или като администратора", -"MySQL username and/or password not valid" => "Невалидно MySQL потребителско име и/или парола", "DB Error: \"%s\"" => "Грешка в базата от данни: \"%s\"", "Offending command was: \"%s\"" => "Проблемната команда беше: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL потребителят '%s'@'localhost' вече съществува", -"Drop this user from MySQL" => "Изтриване на потребителя от MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL потребителят '%s'@'%%' вече съществува.", -"Drop this user from MySQL." => "Изтриване на потребителя от MySQL.", "Oracle connection could not be established" => "Oracle връзка не можа да се осъществи", "Oracle username and/or password not valid" => "Невалидно Oracle потребителско име и/или парола", "Offending command was: \"%s\", name: %s, password: %s" => "Проблемната команда беше: \"%s\", име: %s, парола: %s", diff --git a/lib/l10n/ca.php b/lib/l10n/ca.php index 4755392d271..22d8c457f5e 100644 --- a/lib/l10n/ca.php +++ b/lib/l10n/ca.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tipus de fitxer desconegut", "Invalid image" => "Imatge no vàlida", "web services under your control" => "controleu els vostres serveis web", -"cannot open \"%s\"" => "no es pot obrir \"%s\"", "ZIP download is turned off." => "La baixada en ZIP està desactivada.", "Files need to be downloaded one by one." => "Els fitxers s'han de baixar d'un en un.", "Back to Files" => "Torna a Fitxers", @@ -32,6 +31,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "L'aplicació no està habilitada", "Authentication error" => "Error d'autenticació", "Token expired. Please reload page." => "El testimoni ha expirat. Torneu a carregar la pàgina.", +"Unknown user" => "Usuari desconegut", "Files" => "Fitxers", "Text" => "Text", "Images" => "Imatges", @@ -40,13 +40,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s no podeu usar punts en el nom de la base de dades", "MS SQL username and/or password not valid: %s" => "Nom d'usuari i/o contrasenya MS SQL no vàlids: %s", "You need to enter either an existing account or the administrator." => "Heu d'escriure un compte existent o el d'administrador.", -"MySQL username and/or password not valid" => "Nom d'usuari i/o contrasenya MySQL no vàlids", +"MySQL/MariaDB username and/or password not valid" => "El nom d'usuari i/o la contrasenya de MySQL/MariaDB no són vàlids", "DB Error: \"%s\"" => "Error DB: \"%s\"", "Offending command was: \"%s\"" => "L'ordre en conflicte és: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "L'usuari MySQL '%s'@'localhost' ja existeix.", -"Drop this user from MySQL" => "Elimina aquest usuari de MySQL", -"MySQL user '%s'@'%%' already exists" => "L'usuari MySQL '%s'@'%%' ja existeix", -"Drop this user from MySQL." => "Elimina aquest usuari de MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "L'usuari MySQL/MariaDB '%s'@'localhost' ja existeix.", +"Drop this user from MySQL/MariaDB" => "Esborreu aquest usuari de MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "L'usuari MySQL/MariaDB '%s'@'%%' ja existeix", +"Drop this user from MySQL/MariaDB." => "Esborreu aquest usuari de MySQL/MariaDB.", "Oracle connection could not be established" => "No s'ha pogut establir la connexió Oracle", "Oracle username and/or password not valid" => "Nom d'usuari i/o contrasenya Oracle no vàlids", "Offending command was: \"%s\", name: %s, password: %s" => "L'ordre en conflicte és: \"%s\", nom: %s, contrasenya: %s", @@ -55,6 +55,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Establiu una contrasenya per l'administrador.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "El servidor web no està configurat correctament per permetre la sincronització de fitxers perquè la interfície WebDAV sembla no funcionar correctament.", "Please double check the <a href='%s'>installation guides</a>." => "Comproveu les <a href='%s'>guies d'instal·lació</a>.", +"%s shared »%s« with you" => "%s ha compartit »%s« amb tu", "Could not find category \"%s\"" => "No s'ha trobat la categoria \"%s\"", "seconds ago" => "segons enrere", "_%n minute ago_::_%n minutes ago_" => array("fa %n minut","fa %n minuts"), @@ -65,6 +66,10 @@ $TRANSLATIONS = array( "last month" => "el mes passat", "_%n month ago_::_%n months ago_" => array("fa %n mes","fa %n mesos"), "last year" => "l'any passat", -"years ago" => "anys enrere" +"years ago" => "anys enrere", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "Només els caràcters següents estan permesos en el nom d'usuari: \"a-z\", \"A-Z\", \"0-9\" i \"_.@-\"", +"A valid username must be provided" => "Heu de facilitar un nom d'usuari vàlid", +"A valid password must be provided" => "Heu de facilitar una contrasenya vàlida", +"The username is already being used" => "El nom d'usuari ja està en ús" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/cs_CZ.php b/lib/l10n/cs_CZ.php index df3a47b5ae3..6fef446d8c4 100644 --- a/lib/l10n/cs_CZ.php +++ b/lib/l10n/cs_CZ.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Přistupujete na server z nedůvěryhodné domény.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "Aplikace \"%s\" nemůže být nainstalována, protože není kompatibilní s touto verzí ownCloud.", "No app name specified" => "Nebyl zadan název aplikace", "Help" => "Nápověda", @@ -11,7 +12,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Neznámý typ souboru", "Invalid image" => "Chybný obrázek", "web services under your control" => "webové služby pod Vaší kontrolou", -"cannot open \"%s\"" => "nelze otevřít \"%s\"", "ZIP download is turned off." => "Stahování v ZIPu je vypnuto.", "Files need to be downloaded one by one." => "Soubory musí být stahovány jednotlivě.", "Back to Files" => "Zpět k souborům", @@ -32,6 +32,7 @@ $TRANSLATIONS = array( "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", "Files" => "Soubory", "Text" => "Text", "Images" => "Obrázky", @@ -40,13 +41,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "V názvu databáze %s nesmíte používat tečky.", "MS SQL username and/or password not valid: %s" => "Uživatelské jméno či heslo MSSQL není platné: %s", "You need to enter either an existing account or the administrator." => "Musíte zadat existující účet či správce.", -"MySQL username and/or password not valid" => "Uživatelské jméno či heslo MySQL není platné", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB uživatelské jméno a/nebo heslo je neplatné", "DB Error: \"%s\"" => "Chyba databáze: \"%s\"", "Offending command was: \"%s\"" => "Příslušný příkaz byl: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Uživatel '%s'@'localhost' již v MySQL existuje.", -"Drop this user from MySQL" => "Zrušte tohoto uživatele z MySQL", -"MySQL user '%s'@'%%' already exists" => "Uživatel '%s'@'%%' již v MySQL existuje", -"Drop this user from MySQL." => "Zrušte tohoto uživatele z MySQL", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB uživatel '%s'@'localhost' již existuje.", +"Drop this user from MySQL/MariaDB" => "Smazat tohoto uživatele z MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB uživatel '%s'@'%%' již existuje", +"Drop this user from MySQL/MariaDB." => "Smazat tohoto uživatele z MySQL/MariaDB.", "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é", "Offending command was: \"%s\", name: %s, password: %s" => "Příslušný příkaz byl: \"%s\", jméno: %s, heslo: %s", @@ -55,6 +56,16 @@ $TRANSLATIONS = array( "Set an admin password." => "Zadejte heslo správce.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Váš webový server není správně nastaven pro umožnění synchronizace, rozhraní WebDAV se zdá být rozbité.", "Please double check the <a href='%s'>installation guides</a>." => "Zkonzultujte, prosím, <a href='%s'>průvodce instalací</a>.", +"%s shared »%s« with you" => "%s s vámi sdílí »%s«", +"Sharing %s failed, because the file does not exist" => "Sdílení %s selhalo, protože soubor neexistuje", +"Sharing %s failed, because the user %s is the item owner" => "Sdílení položky %s selhalo, protože uživatel %s je jejím vlastníkem", +"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 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", +"Sharing %s failed, because sharing with links is not allowed" => "Sdílení položky %s selhalo, protože sdílení pomocí linků není povoleno", +"Setting permissions for %s failed, because the item was not found" => "Nastavení práv pro %s selhalo, protože položka nebyla nalezena", "Could not find category \"%s\"" => "Nelze nalézt kategorii \"%s\"", "seconds ago" => "před pár sekundami", "_%n minute ago_::_%n minutes ago_" => array("před %n minutou","před %n minutami","před %n minutami"), @@ -65,6 +76,10 @@ $TRANSLATIONS = array( "last month" => "minulý měsíc", "_%n month ago_::_%n months ago_" => array("před %n měsícem","před %n měsíci","před %n měsíci"), "last year" => "minulý rok", -"years ago" => "před lety" +"years ago" => "před lety", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "Pouze následující znaky jsou povoleny v uživatelském jménu: \"a-z\", \"A-Z\", \"0-9\" a \"_.@-\"", +"A valid username must be provided" => "Musíte zadat platné uživatelské jméno", +"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" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/lib/l10n/cy_GB.php b/lib/l10n/cy_GB.php index 0a52f5df776..57cb02653f5 100644 --- a/lib/l10n/cy_GB.php +++ b/lib/l10n/cy_GB.php @@ -21,13 +21,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s does dim hawl defnyddio dot yn enw'r gronfa ddata", "MS SQL username and/or password not valid: %s" => "Enw a/neu gyfrinair MS SQL annilys: %s", "You need to enter either an existing account or the administrator." => "Rhaid i chi naill ai gyflwyno cyfrif presennol neu'r gweinyddwr.", -"MySQL username and/or password not valid" => "Enw a/neu gyfrinair MySQL annilys", "DB Error: \"%s\"" => "Gwall DB: \"%s\"", "Offending command was: \"%s\"" => "Y gorchymyn wnaeth beri tramgwydd oedd: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Defnyddiwr MySQL '%s'@'localhost' yn bodoli eisoes.", -"Drop this user from MySQL" => "Gollwng y defnyddiwr hwn o MySQL", -"MySQL user '%s'@'%%' already exists" => "Defnyddiwr MySQL '%s'@'%%' eisoes yn bodoli", -"Drop this user from MySQL." => "Gollwng y defnyddiwr hwn o MySQL.", "Oracle username and/or password not valid" => "Enw a/neu gyfrinair Oracle annilys", "Offending command was: \"%s\", name: %s, password: %s" => "Y gorchymyn wnaeth beri tramgwydd oedd: \"%s\", enw: %s, cyfrinair: %s", "PostgreSQL username and/or password not valid" => "Enw a/neu gyfrinair PostgreSQL annilys", diff --git a/lib/l10n/da.php b/lib/l10n/da.php index 65eb7466b6a..3be45001030 100644 --- a/lib/l10n/da.php +++ b/lib/l10n/da.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Ukendt filtype", "Invalid image" => "Ugyldigt billede", "web services under your control" => "Webtjenester under din kontrol", -"cannot open \"%s\"" => "Kan ikke åbne \"%s\"", "ZIP download is turned off." => "ZIP-download er slået fra.", "Files need to be downloaded one by one." => "Filer skal downloades en for en.", "Back to Files" => "Tilbage til Filer", @@ -32,6 +31,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "Programmet er ikke aktiveret", "Authentication error" => "Adgangsfejl", "Token expired. Please reload page." => "Adgang er udløbet. Genindlæs siden.", +"Unknown user" => "Ukendt bruger", "Files" => "Filer", "Text" => "SMS", "Images" => "Billeder", @@ -40,13 +40,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s du må ikke bruge punktummer i databasenavnet.", "MS SQL username and/or password not valid: %s" => "MS SQL brugernavn og/eller adgangskode ikke er gyldigt: %s", "You need to enter either an existing account or the administrator." => "Du bliver nødt til at indtaste en eksisterende bruger eller en administrator.", -"MySQL username and/or password not valid" => "MySQL brugernavn og/eller kodeord er ikke gyldigt.", +"MySQL/MariaDB username and/or password not valid" => "Ugyldigt MySQL/MariaDB brugernavn og/eller kodeord ", "DB Error: \"%s\"" => "Databasefejl: \"%s\"", "Offending command was: \"%s\"" => "Fejlende kommando var: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL brugeren '%s'@'localhost' eksisterer allerede.", -"Drop this user from MySQL" => "Slet denne bruger fra MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL brugeren '%s'@'%%' eksisterer allerede.", -"Drop this user from MySQL." => "Slet denne bruger fra MySQL", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB bruger '%s'@'localhost' eksistere allerede.", +"Drop this user from MySQL/MariaDB" => "Slet denne bruger fra MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB bruger '%s'@'%%' eksistere allerede", +"Drop this user from MySQL/MariaDB." => "Drop denne bruger fra MySQL/MariaDB.", "Oracle connection could not be established" => "Oracle forbindelsen kunne ikke etableres", "Oracle username and/or password not valid" => "Oracle brugernavn og/eller kodeord er ikke gyldigt.", "Offending command was: \"%s\", name: %s, password: %s" => "Fejlende kommando var: \"%s\", navn: %s, password: %s", @@ -55,6 +55,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Angiv et admin kodeord.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Din webserver er endnu ikke sat op til at tillade fil synkronisering fordi WebDAV grænsefladen virker ødelagt.", "Please double check the <a href='%s'>installation guides</a>." => "Dobbelttjek venligst <a href='%s'>installations vejledningerne</a>.", +"%s shared »%s« with you" => "%s delte »%s« med sig", "Could not find category \"%s\"" => "Kunne ikke finde kategorien \"%s\"", "seconds ago" => "sekunder siden", "_%n minute ago_::_%n minutes ago_" => array("%n minut siden","%n minutter siden"), @@ -65,6 +66,8 @@ $TRANSLATIONS = array( "last month" => "sidste måned", "_%n month ago_::_%n months ago_" => array("%n måned siden","%n måneder siden"), "last year" => "sidste år", -"years ago" => "år siden" +"years ago" => "år siden", +"A valid username must be provided" => "Et gyldigt brugernavn skal angives", +"A valid password must be provided" => "En gyldig adgangskode skal angives" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/de.php b/lib/l10n/de.php index b1045892fb1..6c518a8b843 100644 --- a/lib/l10n/de.php +++ b/lib/l10n/de.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Du greifst von einer nicht vertrauenswürdigen Domain auf den Server zu.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Bitte kontaktiere Deinen Administrator. Wenn du aktuell Administrator dieser Instanz bist, konfiguriere bitte die \"trusted_domain\" - Einstellung in config/config.php. Eine Beispielkonfiguration wird unter config/config.sample.php bereit gestellt.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "Applikation \"%s\" kann nicht installiert werden, da sie mit dieser ownCloud Version nicht kompatibel ist.", "No app name specified" => "Es wurde kein Applikation-Name angegeben", "Help" => "Hilfe", @@ -11,7 +13,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Unbekannter Dateityp", "Invalid image" => "Ungültiges Bild", "web services under your control" => "Web-Services unter Deiner Kontrolle", -"cannot open \"%s\"" => "Öffnen von \"%s\" fehlgeschlagen", "ZIP download is turned off." => "Der ZIP-Download ist deaktiviert.", "Files need to be downloaded one by one." => "Die Dateien müssen einzeln heruntergeladen werden.", "Back to Files" => "Zurück zu \"Dateien\"", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "Die Anwendung ist nicht aktiviert", "Authentication error" => "Fehler bei der Anmeldung", "Token expired. Please reload page." => "Token abgelaufen. Bitte lade die Seite neu.", +"Unknown user" => "Unbekannter Benutzer", "Files" => "Dateien", "Text" => "Text", "Images" => "Bilder", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s Der Datenbank-Name darf keine Punkte enthalten", "MS SQL username and/or password not valid: %s" => "MS SQL Benutzername und/oder Password ungültig: %s", "You need to enter either an existing account or the administrator." => "Du musst entweder ein existierendes Benutzerkonto oder das Administratoren-Konto angeben.", -"MySQL username and/or password not valid" => "MySQL Benutzername und/oder Passwort ungültig", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB Benutzername und/oder Passwort sind nicht gültig", "DB Error: \"%s\"" => "DB Fehler: \"%s\"", "Offending command was: \"%s\"" => "Fehlerhafter Befehl war: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL Benutzer '%s'@'localhost' existiert bereits.", -"Drop this user from MySQL" => "Lösche diesen Benutzer von MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL Benutzer '%s'@'%%' existiert bereits", -"Drop this user from MySQL." => "Lösche diesen Benutzer aus MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB Benutzer '%s'@'localhost' existiert bereits.", +"Drop this user from MySQL/MariaDB" => "Lösche diesen Benutzer von MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB Benutzer '%s'@'%%' existiert bereits", +"Drop this user from MySQL/MariaDB." => "Lösche diesen Benutzer von MySQL/MariaDB.", "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", "Offending command was: \"%s\", name: %s, password: %s" => "Fehlerhafter Befehl war: \"%s\", Name: %s, Passwort: %s", @@ -55,6 +57,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Setze Administrator Passwort", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Dein Web-Server ist noch nicht für Datei-Synchronisation bereit, weil die WebDAV-Schnittstelle vermutlich defekt ist.", "Please double check the <a href='%s'>installation guides</a>." => "Bitte prüfe die <a href='%s'>Installationsanleitungen</a>.", +"%s shared »%s« with you" => "%s teilte »%s« mit Dir", +"Sharing %s failed, because the file does not exist" => "Freigabe von %s fehlgeschlagen, da die Datei nicht existiert", +"Sharing %s failed, because the user %s is the item owner" => "Freigabe von %s fehlgeschlagen, da der Nutzer %s Besitzer des Objektes ist", +"Sharing %s failed, because the user %s does not exist" => "Freigabe von %s fehlgeschlagen, da der Nutzer %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 Nutzer %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 Objekt schon mit %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", +"Sharing %s failed, because sharing with links is not allowed" => "Freigabe von %s fehlgeschlagen, da das Teilen von Verknüpfungen nicht erlaubt ist", +"Share type %s is not valid for %s" => "Freigabetyp %s ist nicht gültig für %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 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 Objekt nicht gefunden wurde", +"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 %s failed, because the user %s is the original sharer" => "Freigabe von %s fehlgeschlagen, da der Nutzer %s der offizielle Freigeber 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", "Could not find category \"%s\"" => "Die Kategorie \"%s\" konnte nicht gefunden werden.", "seconds ago" => "Gerade eben", "_%n minute ago_::_%n minutes ago_" => array("Vor %n Minute","Vor %n Minuten"), @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "Letzten Monat", "_%n month ago_::_%n months ago_" => array("Vor %n Monat","Vor %n Monaten"), "last year" => "Letztes Jahr", -"years ago" => "Vor Jahren" +"years ago" => "Vor Jahren", +"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", +"A valid password must be provided" => "Es muss ein gültiges Passwort angegeben werden", +"The username is already being used" => "Dieser Benutzername existiert bereits" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/de_AT.php b/lib/l10n/de_AT.php index 15f78e0bce6..18e8e8b51ba 100644 --- a/lib/l10n/de_AT.php +++ b/lib/l10n/de_AT.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"Personal" => "Persönlich", +"Settings" => "Einstellungen", "_%n minute ago_::_%n minutes ago_" => array("",""), "_%n hour ago_::_%n hours ago_" => array("",""), "_%n day go_::_%n days ago_" => array("",""), diff --git a/lib/l10n/de_CH.php b/lib/l10n/de_CH.php index 7325aad931e..e034cc3c209 100644 --- a/lib/l10n/de_CH.php +++ b/lib/l10n/de_CH.php @@ -9,7 +9,6 @@ $TRANSLATIONS = array( "Admin" => "Administrator", "Failed to upgrade \"%s\"." => "Konnte \"%s\" nicht aktualisieren.", "web services under your control" => "Web-Services unter Ihrer Kontrolle", -"cannot open \"%s\"" => "Öffnen von \"%s\" fehlgeschlagen", "ZIP download is turned off." => "Der ZIP-Download ist deaktiviert.", "Files need to be downloaded one by one." => "Die Dateien müssen einzeln heruntergeladen werden.", "Back to Files" => "Zurück zu \"Dateien\"", @@ -27,13 +26,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s Der Datenbank-Name darf keine Punkte enthalten", "MS SQL username and/or password not valid: %s" => "MS SQL Benutzername und/oder Passwort ungültig: %s", "You need to enter either an existing account or the administrator." => "Sie müssen entweder ein existierendes Benutzerkonto oder das Administratoren-Konto angeben.", -"MySQL username and/or password not valid" => "MySQL Benutzername und/oder Passwort ungültig", "DB Error: \"%s\"" => "DB Fehler: \"%s\"", "Offending command was: \"%s\"" => "Fehlerhafter Befehl war: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL Benutzer '%s'@'localhost' existiert bereits.", -"Drop this user from MySQL" => "Lösche diesen Benutzer aus MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL Benutzer '%s'@'%%' existiert bereits", -"Drop this user from MySQL." => "Lösche diesen Benutzer aus MySQL.", "Oracle connection could not be established" => "Die Oracle-Verbindung konnte nicht aufgebaut werden.", "Oracle username and/or password not valid" => "Oracle Benutzername und/oder Passwort ungültig", "Offending command was: \"%s\", name: %s, password: %s" => "Fehlerhafter Befehl war: \"%s\", Name: %s, Passwort: %s", @@ -42,6 +36,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Setze Administrator Passwort", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Ihr Web-Server ist noch nicht für eine Datei-Synchronisation konfiguriert, weil die WebDAV-Schnittstelle vermutlich defekt ist.", "Please double check the <a href='%s'>installation guides</a>." => "Bitte prüfen Sie die <a href='%s'>Installationsanleitungen</a>.", +"%s shared »%s« with you" => "%s teilt »%s« mit Ihnen", "Could not find category \"%s\"" => "Die Kategorie «%s» konnte nicht gefunden werden.", "seconds ago" => "Gerade eben", "_%n minute ago_::_%n minutes ago_" => array("","Vor %n Minuten"), @@ -52,6 +47,8 @@ $TRANSLATIONS = array( "last month" => "Letzten Monat", "_%n month ago_::_%n months ago_" => array("","Vor %n Monaten"), "last year" => "Letztes Jahr", -"years ago" => "Vor Jahren" +"years ago" => "Vor Jahren", +"A valid username must be provided" => "Es muss ein gültiger Benutzername angegeben werden", +"A valid password must be provided" => "Es muss ein gültiges Passwort angegeben werden" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/de_DE.php b/lib/l10n/de_DE.php index 1a1c9783f42..6e3ec141a21 100644 --- a/lib/l10n/de_DE.php +++ b/lib/l10n/de_DE.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Sie greifen von einer nicht vertrauenswürdigen Domain auf den Server zu.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Bitte kontaktieren Sie Ihren Administrator. Wenn Sie aktuell Administrator dieser Instanz sind, konfigurieren Sie bitte die \"trusted_domain\" - Einstellung in config/config.php. Eine Beispielkonfiguration wird unter config/config.sample.php bereit gestellt.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "Applikation \"%s\" kann nicht installiert werden, da sie mit dieser ownCloud Version nicht kompatibel ist.", "No app name specified" => "Es wurde kein Applikation-Name angegeben", "Help" => "Hilfe", @@ -11,7 +13,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Unbekannter Dateityp", "Invalid image" => "Ungültiges Bild", "web services under your control" => "Web-Services unter Ihrer Kontrolle", -"cannot open \"%s\"" => "Öffnen von \"%s\" fehlgeschlagen", "ZIP download is turned off." => "Der ZIP-Download ist deaktiviert.", "Files need to be downloaded one by one." => "Die Dateien müssen einzeln heruntergeladen werden.", "Back to Files" => "Zurück zu \"Dateien\"", @@ -22,7 +23,7 @@ $TRANSLATIONS = array( "No path specified when installing app from local file" => "Bei der Installation der Applikation aus einer lokalen Datei wurde kein Pfad angegeben", "Archives of type %s are not supported" => "Archive des Typs %s werden nicht unterstützt.", "Failed to open archive when installing app" => "Das Archiv konnte bei der Installation der Applikation nicht geöffnet werden", -"App does not provide an info.xml file" => "Die Applikation enthält keine info,xml Datei", +"App does not provide an info.xml file" => "Die Applikation enthält keine info.xml Datei", "App can't be installed because of not allowed code in the App" => "Die Applikation kann auf Grund von unerlaubten Code nicht installiert werden", "App can't be installed because it is not compatible with this version of ownCloud" => "Die Anwendung konnte nicht installiert werden, weil Sie nicht mit dieser Version von ownCloud kompatibel ist.", "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "Die Applikation konnte nicht installiert werden, da diese das <shipped>true</shipped> Tag beinhaltet und dieses, bei nicht mitausgelieferten Applikationen, nicht erlaubt ist ist", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "Die Anwendung ist nicht aktiviert", "Authentication error" => "Authentifizierungs-Fehler", "Token expired. Please reload page." => "Token abgelaufen. Bitte laden Sie die Seite neu.", +"Unknown user" => "Unbekannter Benutzer", "Files" => "Dateien", "Text" => "Text", "Images" => "Bilder", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s Der Datenbank-Name darf keine Punkte enthalten", "MS SQL username and/or password not valid: %s" => "MS SQL Benutzername und/oder Passwort ungültig: %s", "You need to enter either an existing account or the administrator." => "Sie müssen entweder ein existierendes Benutzerkonto oder das Administratoren-Konto angeben.", -"MySQL username and/or password not valid" => "MySQL Benutzername und/oder Passwort ungültig", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB Benutzername und/oder Passwort sind nicht gültig", "DB Error: \"%s\"" => "DB Fehler: \"%s\"", "Offending command was: \"%s\"" => "Fehlerhafter Befehl war: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL Benutzer '%s'@'localhost' existiert bereits.", -"Drop this user from MySQL" => "Lösche diesen Benutzer aus MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL Benutzer '%s'@'%%' existiert bereits", -"Drop this user from MySQL." => "Lösche diesen Benutzer aus MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB Benutzer '%s'@'localhost' existiert bereits.", +"Drop this user from MySQL/MariaDB" => "Löschen Sie diesen Benutzer von MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB Benutzer '%s'@'%%' existiert bereits", +"Drop this user from MySQL/MariaDB." => "Löschen Sie diesen Benutzer von MySQL/MariaDB.", "Oracle connection could not be established" => "Die Oracle-Verbindung konnte nicht aufgebaut werden.", "Oracle username and/or password not valid" => "Oracle Benutzername und/oder Passwort ungültig", "Offending command was: \"%s\", name: %s, password: %s" => "Fehlerhafter Befehl war: \"%s\", Name: %s, Passwort: %s", @@ -55,6 +57,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Setze Administrator Passwort", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Ihr Web-Server ist noch nicht für eine Datei-Synchronisation konfiguriert, weil die WebDAV-Schnittstelle vermutlich defekt ist.", "Please double check the <a href='%s'>installation guides</a>." => "Bitte prüfen Sie die <a href='%s'>Installationsanleitungen</a>.", +"%s shared »%s« with you" => "%s hat »%s« mit Ihnen geteilt", +"Sharing %s failed, because the file does not exist" => "Freigabe von %s fehlgeschlagen, da die Datei nicht existiert", +"Sharing %s failed, because the user %s is the item owner" => "Freigabe von %s fehlgeschlagen, da der Nutzer %s Besitzer des Objektes ist", +"Sharing %s failed, because the user %s does not exist" => "Freigabe von %s fehlgeschlagen, da der Nutzer %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 Nutzer %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 Objekt schon mit %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", +"Sharing %s failed, because sharing with links is not allowed" => "Freigabe von %s fehlgeschlagen, da das Teilen von Verknüpfungen nicht erlaubt ist", +"Share type %s is not valid for %s" => "Freigabetyp %s ist nicht gültig für %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 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 Objekt nicht gefunden wurde", +"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 %s failed, because the user %s is the original sharer" => "Freigabe von %s fehlgeschlagen, da der Nutzer %s der offizielle Freigeber 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", "Could not find category \"%s\"" => "Die Kategorie \"%s\" konnte nicht gefunden werden.", "seconds ago" => "Gerade eben", "_%n minute ago_::_%n minutes ago_" => array("Vor %n Minute","Vor %n Minuten"), @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "Letzten Monat", "_%n month ago_::_%n months ago_" => array("Vor %n Monat","Vor %n Monaten"), "last year" => "Letztes Jahr", -"years ago" => "Vor Jahren" +"years ago" => "Vor Jahren", +"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", +"A valid password must be provided" => "Es muss ein gültiges Passwort angegeben werden", +"The username is already being used" => "Der Benutzername existiert bereits" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/el.php b/lib/l10n/el.php index 7f7797bbc7a..109104e4061 100644 --- a/lib/l10n/el.php +++ b/lib/l10n/el.php @@ -6,12 +6,11 @@ $TRANSLATIONS = array( "Personal" => "Προσωπικά", "Settings" => "Ρυθμίσεις", "Users" => "Χρήστες", -"Admin" => "Διαχειριστής", +"Admin" => "Διαχείριση", "Failed to upgrade \"%s\"." => "Αποτυχία αναβάθμισης του \"%s\".", "Unknown filetype" => "Άγνωστος τύπος αρχείου", "Invalid image" => "Μη έγκυρη εικόνα", "web services under your control" => "υπηρεσίες δικτύου υπό τον έλεγχό σας", -"cannot open \"%s\"" => "αδυναμία ανοίγματος \"%s\"", "ZIP download is turned off." => "Η λήψη ZIP απενεργοποιήθηκε.", "Files need to be downloaded one by one." => "Τα αρχεία πρέπει να ληφθούν ένα-ένα.", "Back to Files" => "Πίσω στα Αρχεία", @@ -25,12 +24,14 @@ $TRANSLATIONS = array( "App does not provide an info.xml file" => "Η εφαρμογή δεν παρέχει αρχείο info.xml", "App can't be installed because of not allowed code in the App" => "Η εφαρμογή δεν μπορεί να εγκατασταθεί λόγω μη-επιτρεπόμενου κώδικα μέσα στην Εφαρμογή", "App can't be installed because it is not compatible with this version of ownCloud" => "Η εφαρμογή δεν μπορεί να εγκατασταθεί επειδή δεν είναι συμβατή με αυτή την έκδοση ownCloud", +"App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "Η εφαρμογή δεν μπορεί να εγκατασταθεί επειδή περιέχει την ετικέτα <shipped>σωστή</shipped> που δεν επιτρέπεται για μη-ενσωματωμένες εφαρμογές", "App can't be installed because the version in info.xml/version is not the same as the version reported from the app store" => "Η εφαρμογή δεν μπορεί να εγκατασταθεί επειδή η έκδοση στο info.xml/version δεν είναι η ίδια με την έκδοση που αναφέρεται στο κατάστημα εφαρμογών", "App directory already exists" => "Ο κατάλογος εφαρμογών υπάρχει ήδη", "Can't create app folder. Please fix permissions. %s" => "Δεν είναι δυνατόν να δημιουργηθεί ο φάκελος εφαρμογής. Παρακαλώ διορθώστε τις άδειες πρόσβασης. %s", "Application is not enabled" => "Δεν ενεργοποιήθηκε η εφαρμογή", "Authentication error" => "Σφάλμα πιστοποίησης", "Token expired. Please reload page." => "Το αναγνωριστικό έληξε. Παρακαλώ φορτώστε ξανά την σελίδα.", +"Unknown user" => "Άγνωστος χρήστης", "Files" => "Αρχεία", "Text" => "Κείμενο", "Images" => "Εικόνες", @@ -39,13 +40,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s μάλλον δεν χρησιμοποιείτε τελείες στο όνομα της βάσης δεδομένων", "MS SQL username and/or password not valid: %s" => "Το όνομα χρήστη και/ή ο κωδικός της MS SQL δεν είναι έγκυρα: %s", "You need to enter either an existing account or the administrator." => "Χρειάζεται να εισάγετε είτε έναν υπάρχον λογαριασμό ή του διαχειριστή.", -"MySQL username and/or password not valid" => "Μη έγκυρος χρήστης και/ή συνθηματικό της MySQL", +"MySQL/MariaDB username and/or password not valid" => "Μη έγκυρο όνομα χρήστη ή/και συνθηματικό της MySQL/MariaDB", "DB Error: \"%s\"" => "Σφάλμα Βάσης Δεδομένων: \"%s\"", "Offending command was: \"%s\"" => "Η εντολη παραβατικοτητας ηταν: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Υπάρχει ήδη ο χρήστης '%s'@'localhost' της MySQL.", -"Drop this user from MySQL" => "Απόρριψη αυτού του χρήστη από την MySQL", -"MySQL user '%s'@'%%' already exists" => "Ο χρήστης '%s'@'%%' της MySQL υπάρχει ήδη", -"Drop this user from MySQL." => "Απόρριψη αυτού του χρήστη από την MySQL", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "Υπάρχει ήδη ο χρήστης '%s'@'localhost' της MySQL/MariaDB", +"Drop this user from MySQL/MariaDB" => "Κατάργηση του χρήστη από MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "Υπάρχει ήδη ο χρήστης '%s'@'%%' της MySQL/MariaDB", +"Drop this user from MySQL/MariaDB." => "Κατάργηση του χρήστη από MySQL/MariaDB.", "Oracle connection could not be established" => "Αδυναμία σύνδεσης Oracle", "Oracle username and/or password not valid" => "Μη έγκυρος χρήστης και/ή συνθηματικό της Oracle", "Offending command was: \"%s\", name: %s, password: %s" => "Η εντολη παραβατικοτητας ηταν: \"%s\", ονομα: %s, κωδικος: %s", @@ -54,6 +55,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Εισάγετε συνθηματικό διαχειριστή.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Ο διακομιστής σας δεν έχει ρυθμιστεί κατάλληλα ώστε να επιτρέπει τον συγχρονισμό αρχείων γιατί η διεπαφή WebDAV πιθανόν να είναι κατεστραμμένη.", "Please double check the <a href='%s'>installation guides</a>." => "Ελέγξτε ξανά τις <a href='%s'>οδηγίες εγκατάστασης</a>.", +"%s shared »%s« with you" => "Ο %s διαμοιράστηκε μαζί σας το »%s«", "Could not find category \"%s\"" => "Αδυναμία εύρεσης κατηγορίας \"%s\"", "seconds ago" => "δευτερόλεπτα πριν", "_%n minute ago_::_%n minutes ago_" => array("","%n λεπτά πριν"), @@ -64,6 +66,8 @@ $TRANSLATIONS = array( "last month" => "τελευταίο μήνα", "_%n month ago_::_%n months ago_" => array("","%n μήνες πριν"), "last year" => "τελευταίο χρόνο", -"years ago" => "χρόνια πριν" +"years ago" => "χρόνια πριν", +"A valid username must be provided" => "Πρέπει να δοθεί έγκυρο όνομα χρήστη", +"A valid password must be provided" => "Πρέπει να δοθεί έγκυρο συνθηματικό" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/en_GB.php b/lib/l10n/en_GB.php index e2e8ee2e541..30245d1dd46 100644 --- a/lib/l10n/en_GB.php +++ b/lib/l10n/en_GB.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "You are accessing the server from an untrusted domain.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "App \"%s\" can't be installed because it is not compatible with this version of ownCloud.", "No app name specified" => "No app name specified", "Help" => "Help", @@ -11,7 +13,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Unknown filetype", "Invalid image" => "Invalid image", "web services under your control" => "web services under your control", -"cannot open \"%s\"" => "cannot open \"%s\"", "ZIP download is turned off." => "ZIP download is turned off.", "Files need to be downloaded one by one." => "Files need to be downloaded one by one.", "Back to Files" => "Back to Files", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "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", "Files" => "Files", "Text" => "Text", "Images" => "Images", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s you may not use dots in the database name", "MS SQL username and/or password not valid: %s" => "MS SQL username and/or password not valid: %s", "You need to enter either an existing account or the administrator." => "You need to enter either an existing account or the administrator.", -"MySQL username and/or password not valid" => "MySQL username and/or password not valid", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB username and/or password not valid", "DB Error: \"%s\"" => "DB Error: \"%s\"", "Offending command was: \"%s\"" => "Offending command was: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL user '%s'@'localhost' exists already.", -"Drop this user from MySQL" => "Drop this user from MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL user '%s'@'%%' already exists", -"Drop this user from MySQL." => "Drop this user from MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB user '%s'@'localhost' exists already.", +"Drop this user from MySQL/MariaDB" => "Drop this user from MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB user '%s'@'%%' already exists", +"Drop this user from MySQL/MariaDB." => "Drop this user from MySQL/MariaDB.", "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", "Offending command was: \"%s\", name: %s, password: %s" => "Offending command was: \"%s\", name: %s, password: %s", @@ -55,6 +57,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Set an admin password.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Your web server is not yet properly setup to allow files synchronisation because the WebDAV interface seems to be broken.", "Please double check the <a href='%s'>installation guides</a>." => "Please double check the <a href='%s'>installation guides</a>.", +"%s shared »%s« with you" => "%s shared \"%s\" with you", +"Sharing %s failed, because the file does not exist" => "Sharing %s failed, because the file does not exist", +"Sharing %s failed, because the user %s is the item owner" => "Sharing %s failed, because the user %s is the item owner", +"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 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", +"Sharing %s failed, because sharing with links is not allowed" => "Sharing %s failed, because sharing with links is not allowed", +"Share type %s is not valid for %s" => "Share type %s is not valid for %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", +"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 %s failed, because the user %s is the original sharer" => "Sharing %s 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", "Could not find category \"%s\"" => "Could not find category \"%s\"", "seconds ago" => "seconds ago", "_%n minute ago_::_%n minutes ago_" => array("%n minute ago","%n minutes ago"), @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "last month", "_%n month ago_::_%n months ago_" => array("%n month ago","%n months ago"), "last year" => "last year", -"years ago" => "years ago" +"years ago" => "years ago", +"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", +"A valid password must be provided" => "A valid password must be provided", +"The username is already being used" => "The username is already being used" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/en_NZ.php b/lib/l10n/en_NZ.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/en_NZ.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/eo.php b/lib/l10n/eo.php index 53d1ec1854d..faf262567c8 100644 --- a/lib/l10n/eo.php +++ b/lib/l10n/eo.php @@ -5,6 +5,8 @@ $TRANSLATIONS = array( "Settings" => "Agordo", "Users" => "Uzantoj", "Admin" => "Administranto", +"Unknown filetype" => "Ne konatas dosiertipo", +"Invalid image" => "Ne validas bildo", "web services under your control" => "TTT-servoj regataj de vi", "ZIP download is turned off." => "ZIP-elŝuto estas malkapabligita.", "Files need to be downloaded one by one." => "Dosieroj devas elŝutiĝi unuope.", @@ -20,12 +22,7 @@ $TRANSLATIONS = array( "%s enter the database name." => "%s enigu la nomon de la datumbazo.", "%s you may not use dots in the database name" => "%s vi ne povas uzi punktojn en la nomo de la datumbazo", "MS SQL username and/or password not valid: %s" => "La uzantonomo de MS SQL aŭ la pasvorto ne validas: %s", -"MySQL username and/or password not valid" => "La uzantonomo de MySQL aŭ la pasvorto ne validas", "DB Error: \"%s\"" => "Datumbaza eraro: “%s”", -"MySQL user '%s'@'localhost' exists already." => "La uzanto de MySQL “%s”@“localhost” jam ekzistas.", -"Drop this user from MySQL" => "Forigi ĉi tiun uzanton el MySQL", -"MySQL user '%s'@'%%' already exists" => "La uzanto de MySQL “%s”@“%%” jam ekzistas", -"Drop this user from MySQL." => "Forigi ĉi tiun uzanton el MySQL.", "Oracle connection could not be established" => "Konekto al Oracle ne povas stariĝi", "Oracle username and/or password not valid" => "La uzantonomo de Oracle aŭ la pasvorto ne validas", "PostgreSQL username and/or password not valid" => "La uzantonomo de PostgreSQL aŭ la pasvorto ne validas", @@ -33,15 +30,16 @@ $TRANSLATIONS = array( "Set an admin password." => "Starigi administran pasvorton.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Via TTT-servilo ankoraŭ ne ĝuste agordiĝis por permesi sinkronigi dosierojn ĉar la WebDAV-interfaco ŝajnas rompita.", "Please double check the <a href='%s'>installation guides</a>." => "Bonvolu duoble kontroli la <a href='%s'>gvidilon por instalo</a>.", +"%s shared »%s« with you" => "%s kunhavigis “%s” kun vi", "Could not find category \"%s\"" => "Ne troviĝis kategorio “%s”", "seconds ago" => "sekundoj antaŭe", -"_%n minute ago_::_%n minutes ago_" => array("",""), -"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n minute ago_::_%n minutes ago_" => array("","antaŭ %n minutoj"), +"_%n hour ago_::_%n hours ago_" => array("","antaŭ %n horoj"), "today" => "hodiaŭ", "yesterday" => "hieraŭ", -"_%n day go_::_%n days ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("","antaŭ %n tagoj"), "last month" => "lastamonate", -"_%n month ago_::_%n months ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","antaŭ %n monatoj"), "last year" => "lastajare", "years ago" => "jaroj antaŭe" ); diff --git a/lib/l10n/es.php b/lib/l10n/es.php index f231cd2bb6e..d1c04b09d38 100644 --- a/lib/l10n/es.php +++ b/lib/l10n/es.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Está accediendo al servidor desde un dominio inseguro.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Contacte a su administrador. Si usted es el administrador, configure \"trusted_domain\" en config/config.php. En config/config.sample.php se encuentra un ejemplo para la configuración.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "La aplicación \"%s\" no puede ser instalada porque no es compatible con esta versión de ownCloud", "No app name specified" => "No se ha especificado nombre de la aplicación", "Help" => "Ayuda", @@ -11,12 +13,11 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tipo de archivo desconocido", "Invalid image" => "Imagen inválida", "web services under your control" => "Servicios web bajo su control", -"cannot open \"%s\"" => "No se puede abrir \"%s\"", "ZIP download is turned off." => "La descarga en ZIP está desactivada.", "Files need to be downloaded one by one." => "Los archivos deben ser descargados uno por uno.", "Back to Files" => "Volver a Archivos", "Selected files too large to generate zip file." => "Los archivos seleccionados son demasiado grandes para generar el archivo zip.", -"Please download the files separately in smaller chunks or kindly ask your administrator." => "Descargue los archivos en trozos más pequeños, por separado o solicítelos amablemente a su administrador.", +"Please download the files separately in smaller chunks or kindly ask your administrator." => "Descargue los archivos en trozos más pequeños, por separado o solicítelos amablemente su administrador.", "No source specified when installing app" => "No se ha especificado origen cuando se ha instalado la aplicación", "No href specified when installing app from http" => "No href especificado cuando se ha instalado la aplicación", "No path specified when installing app from local file" => "Sin path especificado cuando se ha instalado la aplicación desde el fichero local", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "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, recarga la página.", +"Unknown user" => "Usuario desconocido", "Files" => "Archivos", "Text" => "Texto", "Images" => "Imágenes", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s puede utilizar puntos en el nombre de la base de datos", "MS SQL username and/or password not valid: %s" => "Usuario y/o contraseña de MS SQL no válidos: %s", "You need to enter either an existing account or the administrator." => "Tiene que ingresar una cuenta existente o la del administrador.", -"MySQL username and/or password not valid" => "Usuario y/o contraseña de MySQL no válidos", +"MySQL/MariaDB username and/or password not valid" => "Nombre de usuario o contraseña de MySQL/MariaDB inválidos", "DB Error: \"%s\"" => "Error BD: \"%s\"", "Offending command was: \"%s\"" => "Comando infractor: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Usuario MySQL '%s'@'localhost' ya existe.", -"Drop this user from MySQL" => "Eliminar este usuario de MySQL", -"MySQL user '%s'@'%%' already exists" => "Usuario MySQL '%s'@'%%' ya existe", -"Drop this user from MySQL." => "Eliminar este usuario de MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "El usuario de MySQL/MariaDB '%s'@'localhost' ya existe.", +"Drop this user from MySQL/MariaDB" => "Eliminar este usuario de MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "El usuario de MySQL/MariaDB '%s'@'%%' ya existe", +"Drop this user from MySQL/MariaDB." => "Eliminar este usuario de MySQL/MariaDB.", "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", "Offending command was: \"%s\", name: %s, password: %s" => "Comando infractor: \"%s\", nombre: %s, contraseña: %s", @@ -55,6 +57,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Configurar la contraseña del administrador.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Su servidor web aún no está configurado adecuadamente para permitir sincronización de archivos ya que la interfaz WebDAV parece no estar funcionando.", "Please double check the <a href='%s'>installation guides</a>." => "Por favor, vuelva a comprobar las <a href='%s'>guías de instalación</a>.", +"%s shared »%s« with you" => "%s ha compatido »%s« contigo", +"Sharing %s failed, because the file does not exist" => "No se pudo compartir %s porque el archivo no existe", +"Sharing %s failed, because the user %s is the item owner" => "Compartiendo %s ha fallado, ya que el usuario %s es el dueño del elemento", +"Sharing %s failed, because the user %s does not exist" => "Compartiendo %s ha fallado, 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" => "Compartiendo %s ha fallado, ya que el usuario %s no es miembro de algún grupo que %s es miembro", +"Sharing %s failed, because this item is already shared with %s" => "Compartiendo %s ha fallado, ya que este elemento ya está compartido con %s", +"Sharing %s failed, because the group %s does not exist" => "Compartiendo %s ha fallado, ya que el grupo %s no existe", +"Sharing %s failed, because %s is not a member of the group %s" => "Compartiendo %s ha fallado, ya que %s no es miembro del grupo %s", +"Sharing %s failed, because sharing with links is not allowed" => "Compartiendo %s ha fallado, ya que compartir con enlaces no está permitido", +"Share type %s is not valid for %s" => "Compartir tipo %s no es válido para %s", +"Setting permissions for %s failed, because the permissions exceed permissions granted to %s" => "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" => "Configuración de permisos para %s ha fallado, ya que el elemento no fue encontrado", +"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 %s failed, because the user %s is the original sharer" => "Compartiendo %s ha fallado, ya que el usuario %s es el compartidor original", +"Sharing %s failed, because the permissions exceed permissions granted to %s" => "Compartiendo %s ha fallado, ya que los permisos superan los permisos otorgados a %s", +"Sharing %s failed, because resharing is not allowed" => "Compartiendo %s ha fallado, ya que volver a compartir no está permitido", +"Sharing %s failed, because the sharing backend for %s could not find its source" => "Compartir %s falló 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" => "Compartiendo %s ha fallado, ya que el archivo no pudo ser encontrado en el cache de archivo", "Could not find category \"%s\"" => "No puede encontrar la categoria \"%s\"", "seconds ago" => "hace segundos", "_%n minute ago_::_%n minutes ago_" => array("Hace %n minuto","Hace %n minutos"), @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "mes pasado", "_%n month ago_::_%n months ago_" => array("Hace %n mes","Hace %n meses"), "last year" => "año pasado", -"years ago" => "hace años" +"years ago" => "hace años", +"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", +"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" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/es_AR.php b/lib/l10n/es_AR.php index bc5fcd7e012..34775b16b22 100644 --- a/lib/l10n/es_AR.php +++ b/lib/l10n/es_AR.php @@ -11,11 +11,11 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tipo de archivo desconocido", "Invalid image" => "Imagen inválida", "web services under your control" => "servicios web sobre los que tenés control", -"cannot open \"%s\"" => "no se puede abrir \"%s\"", "ZIP download is turned off." => "La descarga en ZIP está desactivada.", "Files need to be downloaded one by one." => "Los archivos deben ser descargados de a uno.", "Back to Files" => "Volver a Archivos", "Selected files too large to generate zip file." => "Los archivos seleccionados son demasiado grandes para generar el archivo zip.", +"Please download the files separately in smaller chunks or kindly ask your administrator." => "Por favor, descargue estos archivos de forma separada en pequeñas partes o pídalo amablemente a su administrador.", "No source specified when installing app" => "No se especificó el origen al instalar la app", "No href specified when installing app from http" => "No se especificó href al instalar la app", "No path specified when installing app from local file" => "No se especificó PATH al instalar la app desde el archivo local", @@ -39,13 +39,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s no podés usar puntos en el nombre de la base de datos", "MS SQL username and/or password not valid: %s" => "Nombre de usuario y contraseña de MS SQL no son válidas: %s", "You need to enter either an existing account or the administrator." => "Tenés que ingresar una cuenta existente o el administrador.", -"MySQL username and/or password not valid" => "Usuario y/o contraseña MySQL no válido", "DB Error: \"%s\"" => "Error DB: \"%s\"", "Offending command was: \"%s\"" => "El comando no comprendido es: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Usuario MySQL '%s'@'localhost' ya existe.", -"Drop this user from MySQL" => "Borrar este usuario de MySQL", -"MySQL user '%s'@'%%' already exists" => "Usuario MySQL '%s'@'%%' ya existe", -"Drop this user from MySQL." => "Borrar este usuario de MySQL", "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 no son válidos", "Offending command was: \"%s\", name: %s, password: %s" => "El comando no comprendido es: \"%s\", nombre: \"%s\", contraseña: \"%s\"", @@ -54,6 +49,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Configurar una contraseña de administrador.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Tu servidor web no está configurado todavía para permitir sincronización de archivos porque la interfaz WebDAV parece no funcionar.", "Please double check the <a href='%s'>installation guides</a>." => "Por favor, comprobá nuevamente la <a href='%s'>guía de instalación</a>.", +"%s shared »%s« with you" => "%s compartió \"%s\" con vos", "Could not find category \"%s\"" => "No fue posible encontrar la categoría \"%s\"", "seconds ago" => "segundos atrás", "_%n minute ago_::_%n minutes ago_" => array("Hace %n minuto","Hace %n minutos"), @@ -64,6 +60,8 @@ $TRANSLATIONS = array( "last month" => "el mes pasado", "_%n month ago_::_%n months ago_" => array("Hace %n mes","Hace %n meses"), "last year" => "el año pasado", -"years ago" => "años atrás" +"years ago" => "años atrás", +"A valid username must be provided" => "Debe ingresar un nombre de usuario válido", +"A valid password must be provided" => "Debe ingresar una contraseña válida" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/es_CL.php b/lib/l10n/es_CL.php index 46158b0ccc7..a2669b110c6 100644 --- a/lib/l10n/es_CL.php +++ b/lib/l10n/es_CL.php @@ -2,9 +2,15 @@ $TRANSLATIONS = array( "Settings" => "Configuración", "Files" => "Archivos", +"seconds ago" => "segundos antes", "_%n minute ago_::_%n minutes ago_" => array("",""), "_%n hour ago_::_%n hours ago_" => array("",""), +"today" => "hoy", +"yesterday" => "ayer", "_%n day go_::_%n days ago_" => array("",""), -"_%n month ago_::_%n months ago_" => array("","") +"last month" => "mes anterior", +"_%n month ago_::_%n months ago_" => array("",""), +"last year" => "último año", +"years ago" => "años anteriores" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/es_CR.php b/lib/l10n/es_CR.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/es_CR.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/es_MX.php b/lib/l10n/es_MX.php index 7454d4966d8..befb076b7de 100644 --- a/lib/l10n/es_MX.php +++ b/lib/l10n/es_MX.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tipo de archivo desconocido", "Invalid image" => "Imagen inválida", "web services under your control" => "Servicios web bajo su control", -"cannot open \"%s\"" => "No se puede abrir \"%s\"", "ZIP download is turned off." => "La descarga en ZIP está desactivada.", "Files need to be downloaded one by one." => "Los archivos deben ser descargados uno por uno.", "Back to Files" => "Volver a Archivos", @@ -40,13 +39,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s puede utilizar puntos en el nombre de la base de datos", "MS SQL username and/or password not valid: %s" => "Usuario y/o contraseña de MS SQL no válidos: %s", "You need to enter either an existing account or the administrator." => "Tiene que ingresar una cuenta existente o la del administrador.", -"MySQL username and/or password not valid" => "Usuario y/o contraseña de MySQL no válidos", "DB Error: \"%s\"" => "Error BD: \"%s\"", "Offending command was: \"%s\"" => "Comando infractor: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Usuario MySQL '%s'@'localhost' ya existe.", -"Drop this user from MySQL" => "Eliminar este usuario de MySQL", -"MySQL user '%s'@'%%' already exists" => "Usuario MySQL '%s'@'%%' ya existe", -"Drop this user from MySQL." => "Eliminar este usuario de MySQL.", "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", "Offending command was: \"%s\", name: %s, password: %s" => "Comando infractor: \"%s\", nombre: %s, contraseña: %s", @@ -55,6 +49,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Configurar la contraseña del administrador.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Su servidor web aún no está configurado adecuadamente para permitir sincronización de archivos ya que la interfaz WebDAV parece no estar funcionando.", "Please double check the <a href='%s'>installation guides</a>." => "Por favor, vuelva a comprobar las <a href='%s'>guías de instalación</a>.", +"%s shared »%s« with you" => "%s ha compartido »%s« contigo", "Could not find category \"%s\"" => "No puede encontrar la categoria \"%s\"", "seconds ago" => "hace segundos", "_%n minute ago_::_%n minutes ago_" => array("Hace %n minuto","Hace %n minutos"), @@ -65,6 +60,8 @@ $TRANSLATIONS = array( "last month" => "mes pasado", "_%n month ago_::_%n months ago_" => array("Hace %n mes","Hace %n meses"), "last year" => "año pasado", -"years ago" => "hace años" +"years ago" => "hace años", +"A valid username must be provided" => "Se debe proporcionar un nombre de usuario válido", +"A valid password must be provided" => "Se debe proporcionar una contraseña válida" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/et_EE.php b/lib/l10n/et_EE.php index 96fceaa04ed..079ff3ba472 100644 --- a/lib/l10n/et_EE.php +++ b/lib/l10n/et_EE.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Sa kasutad serverit usalduseta asukohast", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "Rakendit \"%s\" ei saa paigaldada, kuna see pole ühilduv selle ownCloud versiooniga.", "No app name specified" => "Ühegi rakendi nime pole määratletud", "Help" => "Abiinfo", @@ -11,7 +12,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tundmatu failitüüp", "Invalid image" => "Vigane pilt", "web services under your control" => "veebitenused sinu kontrolli all", -"cannot open \"%s\"" => "ei suuda avada \"%s\"", "ZIP download is turned off." => "ZIP-ina allalaadimine on välja lülitatud.", "Files need to be downloaded one by one." => "Failid tuleb alla laadida ükshaaval.", "Back to Files" => "Tagasi failide juurde", @@ -32,6 +32,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "Rakendus pole sisse lülitatud", "Authentication error" => "Autentimise viga", "Token expired. Please reload page." => "Kontrollkood aegus. Paelun lae leht uuesti.", +"Unknown user" => "Tundmatu kasutaja", "Files" => "Failid", "Text" => "Tekst", "Images" => "Pildid", @@ -40,13 +41,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s punktide kasutamine andmebaasi nimes pole lubatud", "MS SQL username and/or password not valid: %s" => "MS SQL kasutajatunnus ja/või parool pole õiged: %s", "You need to enter either an existing account or the administrator." => "Sisesta kas juba olemasolev konto või administrator.", -"MySQL username and/or password not valid" => "MySQL kasutajatunnus ja/või parool pole õiged", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB kasutajatunnus ja/või parool pole õiged", "DB Error: \"%s\"" => "Andmebaasi viga: \"%s\"", "Offending command was: \"%s\"" => "Tõrkuv käsk oli: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL kasutaja '%s'@'localhost' on juba olemas.", -"Drop this user from MySQL" => "Kustuta see kasutaja MySQL-ist", -"MySQL user '%s'@'%%' already exists" => "MySQL kasutaja '%s'@'%%' on juba olemas", -"Drop this user from MySQL." => "Kustuta see kasutaja MySQL-ist.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB kasutaja '%s'@'localhost' on juba olemas.", +"Drop this user from MySQL/MariaDB" => "Kustuta see MySQL/MariaDB kasutaja", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB user '%s'@'%%' on juba olemas", +"Drop this user from MySQL/MariaDB." => "Kustuta see MySQL/MariaDB kasutaja.", "Oracle connection could not be established" => "Ei suuda luua ühendust Oracle baasiga", "Oracle username and/or password not valid" => "Oracle kasutajatunnus ja/või parool pole õiged", "Offending command was: \"%s\", name: %s, password: %s" => "Tõrkuv käsk oli: \"%s\", nimi: %s, parool: %s", @@ -55,6 +56,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Määra admini parool.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Veebiserveri ei ole veel korralikult seadistatud võimaldamaks failide sünkroniseerimist, kuna WebDAV liides näib olevat mittetoimiv.", "Please double check the <a href='%s'>installation guides</a>." => "Palun tutvu veelkord <a href='%s'>paigalduse juhenditega</a>.", +"%s shared »%s« with you" => "%s jagas sinuga »%s«", +"Sharing %s failed, because the file does not exist" => "%s jagamine ebaõnnestus, kuna faili pole olemas", +"Sharing %s failed, because the user %s is the item owner" => "%s jagamine ebaõnnestus, kuna kuna kasutaja %s on üksuse omanik", +"Sharing %s failed, because the user %s does not exist" => "%s jagamine ebaõnnestus, kuna kasutajat %s pole olemas", +"Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" => "%s jagamine ebaõnnestus, kuna kasutaja %s pole ühegi grupi liige, millede liige on %s", +"Sharing %s failed, because this item is already shared with %s" => "%s jagamine ebaõnnestus, kuna see üksus on juba jagatud %s", +"Sharing %s failed, because the group %s does not exist" => "%s jagamine ebaõnnestus, kuna gruppi %s pole olemas", +"Sharing %s failed, because %s is not a member of the group %s" => "%s jagamine ebaõnnestus, kuna %s pole grupi %s liige", +"Sharing %s failed, because sharing with links is not allowed" => "%s jagamine ebaõnnestus, kuna linkidega jagamine pole lubatud", +"Share type %s is not valid for %s" => "Jagamise tüüp %s ei ole õige %s jaoks", +"Setting permissions for %s failed, because the permissions exceed permissions granted to %s" => "Lubade seadistus %s jaoks ebaõnnestus, kuna antud õigused ületavad %s jaoks määratud õigusi", +"Setting permissions for %s failed, because the item was not found" => "Lubade seadistus %s jaoks ebaõnnestus, kuna üksust ei leitud", +"Sharing backend %s must implement the interface OCP\\Share_Backend" => "Jagamise tagarakend %s peab kasutusele võtma OCP\\Share_Backend liidese", +"Sharing backend %s not found" => "Jagamise tagarakendit %s ei leitud", +"Sharing backend for %s not found" => "Jagamise tagarakendit %s jaoks ei leitud", +"Sharing %s failed, because the user %s is the original sharer" => "%s jagamine ebaõnnestus, kuna kasutaja %s on algne jagaja", +"Sharing %s failed, because the permissions exceed permissions granted to %s" => "%s jagamine ebaõnnestus, kuna antud õigused ületavad %s jaoks määratud õigusi", +"Sharing %s failed, because resharing is not allowed" => "%s jagamine ebaõnnestus, kuna edasijagamine pole lubatud", +"Sharing %s failed, because the sharing backend for %s could not find its source" => "%s jagamine ebaõnnestus, kuna jagamise tagarakend ei suutnud leida %s jaoks lähteallikat", +"Sharing %s failed, because the file could not be found in the file cache" => "%s jagamine ebaõnnestus, kuna faili ei suudetud leida failide puhvrist", "Could not find category \"%s\"" => "Ei leia kategooriat \"%s\"", "seconds ago" => "sekundit tagasi", "_%n minute ago_::_%n minutes ago_" => array("","%n minutit tagasi"), @@ -65,6 +86,10 @@ $TRANSLATIONS = array( "last month" => "viimasel kuul", "_%n month ago_::_%n months ago_" => array("","%n kuud tagasi"), "last year" => "viimasel aastal", -"years ago" => "aastat tagasi" +"years ago" => "aastat tagasi", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "Kasutajanimes on lubatud ainult järgnevad tähemärgid: \"a-z\", \"A-Z\", \"0-9\" ja \"_.@-\"", +"A valid username must be provided" => "Sisesta nõuetele vastav kasutajatunnus", +"A valid password must be provided" => "Sisesta nõuetele vastav parool", +"The username is already being used" => "Kasutajanimi on juba kasutuses" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/eu.php b/lib/l10n/eu.php index e3f18fca47a..7a8a11f5b32 100644 --- a/lib/l10n/eu.php +++ b/lib/l10n/eu.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Fitxategi mota ezezaguna", "Invalid image" => "Baliogabeko irudia", "web services under your control" => "web zerbitzuak zure kontrolpean", -"cannot open \"%s\"" => "ezin da \"%s\" ireki", "ZIP download is turned off." => "ZIP deskarga ez dago gaituta.", "Files need to be downloaded one by one." => "Fitxategiak banan-banan deskargatu behar dira.", "Back to Files" => "Itzuli fitxategietara", @@ -40,13 +39,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s ezin duzu punturik erabili datu basearen izenean.", "MS SQL username and/or password not valid: %s" => "MS SQL erabiltzaile izena edota pasahitza ez dira egokiak: %s", "You need to enter either an existing account or the administrator." => "Existitzen den kontu bat edo administradorearena jarri behar duzu.", -"MySQL username and/or password not valid" => "MySQL erabiltzaile edota pasahitza ez dira egokiak.", "DB Error: \"%s\"" => "DB errorea: \"%s\"", "Offending command was: \"%s\"" => "Errorea komando honek sortu du: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL '%s'@'localhost' erabiltzailea dagoeneko existitzen da.", -"Drop this user from MySQL" => "Ezabatu erabiltzaile hau MySQLtik", -"MySQL user '%s'@'%%' already exists" => "MySQL '%s'@'%%' erabiltzailea dagoeneko existitzen da", -"Drop this user from MySQL." => "Ezabatu erabiltzaile hau MySQLtik.", "Oracle connection could not be established" => "Ezin da Oracle konexioa sortu", "Oracle username and/or password not valid" => "Oracle erabiltzaile edota pasahitza ez dira egokiak.", "Offending command was: \"%s\", name: %s, password: %s" => "Errorea komando honek sortu du: \"%s\", izena: %s, pasahitza: %s", @@ -55,6 +49,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Ezarri administraziorako pasahitza.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Zure web zerbitzaria ez dago oraindik ongi konfiguratuta fitxategien sinkronizazioa egiteko, WebDAV interfazea ongi ez dagoela dirudi.", "Please double check the <a href='%s'>installation guides</a>." => "Mesedez begiratu <a href='%s'>instalazio gidak</a>.", +"%s shared »%s« with you" => "%s-ek »%s« zurekin partekatu du", "Could not find category \"%s\"" => "Ezin da \"%s\" kategoria aurkitu", "seconds ago" => "segundu", "_%n minute ago_::_%n minutes ago_" => array("orain dela minutu %n","orain dela %n minutu"), @@ -65,6 +60,9 @@ $TRANSLATIONS = array( "last month" => "joan den hilabetean", "_%n month ago_::_%n months ago_" => array("orain dela hilabete %n","orain dela %n hilabete"), "last year" => "joan den urtean", -"years ago" => "urte" +"years ago" => "urte", +"A valid username must be provided" => "Baliozko erabiltzaile izena eman behar da", +"A valid password must be provided" => "Baliozko pasahitza eman behar da", +"The username is already being used" => "Erabiltzaile izena dagoeneko erabiltzen ari da" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/eu_ES.php b/lib/l10n/eu_ES.php new file mode 100644 index 00000000000..35192433a2a --- /dev/null +++ b/lib/l10n/eu_ES.php @@ -0,0 +1,9 @@ +<?php +$TRANSLATIONS = array( +"Personal" => "Pertsonala", +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/fa.php b/lib/l10n/fa.php index 788b3703966..c1c16cf94b4 100644 --- a/lib/l10n/fa.php +++ b/lib/l10n/fa.php @@ -21,13 +21,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s شما نباید از نقطه در نام پایگاه داده استفاده نمایید.", "MS SQL username and/or password not valid: %s" => "نام کاربری و / یا رمزعبور MS SQL معتبر نیست: %s", "You need to enter either an existing account or the administrator." => "شما نیاز به وارد کردن یک حساب کاربری موجود یا حساب مدیریتی دارید.", -"MySQL username and/or password not valid" => "نام کاربری و / یا رمزعبور MySQL معتبر نیست.", "DB Error: \"%s\"" => "خطای پایگاه داده: \"%s\"", "Offending command was: \"%s\"" => "دستور متخلف عبارت است از: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "کاربرMySQL '%s'@'localhost' درحال حاضر موجود است.", -"Drop this user from MySQL" => "این کاربر را از MySQL حذف نمایید.", -"MySQL user '%s'@'%%' already exists" => "کاربر'%s'@'%%' MySQL در حال حاضر موجود است.", -"Drop this user from MySQL." => "این کاربر را از MySQL حذف نمایید.", "Oracle connection could not be established" => "ارتباط اراکل نمیتواند برقرار باشد.", "Oracle username and/or password not valid" => "نام کاربری و / یا رمزعبور اراکل معتبر نیست.", "Offending command was: \"%s\", name: %s, password: %s" => "دستور متخلف عبارت است از: \"%s\"، نام: \"%s\"، رمزعبور:\"%s\"", @@ -36,6 +31,7 @@ $TRANSLATIONS = array( "Set an admin password." => "یک رمزعبور برای مدیر تنظیم نمایید.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "احتمالاً وب سرور شما طوری تنظیم نشده است که اجازه ی همگام سازی فایلها را بدهد زیرا به نظر میرسد رابط WebDAV از کار افتاده است.", "Please double check the <a href='%s'>installation guides</a>." => "لطفاً دوباره <a href='%s'>راهنمای نصب</a>را بررسی کنید.", +"%s shared »%s« with you" => "%s به اشتراک گذاشته شده است »%s« توسط شما", "Could not find category \"%s\"" => "دسته بندی %s یافت نشد", "seconds ago" => "ثانیهها پیش", "_%n minute ago_::_%n minutes ago_" => array(""), @@ -46,6 +42,8 @@ $TRANSLATIONS = array( "last month" => "ماه قبل", "_%n month ago_::_%n months ago_" => array(""), "last year" => "سال قبل", -"years ago" => "سالهای قبل" +"years ago" => "سالهای قبل", +"A valid username must be provided" => "نام کاربری صحیح باید وارد شود", +"A valid password must be provided" => "رمز عبور صحیح باید وارد شود" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/fi_FI.php b/lib/l10n/fi_FI.php index 573704da44c..e87c7038807 100644 --- a/lib/l10n/fi_FI.php +++ b/lib/l10n/fi_FI.php @@ -23,11 +23,13 @@ $TRANSLATIONS = array( "App does not provide an info.xml file" => "Sovellus ei sisällä info.xml-tiedostoa", "App can't be installed because of not allowed code in the App" => "Sovellusta ei voi asentaa, koska sovellus sisältää kiellettyä koodia", "App can't be installed because it is not compatible with this version of ownCloud" => "Sovellusta ei voi asentaa, koska se ei ole yhteensopiva käytössä olevan ownCloud-version kanssa", +"App can't be installed because the version in info.xml/version is not the same as the version reported from the app store" => "Sovellusta ei voi asentaa, koska info.xml/version ilmoittaa versioksi eri arvon kuin sovelluskauppa", "App directory already exists" => "Sovelluskansio on jo olemassa", "Can't create app folder. Please fix permissions. %s" => "Sovelluskansion luominen ei onnistu. Korjaa käyttöoikeudet. %s", "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ä", "Files" => "Tiedostot", "Text" => "Teksti", "Images" => "Kuvat", @@ -35,18 +37,33 @@ $TRANSLATIONS = array( "%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ä", "MS SQL username and/or password not valid: %s" => "MS SQL -käyttäjätunnus ja/tai -salasana on väärin: %s", -"MySQL username and/or password not valid" => "MySQL:n käyttäjätunnus ja/tai salasana on väärin", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB-käyttäjätunnus ja/tai salasana on virheellinen", "DB Error: \"%s\"" => "Tietokantavirhe: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL-käyttäjä '%s'@'localhost' on jo olemassa.", -"Drop this user from MySQL" => "Pudota tämä käyttäjä MySQL:stä", -"MySQL user '%s'@'%%' already exists" => "MySQL-käyttäjä '%s'@'%%' on jo olemassa", -"Drop this user from MySQL." => "Pudota tämä käyttäjä MySQL:stä.", +"Offending command was: \"%s\"" => "Loukkaava komento oli: \"%s\"", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB-käyttäjä '%s'@'localhost' on jo olemassa.", +"Drop this user from MySQL/MariaDB" => "Pudota tämä käyttäjä MySQL/MariaDB:stä", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB-käyttäjä '%s'@'%%' on jo olemassa", +"Drop this user from MySQL/MariaDB." => "Pudota tämä käyttäjä MySQL/MariaDB:stä.", "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", +"Offending command was: \"%s\", name: %s, password: %s" => "Loukkaava komento oli: \"%s\", nimi: %s, salasana: %s", "PostgreSQL username and/or password not valid" => "PostgreSQL:n käyttäjätunnus ja/tai salasana on väärin", "Set an admin username." => "Aseta ylläpitäjän käyttäjätunnus.", "Set an admin password." => "Aseta ylläpitäjän salasana.", +"Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Web-palvelimen asetukset eivät ole kelvolliset tiedostojen synkronointia varten, koska WebDAV-liitäntä vaikuttaa olevan rikki.", "Please double check the <a href='%s'>installation guides</a>." => "Lue tarkasti <a href='%s'>asennusohjeet</a>.", +"%s shared »%s« with you" => "%s jakoi kohteen »%s« kanssasi", +"Sharing %s failed, because the file does not exist" => "Kohteen %s jakaminen epäonnistui, koska tiedostoa ei ole olemassa", +"Sharing %s failed, because the user %s is the item owner" => "Kohteen %s jakaminen epäonnistui, koska käyttäjä %s on kohteen omistaja", +"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 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", +"Sharing %s failed, because sharing with links is not allowed" => "Kohteen %s jakaminen epäonnistui, koska jakaminen linkkejä käyttäen ei ole sallittu", +"Setting permissions for %s failed, because the item was not found" => "Kohteen %s oikeuksien asettaminen epäonnistui, koska kohdetta ei löytynyt", +"Sharing %s failed, because the user %s is the original sharer" => "Kohteen %s jakaminen epäonnistui, koska käyttäjä %s on 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", "Could not find category \"%s\"" => "Luokkaa \"%s\" ei löytynyt", "seconds ago" => "sekuntia sitten", "_%n minute ago_::_%n minutes ago_" => array("%n minuutti sitten","%n minuuttia sitten"), @@ -57,6 +74,10 @@ $TRANSLATIONS = array( "last month" => "viime kuussa", "_%n month ago_::_%n months ago_" => array("%n kuukausi sitten","%n kuukautta sitten"), "last year" => "viime vuonna", -"years ago" => "vuotta sitten" +"years ago" => "vuotta sitten", +"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", +"A valid password must be provided" => "Anna kelvollinen salasana", +"The username is already being used" => "Käyttäjätunnus on jo käytössä" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/fr.php b/lib/l10n/fr.php index 75a4f277271..3bfa3fe8916 100644 --- a/lib/l10n/fr.php +++ b/lib/l10n/fr.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Vous accédez au serveur à partir d'un domaine non-approuvé.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "L'application \"%s\" ne peut être installée car elle n'est pas compatible avec cette version de ownCloud.", "No app name specified" => "Aucun nom d'application spécifié", "Help" => "Aide", @@ -11,7 +12,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Type de fichier inconnu", "Invalid image" => "Image invalide", "web services under your control" => "services web sous votre contrôle", -"cannot open \"%s\"" => "impossible d'ouvrir \"%s\"", "ZIP download is turned off." => "Téléchargement ZIP désactivé.", "Files need to be downloaded one by one." => "Les fichiers nécessitent d'être téléchargés un par un.", "Back to Files" => "Retour aux Fichiers", @@ -32,6 +32,7 @@ $TRANSLATIONS = array( "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", "Files" => "Fichiers", "Text" => "Texte", "Images" => "Images", @@ -40,13 +41,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s vous nez pouvez pas utiliser de points dans le nom de la base de données", "MS SQL username and/or password not valid: %s" => "Le nom d'utilisateur et/ou le mot de passe de la base MS SQL est invalide : %s", "You need to enter either an existing account or the administrator." => "Vous devez spécifier soit le nom d'un compte existant, soit celui de l'administrateur.", -"MySQL username and/or password not valid" => "Nom d'utilisateur et/ou mot de passe de la base MySQL invalide", +"MySQL/MariaDB username and/or password not valid" => "Nom d'utilisateur et/ou mot de passe MySQL/MariaDB invalide", "DB Error: \"%s\"" => "Erreur de la base de données : \"%s\"", "Offending command was: \"%s\"" => "La requête en cause est : \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "L'utilisateur MySQL '%s'@'localhost' existe déjà.", -"Drop this user from MySQL" => "Retirer cet utilisateur de la base MySQL", -"MySQL user '%s'@'%%' already exists" => "L'utilisateur MySQL '%s'@'%%' existe déjà", -"Drop this user from MySQL." => "Retirer cet utilisateur de la base MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "L'utilisateur MySQL/MariaDB '%s'@'localhost' existe déjà.", +"Drop this user from MySQL/MariaDB" => "Retirer cet utilisateur de la base MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "L'utilisateur MySQL/MariaDB '%s'@'%%' existe déjà", +"Drop this user from MySQL/MariaDB." => "Retirer cet utilisateur de la base MySQL/MariaDB.", "Oracle connection could not be established" => "La connexion Oracle ne peut pas être établie", "Oracle username and/or password not valid" => "Nom d'utilisateur et/ou mot de passe de la base Oracle invalide", "Offending command was: \"%s\", name: %s, password: %s" => "La requête en cause est : \"%s\", nom : %s, mot de passe : %s", @@ -55,6 +56,22 @@ $TRANSLATIONS = array( "Set an admin password." => "Spécifiez un mot de passe administrateur.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Votre serveur web, n'est pas correctement configuré pour permettre la synchronisation des fichiers, car l'interface WebDav ne fonctionne pas comme il faut.", "Please double check the <a href='%s'>installation guides</a>." => "Veuillez vous référer au <a href='%s'>guide d'installation</a>.", +"%s shared »%s« with you" => "%s partagé »%s« avec vous", +"Sharing %s failed, because the file does not exist" => "Le partage de %s a échoué car le fichier n'existe pas", +"Sharing %s failed, because the user %s is the item owner" => "Le partage de %s a échoué car l'utilisateur %s est le propriétaire de l'objet", +"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 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", +"Sharing %s failed, because sharing with links is not allowed" => "Le partage de %s a échoué car un partage de lien n'est pas permis", +"Share type %s is not valid for %s" => "Le type de partage %s n'est pas valide pour %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 celle accordée à %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é", +"Sharing %s failed, because the user %s is the original sharer" => "Le partage de %s a échoué car l'utilisateur %s est déjà l'utilisateur à l'origine du partage.", +"Sharing %s failed, because the permissions exceed permissions granted to %s" => "Le partage de %s a échoué car les permissions dépassent les permissions 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 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.", "Could not find category \"%s\"" => "Impossible de trouver la catégorie \"%s\"", "seconds ago" => "il y a quelques secondes", "_%n minute ago_::_%n minutes ago_" => array("","il y a %n minutes"), @@ -65,6 +82,10 @@ $TRANSLATIONS = array( "last month" => "le mois dernier", "_%n month ago_::_%n months ago_" => array("","Il y a %n mois"), "last year" => "l'année dernière", -"years ago" => "il y a plusieurs années" +"years ago" => "il y a plusieurs années", +"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 \"_.@-\"", +"A valid username must be provided" => "Un nom d'utilisateur valide doit être saisi", +"A valid password must be provided" => "Un mot de passe valide doit être saisi", +"The username is already being used" => "Le nom d'utilisateur est déjà utilisé" ); $PLURAL_FORMS = "nplurals=2; plural=(n > 1);"; diff --git a/lib/l10n/gl.php b/lib/l10n/gl.php index 81a62021556..3a437ce442d 100644 --- a/lib/l10n/gl.php +++ b/lib/l10n/gl.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Esta accedendo desde un dominio non fiábel.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Póñase en contacto co administrador. Se vostede é administrador desta instancia, configure o parámetro «trusted_domain» en config/config.php. Dispón dun exemplo de configuración en config/config.sample.php.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "Non é posíbel instalar o aplicativo «%s» por non seren compatíbel con esta versión do ownCloud.", "No app name specified" => "Non se especificou o nome do aplicativo", "Help" => "Axuda", @@ -11,7 +13,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tipo de ficheiro descoñecido", "Invalid image" => "Imaxe incorrecta", "web services under your control" => "servizos web baixo o seu control", -"cannot open \"%s\"" => "non foi posíbel abrir «%s»", "ZIP download is turned off." => "As descargas ZIP están desactivadas.", "Files need to be downloaded one by one." => "Os ficheiros necesitan seren descargados dun en un.", "Back to Files" => "Volver aos ficheiros", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "O aplicativo non está activado", "Authentication error" => "Produciuse un erro de autenticación", "Token expired. Please reload page." => "Testemuña caducada. Recargue a páxina.", +"Unknown user" => "Usuario descoñecido", "Files" => "Ficheiros", "Text" => "Texto", "Images" => "Imaxes", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s non se poden empregar puntos na base de datos", "MS SQL username and/or password not valid: %s" => "Nome de usuario e/ou contrasinal de MS SQL incorrecto: %s", "You need to enter either an existing account or the administrator." => "Deberá introducir unha conta existente ou o administrador.", -"MySQL username and/or password not valid" => "Nome de usuario e/ou contrasinal de MySQL incorrecto", +"MySQL/MariaDB username and/or password not valid" => "O nome e/ou o contrasinal do usuario de MySQL/MariaDB non é correcto", "DB Error: \"%s\"" => "Produciuse un erro na base de datos: «%s»", "Offending command was: \"%s\"" => "A orde ofensiva foi: «%s»", -"MySQL user '%s'@'localhost' exists already." => "O usuario MySQL '%s'@'localhost' xa existe.", -"Drop this user from MySQL" => "Omitir este usuario de MySQL", -"MySQL user '%s'@'%%' already exists" => "O usuario MySQL «%s»@«%%» xa existe.", -"Drop this user from MySQL." => "Omitir este usuario de MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "Xa existe o usuario «%s»@«localhost» no MySQL/MariaDB.", +"Drop this user from MySQL/MariaDB" => "Eliminar este usuario do MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "Xa existe o usuario «%s»@«%%» no MySQL/MariaDB", +"Drop this user from MySQL/MariaDB." => "Eliminar este usuario do MySQL/MariaDB.", "Oracle connection could not be established" => "Non foi posíbel estabelecer a conexión con Oracle", "Oracle username and/or password not valid" => "Nome de usuario e/ou contrasinal de Oracle incorrecto", "Offending command was: \"%s\", name: %s, password: %s" => "A orde ofensiva foi: «%s», nome: %s, contrasinal: %s", @@ -55,16 +57,40 @@ $TRANSLATIONS = array( "Set an admin password." => "Estabeleza un contrasinal de administrador", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "O seu servidor web non está aínda configurado adecuadamente para permitir a sincronización de ficheiros xa que semella que a interface WebDAV non está a funcionar.", "Please double check the <a href='%s'>installation guides</a>." => "Volva comprobar as <a href='%s'>guías de instalación</a>", +"%s shared »%s« with you" => "%s compartiu «%s» con vostede", +"Sharing %s failed, because the file does not exist" => "Fallou a compartición de %s, o ficheiro non existe", +"Sharing %s failed, because the user %s is the item owner" => "Fallou a compartición de %s, o propietario do elemento é o usuario %s", +"Sharing %s failed, because the user %s does not exist" => "Fallou a compartición de %s, o usuario %s non existe", +"Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" => "Fallou a compartición de %s, o usuario %s non é membro de ningún grupo que sexa membro de %s", +"Sharing %s failed, because this item is already shared with %s" => "Fallou a compartición de %s, este elemento xa está compartido con %s", +"Sharing %s failed, because the group %s does not exist" => "Fallou a compartición de %s, o grupo %s non existe", +"Sharing %s failed, because %s is not a member of the group %s" => "Fallou a compartición de %s, %s non é membro do grupo %s", +"Sharing %s failed, because sharing with links is not allowed" => "Fallou a compartición de %s, non está permitido compartir con ligazóns", +"Share type %s is not valid for %s" => "Non se admite a compartición do tipo %s para %s", +"Setting permissions for %s failed, because the permissions exceed permissions granted to %s" => "Non é posíbel estabelecer permisos para %s, os permisos superan os permisos concedidos a %s", +"Setting permissions for %s failed, because the item was not found" => "Non é posíbel estabelecer permisos para %s, non se atopa o elemento", +"Sharing backend %s must implement the interface OCP\\Share_Backend" => "A infraestrutura de compartición %s ten que implementar a interface OCP\\Share_Backend", +"Sharing backend %s not found" => "Non se atopou a infraestrutura de compartición %s", +"Sharing backend for %s not found" => "Non se atopou a infraestrutura de compartición para %s", +"Sharing %s failed, because the user %s is the original sharer" => "Fallou a compartición de %s, compartición orixinal é do usuario %s", +"Sharing %s failed, because the permissions exceed permissions granted to %s" => "Fallou a compartición de %s, os permisos superan os permisos concedidos a %s", +"Sharing %s failed, because resharing is not allowed" => "Fallou a compartición de %s, non está permitido repetir a compartción", +"Sharing %s failed, because the sharing backend for %s could not find its source" => "Fallou a compartición de %s, a infraestrutura de compartición para %s non foi quen de atopar a orixe", +"Sharing %s failed, because the file could not be found in the file cache" => "Fallou a compartición de %s, non foi posíbel atopar o ficheiro na caché de ficheiros", "Could not find category \"%s\"" => "Non foi posíbel atopar a categoría «%s»", "seconds ago" => "segundos atrás", "_%n minute ago_::_%n minutes ago_" => array("hai %n minuto","hai %n minutos"), "_%n hour ago_::_%n hours ago_" => array("hai %n hora","hai %n horas"), "today" => "hoxe", "yesterday" => "onte", -"_%n day go_::_%n days ago_" => array("hai %n día","hai %n días"), +"_%n day go_::_%n days ago_" => array("hai %n día","vai %n días"), "last month" => "último mes", "_%n month ago_::_%n months ago_" => array("hai %n mes","hai %n meses"), "last year" => "último ano", -"years ago" => "anos atrás" +"years ago" => "anos atrás", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "Só se permiten os seguintes caracteres no nome de usuario: «a-z», «A-Z», «0-9», e «_.@-»", +"A valid username must be provided" => "Debe fornecer un nome de usuario", +"A valid password must be provided" => "Debe fornecer un contrasinal", +"The username is already being used" => "Este nome de usuario xa está a ser usado" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/he.php b/lib/l10n/he.php index 5bbfffe9ae9..27267c2e34d 100644 --- a/lib/l10n/he.php +++ b/lib/l10n/he.php @@ -18,6 +18,7 @@ $TRANSLATIONS = array( "Images" => "תמונות", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "שרת האינטרנט שלך אינו מוגדר לצורכי סנכרון קבצים עדיין כיוון שמנשק ה־WebDAV כנראה אינו תקין.", "Please double check the <a href='%s'>installation guides</a>." => "נא לעיין שוב ב<a href='%s'>מדריכי ההתקנה</a>.", +"%s shared »%s« with you" => "%s שיתף/שיתפה איתך את »%s«", "Could not find category \"%s\"" => "לא ניתן למצוא את הקטגוריה „%s“", "seconds ago" => "שניות", "_%n minute ago_::_%n minutes ago_" => array("","לפני %n דקות"), @@ -28,6 +29,8 @@ $TRANSLATIONS = array( "last month" => "חודש שעבר", "_%n month ago_::_%n months ago_" => array("","לפני %n חודשים"), "last year" => "שנה שעברה", -"years ago" => "שנים" +"years ago" => "שנים", +"A valid username must be provided" => "יש לספק שם משתמש תקני", +"A valid password must be provided" => "יש לספק ססמה תקנית" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/hi_IN.php b/lib/l10n/hi_IN.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/hi_IN.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/hu_HU.php b/lib/l10n/hu_HU.php index efaf2a2fd48..0637773d17c 100644 --- a/lib/l10n/hu_HU.php +++ b/lib/l10n/hu_HU.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Ismeretlen file tipús", "Invalid image" => "Hibás kép", "web services under your control" => "webszolgáltatások saját kézben", -"cannot open \"%s\"" => "nem sikerült megnyitni \"%s\"", "ZIP download is turned off." => "A ZIP-letöltés nincs engedélyezve.", "Files need to be downloaded one by one." => "A fájlokat egyenként kell letölteni.", "Back to Files" => "Vissza a Fájlokhoz", @@ -40,13 +39,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s az adatbázis neve nem tartalmazhat pontot", "MS SQL username and/or password not valid: %s" => "Az MS SQL felhasználónév és/vagy jelszó érvénytelen: %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", -"MySQL username and/or password not valid" => "A MySQL felhasználói név és/vagy jelszó érvénytelen", "DB Error: \"%s\"" => "Adatbázis hiba: \"%s\"", "Offending command was: \"%s\"" => "A hibát ez a parancs okozta: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "A '%s'@'localhost' MySQL felhasználó már létezik.", -"Drop this user from MySQL" => "Törölje ezt a felhasználót a MySQL-ből", -"MySQL user '%s'@'%%' already exists" => "A '%s'@'%%' MySQL felhasználó már létezik", -"Drop this user from MySQL." => "Törölje ezt a felhasználót a MySQL-ből.", "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", "Offending command was: \"%s\", name: %s, password: %s" => "A hibát okozó parancs ez volt: \"%s\", login név: %s, jelszó: %s", @@ -55,6 +49,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Állítson be egy jelszót az adminisztrációhoz.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Az Ön webkiszolgálója nincs megfelelően beállítva az állományok szinkronizálásához, mert a WebDAV-elérés úgy tűnik, nem működik.", "Please double check the <a href='%s'>installation guides</a>." => "Kérjük tüzetesen tanulmányozza át a <a href='%s'>telepítési útmutatót</a>.", +"%s shared »%s« with you" => "%s megosztotta Önnel ezt: »%s«", "Could not find category \"%s\"" => "Ez a kategória nem található: \"%s\"", "seconds ago" => "pár másodperce", "_%n minute ago_::_%n minutes ago_" => array("","%n perccel ezelőtt"), @@ -65,6 +60,9 @@ $TRANSLATIONS = array( "last month" => "múlt hónapban", "_%n month ago_::_%n months ago_" => array("%n hónappal ezelőtt","%n hónappal ezelőtt"), "last year" => "tavaly", -"years ago" => "több éve" +"years ago" => "több éve", +"A valid username must be provided" => "Érvényes felhasználónevet kell megadnia", +"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" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/ia.php b/lib/l10n/ia.php index 34f43bc424a..1fb600d76b4 100644 --- a/lib/l10n/ia.php +++ b/lib/l10n/ia.php @@ -5,12 +5,20 @@ $TRANSLATIONS = array( "Settings" => "Configurationes", "Users" => "Usatores", "Admin" => "Administration", +"Unknown filetype" => "Typo de file incognite", +"Invalid image" => "Imagine invalide", "web services under your control" => "servicios web sub tu controlo", "Files" => "Files", "Text" => "Texto", -"_%n minute ago_::_%n minutes ago_" => array("",""), -"_%n hour ago_::_%n hours ago_" => array("",""), +"seconds ago" => "secundas passate", +"_%n minute ago_::_%n minutes ago_" => array("","%n minutas passate"), +"_%n hour ago_::_%n hours ago_" => array("","%n horas passate"), +"today" => "hodie", +"yesterday" => "heri", "_%n day go_::_%n days ago_" => array("",""), -"_%n month ago_::_%n months ago_" => array("","") +"last month" => "ultime mense", +"_%n month ago_::_%n months ago_" => array("",""), +"last year" => "ultime anno", +"years ago" => "annos passate" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/id.php b/lib/l10n/id.php index 27d7843104b..d694967cd42 100644 --- a/lib/l10n/id.php +++ b/lib/l10n/id.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tipe berkas tak dikenal", "Invalid image" => "Gambar tidak sah", "web services under your control" => "layanan web dalam kendali anda", -"cannot open \"%s\"" => "tidak dapat membuka \"%s\"", "ZIP download is turned off." => "Pengunduhan ZIP dimatikan.", "Files need to be downloaded one by one." => "Berkas harus diunduh satu persatu.", "Back to Files" => "Kembali ke Berkas", @@ -40,13 +39,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s anda tidak boleh menggunakan karakter titik pada nama basis data", "MS SQL username and/or password not valid: %s" => "Nama pengguna dan/atau sandi MySQL tidak sah: %s", "You need to enter either an existing account or the administrator." => "Anda harus memasukkan akun yang sudah ada atau administrator.", -"MySQL username and/or password not valid" => "Nama pengguna dan/atau sandi MySQL tidak sah", "DB Error: \"%s\"" => "Galat Basis Data: \"%s\"", "Offending command was: \"%s\"" => "Perintah yang bermasalah: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Pengguna MySQL '%s'@'localhost' sudah ada.", -"Drop this user from MySQL" => "Hapus pengguna ini dari MySQL", -"MySQL user '%s'@'%%' already exists" => "Pengguna MySQL '%s'@'%%' sudah ada.", -"Drop this user from MySQL." => "Hapus pengguna ini dari MySQL.", "Oracle connection could not be established" => "Koneksi Oracle tidak dapat dibuat", "Oracle username and/or password not valid" => "Nama pengguna dan/atau sandi Oracle tidak sah", "Offending command was: \"%s\", name: %s, password: %s" => "Perintah yang bermasalah: \"%s\", nama pengguna: %s, sandi: %s", @@ -55,6 +49,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Atur sandi admin.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Web server Anda belum dikonfigurasikan dengan baik untuk mengizinkan sinkronisasi berkas karena tampaknya antarmuka WebDAV rusak.", "Please double check the <a href='%s'>installation guides</a>." => "Silakan periksa ulang <a href='%s'>panduan instalasi</a>.", +"%s shared »%s« with you" => "%s membagikan »%s« dengan anda", "Could not find category \"%s\"" => "Tidak menemukan kategori \"%s\"", "seconds ago" => "beberapa detik yang lalu", "_%n minute ago_::_%n minutes ago_" => array("%n menit yang lalu"), @@ -65,6 +60,8 @@ $TRANSLATIONS = array( "last month" => "bulan kemarin", "_%n month ago_::_%n months ago_" => array("%n bulan yang lalu"), "last year" => "tahun kemarin", -"years ago" => "beberapa tahun lalu" +"years ago" => "beberapa tahun lalu", +"A valid username must be provided" => "Tuliskan nama pengguna yang valid", +"A valid password must be provided" => "Tuliskan sandi yang valid" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/it.php b/lib/l10n/it.php index cd2073bfd0a..db0f99a8de9 100644 --- a/lib/l10n/it.php +++ b/lib/l10n/it.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Stai accedendo al server da un dominio non affidabile.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Contatta il tuo amministratore di sistema. Se sei un amministratore, configura l'impostazione \"trusted_domain\" in config/config.php. Un esempio di configurazione è disponibile in config/config.sample.php.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "L'applicazione \"%s\" non può essere installata poiché non è compatibile con questa versione di ownCloud.", "No app name specified" => "Il nome dell'applicazione non è specificato", "Help" => "Aiuto", @@ -11,7 +13,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tipo di file sconosciuto", "Invalid image" => "Immagine non valida", "web services under your control" => "servizi web nelle tue mani", -"cannot open \"%s\"" => "impossibile aprire \"%s\"", "ZIP download is turned off." => "Lo scaricamento in formato ZIP è stato disabilitato.", "Files need to be downloaded one by one." => "I file devono essere scaricati uno alla volta.", "Back to Files" => "Torna ai file", @@ -25,13 +26,14 @@ $TRANSLATIONS = array( "App does not provide an info.xml file" => "L'applicazione non fornisce un file info.xml", "App can't be installed because of not allowed code in the App" => "L'applicazione non può essere installata a causa di codice non consentito al suo interno", "App can't be installed because it is not compatible with this version of ownCloud" => "L'applicazione non può essere installata poiché non è compatibile con questa versione di ownCloud", -"App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "L'applicazione non può essere installata poiché contiene il tag <shipped>true<shipped> che non è permesso alle applicazioni non shipped", +"App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "L'applicazione non può essere installata poiché contiene il tag <shipped>true<shipped> che è consentito per le applicazioni native", "App can't be installed because the version in info.xml/version is not the same as the version reported from the app store" => "L'applicazione non può essere installata poiché la versione in info.xml/version non è la stessa riportata dall'app store", "App directory already exists" => "La cartella dell'applicazione esiste già", "Can't create app folder. Please fix permissions. %s" => "Impossibile creare la cartella dell'applicazione. Correggi i permessi. %s", "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", "Files" => "File", "Text" => "Testo", "Images" => "Immagini", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s non dovresti utilizzare punti nel nome del database", "MS SQL username and/or password not valid: %s" => "Nome utente e/o password MS SQL non validi: %s", "You need to enter either an existing account or the administrator." => "È necessario inserire un account esistente o l'amministratore.", -"MySQL username and/or password not valid" => "Nome utente e/o password di MySQL non validi", +"MySQL/MariaDB username and/or password not valid" => "Nome utente e/o password di MySQL/MariaDB non validi", "DB Error: \"%s\"" => "Errore DB: \"%s\"", "Offending command was: \"%s\"" => "Il comando non consentito era: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "L'utente MySQL '%s'@'localhost' esiste già.", -"Drop this user from MySQL" => "Elimina questo utente da MySQL", -"MySQL user '%s'@'%%' already exists" => "L'utente MySQL '%s'@'%%' esiste già", -"Drop this user from MySQL." => "Elimina questo utente da MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "L'utente MySQL/MariaDB '%s'@'localhost' esiste già.", +"Drop this user from MySQL/MariaDB" => "Elimina questo utente da MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "L'utente MySQL/MariaDB '%s'@'%%' esiste già", +"Drop this user from MySQL/MariaDB." => "Elimina questo utente da MySQL/MariaDB.", "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", "Offending command was: \"%s\", name: %s, password: %s" => "Il comando non consentito era: \"%s\", nome: %s, password: %s", @@ -55,6 +57,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Imposta una password di amministrazione.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Il tuo server web non è configurato correttamente per consentire la sincronizzazione dei file poiché l'interfaccia WebDAV sembra essere danneggiata.", "Please double check the <a href='%s'>installation guides</a>." => "Leggi attentamente le <a href='%s'>guide d'installazione</a>.", +"%s shared »%s« with you" => "%s ha condiviso «%s» con te", +"Sharing %s failed, because the file does not exist" => "Condivisione di %s non riuscita, poiché il file non esiste", +"Sharing %s failed, because the user %s is the item owner" => "Condivisione di %s non riuscita, poiché l'utente %s è il proprietario dell'oggetto", +"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 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", +"Sharing %s failed, because sharing with links is not allowed" => "Condivisione di %s non riuscita, poiché i collegamenti non sono consentiti", +"Share type %s is not valid for %s" => "Il tipo di condivisione %s non è valido per %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", +"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 %s failed, because the user %s is the original sharer" => "Condivisione di %s non riuscita, poiché l'utente %s l'ha condiviso precedentemente", +"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", "Could not find category \"%s\"" => "Impossibile trovare la categoria \"%s\"", "seconds ago" => "secondi fa", "_%n minute ago_::_%n minutes ago_" => array("%n minuto fa","%n minuti fa"), @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "mese scorso", "_%n month ago_::_%n months ago_" => array("%n mese fa","%n mesi fa"), "last year" => "anno scorso", -"years ago" => "anni fa" +"years ago" => "anni fa", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "Solo i seguenti caratteri sono ammessi in un nome utente: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-\"", +"A valid username must be provided" => "Deve essere fornito un nome utente valido", +"A valid password must be provided" => "Deve essere fornita una password valida", +"The username is already being used" => "Il nome utente è già utilizzato" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/ja.php b/lib/l10n/ja.php new file mode 100644 index 00000000000..ec5352242d5 --- /dev/null +++ b/lib/l10n/ja.php @@ -0,0 +1,81 @@ +<?php +$TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "あなたはサーバに、信用が付与されていないドメインからアクセスしています。", +"App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => " \"%s\" アプリは、このバージョンのownCloudと互換性がないためインストールできません。", +"No app name specified" => "アプリ名が未指定", +"Help" => "ヘルプ", +"Personal" => "個人", +"Settings" => "設定", +"Users" => "ユーザー", +"Admin" => "管理", +"Failed to upgrade \"%s\"." => "\"%s\" へのアップグレードに失敗しました。", +"Unknown filetype" => "不明なファイルタイプ", +"Invalid image" => "無効な画像", +"web services under your control" => "管理下のウェブサービス", +"ZIP download is turned off." => "ZIPダウンロードは無効です。", +"Files need to be downloaded one by one." => "ファイルは1つずつダウンロードする必要があります。", +"Back to Files" => "ファイルに戻る", +"Selected files too large to generate zip file." => "選択したファイルはZIPファイルの生成には大きすぎます。", +"Please download the files separately in smaller chunks or kindly ask your administrator." => "少しずつに分けてファイルをダウンロードするか、管理者に問い合わせてください。", +"No source specified when installing app" => "アプリインストール時のソースが未指定", +"No href specified when installing app from http" => "アプリインストール時のhttpの URL が未指定", +"No path specified when installing app from local file" => "アプリインストール時のローカルファイルのパスが未指定", +"Archives of type %s are not supported" => "\"%s\"タイプのアーカイブ形式は未サポート", +"Failed to open archive when installing app" => "アプリをインストール中にアーカイブファイルを開けませんでした。", +"App does not provide an info.xml file" => "アプリにinfo.xmlファイルが入っていません", +"App can't be installed because of not allowed code in the App" => "アプリで許可されないコードが入っているのが原因でアプリがインストールできません", +"App can't be installed because it is not compatible with this version of ownCloud" => "アプリは、このバージョンのownCloudと互換性がないためインストールできません。", +"App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "非shippedアプリには許可されない<shipped>true</shipped>タグが含まれているためにアプリをインストールできません。", +"App can't be installed because the version in info.xml/version is not the same as the version reported from the app store" => "info.xml/versionのバージョンがアプリストアのバージョンと合っていないため、アプリはインストールされません", +"App directory already exists" => "アプリディレクトリはすでに存在します", +"Can't create app folder. Please fix permissions. %s" => "アプリフォルダーを作成できませんでした。%s のパーミッションを修正してください。", +"Application is not enabled" => "アプリケーションは無効です", +"Authentication error" => "認証エラー", +"Token expired. Please reload page." => "トークンが無効になりました。ページを再読込してください。", +"Unknown user" => "不明なユーザー", +"Files" => "ファイル", +"Text" => "TTY TDD", +"Images" => "画像", +"%s enter the database username." => "%s のデータベースのユーザー名を入力してください。", +"%s enter the database name." => "%s のデータベース名を入力してください。", +"%s you may not use dots in the database name" => "%s ではデータベース名にドットを利用できないかもしれません。", +"MS SQL username and/or password not valid: %s" => "MS SQL サーバーのユーザー名/パスワードが正しくありません: %s", +"You need to enter either an existing account or the administrator." => "既存のアカウントもしくは管理者のどちらかを入力する必要があります。", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB のユーザー名及び/またはパスワードが無効", +"DB Error: \"%s\"" => "DBエラー: \"%s\"", +"Offending command was: \"%s\"" => "違反コマンド: \"%s\"", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB のユーザー '%s'@'localhost' はすでに存在します。", +"Drop this user from MySQL/MariaDB" => "MySQL/MariaDB からこのユーザーを削除", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB のユーザー '%s'@'%%' はすでに存在します", +"Drop this user from MySQL/MariaDB." => "MySQL/MariaDB からこのユーザーを削除。", +"Oracle connection could not be established" => "Oracleへの接続が確立できませんでした。", +"Oracle username and/or password not valid" => "Oracleのユーザー名もしくはパスワードは有効ではありません", +"Offending command was: \"%s\", name: %s, password: %s" => "違反コマンド: \"%s\"、名前: %s、パスワード: %s", +"PostgreSQL username and/or password not valid" => "PostgreSQLのユーザー名もしくはパスワードは有効ではありません", +"Set an admin username." => "管理者のユーザー名を設定", +"Set an admin password." => "管理者のパスワードを設定。", +"Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "WebDAVインターフェースに問題があると思われるため、WEBサーバーはまだファイルの同期を許可するよう適切に設定されていません。", +"Please double check the <a href='%s'>installation guides</a>." => "<a href='%s'>インストールガイド</a>をよく確認してください。", +"%s shared »%s« with you" => "%sが あなたと »%s«を共有しました", +"Sharing %s failed, because the file does not exist" => "%s の共有に失敗しました。そのようなファイルは存在しないからです。", +"Sharing backend %s not found" => "共有バックエンド %s が見つかりません", +"Sharing backend for %s not found" => "%s のための共有バックエンドが見つかりません", +"Sharing %s failed, because the user %s is the original sharer" => "%s の共有に失敗しました。ユーザ %s が元々の共有者であるからです。", +"Sharing %s failed, because the permissions exceed permissions granted to %s" => "%s の共有に失敗しました。%s に付与されている許可を超えているからです。", +"Sharing %s failed, because resharing is not allowed" => "%s の共有に失敗しました。再共有が許されていないからです。", +"Could not find category \"%s\"" => "カテゴリ \"%s\" が見つかりませんでした", +"seconds ago" => "数秒前", +"_%n minute ago_::_%n minutes ago_" => array("%n 分前"), +"_%n hour ago_::_%n hours ago_" => array("%n 時間前"), +"today" => "今日", +"yesterday" => "1日前", +"_%n day go_::_%n days ago_" => array("%n日前"), +"last month" => "1ヶ月前", +"_%n month ago_::_%n months ago_" => array("%nヶ月前"), +"last year" => "1年前", +"years ago" => "年前", +"A valid username must be provided" => "有効なユーザー名を指定する必要があります", +"A valid password must be provided" => "有効なパスワードを指定する必要があります", +"The username is already being used" => "ユーザ名はすでに使われています" +); +$PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/ja_JP.php b/lib/l10n/ja_JP.php index 9c5c0ba4763..015c885904e 100644 --- a/lib/l10n/ja_JP.php +++ b/lib/l10n/ja_JP.php @@ -1,17 +1,16 @@ <?php $TRANSLATIONS = array( -"App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => " \"%s\" アプリは、このバージョンのownCloudと互換性がない為、インストールできません。", +"App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => " \"%s\" アプリは、このバージョンのownCloudと互換性がないためインストールできません。", "No app name specified" => "アプリ名が未指定", "Help" => "ヘルプ", "Personal" => "個人", "Settings" => "設定", -"Users" => "ユーザ", +"Users" => "ユーザー", "Admin" => "管理", "Failed to upgrade \"%s\"." => "\"%s\" へのアップグレードに失敗しました。", "Unknown filetype" => "不明なファイルタイプ", "Invalid image" => "無効な画像", "web services under your control" => "管理下のウェブサービス", -"cannot open \"%s\"" => "\"%s\" が開けません", "ZIP download is turned off." => "ZIPダウンロードは無効です。", "Files need to be downloaded one by one." => "ファイルは1つずつダウンロードする必要があります。", "Back to Files" => "ファイルに戻る", @@ -24,47 +23,49 @@ $TRANSLATIONS = array( "Failed to open archive when installing app" => "アプリをインストール中にアーカイブファイルを開けませんでした。", "App does not provide an info.xml file" => "アプリにinfo.xmlファイルが入っていません", "App can't be installed because of not allowed code in the App" => "アプリで許可されないコードが入っているのが原因でアプリがインストールできません", -"App can't be installed because it is not compatible with this version of ownCloud" => "アプリは、このバージョンのownCloudと互換性がない為、インストールできません。", -"App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "非shippedアプリには許可されない<shipped>true</shipped>タグが含まれているためにアプリをインストール出来ません。", -"App can't be installed because the version in info.xml/version is not the same as the version reported from the app store" => "info.xml/versionのバージョンがアプリストアのバージョンと合っていない為、アプリはインストールされません", -"App directory already exists" => "アプリディレクトリは既に存在します", -"Can't create app folder. Please fix permissions. %s" => "アプリフォルダを作成出来ませんでした。%s のパーミッションを修正してください。", +"App can't be installed because it is not compatible with this version of ownCloud" => "アプリは、このバージョンのownCloudと互換性がないためインストールできません。", +"App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "非shippedアプリには許可されない<shipped>true</shipped>タグが含まれているためにアプリをインストールできません。", +"App can't be installed because the version in info.xml/version is not the same as the version reported from the app store" => "info.xml/versionのバージョンがアプリストアのバージョンと合っていないため、アプリはインストールされません", +"App directory already exists" => "アプリディレクトリはすでに存在します", +"Can't create app folder. Please fix permissions. %s" => "アプリフォルダーを作成できませんでした。%s のパーミッションを修正してください。", "Application is not enabled" => "アプリケーションは無効です", "Authentication error" => "認証エラー", "Token expired. Please reload page." => "トークンが無効になりました。ページを再読込してください。", +"Unknown user" => "不明なユーザー", "Files" => "ファイル", "Text" => "TTY TDD", "Images" => "画像", -"%s enter the database username." => "%s のデータベースのユーザ名を入力してください。", +"%s enter the database username." => "%s のデータベースのユーザー名を入力してください。", "%s enter the database name." => "%s のデータベース名を入力してください。", "%s you may not use dots in the database name" => "%s ではデータベース名にドットを利用できないかもしれません。", "MS SQL username and/or password not valid: %s" => "MS SQL サーバーのユーザー名/パスワードが正しくありません: %s", "You need to enter either an existing account or the administrator." => "既存のアカウントもしくは管理者のどちらかを入力する必要があります。", -"MySQL username and/or password not valid" => "MySQLのユーザ名もしくはパスワードは有効ではありません", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB のユーザー名及び/またはパスワードが無効", "DB Error: \"%s\"" => "DBエラー: \"%s\"", "Offending command was: \"%s\"" => "違反コマンド: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQLのユーザ '%s'@'localhost' はすでに存在します。", -"Drop this user from MySQL" => "MySQLからこのユーザを削除", -"MySQL user '%s'@'%%' already exists" => "MySQLのユーザ '%s'@'%%' はすでに存在します。", -"Drop this user from MySQL." => "MySQLからこのユーザを削除する。", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB のユーザー '%s'@'localhost' はすでに存在します。", +"Drop this user from MySQL/MariaDB" => "MySQL/MariaDB からこのユーザーを削除", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB のユーザー '%s'@'%%' はすでに存在します", +"Drop this user from MySQL/MariaDB." => "MySQL/MariaDB からこのユーザーを削除。", "Oracle connection could not be established" => "Oracleへの接続が確立できませんでした。", -"Oracle username and/or password not valid" => "Oracleのユーザ名もしくはパスワードは有効ではありません", +"Oracle username and/or password not valid" => "Oracleのユーザー名もしくはパスワードは有効ではありません", "Offending command was: \"%s\", name: %s, password: %s" => "違反コマンド: \"%s\"、名前: %s、パスワード: %s", -"PostgreSQL username and/or password not valid" => "PostgreSQLのユーザ名もしくはパスワードは有効ではありません", -"Set an admin username." => "管理者のユーザ名を設定。", +"PostgreSQL username and/or password not valid" => "PostgreSQLのユーザー名もしくはパスワードは有効ではありません", +"Set an admin username." => "管理者のユーザー名を設定", "Set an admin password." => "管理者のパスワードを設定。", -"Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "WebDAVインタフェースが動作していないと考えられるため、あなたのWEBサーバはまだファイルの同期を許可するように適切な設定がされていません。", +"Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "WebDAVインターフェースに問題があると思われるため、WEBサーバーはまだファイルの同期を許可するよう適切に設定されていません。", "Please double check the <a href='%s'>installation guides</a>." => "<a href='%s'>インストールガイド</a>をよく確認してください。", +"%s shared »%s« with you" => "%sが あなたと »%s«を共有しました", "Could not find category \"%s\"" => "カテゴリ \"%s\" が見つかりませんでした", "seconds ago" => "数秒前", "_%n minute ago_::_%n minutes ago_" => array("%n 分前"), "_%n hour ago_::_%n hours ago_" => array("%n 時間前"), "today" => "今日", -"yesterday" => "昨日", -"_%n day go_::_%n days ago_" => array("%n 日前"), -"last month" => "一月前", -"_%n month ago_::_%n months ago_" => array("%n ヶ月前"), -"last year" => "一年前", +"yesterday" => "1日前", +"_%n day go_::_%n days ago_" => array("%n日前"), +"last month" => "1ヶ月前", +"_%n month ago_::_%n months ago_" => array("%nヶ月前"), +"last year" => "1年前", "years ago" => "年前" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/jv.php b/lib/l10n/jv.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/jv.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/ka_GE.php b/lib/l10n/ka_GE.php index 0cf6ab333e8..f15563c297d 100644 --- a/lib/l10n/ka_GE.php +++ b/lib/l10n/ka_GE.php @@ -21,13 +21,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s არ მიუთითოთ წერტილი ბაზის სახელში", "MS SQL username and/or password not valid: %s" => "MS SQL მომხმარებელი და/ან პაროლი არ არის მართებული: %s", "You need to enter either an existing account or the administrator." => "თქვენ უნდა შეიყვანოთ არსებული მომხმარებელის სახელი ან ადმინისტრატორი.", -"MySQL username and/or password not valid" => "MySQL იუზერნეიმი და/ან პაროლი არ არის სწორი", "DB Error: \"%s\"" => "DB შეცდომა: \"%s\"", "Offending command was: \"%s\"" => "Offending ბრძანება იყო: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL მომხმარებელი '%s'@'localhost' უკვე არსებობს.", -"Drop this user from MySQL" => "წაშალე ეს მომხამრებელი MySQL–იდან", -"MySQL user '%s'@'%%' already exists" => "MySQL მომხმარებელი '%s'@'%%' უკვე არსებობს", -"Drop this user from MySQL." => "წაშალე ეს მომხამრებელი MySQL–იდან", "Oracle username and/or password not valid" => "Oracle იუზერნეიმი და/ან პაროლი არ არის სწორი", "Offending command was: \"%s\", name: %s, password: %s" => "Offending ბრძანება იყო: \"%s\", სახელი: %s, პაროლი: %s", "PostgreSQL username and/or password not valid" => "PostgreSQL იუზერნეიმი და/ან პაროლი არ არის სწორი", @@ -45,6 +40,8 @@ $TRANSLATIONS = array( "last month" => "გასულ თვეში", "_%n month ago_::_%n months ago_" => array(""), "last year" => "ბოლო წელს", -"years ago" => "წლის წინ" +"years ago" => "წლის წინ", +"A valid username must be provided" => "უნდა მიუთითოთ არსებული მომხმარებლის სახელი", +"A valid password must be provided" => "უნდა მიუთითოთ არსებული პაროლი" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/km.php b/lib/l10n/km.php index e7b09649a24..d5fdc025760 100644 --- a/lib/l10n/km.php +++ b/lib/l10n/km.php @@ -1,8 +1,46 @@ <?php $TRANSLATIONS = array( -"_%n minute ago_::_%n minutes ago_" => array(""), -"_%n hour ago_::_%n hours ago_" => array(""), -"_%n day go_::_%n days ago_" => array(""), -"_%n month ago_::_%n months ago_" => array("") +"App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "មិនអាចដំឡើងកម្មវិធី \"%s\" បាន ព្រោះតែវាមិនត្រូវគ្នានឹងកំណែ ownCloud នេះទេ។", +"No app name specified" => "មិនបានបញ្ជាក់ឈ្មោះកម្មវិធី", +"Help" => "ជំនួយ", +"Personal" => "ផ្ទាល់ខ្លួន", +"Settings" => "ការកំណត់", +"Users" => "អ្នកប្រើ", +"Admin" => "អ្នកគ្រប់គ្រង", +"Unknown filetype" => "មិនស្គាល់ប្រភេទឯកសារ", +"Invalid image" => "រូបភាពមិនត្រឹមត្រូវ", +"web services under your control" => "សេវាកម្មវេបក្រោមការការបញ្ជារបស់អ្នក", +"ZIP download is turned off." => "បានបិទការទាញយក ZIP ។", +"Files need to be downloaded one by one." => "ត្រូវការទាញយកឯកសារម្ដងមួយៗ។", +"Back to Files" => "ត្រឡប់ទៅឯកសារ", +"Selected files too large to generate zip file." => "ឯកសារដែលបានជ្រើស មានទំហំធំពេកក្នុងការបង្កើតជា zip ។", +"App directory already exists" => "មានទីតាំងផ្ទុកកម្មវិធីរួចហើយ", +"Can't create app folder. Please fix permissions. %s" => "មិនអាចបង្កើតថតកម្មវិធី។ សូមកែសម្រួលសិទ្ធិ។ %s", +"Application is not enabled" => "មិនបានបើកកម្មវិធី", +"Authentication error" => "កំហុសការផ្ទៀងផ្ទាត់ភាពត្រឹមត្រូវ", +"Files" => "ឯកសារ", +"Text" => "អត្ថបទ", +"Images" => "រូបភាព", +"%s enter the database username." => "%s វាយបញ្ចូលឈ្មោះអ្នកប្រើមូលដ្ឋានទិន្នន័យ។", +"%s enter the database name." => "%s វាយបញ្ចូលឈ្មោះមូលដ្ឋានទិន្នន័យ។", +"%s you may not use dots in the database name" => "%s អ្នកអាចមិនប្រើសញ្ញាចុចនៅក្នុងឈ្មោះមូលដ្ឋានទិន្នន័យ", +"DB Error: \"%s\"" => "កំហុស DB៖ \"%s\"", +"Oracle connection could not be established" => "មិនអាចបង្កើតការតភ្ជាប់ Oracle", +"PostgreSQL username and/or password not valid" => "ឈ្មោះអ្នកប្រើ និង/ឬ ពាក្យសម្ងាត់ PostgreSQL គឺមិនត្រូវទេ", +"Set an admin username." => "កំណត់ឈ្មោះអ្នកគ្រប់គ្រង។", +"Set an admin password." => "កំណត់ពាក្យសម្ងាត់អ្នកគ្រប់គ្រង។", +"Could not find category \"%s\"" => "រកមិនឃើញចំណាត់ក្រុម \"%s\"", +"seconds ago" => "វិនាទីមុន", +"_%n minute ago_::_%n minutes ago_" => array("%n នាទីមុន"), +"_%n hour ago_::_%n hours ago_" => array("%n ម៉ោងមុន"), +"today" => "ថ្ងៃនេះ", +"yesterday" => "ម្សិលមិញ", +"_%n day go_::_%n days ago_" => array("%n ថ្ងៃមុន"), +"last month" => "ខែមុន", +"_%n month ago_::_%n months ago_" => array("%n ខែមុន"), +"last year" => "ឆ្នាំមុន", +"years ago" => "ឆ្នាំមុន", +"A valid username must be provided" => "ត្រូវផ្ដល់ឈ្មោះអ្នកប្រើឲ្យបានត្រឹមត្រូវ", +"A valid password must be provided" => "ត្រូវផ្ដល់ពាក្យសម្ងាត់ឲ្យបានត្រឹមត្រូវ" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/ko.php b/lib/l10n/ko.php index b33ad01546f..977a491ad74 100644 --- a/lib/l10n/ko.php +++ b/lib/l10n/ko.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "알 수 없는 파일 형식", "Invalid image" => "잘못된 그림", "web services under your control" => "내가 관리하는 웹 서비스", -"cannot open \"%s\"" => "\"%s\"을(를) 열 수 없습니다.", "ZIP download is turned off." => "ZIP 다운로드가 비활성화 되었습니다.", "Files need to be downloaded one by one." => "파일을 개별적으로 다운로드해야 합니다.", "Back to Files" => "파일로 돌아가기", @@ -32,6 +31,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "앱이 활성화되지 않았습니다", "Authentication error" => "인증 오류", "Token expired. Please reload page." => "토큰이 만료되었습니다. 페이지를 새로 고치십시오.", +"Unknown user" => "알려지지 않은 사용자", "Files" => "파일", "Text" => "텍스트", "Images" => "그림", @@ -40,13 +40,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s 데이터베이스 이름에는 마침표를 사용할 수 없습니다", "MS SQL username and/or password not valid: %s" => "MS SQL 사용자 이름이나 암호가 잘못되었습니다: %s", "You need to enter either an existing account or the administrator." => "기존 계정이나 administrator(관리자)를 입력해야 합니다.", -"MySQL username and/or password not valid" => "MySQL 사용자 이름이나 암호가 잘못되었습니다.", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB 사용자 명 혹은 비밀번호가 일치하지 않습니다", "DB Error: \"%s\"" => "DB 오류: \"%s\"", "Offending command was: \"%s\"" => "잘못된 명령: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL 사용자 '%s'@'localhost'이(가) 이미 존재합니다.", -"Drop this user from MySQL" => "이 사용자를 MySQL에서 삭제하십시오", -"MySQL user '%s'@'%%' already exists" => "MySQL 사용자 '%s'@'%%'이(가) 이미 존재합니다.", -"Drop this user from MySQL." => "이 사용자를 MySQL에서 삭제하십시오.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB '%s'@'localhost' 사용자가 이미 존재합니다", +"Drop this user from MySQL/MariaDB" => "MySQL/MariaDB에서 이 사용자 제거하기", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB '%s'@'%%' 사용자가 이미 존재합니다", +"Drop this user from MySQL/MariaDB." => "MySQL/MariaDB에서 이 사용자 제거하기", "Oracle connection could not be established" => "Oracle 연결을 수립할 수 없습니다.", "Oracle username and/or password not valid" => "Oracle 사용자 이름이나 암호가 잘못되었습니다.", "Offending command was: \"%s\", name: %s, password: %s" => "잘못된 명령: \"%s\", 이름: %s, 암호: %s", @@ -55,6 +55,7 @@ $TRANSLATIONS = array( "Set an admin password." => "관리자의 암호를 설정합니다.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "WebDAV 인터페이스가 제대로 작동하지 않습니다. 웹 서버에서 파일 동기화를 사용할 수 있도록 설정이 제대로 되지 않은 것 같습니다.", "Please double check the <a href='%s'>installation guides</a>." => "<a href='%s'>설치 가이드</a>를 다시 한 번 확인하십시오.", +"%s shared »%s« with you" => "%s 님이 %s을(를) 공유하였습니다", "Could not find category \"%s\"" => "분류 \"%s\"을(를) 찾을 수 없습니다.", "seconds ago" => "초 전", "_%n minute ago_::_%n minutes ago_" => array("%n분 전 "), @@ -65,6 +66,10 @@ $TRANSLATIONS = array( "last month" => "지난 달", "_%n month ago_::_%n months ago_" => array("%n달 전 "), "last year" => "작년", -"years ago" => "년 전" +"years ago" => "년 전", +"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" => "올바른 사용자 이름을 입력해야 함", +"A valid password must be provided" => "올바른 암호를 입력해야 함", +"The username is already being used" => "이 사용자명은 현재 사용중입니다" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/ku_IQ.php b/lib/l10n/ku_IQ.php index c99f9dd2a12..133fe99f2d3 100644 --- a/lib/l10n/ku_IQ.php +++ b/lib/l10n/ku_IQ.php @@ -5,6 +5,7 @@ $TRANSLATIONS = array( "Users" => "بهكارهێنهر", "Admin" => "بهڕێوهبهری سهرهكی", "web services under your control" => "ڕاژهی وێب لهژێر چاودێریت دایه", +"Files" => "پهڕگەکان", "_%n minute ago_::_%n minutes ago_" => array("",""), "_%n hour ago_::_%n hours ago_" => array("",""), "_%n day go_::_%n days ago_" => array("",""), diff --git a/lib/l10n/lb.php b/lib/l10n/lb.php index 629d5b11c30..9caa876655a 100644 --- a/lib/l10n/lb.php +++ b/lib/l10n/lb.php @@ -11,6 +11,7 @@ $TRANSLATIONS = array( "Authentication error" => "Authentifikatioun's Fehler", "Files" => "Dateien", "Text" => "SMS", +"%s shared »%s« with you" => "Den/D' %s huet »%s« mat dir gedeelt", "seconds ago" => "Sekonnen hir", "_%n minute ago_::_%n minutes ago_" => array("","%n Minutten hir"), "_%n hour ago_::_%n hours ago_" => array("",""), diff --git a/lib/l10n/lt_LT.php b/lib/l10n/lt_LT.php index 25957702d2d..88aca7aba46 100644 --- a/lib/l10n/lt_LT.php +++ b/lib/l10n/lt_LT.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Nežinomas failo tipas", "Invalid image" => "Netinkamas paveikslėlis", "web services under your control" => "jūsų valdomos web paslaugos", -"cannot open \"%s\"" => "nepavyksta atverti „%s“", "ZIP download is turned off." => "ZIP atsisiuntimo galimybė yra išjungta.", "Files need to be downloaded one by one." => "Failai turi būti parsiunčiami vienas po kito.", "Back to Files" => "Atgal į Failus", @@ -40,13 +39,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s negalite naudoti taškų duombazės pavadinime", "MS SQL username and/or password not valid: %s" => "MS SQL naudotojo vardas ir/arba slaptažodis netinka: %s", "You need to enter either an existing account or the administrator." => "Turite prisijungti su egzistuojančia paskyra arba su administratoriumi.", -"MySQL username and/or password not valid" => "Neteisingas MySQL naudotojo vardas ir/arba slaptažodis", "DB Error: \"%s\"" => "DB klaida: \"%s\"", "Offending command was: \"%s\"" => "Vykdyta komanda buvo: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL naudotojas '%s'@'localhost' jau egzistuoja.", -"Drop this user from MySQL" => "Pašalinti šį naudotoją iš MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL naudotojas '%s'@'%%' jau egzistuoja", -"Drop this user from MySQL." => "Pašalinti šį naudotoją iš MySQL.", "Oracle connection could not be established" => "Nepavyko sukurti Oracle ryšio", "Oracle username and/or password not valid" => "Neteisingas Oracle naudotojo vardas ir/arba slaptažodis", "Offending command was: \"%s\", name: %s, password: %s" => "Vykdyta komanda buvo: \"%s\", name: %s, password: %s", @@ -55,6 +49,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Nustatyti administratoriaus slaptažodį.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Jūsų serveris nėra tvarkingai nustatytas leisti failų sinchronizaciją, nes WebDAV sąsaja panašu, kad yra sugadinta.", "Please double check the <a href='%s'>installation guides</a>." => "Prašome pažiūrėkite dar kartą <a href='%s'>diegimo instrukcijas</a>.", +"%s shared »%s« with you" => "%s pasidalino »%s« su tavimi", "Could not find category \"%s\"" => "Nepavyko rasti kategorijos „%s“", "seconds ago" => "prieš sekundę", "_%n minute ago_::_%n minutes ago_" => array("prieš %n min.","Prieš % minutes","Prieš %n minučių"), @@ -65,6 +60,8 @@ $TRANSLATIONS = array( "last month" => "praeitą mėnesį", "_%n month ago_::_%n months ago_" => array("Prieš %n mėnesį","Prieš %n mėnesius","Prieš %n mėnesių"), "last year" => "praeitais metais", -"years ago" => "prieš metus" +"years ago" => "prieš metus", +"A valid username must be provided" => "Vartotojo vardas turi būti tinkamas", +"A valid password must be provided" => "Slaptažodis turi būti tinkamas" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/lib/l10n/lv.php b/lib/l10n/lv.php index 8ecee5bdae8..1c8a31aff2f 100644 --- a/lib/l10n/lv.php +++ b/lib/l10n/lv.php @@ -7,7 +7,6 @@ $TRANSLATIONS = array( "Admin" => "Administratori", "Failed to upgrade \"%s\"." => "Kļūda atjauninot \"%s\"", "web services under your control" => "tīmekļa servisi tavā varā", -"cannot open \"%s\"" => "Nevar atvērt \"%s\"", "ZIP download is turned off." => "ZIP lejupielādēšana ir izslēgta.", "Files need to be downloaded one by one." => "Datnes var lejupielādēt tikai katru atsevišķi.", "Back to Files" => "Atpakaļ pie datnēm", @@ -23,13 +22,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s datubāžu nosaukumos nedrīkst izmantot punktus", "MS SQL username and/or password not valid: %s" => "Nav derīga MySQL parole un/vai lietotājvārds — %s", "You need to enter either an existing account or the administrator." => "Jums jāievada vai nu esošs vai administratora konts.", -"MySQL username and/or password not valid" => "Nav derīga MySQL parole un/vai lietotājvārds", "DB Error: \"%s\"" => "DB kļūda — “%s”", "Offending command was: \"%s\"" => "Vainīgā komanda bija “%s”", -"MySQL user '%s'@'localhost' exists already." => "MySQL lietotājs %s'@'localhost' jau eksistē.", -"Drop this user from MySQL" => "Izmest šo lietotāju no MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL lietotājs '%s'@'%%' jau eksistē", -"Drop this user from MySQL." => "Izmest šo lietotāju no MySQL.", "Oracle connection could not be established" => "Nevar izveidot savienojumu ar Oracle", "Oracle username and/or password not valid" => "Nav derīga Oracle parole un/vai lietotājvārds", "Offending command was: \"%s\", name: %s, password: %s" => "Vainīgā komanda bija \"%s\", vārds: %s, parole: %s", @@ -38,6 +32,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Iestatiet administratora paroli.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Jūsu serveris vēl nav pareizi iestatīts, lai ļautu sinhronizēt datnes, jo izskatās, ka WebDAV saskarne ir salauzta.", "Please double check the <a href='%s'>installation guides</a>." => "Lūdzu, vēlreiz pārbaudiet <a href='%s'>instalēšanas palīdzību</a>.", +"%s shared »%s« with you" => "%s kopīgots »%s« ar jums", "Could not find category \"%s\"" => "Nevarēja atrast kategoriju “%s”", "seconds ago" => "sekundes atpakaļ", "_%n minute ago_::_%n minutes ago_" => array("","","Pirms %n minūtēm"), @@ -48,6 +43,9 @@ $TRANSLATIONS = array( "last month" => "pagājušajā mēnesī", "_%n month ago_::_%n months ago_" => array("","","Pirms %n mēnešiem"), "last year" => "gājušajā gadā", -"years ago" => "gadus atpakaļ" +"years ago" => "gadus atpakaļ", +"A valid username must be provided" => "Jānorāda derīgs lietotājvārds", +"A valid password must be provided" => "Jānorāda derīga parole", +"The username is already being used" => "Šāds lietotājvārds jau tiek izmantots" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"; diff --git a/lib/l10n/mk.php b/lib/l10n/mk.php index deaf1bc8d05..a0b3c39865a 100644 --- a/lib/l10n/mk.php +++ b/lib/l10n/mk.php @@ -28,6 +28,8 @@ $TRANSLATIONS = array( "last month" => "минатиот месец", "_%n month ago_::_%n months ago_" => array("",""), "last year" => "минатата година", -"years ago" => "пред години" +"years ago" => "пред години", +"A valid username must be provided" => "Мора да се обезбеди валидно корисничко име ", +"A valid password must be provided" => "Мора да се обезбеди валидна лозинка" ); $PLURAL_FORMS = "nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"; diff --git a/lib/l10n/ml.php b/lib/l10n/ml.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/ml.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/mn.php b/lib/l10n/mn.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/mn.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/nb_NO.php b/lib/l10n/nb_NO.php index 5da36f9be37..fd9b631f23c 100644 --- a/lib/l10n/nb_NO.php +++ b/lib/l10n/nb_NO.php @@ -1,10 +1,13 @@ <?php $TRANSLATIONS = array( +"App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "App \"%s\" kan ikke installeres fordi den ikke er kompatibel med denne versjonen av ownCloud.", +"No app name specified" => "Intet app-navn spesifisert", "Help" => "Hjelp", "Personal" => "Personlig", "Settings" => "Innstillinger", "Users" => "Brukere", "Admin" => "Admin", +"Failed to upgrade \"%s\"." => "Klarte ikke å oppgradere \"%s\".", "Unknown filetype" => "Ukjent filtype", "Invalid image" => "Ugyldig bilde", "web services under your control" => "web tjenester du kontrollerer", @@ -12,14 +15,41 @@ $TRANSLATIONS = array( "Files need to be downloaded one by one." => "Filene må lastes ned en om gangen", "Back to Files" => "Tilbake til filer", "Selected files too large to generate zip file." => "De valgte filene er for store til å kunne generere ZIP-fil", +"Please download the files separately in smaller chunks or kindly ask your administrator." => "Vennligst last ned filene separat i mindre deler eller spør administratoren pent.", +"No source specified when installing app" => "Ingen kilde spesifisert ved installering av app", +"No href specified when installing app from http" => "Ingen href spesifisert ved installering av app fra http", +"No path specified when installing app from local file" => "Ingen sti spesifisert ved installering av app fra lokal fil", +"Archives of type %s are not supported" => "Arkiver av type %s støttes ikke", +"Failed to open archive when installing app" => "Klarte ikke å åpne arkiv ved installering av app", +"App does not provide an info.xml file" => "App-en inneholder ikke filen info.xml", +"App can't be installed because of not allowed code in the App" => "App kan ikke installeres på grunn av ulovlig kode i appen.", +"App can't be installed because it is not compatible with this version of ownCloud" => "App kan ikke installeres fordi den ikke er kompatibel med denne versjonen av ownCloud", +"App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "App kan ikke installeres fordi den inneholder tag <shipped>true</shipped> som ikke er tillatt for apper som ikke leveres med systemet", +"App can't be installed because the version in info.xml/version is not the same as the version reported from the app store" => "App kan ikke installeres fordi versjonen i info.xml/version ikke er den samme som versjonen som rapporteres fra app-butikken", +"App directory already exists" => "App-mappe finnes allerede", +"Can't create app folder. Please fix permissions. %s" => "Kan ikke opprette app-mappe. Vennligst ordne opp i tillatelser. %s", "Application is not enabled" => "Applikasjon er ikke påslått", "Authentication error" => "Autentikasjonsfeil", "Token expired. Please reload page." => "Symbol utløpt. Vennligst last inn siden på nytt.", "Files" => "Filer", "Text" => "Tekst", "Images" => "Bilder", +"%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", +"MS SQL username and/or password not valid: %s" => "MS SQL-brukernavn og/eller passord ikke gyldig: %s", +"You need to enter either an existing account or the administrator." => "Du må legge inn enten en eksisterende konto eller administratoren.", +"DB Error: \"%s\"" => "Databasefeil: \"%s\"", +"Offending command was: \"%s\"" => "Kommandoen som feilet: \"%s\"", +"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", +"Offending command was: \"%s\", name: %s, password: %s" => "Kommando som feilet: \"%s\", navn: %s, passord: %s", +"PostgreSQL username and/or password not valid" => "PostgreSQL-brukernavn og/eller passord er ikke gyldig", +"Set an admin username." => "Sett et admin-brukernavn.", +"Set an admin password." => "Sett et admin-passord.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Din nettservev er ikke konfigurert korrekt for filsynkronisering. WebDAV ser ut til å ikke funkere.", "Please double check the <a href='%s'>installation guides</a>." => "Vennligst dobbelsjekk <a href='%s'>installasjonsguiden</a>.", +"%s shared »%s« with you" => "%s delte »%s« med deg", "Could not find category \"%s\"" => "Kunne ikke finne kategori \"%s\"", "seconds ago" => "sekunder siden", "_%n minute ago_::_%n minutes ago_" => array("","%n minutter siden"), @@ -30,6 +60,8 @@ $TRANSLATIONS = array( "last month" => "forrige måned", "_%n month ago_::_%n months ago_" => array("","%n dager siden"), "last year" => "forrige år", -"years ago" => "år siden" +"years ago" => "år siden", +"A valid username must be provided" => "Oppgi et gyldig brukernavn", +"A valid password must be provided" => "Oppgi et gyldig passord" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/nl.php b/lib/l10n/nl.php index 2f6205fcf1c..736de5877c0 100644 --- a/lib/l10n/nl.php +++ b/lib/l10n/nl.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "U benadert de server vanaf een niet vertrouwd domein.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Neem contact op met uw beheerder. Als u de beheerder van deze service bent, configureer dan de \"trusted_domain\" instelling in config/config.php. Een voorbeeldconfiguratie is gegeven in config/config.sample.php.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "App \"%s\" kan niet worden geïnstalleerd omdat die niet compatible is met deze versie van ownCloud.", "No app name specified" => "De app naam is niet gespecificeerd.", "Help" => "Help", @@ -11,7 +13,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Onbekend bestandsformaat", "Invalid image" => "Ongeldige afbeelding", "web services under your control" => "Webdiensten in eigen beheer", -"cannot open \"%s\"" => "Kon \"%s\" niet openen", "ZIP download is turned off." => "ZIP download is uitgeschakeld.", "Files need to be downloaded one by one." => "Bestanden moeten één voor één worden gedownload.", "Back to Files" => "Terug naar bestanden", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "De applicatie is niet actief", "Authentication error" => "Authenticatie fout", "Token expired. Please reload page." => "Token verlopen. Herlaad de pagina.", +"Unknown user" => "Onbekende gebruiker", "Files" => "Bestanden", "Text" => "Tekst", "Images" => "Afbeeldingen", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s er mogen geen puntjes in de databasenaam voorkomen", "MS SQL username and/or password not valid: %s" => "MS SQL gebruikersnaam en/of wachtwoord niet geldig: %s", "You need to enter either an existing account or the administrator." => "Geef of een bestaand account op of het beheerdersaccount.", -"MySQL username and/or password not valid" => "MySQL gebruikersnaam en/of wachtwoord ongeldig", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB gebruikersnaam en/of wachtwoord ongeldig", "DB Error: \"%s\"" => "DB Fout: \"%s\"", "Offending command was: \"%s\"" => "Onjuiste commande was: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL gebruiker '%s'@'localhost' bestaat al.", -"Drop this user from MySQL" => "Verwijder deze gebruiker uit MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQL gebruiker '%s'@'%%' bestaat al", -"Drop this user from MySQL." => "Verwijder deze gebruiker uit MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB gebruiker '%s'@'localhost' bestaat al.", +"Drop this user from MySQL/MariaDB" => "Verwijder deze gebruiker uit MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB gebruiker '%s'@'%%' bestaat al", +"Drop this user from MySQL/MariaDB." => "Verwijder deze gebruiker uit MySQL/MariaDB.", "Oracle connection could not be established" => "Er kon geen verbinding met Oracle worden bereikt", "Oracle username and/or password not valid" => "Oracle gebruikersnaam en/of wachtwoord ongeldig", "Offending command was: \"%s\", name: %s, password: %s" => "Onjuiste commando was: \"%s\", naam: %s, wachtwoord: %s", @@ -55,6 +57,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Stel een beheerderswachtwoord in.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Uw webserver is nog niet goed ingesteld voor bestandssynchronisatie omdat de WebDAV interface verbroken lijkt.", "Please double check the <a href='%s'>installation guides</a>." => "Controleer de <a href='%s'>installatiehandleiding</a> goed.", +"%s shared »%s« with you" => "%s deelde »%s« met jou", +"Sharing %s failed, because the file does not exist" => "Delen van %s is mislukt, omdat het bestand niet bestaat", +"Sharing %s failed, because the user %s is the item owner" => "Delen van %s is mislukt, omdat de gebruiker %s de eigenaar is", +"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 the group %s does not exist" => "Delen van %s is mislukt, omdat 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", +"Sharing %s failed, because sharing with links is not allowed" => "Delen van %s is mislukt, omdat het delen met links niet is toegestaan", +"Share type %s is not valid for %s" => "Delen van type %s is niet geldig voor %s", +"Setting permissions for %s failed, because the permissions exceed permissions granted to %s" => "Instellen van de permissies voor %s is mislukt, omdat de permissies hoger zijn dan de aan %s toegekende permissies", +"Setting permissions for %s failed, because the item was not found" => "Instellen van de permissies voor %s is mislukt, omdat het object niet is gevonden", +"Sharing backend %s must implement the interface OCP\\Share_Backend" => "Het share-backend %s moet de OCP\\Share_Backend interface implementeren", +"Sharing backend %s not found" => "Het share-backend %s is niet gevonden", +"Sharing backend for %s not found" => "Het share-backend voor %s is niet gevonden", +"Sharing %s failed, because the user %s is the original sharer" => "Delen van %s is 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 de aan %s toegekende autorisaties overschrijden", +"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 share-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 bestandscache kon worden gevonden", "Could not find category \"%s\"" => "Kon categorie \"%s\" niet vinden", "seconds ago" => "seconden geleden", "_%n minute ago_::_%n minutes ago_" => array("%n minuut geleden","%n minuten geleden"), @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "vorige maand", "_%n month ago_::_%n months ago_" => array("%n maand geleden","%n maanden geleden"), "last year" => "vorig jaar", -"years ago" => "jaar geleden" +"years ago" => "jaar geleden", +"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\", and \"_.@-\"", +"A valid username must be provided" => "Er moet een geldige gebruikersnaam worden opgegeven", +"A valid password must be provided" => "Er moet een geldig wachtwoord worden opgegeven", +"The username is already being used" => "De gebruikersnaam bestaat al" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/nn_NO.php b/lib/l10n/nn_NO.php index e8bf8dfdef4..77aebb26ffa 100644 --- a/lib/l10n/nn_NO.php +++ b/lib/l10n/nn_NO.php @@ -13,6 +13,7 @@ $TRANSLATIONS = array( "Text" => "Tekst", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Tenaren din er ikkje enno rett innstilt til å tilby filsynkronisering sidan WebDAV-grensesnittet ser ut til å vera øydelagt.", "Please double check the <a href='%s'>installation guides</a>." => "Ver venleg og dobbeltsjekk <a href='%s'>installasjonsrettleiinga</a>.", +"%s shared »%s« with you" => "%s delte «%s» med deg", "seconds ago" => "sekund sidan", "_%n minute ago_::_%n minutes ago_" => array("","%n minutt sidan"), "_%n hour ago_::_%n hours ago_" => array("","%n timar sidan"), @@ -22,6 +23,8 @@ $TRANSLATIONS = array( "last month" => "førre månad", "_%n month ago_::_%n months ago_" => array("","%n månadar sidan"), "last year" => "i fjor", -"years ago" => "år sidan" +"years ago" => "år sidan", +"A valid username must be provided" => "Du må oppgje eit gyldig brukarnamn", +"A valid password must be provided" => "Du må oppgje eit gyldig passord" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/or_IN.php b/lib/l10n/or_IN.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/or_IN.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/pl.php b/lib/l10n/pl.php index fe3e876916a..59fc613dfde 100644 --- a/lib/l10n/pl.php +++ b/lib/l10n/pl.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Dostajesz się do serwera z niezaufanej domeny.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Proszę skontaktuj się z administratorem. Jeśli jesteś administratorem tej instancji, skonfiguruj parametr \"trusted_domain\" w pliku config/config.php. Przykładowa konfiguracja jest dostępna w pliku config/config.sample.php.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "Aplikacja \"%s\" nie może zostać zainstalowana, ponieważ nie jest zgodna z tą wersją ownCloud.", "No app name specified" => "Nie określono nazwy aplikacji", "Help" => "Pomoc", @@ -11,7 +13,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Nieznany typ pliku", "Invalid image" => "Błędne zdjęcie", "web services under your control" => "Kontrolowane serwisy", -"cannot open \"%s\"" => "Nie można otworzyć \"%s\"", "ZIP download is turned off." => "Pobieranie ZIP jest wyłączone.", "Files need to be downloaded one by one." => "Pliki muszą zostać pobrane pojedynczo.", "Back to Files" => "Wróć do plików", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "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", "Files" => "Pliki", "Text" => "Połączenie tekstowe", "Images" => "Obrazy", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s nie można używać kropki w nazwie bazy danych", "MS SQL username and/or password not valid: %s" => "Nazwa i/lub hasło serwera MS SQL jest niepoprawne: %s.", "You need to enter either an existing account or the administrator." => "Należy wprowadzić istniejące konto użytkownika lub administratora.", -"MySQL username and/or password not valid" => "MySQL: Nazwa użytkownika i/lub hasło jest niepoprawne", +"MySQL/MariaDB username and/or password not valid" => "Użytkownik i/lub hasło do MySQL/MariaDB są niepoprawne", "DB Error: \"%s\"" => "Błąd DB: \"%s\"", "Offending command was: \"%s\"" => "Niepoprawna komenda: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Użytkownik MySQL '%s'@'localhost' już istnieje", -"Drop this user from MySQL" => "Usuń tego użytkownika z MySQL", -"MySQL user '%s'@'%%' already exists" => "Użytkownik MySQL '%s'@'%%t' już istnieje", -"Drop this user from MySQL." => "Usuń tego użytkownika z MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "Użytkownik '%s'@'localhost' MySQL/MariaDB już istnieje.", +"Drop this user from MySQL/MariaDB" => "Usuń tego użytkownika z MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "Użytkownik '%s'@'%%' MySQL/MariaDB już istnieje.", +"Drop this user from MySQL/MariaDB." => "Usuń tego użytkownika z MySQL/MariaDB", "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", "Offending command was: \"%s\", name: %s, password: %s" => "Niepoprawne polecania: \"%s\", nazwa: %s, hasło: %s", @@ -55,6 +57,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Ustaw hasło administratora.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Serwer internetowy nie jest jeszcze poprawnie skonfigurowany, aby umożliwić synchronizację plików, ponieważ interfejs WebDAV wydaje się być uszkodzony.", "Please double check the <a href='%s'>installation guides</a>." => "Sprawdź ponownie <a href='%s'>przewodniki instalacji</a>.", +"%s shared »%s« with you" => "%s Współdzielone »%s« z tobą", +"Sharing %s failed, because the file does not exist" => "Wspóldzielenie %s nie powiodło się. ponieważ plik nie istnieje", +"Sharing %s failed, because the user %s is the item owner" => "Współdzielenie %s nie powiodło się, ponieważ użytkownik %s jest właścicielem elementu", +"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 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", +"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", +"Share type %s is not valid for %s" => "Typ udziału %s nie jest właściwy dla %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", +"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 %s failed, because the user %s is the original sharer" => "Współdzielenie %s nie powiodło się, ponieważ użytkownik %s jest udostępniającym", +"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", "Could not find category \"%s\"" => "Nie można odnaleźć kategorii \"%s\"", "seconds ago" => "sekund temu", "_%n minute ago_::_%n minutes ago_" => array("%n minute temu","%n minut temu","%n minut temu"), @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "w zeszłym miesiącu", "_%n month ago_::_%n months ago_" => array("%n miesiąc temu","%n miesięcy temu","%n miesięcy temu"), "last year" => "w zeszłym roku", -"years ago" => "lat temu" +"years ago" => "lat temu", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "W nazwach użytkowników dozwolone są wyłącznie następujące znaki: \"a-z\", \"A-Z\", \"0-9\", oraz \"_.@-\"", +"A valid username must be provided" => "Należy podać prawidłową nazwę użytkownika", +"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" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/lib/l10n/pl_PL.php b/lib/l10n/pl_PL.php deleted file mode 100644 index 5494e3dab25..00000000000 --- a/lib/l10n/pl_PL.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php -$TRANSLATIONS = array( -"Settings" => "Ustawienia" -); -$PLURAL_FORMS = "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/lib/l10n/pt_BR.php b/lib/l10n/pt_BR.php index cc20fb3cb02..ddb6934357a 100644 --- a/lib/l10n/pt_BR.php +++ b/lib/l10n/pt_BR.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Você está acessando o servidor a partir de um domínio não confiável.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Por favor, contate o administrador. Se você é um administrador desta instância, configurre o \"trusted_domain\" em config/config.php. Um exemplo de configuração é fornecido em config/config.sample.php.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "O aplicativo \"%s\" não pode ser instalado porque não é compatível com esta versão do ownCloud.", "No app name specified" => "O nome do aplicativo não foi especificado.", "Help" => "Ajuda", @@ -11,7 +13,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Tipo de arquivo desconhecido", "Invalid image" => "Imagem inválida", "web services under your control" => "serviços web sob seu controle", -"cannot open \"%s\"" => "não pode abrir \"%s\"", "ZIP download is turned off." => "Download ZIP está desligado.", "Files need to be downloaded one by one." => "Arquivos precisam ser baixados um de cada vez.", "Back to Files" => "Voltar para Arquivos", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "Aplicação não está habilitada", "Authentication error" => "Erro de autenticação", "Token expired. Please reload page." => "Token expirou. Por favor recarregue a página.", +"Unknown user" => "Usuário desconhecido", "Files" => "Arquivos", "Text" => "Texto", "Images" => "Imagens", @@ -40,13 +42,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s você não pode usar pontos no nome do banco de dados", "MS SQL username and/or password not valid: %s" => "Nome de usuário e/ou senha MS SQL inválido(s): %s", "You need to enter either an existing account or the administrator." => "Você precisa inserir uma conta existente ou o administrador.", -"MySQL username and/or password not valid" => "Nome de usuário e/ou senha MySQL inválido(s)", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB nome de usuário e/ou senha não é válida", "DB Error: \"%s\"" => "Erro no BD: \"%s\"", "Offending command was: \"%s\"" => "Comando ofensivo era: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "O usuário MySQL '%s'@'localhost' já existe.", -"Drop this user from MySQL" => "Derrubar este usuário do MySQL", -"MySQL user '%s'@'%%' already exists" => "Usuário MySQL '%s'@'%%' já existe", -"Drop this user from MySQL." => "Derrube este usuário do MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB usuário '%s'@'localhost' já existe.", +"Drop this user from MySQL/MariaDB" => "Eliminar esse usuário de MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB usuário '%s'@'%%' já existe", +"Drop this user from MySQL/MariaDB." => "Eliminar esse usuário de MySQL/MariaDB", "Oracle connection could not be established" => "Conexão Oracle não pode ser estabelecida", "Oracle username and/or password not valid" => "Nome de usuário e/ou senha Oracle inválido(s)", "Offending command was: \"%s\", name: %s, password: %s" => "Comando ofensivo era: \"%s\", nome: %s, senha: %s", @@ -55,6 +57,26 @@ $TRANSLATIONS = array( "Set an admin password." => "Defina uma senha de administrador.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Seu servidor web não está configurado corretamente para permitir sincronização de arquivos porque a interface WebDAV parece estar quebrada.", "Please double check the <a href='%s'>installation guides</a>." => "Por favor, confira os <a href='%s'>guias de instalação</a>.", +"%s shared »%s« with you" => "%s compartilhou »%s« com você", +"Sharing %s failed, because the file does not exist" => "Compartilhamento %s falhou, porque o arquivo não existe", +"Sharing %s failed, because the user %s is the item owner" => "Compartilhamento %s falhou, porque o usuário %s é o proprietário do item", +"Sharing %s failed, because the user %s does not exist" => "Compartilhamento %s falhou, porque 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" => "Compartilhamento %s falhou, porque 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" => "Compartilhamento %s falhou, porque este ítem já está compartilhado com %s", +"Sharing %s failed, because the group %s does not exist" => "Compartilhamento %s falhou, porque o grupo %s não existe", +"Sharing %s failed, because %s is not a member of the group %s" => "Compartilhamento %s falhou, porque %s não é membro do grupo %s", +"Sharing %s failed, because sharing with links is not allowed" => "Compartilhamento %s falhou, porque compartilhamento com links não é permitido", +"Share type %s is not valid for %s" => "Tipo de compartilhamento %s não é válido para %s", +"Setting permissions for %s failed, because the permissions exceed permissions granted to %s" => "Definir permissões para %s falhou, porque as permissões excedem as permissões concedidas a %s", +"Setting permissions for %s failed, because the item was not found" => "Definir permissões para %s falhou, porque o item não foi encontrado", +"Sharing backend %s must implement the interface OCP\\Share_Backend" => "Compartilhando backend %s deve implementar a interface OCP\\Share_Backend", +"Sharing backend %s not found" => "Compartilhamento backend %s não encontrado", +"Sharing backend for %s not found" => "Compartilhamento backend para %s não encontrado", +"Sharing %s failed, because the user %s is the original sharer" => "Compartilhando %s falhou, porque o usuário %s é o compartilhador original", +"Sharing %s failed, because the permissions exceed permissions granted to %s" => "Compartilhamento %s falhou, porque as permissões excedem as permissões concedidas a %s", +"Sharing %s failed, because resharing is not allowed" => "Compartilhamento %s falhou, porque recompartilhamentos não são permitidos", +"Sharing %s failed, because the sharing backend for %s could not find its source" => "Compartilhamento %s falhou, porque a infra-estrutura 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" => "Compartilhamento %s falhou, porque o arquivo não pôde ser encontrado no cache de arquivos", "Could not find category \"%s\"" => "Impossível localizar categoria \"%s\"", "seconds ago" => "segundos atrás", "_%n minute ago_::_%n minutes ago_" => array("","ha %n minutos"), @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "último mês", "_%n month ago_::_%n months ago_" => array("","ha %n meses"), "last year" => "último ano", -"years ago" => "anos atrás" +"years ago" => "anos atrás", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "Somente os seguintes caracteres são permitidos no nome do usuário: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-\"", +"A valid username must be provided" => "Forneça um nome de usuário válido", +"A valid password must be provided" => "Forneça uma senha válida", +"The username is already being used" => "Este nome de usuário já está sendo usado" ); $PLURAL_FORMS = "nplurals=2; plural=(n > 1);"; diff --git a/lib/l10n/pt_PT.php b/lib/l10n/pt_PT.php index bd9165ebb1a..ad424933331 100644 --- a/lib/l10n/pt_PT.php +++ b/lib/l10n/pt_PT.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Ficheiro desconhecido", "Invalid image" => "Imagem inválida", "web services under your control" => "serviços web sob o seu controlo", -"cannot open \"%s\"" => "Não foi possível abrir \"%s\"", "ZIP download is turned off." => "Descarregamento em ZIP está desligado.", "Files need to be downloaded one by one." => "Os ficheiros precisam de ser descarregados um por um.", "Back to Files" => "Voltar a Ficheiros", @@ -32,6 +31,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "A aplicação não está activada", "Authentication error" => "Erro na autenticação", "Token expired. Please reload page." => "O token expirou. Por favor recarregue a página.", +"Unknown user" => "Utilizador desconhecido", "Files" => "Ficheiros", "Text" => "Texto", "Images" => "Imagens", @@ -40,13 +40,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s não é permitido utilizar pontos (.) no nome da base de dados", "MS SQL username and/or password not valid: %s" => "Nome de utilizador/password do MySQL é inválido: %s", "You need to enter either an existing account or the administrator." => "Precisa de introduzir uma conta existente ou de administrador", -"MySQL username and/or password not valid" => "Nome de utilizador/password do MySQL inválida", +"MySQL/MariaDB username and/or password not valid" => "Nome de utilizador/password do MySQL/Maria DB inválida", "DB Error: \"%s\"" => "Erro na BD: \"%s\"", "Offending command was: \"%s\"" => "O comando gerador de erro foi: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "O utilizador '%s'@'localhost' do MySQL já existe.", -"Drop this user from MySQL" => "Eliminar este utilizador do MySQL", -"MySQL user '%s'@'%%' already exists" => "O utilizador '%s'@'%%' do MySQL já existe", -"Drop this user from MySQL." => "Eliminar este utilizador do MySQL", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "O utilizador '%s'@'localhost' do MySQL/MariaDB já existe.", +"Drop this user from MySQL/MariaDB" => "Eliminar este utilizador do MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "O utilizador '%s'@'%%' do MySQL/MariaDB já existe", +"Drop this user from MySQL/MariaDB." => "Eliminar este utilizador do MySQL/MariaDB", "Oracle connection could not be established" => "Não foi possível estabelecer a ligação Oracle", "Oracle username and/or password not valid" => "Nome de utilizador/password do Oracle inválida", "Offending command was: \"%s\", name: %s, password: %s" => "O comando gerador de erro foi: \"%s\", nome: %s, password: %s", @@ -55,6 +55,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Definiar uma password de administrador", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "O seu servidor web não está configurado correctamente para autorizar sincronização de ficheiros, pois o interface WebDAV parece estar com problemas.", "Please double check the <a href='%s'>installation guides</a>." => "Por favor verifique <a href='%s'>installation guides</a>.", +"%s shared »%s« with you" => "%s partilhado »%s« consigo", "Could not find category \"%s\"" => "Não foi encontrado a categoria \"%s\"", "seconds ago" => "Minutos atrás", "_%n minute ago_::_%n minutes ago_" => array("","%n minutos atrás"), @@ -65,6 +66,8 @@ $TRANSLATIONS = array( "last month" => "ultímo mês", "_%n month ago_::_%n months ago_" => array("","%n meses atrás"), "last year" => "ano passado", -"years ago" => "anos atrás" +"years ago" => "anos atrás", +"A valid username must be provided" => "Um nome de utilizador válido deve ser fornecido", +"A valid password must be provided" => "Uma password válida deve ser fornecida" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/ro.php b/lib/l10n/ro.php index 94ff7a4326a..87e9a92c464 100644 --- a/lib/l10n/ro.php +++ b/lib/l10n/ro.php @@ -20,6 +20,7 @@ $TRANSLATIONS = array( "Images" => "Imagini", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Serverul de web nu este încă setat corespunzător pentru a permite sincronizarea fișierelor deoarece interfața WebDAV pare a fi întreruptă.", "Please double check the <a href='%s'>installation guides</a>." => "Vă rugăm să verificați <a href='%s'>ghiduri de instalare</a>.", +"%s shared »%s« with you" => "%s Partajat »%s« cu tine de", "Could not find category \"%s\"" => "Cloud nu a gasit categoria \"%s\"", "seconds ago" => "secunde în urmă", "_%n minute ago_::_%n minutes ago_" => array("","","acum %n minute"), @@ -30,6 +31,8 @@ $TRANSLATIONS = array( "last month" => "ultima lună", "_%n month ago_::_%n months ago_" => array("","",""), "last year" => "ultimul an", -"years ago" => "ani în urmă" +"years ago" => "ani în urmă", +"A valid username must be provided" => "Trebuie să furnizaţi un nume de utilizator valid", +"A valid password must be provided" => "Trebuie să furnizaţi o parolă validă" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"; diff --git a/lib/l10n/ru.php b/lib/l10n/ru.php index 34d1730aaf2..2611fcf7a6b 100644 --- a/lib/l10n/ru.php +++ b/lib/l10n/ru.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Неизвестный тип файла", "Invalid image" => "Изображение повреждено", "web services under your control" => "веб-сервисы под вашим управлением", -"cannot open \"%s\"" => "не могу открыть \"%s\"", "ZIP download is turned off." => "ZIP-скачивание отключено.", "Files need to be downloaded one by one." => "Файлы должны быть загружены по одному.", "Back to Files" => "Назад к файлам", @@ -40,13 +39,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s Вы не можете использовать точки в имени базы данных", "MS SQL username and/or password not valid: %s" => "Имя пользователя и/или пароль MS SQL не подходит: %s", "You need to enter either an existing account or the administrator." => "Вы должны войти или в существующий аккаунт или под администратором.", -"MySQL username and/or password not valid" => "Неверное имя пользователя и/или пароль MySQL", "DB Error: \"%s\"" => "Ошибка БД: \"%s\"", "Offending command was: \"%s\"" => "Вызываемая команда была: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Пользователь MySQL '%s'@'localhost' уже существует.", -"Drop this user from MySQL" => "Удалить этого пользователя из MySQL", -"MySQL user '%s'@'%%' already exists" => "Пользователь MySQL '%s'@'%%' уже существует", -"Drop this user from MySQL." => "Удалить этого пользователя из MySQL.", "Oracle connection could not be established" => "соединение с Oracle не может быть установлено", "Oracle username and/or password not valid" => "Неверное имя пользователя и/или пароль Oracle", "Offending command was: \"%s\", name: %s, password: %s" => "Вызываемая команда была: \"%s\", имя: %s, пароль: %s", @@ -55,6 +49,7 @@ $TRANSLATIONS = array( "Set an admin password." => "становит пароль для admin.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Ваш веб сервер до сих пор не настроен правильно для возможности синхронизации файлов, похоже что проблема в неисправности интерфейса WebDAV.", "Please double check the <a href='%s'>installation guides</a>." => "Пожалуйста, дважды просмотрите <a href='%s'>инструкции по установке</a>.", +"%s shared »%s« with you" => "%s поделился »%s« с вами", "Could not find category \"%s\"" => "Категория \"%s\" не найдена", "seconds ago" => "несколько секунд назад", "_%n minute ago_::_%n minutes ago_" => array("%n минута назад","%n минуты назад","%n минут назад"), @@ -65,6 +60,8 @@ $TRANSLATIONS = array( "last month" => "в прошлом месяце", "_%n month ago_::_%n months ago_" => array("%n месяц назад","%n месяца назад","%n месяцев назад"), "last year" => "в прошлом году", -"years ago" => "несколько лет назад" +"years ago" => "несколько лет назад", +"A valid username must be provided" => "Укажите правильное имя пользователя", +"A valid password must be provided" => "Укажите валидный пароль" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/lib/l10n/sk_SK.php b/lib/l10n/sk_SK.php index 59c45e2b0bc..6e3a48a74fb 100644 --- a/lib/l10n/sk_SK.php +++ b/lib/l10n/sk_SK.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Neznámy typ súboru", "Invalid image" => "Chybný obrázok", "web services under your control" => "webové služby pod Vašou kontrolou", -"cannot open \"%s\"" => "nemožno otvoriť \"%s\"", "ZIP download is turned off." => "Sťahovanie súborov ZIP je vypnuté.", "Files need to be downloaded one by one." => "Súbory musia byť nahrávané jeden za druhým.", "Back to Files" => "Späť na súbory", @@ -32,6 +31,7 @@ $TRANSLATIONS = array( "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ľ", "Files" => "Súbory", "Text" => "Text", "Images" => "Obrázky", @@ -40,13 +40,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "V názve databázy %s nemôžete používať bodky", "MS SQL username and/or password not valid: %s" => "Používateľské meno, alebo heslo MS SQL nie je platné: %s", "You need to enter either an existing account or the administrator." => "Musíte zadať jestvujúci účet alebo administrátora.", -"MySQL username and/or password not valid" => "Používateľské meno a/alebo heslo pre MySQL databázu je neplatné", +"MySQL/MariaDB username and/or password not valid" => "Používateľské meno a/alebo heslo pre MySQL/MariaDB databázu je neplatné", "DB Error: \"%s\"" => "Chyba DB: \"%s\"", "Offending command was: \"%s\"" => "Podozrivý príkaz bol: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Používateľ '%s'@'localhost' už v MySQL existuje.", -"Drop this user from MySQL" => "Zahodiť používateľa z MySQL.", -"MySQL user '%s'@'%%' already exists" => "Používateľ '%s'@'%%' už v MySQL existuje", -"Drop this user from MySQL." => "Zahodiť používateľa z MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "Používateľ '%s'@'localhost' už v MySQL/MariaDB existuje.", +"Drop this user from MySQL/MariaDB" => "Zahodiť používateľa z MySQL/MariaDB.", +"MySQL/MariaDB user '%s'@'%%' already exists" => "Používateľ '%s'@'%%' už v MySQL/MariaDB existuje", +"Drop this user from MySQL/MariaDB." => "Zahodiť používateľa z MySQL/MariaDB.", "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é", "Offending command was: \"%s\", name: %s, password: %s" => "Podozrivý príkaz bol: \"%s\", meno: %s, heslo: %s", @@ -55,6 +55,17 @@ $TRANSLATIONS = array( "Set an admin password." => "Zadajte heslo administrátora.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Váš webový server nie je správne nastavený na synchronizáciu, pretože rozhranie WebDAV je poškodené.", "Please double check the <a href='%s'>installation guides</a>." => "Prosím skontrolujte <a href='%s'>inštalačnú príručku</a>.", +"%s shared »%s« with you" => "%s s vami zdieľa »%s«", +"Sharing %s failed, because the file does not exist" => "Zdieľanie %s zlyhalo, pretože súbor neexistuje", +"Sharing %s failed, because the user %s is the item owner" => "Zdieľanie %s zlyhalo, pretože používateľ %s je vlastníkom položky", +"Sharing %s failed, because the user %s does not exist" => "Zdieľanie %s zlyhalo, pretože používateľ %s neexistuje", +"Sharing %s failed, because this item is already shared with %s" => "Zdieľanie %s zlyhalo, pretože táto položka už je zdieľaná s %s", +"Sharing %s failed, because the group %s does not exist" => "Zdieľanie %s zlyhalo, pretože skupina %s neexistuje", +"Sharing %s failed, because %s is not a member of the group %s" => "Zdieľanie %s zlyhalo, pretože %s nie je členom skupiny %s", +"Sharing %s failed, because sharing with links is not allowed" => "Zdieľanie %s zlyhalo, pretože zdieľanie odkazom nie je povolené", +"Share type %s is not valid for %s" => "Typ zdieľania %s nie je platný pre %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", "Could not find category \"%s\"" => "Nemožno nájsť danú kategóriu \"%s\"", "seconds ago" => "pred sekundami", "_%n minute ago_::_%n minutes ago_" => array("pred %n minútou","pred %n minútami","pred %n minútami"), @@ -65,6 +76,10 @@ $TRANSLATIONS = array( "last month" => "minulý mesiac", "_%n month ago_::_%n months ago_" => array("pred %n mesiacom","pred %n mesiacmi","pred %n mesiacmi"), "last year" => "minulý rok", -"years ago" => "pred rokmi" +"years ago" => "pred rokmi", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "V mene používateľa sú povolené len nasledovné znaky: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"", +"A valid username must be provided" => "Musíte zadať platné používateľské meno", +"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é" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/lib/l10n/sl.php b/lib/l10n/sl.php index 3cc8dd130c8..eeaa0ddf96b 100644 --- a/lib/l10n/sl.php +++ b/lib/l10n/sl.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Neznana vrsta datoteke", "Invalid image" => "Neveljavna slika", "web services under your control" => "spletne storitve pod vašim nadzorom", -"cannot open \"%s\"" => "ni mogoče odpreti \"%s\"", "ZIP download is turned off." => "Prejemanje datotek v paketu ZIP je onemogočeno.", "Files need to be downloaded one by one." => "Datoteke je mogoče prejeti le posamično.", "Back to Files" => "Nazaj na datoteke", @@ -32,6 +31,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "Program ni omogočen", "Authentication error" => "Napaka overjanja", "Token expired. Please reload page." => "Žeton je potekel. Stran je treba ponovno naložiti.", +"Unknown user" => "Neznan uporabnik", "Files" => "Datoteke", "Text" => "Besedilo", "Images" => "Slike", @@ -40,13 +40,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s - v imenu podatkovne zbirke ni dovoljeno uporabljati pik.", "MS SQL username and/or password not valid: %s" => "Uporabniško ime ali geslo MS SQL ni veljavno: %s", "You need to enter either an existing account or the administrator." => "Prijaviti se je treba v obstoječi ali pa skrbniški račun.", -"MySQL username and/or password not valid" => "Uporabniško ime ali geslo MySQL ni veljavno", +"MySQL/MariaDB username and/or password not valid" => "Uporabniško ime ali geslo za MySQL/MariaDB ni veljavno", "DB Error: \"%s\"" => "Napaka podatkovne zbirke: \"%s\"", "Offending command was: \"%s\"" => "Napačni ukaz je: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Uporabnik MySQL '%s'@'localhost' že obstaja.", -"Drop this user from MySQL" => "Odstrani uporabnika iz podatkovne zbirke MySQL", -"MySQL user '%s'@'%%' already exists" => "Uporabnik MySQL '%s'@'%%' že obstaja.", -"Drop this user from MySQL." => "Odstrani uporabnika iz podatkovne zbirke MySQL", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "Uporabnik podatkovne zbirke MySQL/MariaDB '%s'@'localhost' že obstaja.", +"Drop this user from MySQL/MariaDB" => "Odstrani uporabnika iz podatkovne zbirke MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "Uporabnik podatkovne zbirke MySQL/MariaDB '%s'@'%%' že obstaja.", +"Drop this user from MySQL/MariaDB." => "Odstrani uporabnika iz podatkovne zbirke MySQL/MariaDB.", "Oracle connection could not be established" => "Povezave s sistemom Oracle ni mogoče vzpostaviti.", "Oracle username and/or password not valid" => "Uporabniško ime ali geslo Oracle ni veljavno", "Offending command was: \"%s\", name: %s, password: %s" => "Napačni ukaz je: \"%s\", ime: %s, geslo: %s", @@ -55,6 +55,17 @@ $TRANSLATIONS = array( "Set an admin password." => "Nastavi geslo skrbnika.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Spletni stražnik še ni ustrezno nastavljen in ne omogoča usklajevanja, saj je nastavitev WebDAV okvarjena.", "Please double check the <a href='%s'>installation guides</a>." => "Preverite <a href='%s'>navodila namestitve</a>.", +"%s shared »%s« with you" => "%s je omogočil souporabo »%s«", +"Sharing %s failed, because the user %s is the item owner" => "Nastavljanje souporabe %s je spodletelo, ker je uporabnik %s lastnik predmeta.", +"Sharing %s failed, because the user %s does not exist" => "Nastavljanje souporabe %s je spodletelo, ker uporabnik %s ne obstaja.", +"Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" => "Nastavljanje souporabe %s je spodletelo, ker uporabnik %s ni član nobene skupine, v kateri je tudi uporabnik %s.", +"Sharing %s failed, because this item is already shared with %s" => "Nastavljanje souporabe %s je spodletela, ker je ima uporabnik %s predmet že v souporabi.", +"Sharing %s failed, because the group %s does not exist" => "Nastavljanje souporabe %s je spodletelo, ker je skupina %s ne obstaja.", +"Sharing %s failed, because %s is not a member of the group %s" => "Nastavljanje souporabe %s je spodletelo, ker uporabnik %s ni član skupine %s.", +"Sharing %s failed, because sharing with links is not allowed" => "Nastavljanje souporabe %s je spodletelo, ker souporaba preko povezave ni dovoljena.", +"Share type %s is not valid for %s" => "Vrsta souporabe %s za %s ni veljavna.", +"Sharing backend %s not found" => "Ozadnjega programa %s za souporabo ni mogoče najti", +"Sharing backend for %s not found" => "Ozadnjega programa za souporabo za %s ni mogoče najti", "Could not find category \"%s\"" => "Kategorije \"%s\" ni mogoče najti.", "seconds ago" => "pred nekaj sekundami", "_%n minute ago_::_%n minutes ago_" => array("pred %n minuto","pred %n minutama","pred %n minutami","pred %n minutami"), @@ -65,6 +76,10 @@ $TRANSLATIONS = array( "last month" => "zadnji mesec", "_%n month ago_::_%n months ago_" => array("pred %n mesecem","pred %n mesecema","pred %n meseci","pred %n meseci"), "last year" => "lansko leto", -"years ago" => "let nazaj" +"years ago" => "let nazaj", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "V uporabniškem imenu je dovoljeno uporabiti le znake: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"", +"A valid username must be provided" => "Navedeno mora biti veljavno uporabniško ime", +"A valid password must be provided" => "Navedeno mora biti veljavno geslo", +"The username is already being used" => "Vpisano uporabniško ime je že v uporabi" ); $PLURAL_FORMS = "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"; diff --git a/lib/l10n/sq.php b/lib/l10n/sq.php index b36aa4ceefc..e29fa2cee53 100644 --- a/lib/l10n/sq.php +++ b/lib/l10n/sq.php @@ -21,13 +21,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s nuk mund të përdorni pikat tek emri i database-it", "MS SQL username and/or password not valid: %s" => "Përdoruesi dhe/apo kodi i MS SQL i pavlefshëm: %s", "You need to enter either an existing account or the administrator." => "Duhet të përdorni një llogari ekzistuese ose llogarinë e administratorit.", -"MySQL username and/or password not valid" => "Përdoruesi dhe/apo kodi i MySQL-it i pavlefshëm.", "DB Error: \"%s\"" => "Veprim i gabuar i DB-it: \"%s\"", "Offending command was: \"%s\"" => "Komanda e gabuar ishte: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Përdoruesi MySQL '%s'@'localhost' ekziston.", -"Drop this user from MySQL" => "Eliminoni këtë përdorues nga MySQL", -"MySQL user '%s'@'%%' already exists" => "Përdoruesi MySQL '%s'@'%%' ekziston", -"Drop this user from MySQL." => "Eliminoni këtë përdorues nga MySQL.", "Oracle username and/or password not valid" => "Përdoruesi dhe/apo kodi i Oracle-it i pavlefshëm", "Offending command was: \"%s\", name: %s, password: %s" => "Komanda e gabuar ishte: \"%s\", përdoruesi: %s, kodi: %s", "PostgreSQL username and/or password not valid" => "Përdoruesi dhe/apo kodi i PostgreSQL i pavlefshëm", @@ -35,6 +30,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Cakto kodin e administratorit.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Serveri web i juaji nuk është konfiguruar akoma për të lejuar sinkronizimin e skedarëve sepse ndërfaqja WebDAV mund të jetë e dëmtuar.", "Please double check the <a href='%s'>installation guides</a>." => "Ju lutemi kontrolloni mirë <a href='%s'>shoqëruesin e instalimit</a>.", +"%s shared »%s« with you" => "%s ndau »%s« me ju", "Could not find category \"%s\"" => "Kategoria \"%s\" nuk u gjet", "seconds ago" => "sekonda më parë", "_%n minute ago_::_%n minutes ago_" => array("","%n minuta më parë"), @@ -45,6 +41,8 @@ $TRANSLATIONS = array( "last month" => "muajin e shkuar", "_%n month ago_::_%n months ago_" => array("","%n muaj më parë"), "last year" => "vitin e shkuar", -"years ago" => "vite më parë" +"years ago" => "vite më parë", +"A valid username must be provided" => "Duhet të jepni një emër të vlefshëm përdoruesi", +"A valid password must be provided" => "Duhet të jepni një fjalëkalim te vlefshëm" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/sr.php b/lib/l10n/sr.php index 47a84803683..f9155bfd575 100644 --- a/lib/l10n/sr.php +++ b/lib/l10n/sr.php @@ -28,6 +28,8 @@ $TRANSLATIONS = array( "last month" => "прошлог месеца", "_%n month ago_::_%n months ago_" => array("","",""), "last year" => "прошле године", -"years ago" => "година раније" +"years ago" => "година раније", +"A valid username must be provided" => "Морате унети исправно корисничко име", +"A valid password must be provided" => "Морате унети исправну лозинку" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/lib/l10n/sr@latin.php b/lib/l10n/sr@latin.php index d8fa9289221..3ef62de9ab8 100644 --- a/lib/l10n/sr@latin.php +++ b/lib/l10n/sr@latin.php @@ -13,7 +13,7 @@ $TRANSLATIONS = array( "_%n hour ago_::_%n hours ago_" => array("","",""), "today" => "Danas", "yesterday" => "juče", -"_%n day go_::_%n days ago_" => array("","",""), +"_%n day go_::_%n days ago_" => array("","","Prije %n dana."), "last month" => "prošlog meseca", "_%n month ago_::_%n months ago_" => array("","",""), "last year" => "prošle godine", diff --git a/lib/l10n/su.php b/lib/l10n/su.php new file mode 100644 index 00000000000..e7b09649a24 --- /dev/null +++ b/lib/l10n/su.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array(""), +"_%n hour ago_::_%n hours ago_" => array(""), +"_%n day go_::_%n days ago_" => array(""), +"_%n month ago_::_%n months ago_" => array("") +); +$PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/sv.php b/lib/l10n/sv.php index ffffe5956f1..ba63531ad76 100644 --- a/lib/l10n/sv.php +++ b/lib/l10n/sv.php @@ -11,7 +11,6 @@ $TRANSLATIONS = array( "Unknown filetype" => "Okänd filtyp", "Invalid image" => "Ogiltig bild", "web services under your control" => "webbtjänster under din kontroll", -"cannot open \"%s\"" => "Kan inte öppna \"%s\"", "ZIP download is turned off." => "Nerladdning av ZIP är avstängd.", "Files need to be downloaded one by one." => "Filer laddas ner en åt gången.", "Back to Files" => "Tillbaka till Filer", @@ -32,6 +31,7 @@ $TRANSLATIONS = array( "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", "Files" => "Filer", "Text" => "Text", "Images" => "Bilder", @@ -40,13 +40,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s du får inte använda punkter i databasnamnet", "MS SQL username and/or password not valid: %s" => "MS SQL-användaren och/eller lösenordet var inte giltigt: %s", "You need to enter either an existing account or the administrator." => "Du måste antingen ange ett befintligt konto eller administratör.", -"MySQL username and/or password not valid" => "MySQL-användarnamnet och/eller lösenordet är felaktigt", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB användarnamn och/eller lösenord är felaktigt", "DB Error: \"%s\"" => "DB error: \"%s\"", "Offending command was: \"%s\"" => "Det felaktiga kommandot var: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL-användaren '%s'@'localhost' existerar redan.", -"Drop this user from MySQL" => "Radera denna användare från MySQL", -"MySQL user '%s'@'%%' already exists" => "MySQl-användare '%s'@'%%' existerar redan", -"Drop this user from MySQL." => "Radera denna användare från MySQL.", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB användare '%s'@'localhost' existerar redan.", +"Drop this user from MySQL/MariaDB" => "Radera denna användare från MySQL/MariaDB", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB användare '%s'@'%%' existerar redan", +"Drop this user from MySQL/MariaDB." => "Radera denna användare från MySQL/MariaDB.", "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", "Offending command was: \"%s\", name: %s, password: %s" => "Det felande kommandot var: \"%s\", name: %s, password: %s", @@ -55,6 +55,25 @@ $TRANSLATIONS = array( "Set an admin password." => "Ange ett administratörslösenord.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Din webbserver är inte korrekt konfigurerad för att tillåta filsynkronisering eftersom WebDAV inte verkar fungera.", "Please double check the <a href='%s'>installation guides</a>." => "Var god kontrollera <a href='%s'>installationsguiden</a>.", +"%s shared »%s« with you" => "%s delade »%s« med dig", +"Sharing %s failed, because the user %s is the item owner" => "Delning %s misslyckades därför att användaren %s är den som äger objektet", +"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 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", +"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", +"Share type %s is not valid for %s" => "Delningstyp %s är inte giltig för %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", +"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 %s failed, because the user %s is the original sharer" => "Delning %s misslyckades därför att användaren %s är den som delade objektet först", +"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", "Could not find category \"%s\"" => "Kunde inte hitta kategorin \"%s\"", "seconds ago" => "sekunder sedan", "_%n minute ago_::_%n minutes ago_" => array("%n minut sedan","%n minuter sedan"), @@ -65,6 +84,10 @@ $TRANSLATIONS = array( "last month" => "förra månaden", "_%n month ago_::_%n months ago_" => array("%n månad sedan","%n månader sedan"), "last year" => "förra året", -"years ago" => "år sedan" +"years ago" => "år sedan", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "Endast följande tecken är tillåtna i ett användarnamn: \"az\", \"AZ\", \"0-9\", och \"_ @ -.\"", +"A valid username must be provided" => "Ett giltigt användarnamn måste anges", +"A valid password must be provided" => "Ett giltigt lösenord måste anges", +"The username is already being used" => "Användarnamnet används redan" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/ta_IN.php b/lib/l10n/ta_IN.php new file mode 100644 index 00000000000..15f78e0bce6 --- /dev/null +++ b/lib/l10n/ta_IN.php @@ -0,0 +1,8 @@ +<?php +$TRANSLATIONS = array( +"_%n minute ago_::_%n minutes ago_" => array("",""), +"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/lib/l10n/te.php b/lib/l10n/te.php index 524ea0c6024..12ae9240191 100644 --- a/lib/l10n/te.php +++ b/lib/l10n/te.php @@ -1,16 +1,17 @@ <?php $TRANSLATIONS = array( "Help" => "సహాయం", +"Personal" => "వ్యక్తిగతం", "Settings" => "అమరికలు", "Users" => "వాడుకరులు", "seconds ago" => "క్షణాల క్రితం", -"_%n minute ago_::_%n minutes ago_" => array("",""), -"_%n hour ago_::_%n hours ago_" => array("",""), +"_%n minute ago_::_%n minutes ago_" => array("","%n నిమిషాల క్రితం"), +"_%n hour ago_::_%n hours ago_" => array("","%n గంటల క్రితం"), "today" => "ఈరోజు", "yesterday" => "నిన్న", -"_%n day go_::_%n days ago_" => array("",""), +"_%n day go_::_%n days ago_" => array("","%n రోజుల క్రితం"), "last month" => "పోయిన నెల", -"_%n month ago_::_%n months ago_" => array("",""), +"_%n month ago_::_%n months ago_" => array("","%n నెలల క్రితం"), "last year" => "పోయిన సంవత్సరం", "years ago" => "సంవత్సరాల క్రితం" ); diff --git a/lib/l10n/tr.php b/lib/l10n/tr.php index 7d25836f7d8..3ff2a70bdef 100644 --- a/lib/l10n/tr.php +++ b/lib/l10n/tr.php @@ -1,7 +1,9 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "Sunucuya güvenilmeyen bir alan adından ulaşıyorsunuz.", +"Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domain\" setting in config/config.php. An example configuration is provided in config/config.sample.php." => "Lütfen yöneticiniz ile iletişime geçin. Eğer bu örneğin bir yöneticisi iseniz, config/config.php dosyası içerisindeki \"trusted_domain\" ayarını yapılandırın. Bu yapılandırmanın bir örneği config/config.sample.php dosyasında verilmiştir.", "App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "ownCloud yazılımının bu sürümü ile uyumlu olmadığı için \"%s\" uygulaması kurulamaz.", -"No app name specified" => "Uygulama adı belirtimedli", +"No app name specified" => "Uygulama adı belirtilmedi", "Help" => "Yardım", "Personal" => "Kişisel", "Settings" => "Ayarlar", @@ -11,10 +13,9 @@ $TRANSLATIONS = array( "Unknown filetype" => "Bilinmeyen dosya türü", "Invalid image" => "Geçersiz resim", "web services under your control" => "kontrolünüzün altındaki web hizmetleri", -"cannot open \"%s\"" => "\"%s\" açılamıyor", "ZIP download is turned off." => "ZIP indirmeleri kapatıldı.", -"Files need to be downloaded one by one." => "Dosyaların birer birer indirilmesi gerekmektedir.", -"Back to Files" => "Dosyalara dön", +"Files need to be downloaded one by one." => "Dosyaların tek tek indirilmesi gerekmektedir.", +"Back to Files" => "Dosyalara Dön", "Selected files too large to generate zip file." => "Seçilen dosyalar bir zip dosyası oluşturmak için fazla büyük.", "Please download the files separately in smaller chunks or kindly ask your administrator." => "Dosyaları ayrı ayrı, küçük parçalar halinde indirin veya yöneticinizden yardım isteyin. ", "No source specified when installing app" => "Uygulama kurulurken bir kaynak belirtilmedi", @@ -32,6 +33,7 @@ $TRANSLATIONS = array( "Application is not enabled" => "Uygulama etkinleştirilmedi", "Authentication error" => "Kimlik doğrulama hatası", "Token expired. Please reload page." => "Jetonun süresi geçti. Lütfen sayfayı yenileyin.", +"Unknown user" => "Bilinmeyen kullanıcı", "Files" => "Dosyalar", "Text" => "Metin", "Images" => "Resimler", @@ -39,24 +41,44 @@ $TRANSLATIONS = array( "%s enter the database name." => "%s veritabanı adını girin.", "%s you may not use dots in the database name" => "%s veritabanı adında nokta kullanamayabilirsiniz", "MS SQL username and/or password not valid: %s" => "MS SQL kullanıcı adı ve/veya parolası geçersiz: %s", -"You need to enter either an existing account or the administrator." => "Bir konto veya kullanici birlemek ihtiyacin. ", -"MySQL username and/or password not valid" => "MySQL kullanıcı adı ve/veya parolası geçerli değil", -"DB Error: \"%s\"" => "DB Hata: ''%s''", -"Offending command was: \"%s\"" => "Komut rahasiz ''%s''. ", -"MySQL user '%s'@'localhost' exists already." => "MySQL kullanici '%s @local host zatan var. ", -"Drop this user from MySQL" => "Bu kullanici MySQLden list disari koymak. ", -"MySQL user '%s'@'%%' already exists" => "MySQL kullanici '%s @ % % zaten var (zaten yazili)", -"Drop this user from MySQL." => "Bu kullanıcıyı MySQL veritabanından kaldır", +"You need to enter either an existing account or the administrator." => "Mevcut bit hesap ya da yönetici hesabını girmelisiniz.", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB kullanıcı adı ve/veya parolası geçersiz", +"DB Error: \"%s\"" => "VT Hatası: \"%s\"", +"Offending command was: \"%s\"" => "Saldırgan komut: \"%s\"", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB kullanıcı '%s'@'localhost' zaten mevcut.", +"Drop this user from MySQL/MariaDB" => "Bu kullanıcıyı MySQL/MariaDB'dan at (drop)", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB kullanıcısı '%s'@'%%' zaten mevcut", +"Drop this user from MySQL/MariaDB." => "Bu kullanıcıyı MySQL/MariaDB'dan at (drop).", "Oracle connection could not be established" => "Oracle bağlantısı kurulamadı", -"Oracle username and/or password not valid" => "Adi klullanici ve/veya parola Oracle mantikli değildir. ", +"Oracle username and/or password not valid" => "Oracle kullanıcı adı ve/veya parolası geçerli değil", "Offending command was: \"%s\", name: %s, password: %s" => "Hatalı komut: \"%s\", ad: %s, parola: %s", -"PostgreSQL username and/or password not valid" => "PostgreSQL adi kullanici ve/veya parola yasal degildir. ", -"Set an admin username." => "Bir adi kullanici vermek. ", -"Set an admin password." => "Parola yonetici birlemek. ", -"Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Web sunucunuz dosya transferi için düzgün bir şekilde yapılandırılmamış. WevDAV arabirimini sorunlu gözüküyor.", +"PostgreSQL username and/or password not valid" => "PostgreSQL kullanıcı adı ve/veya parolası geçerli değil", +"Set an admin username." => "Bir yönetici kullanıcı adı ayarlayın.", +"Set an admin password." => "Bir yönetici kullanıcı parolası ayarlayın.", +"Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Web sunucunuz dosya eşitlemesine izin vermek üzere düzgün bir şekilde yapılandırılmamış. WebDAV arayüzü sorunlu görünüyor.", "Please double check the <a href='%s'>installation guides</a>." => "Lütfen <a href='%s'>kurulum kılavuzlarını</a> iki kez kontrol edin.", +"%s shared »%s« with you" => "%s sizinle »%s« paylaşımında bulundu", +"Sharing %s failed, because the file does not exist" => "%s paylaşımı, dosya mevcut olmadığından başarısız oldu", +"Sharing %s failed, because the user %s is the item owner" => "%s paylaşımı, %s öge sahibi olduğundan başarısız oldu", +"Sharing %s failed, because the user %s does not exist" => "%s paylaşımı, %s kullanıcısı mevcut olmadığından başarısız oldu", +"Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" => "%s paylaşımı, %s kullanıcısının %s üyeliklerinden birine sahip olmadığından başarısız oldu", +"Sharing %s failed, because this item is already shared with %s" => "%s paylaşımı, %s ile zaten paylaşıldığından dolayı başarısız oldu", +"Sharing %s failed, because the group %s does not exist" => "%s paylaşımı, %s grubu mevcut olmadığından başarısız oldu", +"Sharing %s failed, because %s is not a member of the group %s" => "%s paylaşımı, %s kullanıcısı %s grup üyesi olmadığından başarısız oldu", +"Sharing %s failed, because sharing with links is not allowed" => "%s paylaşımı, bağlantılar ile paylaşım izin verilmediğinden başarısız oldu", +"Share type %s is not valid for %s" => "%s paylaşım türü %s için geçerli değil", +"Setting permissions for %s failed, because the permissions exceed permissions granted to %s" => "%s için izinler, izinler %s için verilen izinleri aştığından dolayı ayarlanamadı", +"Setting permissions for %s failed, because the item was not found" => "%s için izinler öge bulunamadığından ayarlanamadı", +"Sharing backend %s must implement the interface OCP\\Share_Backend" => "Paylaşma arka ucu %s OCP\\Share_Backend arayüzünü desteklemeli", +"Sharing backend %s not found" => "Paylaşım arka ucu %s bulunamadı", +"Sharing backend for %s not found" => "%s için paylaşım arka ucu bulunamadı", +"Sharing %s failed, because the user %s is the original sharer" => "%s paylaşımı, %s kullanıcısı özgün paylaşan kişi olduğundan başarısız oldu", +"Sharing %s failed, because the permissions exceed permissions granted to %s" => "%s paylaşımı, izinler %s için verilen izinleri aştığından dolayı başarısız oldu", +"Sharing %s failed, because resharing is not allowed" => "%s paylaşımı, tekrar paylaşımın izin verilmemesinden dolayı başarısız oldu", +"Sharing %s failed, because the sharing backend for %s could not find its source" => "%s paylaşımı, %s için arka ucun kaynağını bulamamasından dolayı başarısız oldu", +"Sharing %s failed, because the file could not be found in the file cache" => "%s paylaşımı, dosyanın dosya önbelleğinde bulunamamasınndan dolayı başarısız oldu", "Could not find category \"%s\"" => "\"%s\" kategorisi bulunamadı", -"seconds ago" => "saniye önce", +"seconds ago" => "saniyeler önce", "_%n minute ago_::_%n minutes ago_" => array("","%n dakika önce"), "_%n hour ago_::_%n hours ago_" => array("","%n saat önce"), "today" => "bugün", @@ -65,6 +87,10 @@ $TRANSLATIONS = array( "last month" => "geçen ay", "_%n month ago_::_%n months ago_" => array("","%n ay önce"), "last year" => "geçen yıl", -"years ago" => "yıl önce" +"years ago" => "yıllar önce", +"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"" => "Kullanıcı adında sadece bu karakterlere izin verilmektedir: \"a-z\", \"A-Z\", \"0-9\", ve \"_.@-\"", +"A valid username must be provided" => "Geçerli bir kullanıcı adı mutlaka sağlanmalı", +"A valid password must be provided" => "Geçerli bir parola mutlaka sağlanmalı", +"The username is already being used" => "Bu kullanıcı adı zaten kullanımda" ); $PLURAL_FORMS = "nplurals=2; plural=(n > 1);"; diff --git a/lib/l10n/ug.php b/lib/l10n/ug.php index e2cf38ecc8c..8cfa99732ab 100644 --- a/lib/l10n/ug.php +++ b/lib/l10n/ug.php @@ -14,6 +14,8 @@ $TRANSLATIONS = array( "today" => "بۈگۈن", "yesterday" => "تۈنۈگۈن", "_%n day go_::_%n days ago_" => array(""), -"_%n month ago_::_%n months ago_" => array("") +"_%n month ago_::_%n months ago_" => array(""), +"A valid username must be provided" => "چوقۇم ئىناۋەتلىك ئىشلەتكۈچى ئىسمىدىن بىرنى تەمىنلەش كېرەك", +"A valid password must be provided" => "چوقۇم ئىناۋەتلىك ئىم تەمىنلەش كېرەك" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/uk.php b/lib/l10n/uk.php index 32e010f1d40..fed6277fdd8 100644 --- a/lib/l10n/uk.php +++ b/lib/l10n/uk.php @@ -23,13 +23,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s не можна використовувати крапки в назві бази даних", "MS SQL username and/or password not valid: %s" => "MS SQL ім'я користувача та/або пароль не дійсні: %s", "You need to enter either an existing account or the administrator." => "Вам потрібно ввести або існуючий обліковий запис або administrator.", -"MySQL username and/or password not valid" => "MySQL ім'я користувача та/або пароль не дійсні", "DB Error: \"%s\"" => "Помилка БД: \"%s\"", "Offending command was: \"%s\"" => "Команда, що викликала проблему: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Користувач MySQL '%s'@'localhost' вже існує.", -"Drop this user from MySQL" => "Видалити цього користувача з MySQL", -"MySQL user '%s'@'%%' already exists" => "Користувач MySQL '%s'@'%%' вже існує", -"Drop this user from MySQL." => "Видалити цього користувача з MySQL.", "Oracle username and/or password not valid" => "Oracle ім'я користувача та/або пароль не дійсні", "Offending command was: \"%s\", name: %s, password: %s" => "Команда, що викликала проблему: \"%s\", ім'я: %s, пароль: %s", "PostgreSQL username and/or password not valid" => "PostgreSQL ім'я користувача та/або пароль не дійсні", @@ -37,6 +32,7 @@ $TRANSLATIONS = array( "Set an admin password." => "Встановіть пароль адміністратора.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Ваш Web-сервер ще не налаштований належним чином для того, щоб дозволити синхронізацію файлів, через те що інтерфейс WebDAV, здається, зламаний.", "Please double check the <a href='%s'>installation guides</a>." => "Будь ласка, перевірте <a href='%s'>інструкції по встановленню</a>.", +"%s shared »%s« with you" => "%s розподілено »%s« з тобою", "Could not find category \"%s\"" => "Не вдалося знайти категорію \"%s\"", "seconds ago" => "секунди тому", "_%n minute ago_::_%n minutes ago_" => array("","","%n хвилин тому"), @@ -47,6 +43,8 @@ $TRANSLATIONS = array( "last month" => "минулого місяця", "_%n month ago_::_%n months ago_" => array("","","%n місяців тому"), "last year" => "минулого року", -"years ago" => "роки тому" +"years ago" => "роки тому", +"A valid username must be provided" => "Потрібно задати вірне ім'я користувача", +"A valid password must be provided" => "Потрібно задати вірний пароль" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/lib/l10n/vi.php b/lib/l10n/vi.php index 5840283110e..c3e09e96310 100644 --- a/lib/l10n/vi.php +++ b/lib/l10n/vi.php @@ -5,6 +5,8 @@ $TRANSLATIONS = array( "Settings" => "Cài đặt", "Users" => "Người dùng", "Admin" => "Quản trị", +"Unknown filetype" => "Không biết kiểu tập tin", +"Invalid image" => "Hình ảnh không hợp lệ", "web services under your control" => "dịch vụ web dưới sự kiểm soát của bạn", "ZIP download is turned off." => "Tải về ZIP đã bị tắt.", "Files need to be downloaded one by one." => "Tập tin cần phải được tải về từng người một.", @@ -16,15 +18,16 @@ $TRANSLATIONS = array( "Files" => "Tập tin", "Text" => "Văn bản", "Images" => "Hình ảnh", +"%s shared »%s« with you" => "%s đã chia sẻ »%s« với bạn", "Could not find category \"%s\"" => "không thể tìm thấy mục \"%s\"", "seconds ago" => "vài giây trước", -"_%n minute ago_::_%n minutes ago_" => array(""), -"_%n hour ago_::_%n hours ago_" => array(""), +"_%n minute ago_::_%n minutes ago_" => array("%n phút trước"), +"_%n hour ago_::_%n hours ago_" => array("%n giờ trước"), "today" => "hôm nay", "yesterday" => "hôm qua", -"_%n day go_::_%n days ago_" => array(""), +"_%n day go_::_%n days ago_" => array("%n ngày trước"), "last month" => "tháng trước", -"_%n month ago_::_%n months ago_" => array(""), +"_%n month ago_::_%n months ago_" => array("%n tháng trước"), "last year" => "năm trước", "years ago" => "năm trước" ); diff --git a/lib/l10n/zh_CN.php b/lib/l10n/zh_CN.php index ae9243cf412..c5261a6aff0 100644 --- a/lib/l10n/zh_CN.php +++ b/lib/l10n/zh_CN.php @@ -1,10 +1,14 @@ <?php $TRANSLATIONS = array( +"You are accessing the server from an untrusted domain." => "您正在访问来自不信任域名的服务器。", +"App \"%s\" can't be installed because it is not compatible with this version of ownCloud." => "App “%s” 无法安装,因为它和当前 ownCloud 版本不兼容。", +"No app name specified" => "没有指定的 App 名称", "Help" => "帮助", "Personal" => "个人", "Settings" => "设置", "Users" => "用户", "Admin" => "管理", +"Failed to upgrade \"%s\"." => "\"%s\" 升级失败。", "Unknown filetype" => "未知的文件类型", "Invalid image" => "无效的图像", "web services under your control" => "您控制的web服务", @@ -12,10 +16,23 @@ $TRANSLATIONS = array( "Files need to be downloaded one by one." => "需要逐一下载文件", "Back to Files" => "回到文件", "Selected files too large to generate zip file." => "选择的文件太大,无法生成 zip 文件。", +"Please download the files separately in smaller chunks or kindly ask your administrator." => "请用较小的分块下载文件或者友好地询问您的管理员。", +"No source specified when installing app" => "安装 App 时未指定来源", +"No href specified when installing app from http" => "从 http 安装 App 时未指定链接", +"No path specified when installing app from local file" => "从本地文件安装 App 时未指定路径", +"Archives of type %s are not supported" => "不支持 %s 的压缩格式", +"Failed to open archive when installing app" => "安装 App 是打开归档失败", "App does not provide an info.xml file" => "应用未提供 info.xml 文件", +"App can't be installed because of not allowed code in the App" => "App 无法安装,因为 App 中有非法代码 ", +"App can't be installed because it is not compatible with this version of ownCloud" => "App 无法安装,因为和当前 ownCloud 版本不兼容", +"App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" => "App 无法安装,因为 App 包含不允许在非内置 App 中使用的 <shipped>true</shipped> 标签", +"App can't be installed because the version in info.xml/version is not the same as the version reported from the app store" => "App 无法安装因为 info.xml/version 中的版本和 App 商店版本不同", +"App directory already exists" => "应用程序目录已存在", +"Can't create app folder. Please fix permissions. %s" => "无法创建应用程序文件夹。请修正权限。%s", "Application is not enabled" => "应用程序未启用", "Authentication error" => "认证出错", "Token expired. Please reload page." => "Token 过期,请刷新页面。", +"Unknown user" => "未知用户", "Files" => "文件", "Text" => "文本", "Images" => "图片", @@ -24,13 +41,13 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s 您不能在数据库名称中使用英文句号。", "MS SQL username and/or password not valid: %s" => "MS SQL 用户名和/或密码无效:%s", "You need to enter either an existing account or the administrator." => "你需要输入一个数据库中已有的账户或管理员账户。", -"MySQL username and/or password not valid" => "MySQL 数据库用户名和/或密码无效", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB 数据库用户名和/或密码无效", "DB Error: \"%s\"" => "数据库错误:\"%s\"", "Offending command was: \"%s\"" => "冲突命令为:\"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL 用户 '%s'@'localhost' 已存在。", -"Drop this user from MySQL" => "建议从 MySQL 数据库中丢弃 Drop 此用户", -"MySQL user '%s'@'%%' already exists" => "MySQL 用户 '%s'@'%%' 已存在", -"Drop this user from MySQL." => "建议从 MySQL 数据库中丢弃 Drop 此用户。", +"MySQL/MariaDB user '%s'@'localhost' exists already." => "MySQL/MariaDB 用户 '%s'@'localhost' 已存在。", +"Drop this user from MySQL/MariaDB" => "建议从 MySQL/MariaDB 数据库中删除此用户", +"MySQL/MariaDB user '%s'@'%%' already exists" => "MySQL/MariaDB 用户 '%s'@'%%' 已存在", +"Drop this user from MySQL/MariaDB." => "建议从 MySQL/MariaDB 数据库中删除此用户。", "Oracle connection could not be established" => "不能建立甲骨文连接", "Oracle username and/or password not valid" => "Oracle 数据库用户名和/或密码无效", "Offending command was: \"%s\", name: %s, password: %s" => "冲突命令为:\"%s\",名称:%s,密码:%s", @@ -39,6 +56,26 @@ $TRANSLATIONS = array( "Set an admin password." => "请设置一个管理员密码。", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "您的Web服务器尚未正确设置以允许文件同步, 因为WebDAV的接口似乎已损坏.", "Please double check the <a href='%s'>installation guides</a>." => "请认真检查<a href='%s'>安装指南</a>.", +"%s shared »%s« with you" => "%s 向您分享了 »%s«", +"Sharing %s failed, because the file does not exist" => "共享 %s 失败,因为文件不存在。", +"Sharing %s failed, because the user %s is the item owner" => "共享 %s 失败,因为用户 %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 the group %s does not exist" => "共享 %s 失败,因为 %s 组不存在", +"Sharing %s failed, because %s is not a member of the group %s" => "共享 %s 失败,因为 %s 不是 %s 组的成员", +"Sharing %s failed, because sharing with links is not allowed" => "共享 %s 失败,因为不允许用链接共享", +"Share type %s is not valid for %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 的权限失败,因为未找到到对应项", +"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 %s failed, because the user %s is the original sharer" => "共享 %s 失败,因为用户 %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 失败,因为未在文件缓存中找到文件。", "Could not find category \"%s\"" => "无法找到分类 \"%s\"", "seconds ago" => "秒前", "_%n minute ago_::_%n minutes ago_" => array("%n 分钟前"), @@ -49,6 +86,10 @@ $TRANSLATIONS = array( "last month" => "上月", "_%n month ago_::_%n months ago_" => array("%n 月前"), "last year" => "去年", -"years ago" => "年前" +"years ago" => "年前", +"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" => "必须提供合法的用户名", +"A valid password must be provided" => "必须提供合法的密码", +"The username is already being used" => "用户名已被使用" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/zh_TW.php b/lib/l10n/zh_TW.php index 35719c8b17e..83616cf6a42 100644 --- a/lib/l10n/zh_TW.php +++ b/lib/l10n/zh_TW.php @@ -11,11 +11,11 @@ $TRANSLATIONS = array( "Unknown filetype" => "未知的檔案類型", "Invalid image" => "無效的圖片", "web services under your control" => "由您控制的網路服務", -"cannot open \"%s\"" => "無法開啓 %s", "ZIP download is turned off." => "ZIP 下載已關閉。", "Files need to be downloaded one by one." => "檔案需要逐一下載。", "Back to Files" => "回到檔案列表", "Selected files too large to generate zip file." => "選擇的檔案太大以致於無法產生壓縮檔。", +"Please download the files separately in smaller chunks or kindly ask your administrator." => "請分割您的檔案後下載,或請詢問您的系統管理員。", "No source specified when installing app" => "沒有指定應用程式安裝來源", "No href specified when installing app from http" => "從 http 安裝應用程式,找不到 href 屬性", "No path specified when installing app from local file" => "從本地檔案安裝應用程式時沒有指定路徑", @@ -39,13 +39,8 @@ $TRANSLATIONS = array( "%s you may not use dots in the database name" => "%s 資料庫名稱不能包含小數點", "MS SQL username and/or password not valid: %s" => "MS SQL 使用者和/或密碼無效:%s", "You need to enter either an existing account or the administrator." => "您必須輸入一個現有的帳號或管理員帳號。", -"MySQL username and/or password not valid" => "MySQL 用戶名和/或密碼無效", "DB Error: \"%s\"" => "資料庫錯誤:\"%s\"", "Offending command was: \"%s\"" => "有問題的指令是:\"%s\"", -"MySQL user '%s'@'localhost' exists already." => "MySQL 使用者 '%s'@'localhost' 已經存在。", -"Drop this user from MySQL" => "在 MySQL 移除這個使用者", -"MySQL user '%s'@'%%' already exists" => "MySQL 使用者 '%s'@'%%' 已經存在", -"Drop this user from MySQL." => "在 MySQL 移除這個使用者。", "Oracle connection could not be established" => "無法建立 Oracle 資料庫連線", "Oracle username and/or password not valid" => "Oracle 用戶名和/或密碼無效", "Offending command was: \"%s\", name: %s, password: %s" => "有問題的指令是:\"%s\" ,使用者:\"%s\",密碼:\"%s\"", @@ -54,6 +49,7 @@ $TRANSLATIONS = array( "Set an admin password." => "設定管理員密碼。", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "您的網頁伺服器尚未被正確設定來進行檔案同步,因為您的 WebDAV 界面似乎無法使用。", "Please double check the <a href='%s'>installation guides</a>." => "請參考<a href='%s'>安裝指南</a>。", +"%s shared »%s« with you" => "%s 與您分享了 %s", "Could not find category \"%s\"" => "找不到分類:\"%s\"", "seconds ago" => "幾秒前", "_%n minute ago_::_%n minutes ago_" => array("%n 分鐘前"), @@ -64,6 +60,8 @@ $TRANSLATIONS = array( "last month" => "上個月", "_%n month ago_::_%n months ago_" => array("%n 個月前"), "last year" => "去年", -"years ago" => "幾年前" +"years ago" => "幾年前", +"A valid username must be provided" => "必須提供一個有效的用戶名", +"A valid password must be provided" => "一定要提供一個有效的密碼" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/private/activitymanager.php b/lib/private/activitymanager.php index 685809581ac..66aa039eb18 100755 --- a/lib/private/activitymanager.php +++ b/lib/private/activitymanager.php @@ -46,7 +46,7 @@ class ActivityManager implements IManager { $type, $priority); } catch (\Exception $ex) { - // TODO: log the excepetion + // TODO: log the exception } } diff --git a/lib/private/api.php b/lib/private/api.php index 3f96196e6df..74887690952 100644 --- a/lib/private/api.php +++ b/lib/private/api.php @@ -65,8 +65,8 @@ class OC_API { $name = strtolower($method).$url; $name = str_replace(array('/', '{', '}'), '_', $name); if(!isset(self::$actions[$name])) { - OC::getRouter()->useCollection('ocs'); - OC::getRouter()->create($name, $url) + OC::$server->getRouter()->useCollection('ocs'); + OC::$server->getRouter()->create($name, $url) ->method($method) ->defaults($defaults) ->requirements($requirements) @@ -116,9 +116,7 @@ class OC_API { ); } $response = self::mergeResponses($responses); - $formats = array('json', 'xml'); - - $format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml'; + $format = self::requestedFormat(); if (self::$logoutRequired) { OC_User::logout(); } @@ -129,9 +127,9 @@ class OC_API { /** * merge the returned result objects into one response * @param array $responses + * @return array|\OC_OCS_Result */ public static function mergeResponses($responses) { - $response = array(); // Sort into shipped and thirdparty $shipped = array( 'succeeded' => array(), @@ -193,7 +191,7 @@ class OC_API { // Merge the successful responses $data = array(); - foreach($responses as $app => $response) { + foreach($responses as $response) { if($response['shipped']) { $data = array_merge_recursive($response['response']->getData(), $data); } else { @@ -270,6 +268,18 @@ class OC_API { * @return string|false (username, or false on failure) */ private static function loginUser(){ + + // reuse existing login + $loggedIn = OC_User::isLoggedIn(); + $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false; + if ($loggedIn === true && $ocsApiRequest) { + + // initialize the user's filesystem + \OC_Util::setUpFS(\OC_User::getUser()); + + return OC_User::getUser(); + } + // basic auth $authUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : ''; $authPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; @@ -283,17 +293,6 @@ class OC_API { return $authUser; } - // reuse existing login - $loggedIn = OC_User::isLoggedIn(); - $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false; - if ($loggedIn === true && $ocsApiRequest) { - - // initialize the user's filesystem - \OC_Util::setUpFS(\OC_User::getUser()); - - return OC_User::getUser(); - } - return false; } @@ -349,4 +348,33 @@ class OC_API { } } + /** + * @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 + */ + public static function setContentType() { + $format = \OC_API::requestedFormat(); + 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'); + } + + } diff --git a/lib/private/app.php b/lib/private/app.php index 048d4d4aeb1..2f55b54b328 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -219,6 +219,8 @@ class OC_App{ $appdata=OC_OCSClient::getApplication($app); $download=OC_OCSClient::getApplicationDownload($app, 1); if(isset($download['downloadlink']) and $download['downloadlink']!='') { + // Replace spaces in download link without encoding entire URL + $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']); $info = array('source'=>'http', 'href'=>$download['downloadlink'], 'appdata'=>$appdata); $app=OC_Installer::installApp($info); } @@ -457,9 +459,11 @@ class OC_App{ return false; } /** - * Get the directory for the given app. - * If the app is defined in multiple directories, the first one is taken. (false if not found) - */ + * 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( ($dir = self::findAppInDirectories($appid)) != false) { return $dir['path'].'/'.$appid; @@ -468,9 +472,11 @@ class OC_App{ } /** - * 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) - */ + * 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; @@ -480,6 +486,7 @@ class OC_App{ /** * get the last version of the app, either from appinfo/version or from appinfo/info.xml + * @param string $appid * @return string */ public static function getAppVersion($appid) { @@ -561,7 +568,7 @@ class OC_App{ /** * @brief Returns the navigation - * @return string + * @return array * * This function returns an array containing all entries added. The * entries are sorted by the key 'order' ascending. Additional to the keys diff --git a/lib/private/appconfig.php b/lib/private/appconfig.php index cdaaebb87e5..0cd6b3bc35b 100644 --- a/lib/private/appconfig.php +++ b/lib/private/appconfig.php @@ -71,6 +71,7 @@ class AppConfig implements \OCP\IAppConfig { /** * @param string $app + * @return \string[] */ private function getAppValues($app) { $appCache = $this->getAppCache($app); @@ -147,7 +148,7 @@ class AppConfig implements \OCP\IAppConfig { */ public function hasKey($app, $key) { $values = $this->getAppValues($app); - return isset($values[$key]); + return array_key_exists($key, $values); } /** @@ -218,8 +219,8 @@ class AppConfig implements \OCP\IAppConfig { /** * get multiply values, either the app or key can be used as wildcard by setting it to false * - * @param boolean $app - * @param string $key + * @param string|false $app + * @param string|false $key * @return array */ public function getValues($app, $key) { diff --git a/lib/private/appframework/dependencyinjection/dicontainer.php b/lib/private/appframework/dependencyinjection/dicontainer.php index 4821ecaf67b..e478225a53d 100644 --- a/lib/private/appframework/dependencyinjection/dicontainer.php +++ b/lib/private/appframework/dependencyinjection/dicontainer.php @@ -92,13 +92,13 @@ class DIContainer extends SimpleContainer implements IAppContainer{ return new SecurityMiddleware($app, $c['Request']); }); - $middleWares = $this->middleWares; - $this['MiddlewareDispatcher'] = $this->share(function($c) use ($middleWares) { + $middleWares = &$this->middleWares; + $this['MiddlewareDispatcher'] = $this->share(function($c) use (&$middleWares) { $dispatcher = new MiddlewareDispatcher(); $dispatcher->registerMiddleware($c['SecurityMiddleware']); foreach($middleWares as $middleWare) { - $dispatcher->registerMiddleware($middleWare); + $dispatcher->registerMiddleware($c[$middleWare]); } return $dispatcher; @@ -133,10 +133,10 @@ class DIContainer extends SimpleContainer implements IAppContainer{ } /** - * @param Middleware $middleWare + * @param string $middleWare * @return boolean|null */ - function registerMiddleWare(Middleware $middleWare) { + function registerMiddleWare($middleWare) { array_push($this->middleWares, $middleWare); } diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php index 40f47a7bd2f..643fa685adc 100644 --- a/lib/private/appframework/http/request.php +++ b/lib/private/appframework/http/request.php @@ -60,7 +60,14 @@ class Request implements \ArrayAccess, \Countable, IRequest { * @param string|false 'requesttoken' the requesttoken or false when not available * @see http://www.php.net/manual/en/reserved.variables.php */ - public function __construct(array $vars=array()) { + public function __construct(array $vars=array(), $stream='php://input') { + + $this->inputStream = $stream; + $this->items['params'] = array(); + + if(!array_key_exists('method', $vars)) { + $vars['method'] = 'GET'; + } foreach($this->allowedKeys as $name) { $this->items[$name] = isset($vars[$name]) @@ -68,25 +75,32 @@ class Request implements \ArrayAccess, \Countable, IRequest { : array(); } - if (defined('PHPUNIT_RUN') && PHPUNIT_RUN - && in_array('fakeinput', stream_get_wrappers())) { - $this->inputStream = 'fakeinput://data'; - } else { - $this->inputStream = 'php://input'; - } - - // Only 'application/x-www-form-urlencoded' requests are automatically - // transformed by PHP, 'application/json' must be decoded manually. - if ($this->method === 'POST' - && strpos($this->getHeader('Content-Type'), 'application/json') !== false - ) { - $this->items['params'] = $this->items['post'] = json_decode(file_get_contents($this->inputStream), true); - } + // '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($vars['method'] === 'POST') { + $this->items['post'] = $params; + } + } + // Handle application/x-www-form-urlencoded for methods other than GET + // or post correctly + } elseif($vars['method'] !== 'GET' + && $vars['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; + } + } $this->items['parameters'] = array_merge( $this->items['get'], $this->items['post'], - $this->items['urlParams'] + $this->items['urlParams'], + $this->items['params'] ); } @@ -313,47 +327,22 @@ class Request implements \ArrayAccess, \Countable, IRequest { * @throws \LogicException */ protected function getContent() { - if ($this->content === false && $this->method === 'PUT') { - throw new \LogicException( - '"put" can only be accessed once if not ' - . 'application/x-www-form-urlencoded or application/json.' - ); - } - // If the content can't be parsed into an array then return a stream resource. if ($this->method === 'PUT' && 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 { + return $this->parameters; } - - if (is_null($this->content)) { - $this->content = file_get_contents($this->inputStream); - - /* - * Normal jquery ajax requests are sent as application/x-www-form-urlencoded - * and in $_GET and $_POST PHP transformes the data into an array. - * The first condition mimics this. - * The second condition allows for sending raw application/json data while - * still getting the result as an array. - * - */ - if (strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) { - parse_str($this->content, $content); - if(is_array($content)) { - $this->content = $content; - } - } elseif (strpos($this->getHeader('Content-Type'), 'application/json') !== false) { - $content = json_decode($this->content, true); - if(is_array($content)) { - $this->content = $content; - } - } - } - - return $this->content; } /** diff --git a/lib/private/appframework/middleware/security/securitymiddleware.php b/lib/private/appframework/middleware/security/securitymiddleware.php index bb02d565fa4..0f160d224ad 100644 --- a/lib/private/appframework/middleware/security/securitymiddleware.php +++ b/lib/private/appframework/middleware/security/securitymiddleware.php @@ -25,8 +25,8 @@ namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Http; -use OC\AppFramework\Http\RedirectResponse; use OC\AppFramework\Utility\MethodAnnotationReader; +use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Middleware; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\JSONResponse; diff --git a/lib/private/appframework/routing/routeconfig.php b/lib/private/appframework/routing/routeconfig.php index 716358444a2..a3bbde6af53 100644 --- a/lib/private/appframework/routing/routeconfig.php +++ b/lib/private/appframework/routing/routeconfig.php @@ -23,6 +23,7 @@ namespace OC\AppFramework\routing; use OC\AppFramework\DependencyInjection\DIContainer; +use OCP\Route\IRouter; /** * Class RouteConfig @@ -36,10 +37,10 @@ class RouteConfig { /** * @param \OC\AppFramework\DependencyInjection\DIContainer $container - * @param \OC_Router $router + * @param \OCP\Route\IRouter $router * @internal param $appName */ - public function __construct(DIContainer $container, \OC_Router $router, $routes) { + public function __construct(DIContainer $container, IRouter $router, $routes) { $this->routes = $routes; $this->container = $container; $this->router = $router; @@ -47,7 +48,7 @@ class RouteConfig { } /** - * The routes and resource will be registered to the \OC_Router + * The routes and resource will be registered to the \OCP\Route\IRouter */ public function register() { @@ -83,7 +84,15 @@ class RouteConfig { // register the route $handler = new RouteActionHandler($this->container, $controllerName, $actionName); - $this->router->create($this->appName.'.'.$controller.'.'.$action, $url)->method($verb)->action($handler); + $router = $this->router->create($this->appName.'.'.$controller.'.'.$action, $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']); + } } } diff --git a/lib/private/appframework/utility/simplecontainer.php b/lib/private/appframework/utility/simplecontainer.php index d08a4879e34..1ad06b9ab23 100644 --- a/lib/private/appframework/utility/simplecontainer.php +++ b/lib/private/appframework/utility/simplecontainer.php @@ -2,9 +2,6 @@ namespace OC\AppFramework\Utility; -// register 3rdparty autoloaders -require_once 'Pimple/Pimple.php'; - /** * Class SimpleContainer * diff --git a/lib/private/archive.php b/lib/private/archive.php index 6f51066ddf8..a62f22cf6d7 100644 --- a/lib/private/archive.php +++ b/lib/private/archive.php @@ -10,7 +10,7 @@ abstract class OC_Archive{ /** * open any of the supported archive types * @param string $path - * @return OC_Archive + * @return OC_Archive|void */ public static function open($path) { $ext=substr($path, strrpos($path, '.')); @@ -29,6 +29,9 @@ abstract class OC_Archive{ } } + /** + * @param $source + */ abstract function __construct($source); /** * add an empty folder to the archive @@ -39,7 +42,7 @@ abstract class OC_Archive{ /** * add a file to the archive * @param string $path - * @param string source either a local file or string data + * @param string $source either a local file or string data * @return bool */ abstract function addFile($path, $source=''); diff --git a/lib/private/arrayparser.php b/lib/private/arrayparser.php index d353e486577..a5e1f6653fc 100644 --- a/lib/private/arrayparser.php +++ b/lib/private/arrayparser.php @@ -32,6 +32,7 @@ class ArrayParser { /** * @param string $string + * @return array|bool|int|null|string */ function parsePHP($string) { $string = $this->stripPHPTags($string); @@ -41,6 +42,7 @@ class ArrayParser { /** * @param string $string + * @return string */ function stripPHPTags($string) { $string = trim($string); @@ -55,6 +57,7 @@ class ArrayParser { /** * @param string $string + * @return string */ function stripAssignAndReturn($string) { $string = trim($string); @@ -67,6 +70,10 @@ class ArrayParser { return $string; } + /** + * @param string $string + * @return array|bool|int|null|string + */ function parse($string) { $string = trim($string); $string = trim($string, ';'); @@ -85,6 +92,7 @@ class ArrayParser { /** * @param string $string + * @return int */ function getType($string) { $string = strtolower($string); @@ -104,6 +112,7 @@ class ArrayParser { /** * @param string $string + * @return string */ function parseString($string) { return substr($string, 1, -1); @@ -111,6 +120,7 @@ class ArrayParser { /** * @param string $string + * @return int */ function parseNum($string) { return intval($string); @@ -118,6 +128,7 @@ class ArrayParser { /** * @param string $string + * @return bool */ function parseBool($string) { $string = strtolower($string); @@ -126,6 +137,7 @@ class ArrayParser { /** * @param string $string + * @return array */ function parseArray($string) { $body = substr($string, 5); @@ -157,6 +169,7 @@ class ArrayParser { /** * @param string $body + * @return array */ function splitArray($body) { $inSingleQuote = false;//keep track if we are inside quotes diff --git a/lib/private/cache.php b/lib/private/cache.php index a311f10a00f..961270c334c 100644 --- a/lib/private/cache.php +++ b/lib/private/cache.php @@ -97,7 +97,7 @@ class Cache { /** * creates cache key based on the files given - * @param $files + * @param string[] $files * @return string */ static public function generateCacheKeyFromFiles($files) { diff --git a/lib/private/cache/file.php b/lib/private/cache/file.php index 8a6ef39f61b..feee9cc32b6 100644 --- a/lib/private/cache/file.php +++ b/lib/private/cache/file.php @@ -1,6 +1,7 @@ <?php /** * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -10,22 +11,22 @@ namespace OC\Cache; class File { protected $storage; + + /** + * Returns the cache storage for the logged in user + * @return \OC\Files\View cache storage + */ protected function getStorage() { if (isset($this->storage)) { return $this->storage; } if(\OC_User::isLoggedIn()) { \OC\Files\Filesystem::initMountPoints(\OC_User::getUser()); - $subdir = 'cache'; - $view = new \OC\Files\View('/' . \OC_User::getUser()); - if(!$view->file_exists($subdir)) { - $view->mkdir($subdir); - } - $this->storage = new \OC\Files\View('/' . \OC_User::getUser().'/'.$subdir); + $this->storage = new \OC\Files\View('/' . \OC_User::getUser() . '/cache'); return $this->storage; }else{ \OC_Log::write('core', 'Can\'t get cache storage, user not logged in', \OC_Log::ERROR); - return false; + throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in'); } } @@ -83,11 +84,6 @@ class File { public function hasKey($key) { $storage = $this->getStorage(); if ($storage && $storage->is_file($key)) { - $mtime = $storage->filemtime($key); - if ($mtime < time()) { - $storage->unlink($key); - return false; - } return true; } return false; diff --git a/lib/private/config.php b/lib/private/config.php index 3649da84973..6701ca0532b 100644 --- a/lib/private/config.php +++ b/lib/private/config.php @@ -77,7 +77,7 @@ class Config { /** * @brief Gets a value from config.php * @param string $key key - * @param string|null $default = null default value + * @param array|bool|string|null $default = null default value * @return string the value or $default * * This function gets the value from config.php. If it does not exist, @@ -172,7 +172,7 @@ class Config { $result = @file_put_contents($this->configFilename, $content); if (!$result) { $defaults = new \OC_Defaults; - $url = \OC_Helper::linkToDocs('admin-dir-permissions'); + $url = \OC_Helper::linkToDocs('admin-dir_permissions'); throw new HintException( "Can't write into config directory!", 'This can usually be fixed by ' diff --git a/lib/private/connector/sabre/aborteduploaddetectionplugin.php b/lib/private/connector/sabre/aborteduploaddetectionplugin.php index ad759d1d84a..1a092a59a82 100644 --- a/lib/private/connector/sabre/aborteduploaddetectionplugin.php +++ b/lib/private/connector/sabre/aborteduploaddetectionplugin.php @@ -22,11 +22,16 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl private $server; /** - * is kept public to allow overwrite for unit testing - * * @var \OC\Files\View */ - public $fileView; + private $fileView; + + /** + * @param \OC\Files\View $view + */ + public function __construct($view) { + $this->fileView = $view; + } /** * This initializes the plugin. @@ -55,7 +60,7 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl // we should only react on PUT which is used for upload // e.g. with LOCK this will not work, but LOCK uses createFile() as well - if ($this->server->httpRequest->getMethod() !== 'PUT' ) { + if ($this->server->httpRequest->getMethod() !== 'PUT') { return; } @@ -70,9 +75,9 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl if (!$expected) { return; } - $actual = $this->getFileView()->filesize($filePath); + $actual = $this->fileView->filesize($filePath); if ($actual != $expected) { - $this->getFileView()->unlink($filePath); + $this->fileView->unlink($filePath); throw new Sabre_DAV_Exception_BadRequest('expected filesize ' . $expected . ' got ' . $actual); } @@ -81,8 +86,7 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl /** * @return string */ - public function getLength() - { + public function getLength() { $req = $this->server->httpRequest; $length = $req->getHeader('X-Expected-Entity-Length'); if (!$length) { @@ -91,17 +95,4 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl return $length; } - - /** - * @return \OC\Files\View - */ - public function getFileView() - { - if (is_null($this->fileView)) { - // initialize fileView - $this->fileView = \OC\Files\Filesystem::getView(); - } - - return $this->fileView; - } } diff --git a/lib/private/connector/sabre/auth.php b/lib/private/connector/sabre/auth.php index 0c84fa6b757..5577273df8c 100644 --- a/lib/private/connector/sabre/auth.php +++ b/lib/private/connector/sabre/auth.php @@ -73,6 +73,20 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic { */ public function authenticate(Sabre_DAV_Server $server, $realm) { + $result = $this->auth($server, $realm); + + // close the session - right after authentication there is not need to write to the session any more + \OC::$session->close(); + + return $result; + } + + /** + * @param Sabre_DAV_Server $server + * @param $realm + * @return bool + */ + private function auth(Sabre_DAV_Server $server, $realm) { if (OC_User::handleApacheAuth() || OC_User::isLoggedIn()) { $user = OC_User::getUser(); OC_Util::setupFS($user); @@ -81,5 +95,5 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic { } return parent::authenticate($server, $realm); - } + } } diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php index 02d1a9f4ba2..1bb526e451e 100644 --- a/lib/private/connector/sabre/directory.php +++ b/lib/private/connector/sabre/directory.php @@ -29,7 +29,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa * 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 succesful creation of the file, you may choose to return the ETag + * 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 @@ -50,30 +50,28 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function createFile($name, $data = null) { - if ($name === 'Shared' && empty($this->path)) { - throw new \Sabre_DAV_Exception_Forbidden(); - } - // for chunked upload also updating a existing file is a "createFile" - // because we create all the chunks before reasamble them to the existing file. + // 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 $info = OC_FileChunking::decodeName($name); - if (!\OC\Files\Filesystem::isCreatable($this->path) && - !\OC\Files\Filesystem::isUpdatable($this->path . '/' . $info['name'])) { + if (!$this->fileView->isCreatable($this->path) && + !$this->fileView->isUpdatable($this->path . '/' . $info['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 (!\OC\Files\Filesystem::isCreatable($this->path)) { + if (!$this->fileView->isCreatable($this->path)) { throw new \Sabre_DAV_Exception_Forbidden(); } } - $path = $this->path . '/' . $name; - $node = new OC_Connector_Sabre_File($path); + $path = $this->fileView->getAbsolutePath($this->path) . '/' . $name; + // using a dummy FileInfo is acceptable here since it will be refreshed after the put is complete + $info = new \OC\Files\FileInfo($path, null, null, array()); + $node = new OC_Connector_Sabre_File($this->fileView, $info); return $node->put($data); } @@ -85,17 +83,12 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa * @return void */ public function createDirectory($name) { - - if ($name === 'Shared' && empty($this->path)) { - throw new \Sabre_DAV_Exception_Forbidden(); - } - - if (!\OC\Files\Filesystem::isCreatable($this->path)) { + if (!$this->fileView->isCreatable($this->path)) { throw new \Sabre_DAV_Exception_Forbidden(); } $newPath = $this->path . '/' . $name; - if(!\OC\Files\Filesystem::mkdir($newPath)) { + if(!$this->fileView->mkdir($newPath)) { throw new Sabre_DAV_Exception_Forbidden('Could not create directory '.$newPath); } @@ -105,14 +98,15 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa * Returns a specific child node, referenced by its name * * @param string $name - * @throws Sabre_DAV_Exception_FileNotFound + * @param \OCP\Files\FileInfo $info + * @throws Sabre_DAV_Exception_NotFound * @return Sabre_DAV_INode */ public function getChild($name, $info = null) { $path = $this->path . '/' . $name; if (is_null($info)) { - $info = \OC\Files\Filesystem::getFileInfo($path); + $info = $this->fileView->getFileInfo($path); } if (!$info) { @@ -120,12 +114,10 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa } if ($info['mimetype'] == 'httpd/unix-directory') { - $node = new OC_Connector_Sabre_Directory($path); + $node = new OC_Connector_Sabre_Directory($this->fileView, $info); } else { - $node = new OC_Connector_Sabre_File($path); + $node = new OC_Connector_Sabre_File($this->fileView, $info); } - - $node->setFileinfoCache($info); return $node; } @@ -136,7 +128,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function getChildren() { - $folder_content = \OC\Files\Filesystem::getDirectoryContent($this->path); + $folder_content = $this->fileView->getDirectoryContent($this->path); $paths = array(); foreach($folder_content as $info) { $paths[] = $this->path.'/'.$info['name']; @@ -167,7 +159,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa $nodes = array(); foreach($folder_content as $info) { - $node = $this->getChild($info['name'], $info); + $node = $this->getChild($info->getName(), $info); $node->setPropertyCache($properties[$this->path.'/'.$info['name']]); $nodes[] = $node; } @@ -183,7 +175,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa public function childExists($name) { $path = $this->path . '/' . $name; - return \OC\Files\Filesystem::file_exists($path); + return $this->fileView->file_exists($path); } @@ -195,15 +187,11 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function delete() { - if ($this->path === 'Shared') { - throw new \Sabre_DAV_Exception_Forbidden(); - } - - if (!\OC\Files\Filesystem::isDeletable($this->path)) { + if (!$this->info->isDeletable()) { throw new \Sabre_DAV_Exception_Forbidden(); } - \OC\Files\Filesystem::rmdir($this->path); + $this->fileView->rmdir($this->path); } @@ -235,7 +223,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa public function getProperties($properties) { $props = parent::getProperties($properties); if (in_array(self::GETETAG_PROPERTYNAME, $properties) && !isset($props[self::GETETAG_PROPERTYNAME])) { - $props[self::GETETAG_PROPERTYNAME] = $this->getETagPropertyForPath($this->path); + $props[self::GETETAG_PROPERTYNAME] = $this->info->getEtag(); } return $props; } diff --git a/lib/private/connector/sabre/exceptionloggerplugin.php b/lib/private/connector/sabre/exceptionloggerplugin.php index 8e77afaf207..5eaf1e87621 100644 --- a/lib/private/connector/sabre/exceptionloggerplugin.php +++ b/lib/private/connector/sabre/exceptionloggerplugin.php @@ -11,6 +11,17 @@ class OC_Connector_Sabre_ExceptionLoggerPlugin extends Sabre_DAV_ServerPlugin { + private $nonFatalExceptions = array( + 'Sabre_DAV_Exception_NotAuthenticated' => 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, + ); + private $appName; /** @@ -43,8 +54,10 @@ class OC_Connector_Sabre_ExceptionLoggerPlugin extends Sabre_DAV_ServerPlugin */ public function logException($e) { $exceptionClass = get_class($e); - if ($exceptionClass !== 'Sabre_DAV_Exception_NotAuthenticated') { - \OCP\Util::logException($this->appName, $e); + $level = \OCP\Util::FATAL; + if (isset($this->nonFatalExceptions[$exceptionClass])) { + $level = \OCP\Util::DEBUG; } + \OCP\Util::logException($this->appName, $e, $level); } } diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index ef6caaf22a7..66b50a87552 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -42,14 +42,16 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * * @param resource $data * @throws Sabre_DAV_Exception_Forbidden + * @throws OC_Connector_Sabre_Exception_UnsupportedMediaType + * @throws Sabre_DAV_Exception_BadRequest + * @throws Sabre_DAV_Exception + * @throws OC_Connector_Sabre_Exception_EntityTooLarge + * @throws Sabre_DAV_Exception_ServiceUnavailable * @return string|null */ public function put($data) { - - $fs = $this->getFS(); - - if ($fs->file_exists($this->path) && - !$fs->isUpdatable($this->path)) { + if ($this->info && $this->fileView->file_exists($this->path) && + !$this->info->isUpdateable()) { throw new \Sabre_DAV_Exception_Forbidden(); } @@ -71,18 +73,11 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D // mark file as partial while uploading (ignored by the scanner) $partpath = $this->path . '.ocTransferId' . rand() . '.part'; - // if file is located in /Shared we write the part file to the users - // root folder because we can't create new files in /shared - // we extend the name with a random number to avoid overwriting a existing file - if (dirname($partpath) === 'Shared') { - $partpath = pathinfo($partpath, PATHINFO_FILENAME) . rand() . '.part'; - } - try { - $putOkay = $fs->file_put_contents($partpath, $data); + $putOkay = $this->fileView->file_put_contents($partpath, $data); if ($putOkay === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::file_put_contents() failed', \OC_Log::ERROR); - $fs->unlink($partpath); + $this->fileView->unlink($partpath); // because we have no clue about the cause we can only throw back a 500/Internal Server Error throw new Sabre_DAV_Exception('Could not write file contents'); } @@ -105,29 +100,30 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D } // rename to correct path - $renameOkay = $fs->rename($partpath, $this->path); - $fileExists = $fs->file_exists($this->path); + $renameOkay = $this->fileView->rename($partpath, $this->path); + $fileExists = $this->fileView->file_exists($this->path); if ($renameOkay === false || $fileExists === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); - $fs->unlink($partpath); + $this->fileView->unlink($partpath); throw new Sabre_DAV_Exception('Could not rename part file to final file'); } // allow sync clients to send the mtime along in a header $mtime = OC_Request::hasModificationTime(); if ($mtime !== false) { - if($fs->touch($this->path, $mtime)) { + if($this->fileView->touch($this->path, $mtime)) { header('X-OC-MTime: accepted'); } } + $this->refreshInfo(); - return $this->getETagPropertyForPath($this->path); + return '"' . $this->info->getEtag() . '"'; } /** * Returns the data * - * @return string + * @return string | resource */ public function get() { @@ -135,7 +131,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D if (\OC_Util::encryptedFiles()) { throw new \Sabre_DAV_Exception_ServiceUnavailable(); } else { - return \OC\Files\Filesystem::fopen($this->path, 'rb'); + return $this->fileView->fopen($this->path, 'rb'); } } @@ -147,16 +143,10 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @throws Sabre_DAV_Exception_Forbidden */ public function delete() { - $fs = $this->getFS(); - - if ($this->path === 'Shared') { - throw new \Sabre_DAV_Exception_Forbidden(); - } - - if (!$fs->isDeletable($this->path)) { + if (!$this->info->isDeletable()) { throw new \Sabre_DAV_Exception_Forbidden(); } - $fs->unlink($this->path); + $this->fileView->unlink($this->path); // remove properties $this->removeProperties(); @@ -169,12 +159,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return int */ public function getSize() { - $this->getFileinfoCache(); - if ($this->fileinfo_cache['size'] > -1) { - return $this->fileinfo_cache['size']; - } else { - return null; - } + return $this->info->getSize(); } /** @@ -189,11 +174,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return mixed */ public function getETag() { - $properties = $this->getProperties(array(self::GETETAG_PROPERTYNAME)); - if (isset($properties[self::GETETAG_PROPERTYNAME])) { - return $properties[self::GETETAG_PROPERTYNAME]; - } - return null; + return '"' . $this->info->getEtag() . '"'; } /** @@ -204,16 +185,14 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return mixed */ public function getContentType() { - if (isset($this->fileinfo_cache['mimetype'])) { - return $this->fileinfo_cache['mimetype']; - } - - return \OC\Files\Filesystem::getMimeType($this->path); + $mimeType = $this->info->getMimetype(); + return \OC_Helper::getSecureMimeType($mimeType); } /** * @param resource $data + * @return null|string */ private function createFileChunked($data) { @@ -245,15 +224,14 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D $chunk_handler->file_assemble($partFile); // here is the final atomic rename - $fs = $this->getFS(); $targetPath = $path . '/' . $info['name']; - $renameOkay = $fs->rename($partFile, $targetPath); - $fileExists = $fs->file_exists($targetPath); + $renameOkay = $this->fileView->rename($partFile, $targetPath); + $fileExists = $this->fileView->file_exists($targetPath); if ($renameOkay === false || $fileExists === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); // only delete if an error occurred and the target file was already created if ($fileExists) { - $fs->unlink($targetPath); + $this->fileView->unlink($targetPath); } throw new Sabre_DAV_Exception('Could not rename part file assembled from chunks'); } @@ -261,12 +239,13 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D // allow sync clients to send the mtime along in a header $mtime = OC_Request::hasModificationTime(); if ($mtime !== false) { - if($fs->touch($targetPath, $mtime)) { + if($this->fileView->touch($targetPath, $mtime)) { header('X-OC-MTime: accepted'); } } - return OC_Connector_Sabre_Node::getETagPropertyForPath($targetPath); + $info = $this->fileView->getFileInfo($targetPath); + return $info->getEtag(); } return null; diff --git a/lib/private/connector/sabre/node.php b/lib/private/connector/sabre/node.php index 5807c5c7f71..eede39cba8b 100644 --- a/lib/private/connector/sabre/node.php +++ b/lib/private/connector/sabre/node.php @@ -20,7 +20,6 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ - abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IProperties { const GETETAG_PROPERTYNAME = '{DAV:}getetag'; const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified'; @@ -29,15 +28,13 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * Allow configuring the method used to generate Etags * * @var array(class_name, function_name) - */ + */ public static $ETagFunction = null; /** - * is kept public to allow overwrite for unit testing - * * @var \OC\Files\View */ - public $fileView; + protected $fileView; /** * The path to the current node @@ -47,52 +44,54 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr protected $path; /** - * node fileinfo cache - * @var array - */ - protected $fileinfo_cache; - /** * node properties cache + * * @var array */ protected $property_cache = null; /** + * @var \OCP\Files\FileInfo + */ + protected $info; + + /** * @brief Sets up the node, expects a full path name - * @param string $path - * @return void + * @param \OC\Files\View $view + * @param \OCP\Files\FileInfo $info */ - public function __construct($path) { - $this->path = $path; + public function __construct($view, $info) { + $this->fileView = $view; + $this->path = $this->fileView->getRelativePath($info->getPath()); + $this->info = $info; } - + protected function refreshInfo() { + $this->info = $this->fileView->getFileInfo($this->path); + } /** * @brief Returns the name of the node * @return string */ public function getName() { - - list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path); - return $name; - + return $this->info->getName(); } /** * @brief Renames the node * @param string $name The new name - * @return void + * @throws Sabre_DAV_Exception_BadRequest + * @throws Sabre_DAV_Exception_Forbidden */ public function setName($name) { - $fs = $this->getFS(); // rename is only allowed if the update privilege is granted - if (!$fs->isUpdatable($this->path)) { + if (!$this->info->isUpdateable()) { throw new \Sabre_DAV_Exception_Forbidden(); } - list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); + list($parentPath,) = Sabre_DAV_URLUtil::splitPath($this->path); list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); if (!\OCP\Util::isValidFileName($newName)) { @@ -102,49 +101,30 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr $newPath = $parentPath . '/' . $newName; $oldPath = $this->path; - $fs->rename($this->path, $newPath); + $this->fileView->rename($this->path, $newPath); $this->path = $newPath; - $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?' - .' WHERE `userid` = ? AND `propertypath` = ?' ); - $query->execute( array( $newPath, OC_User::getUser(), $oldPath )); - - } - - public function setFileinfoCache($fileinfo_cache) - { - $this->fileinfo_cache = $fileinfo_cache; - } - - /** - * @brief Ensure that the fileinfo cache is filled - * @note Uses OC_FileCache or a direct stat - */ - protected function getFileinfoCache() { - if (!isset($this->fileinfo_cache)) { - if ($fileinfo_cache = \OC\Files\Filesystem::getFileInfo($this->path)) { - } else { - $fileinfo_cache = \OC\Files\Filesystem::stat($this->path); - } - - $this->fileinfo_cache = $fileinfo_cache; - } + $query = OC_DB::prepare('UPDATE `*PREFIX*properties` SET `propertypath` = ?' + . ' WHERE `userid` = ? AND `propertypath` = ?'); + $query->execute(array($newPath, OC_User::getUser(), $oldPath)); + $this->refreshInfo(); } - public function setPropertyCache($property_cache) - { + public function setPropertyCache($property_cache) { $this->property_cache = $property_cache; } /** * @brief Returns the last modification time, as a unix timestamp - * @return int + * @return int timestamp as integer */ public function getLastModified() { - $this->getFileinfoCache(); - return $this->fileinfo_cache['mtime']; - + $timestamp = $this->info->getMtime(); + if (!empty($timestamp)) { + return (int)$timestamp; + } + return $timestamp; } /** @@ -153,39 +133,40 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * Even if the modification time is set to a custom value the access time is set to now. */ public function touch($mtime) { - \OC\Files\Filesystem::touch($this->path, $mtime); + $this->fileView->touch($this->path, $mtime); + $this->refreshInfo(); } /** * @brief Updates properties on this node, * @see Sabre_DAV_IProperties::updateProperties + * @param array $properties * @return boolean */ public function updateProperties($properties) { $existing = $this->getProperties(array()); - foreach($properties as $propertyName => $propertyValue) { + 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 )) { - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`' - .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' ); - $query->execute( array( OC_User::getUser(), $this->path, $propertyName )); + if (array_key_exists($propertyName, $existing)) { + $query = OC_DB::prepare('DELETE FROM `*PREFIX*properties`' + . ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'); + $query->execute(array(OC_User::getUser(), $this->path, $propertyName)); } - } - else { - if( strcmp( $propertyName, self::GETETAG_PROPERTYNAME) === 0 ) { - \OC\Files\Filesystem::putFileInfo($this->path, array('etag'=> $propertyValue)); - } elseif( strcmp( $propertyName, self::LASTMODIFIED_PROPERTYNAME) === 0 ) { + } else { + if (strcmp($propertyName, self::GETETAG_PROPERTYNAME) === 0) { + \OC\Files\Filesystem::putFileInfo($this->path, array('etag' => $propertyValue)); + } elseif (strcmp($propertyName, self::LASTMODIFIED_PROPERTYNAME) === 0) { $this->touch($propertyValue); } else { - if(!array_key_exists( $propertyName, $existing )) { - $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*properties`' - .' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' ); - $query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue )); + if (!array_key_exists($propertyName, $existing)) { + $query = OC_DB::prepare('INSERT INTO `*PREFIX*properties`' + . ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)'); + $query->execute(array(OC_User::getUser(), $this->path, $propertyName, $propertyValue)); } else { - $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' - .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' ); - $query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName )); + $query = OC_DB::prepare('UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' + . ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'); + $query->execute(array($propertyValue, OC_User::getUser(), $this->path, $propertyName)); } } } @@ -199,9 +180,9 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * removes all properties for this node and user */ public function removeProperties() { - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`' - .' WHERE `userid` = ? AND `propertypath` = ?' ); - $query->execute( array( OC_User::getUser(), $this->path)); + $query = OC_DB::prepare('DELETE FROM `*PREFIX*properties`' + . ' WHERE `userid` = ? AND `propertypath` = ?'); + $query->execute(array(OC_User::getUser(), $this->path)); $this->setPropertyCache(null); } @@ -219,29 +200,23 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr if (is_null($this->property_cache)) { $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'; - $result = OC_DB::executeAudited( $sql, array( OC_User::getUser(), $this->path ) ); + $result = OC_DB::executeAudited($sql, array(OC_User::getUser(), $this->path)); $this->property_cache = array(); - while( $row = $result->fetchRow()) { + while ($row = $result->fetchRow()) { $this->property_cache[$row['propertyname']] = $row['propertyvalue']; } - // Don't call the static getETagPropertyForPath, its result is not cached - $this->getFileinfoCache(); - if ($this->fileinfo_cache['etag']) { - $this->property_cache[self::GETETAG_PROPERTYNAME] = '"'.$this->fileinfo_cache['etag'].'"'; - } else { - $this->property_cache[self::GETETAG_PROPERTYNAME] = null; - } + $this->property_cache[self::GETETAG_PROPERTYNAME] = '"' . $this->info->getEtag() . '"'; } // if the array was empty, we need to return everything - if(count($properties) == 0) { + if (count($properties) == 0) { return $this->property_cache; } $props = array(); - foreach($properties as $property) { + foreach ($properties as $property) { if (isset($this->property_cache[$property])) { $props[$property] = $this->property_cache[$property]; } @@ -251,35 +226,12 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * Returns the ETag surrounded by double-quotes for this path. - * @param string $path Path of the file - * @return string|null Returns null if the ETag can not effectively be determined - */ - protected function getETagPropertyForPath($path) { - $data = $this->getFS()->getFileInfo($path); - if (isset($data['etag'])) { - return '"'.$data['etag'].'"'; - } - return null; - } - - protected function getFS() { - if (is_null($this->fileView)) { - $this->fileView = \OC\Files\Filesystem::getView(); - } - return $this->fileView; - } - - /** * @return string|null */ - public function getFileId() - { - $this->getFileinfoCache(); - - if (isset($this->fileinfo_cache['fileid'])) { + public function getFileId() { + if ($this->info->getId()) { $instanceId = OC_Util::getInstanceId(); - $id = sprintf('%08d', $this->fileinfo_cache['fileid']); + $id = sprintf('%08d', $this->info->getId()); return $id . $instanceId; } diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php index d2fa425b22c..35cc1679ab6 100644 --- a/lib/private/connector/sabre/objecttree.php +++ b/lib/private/connector/sabre/objecttree.php @@ -8,25 +8,45 @@ namespace OC\Connector\Sabre; +use OC\Files\FileInfo; use OC\Files\Filesystem; class ObjectTree extends \Sabre_DAV_ObjectTree { /** - * keep this public to allow mock injection during unit test - * * @var \OC\Files\View */ - public $fileView; + protected $fileView; + + /** + * Creates the object + * + * This method expects the rootObject to be passed as a parameter + */ + public function __construct() { + } + + /** + * @param \Sabre_DAV_ICollection $rootNode + * @param \OC\Files\View $view + */ + public function init(\Sabre_DAV_ICollection $rootNode, \OC\Files\View $view) { + $this->rootNode = $rootNode; + $this->fileView = $view; + } /** * Returns the INode object for the requested path * * @param string $path + * @throws \Sabre_DAV_Exception_ServiceUnavailable * @throws \Sabre_DAV_Exception_NotFound * @return \Sabre_DAV_INode */ public function getNodeForPath($path) { + if (!$this->fileView) { + throw new \Sabre_DAV_Exception_ServiceUnavailable('filesystem not setup'); + } $path = trim($path, '/'); if (isset($this->cache[$path])) { @@ -40,31 +60,34 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { if (pathinfo($path, PATHINFO_EXTENSION) === 'part') { // read from storage - $absPath = $this->getFileView()->getAbsolutePath($path); + $absPath = $this->fileView->getAbsolutePath($path); list($storage, $internalPath) = Filesystem::resolvePath('/' . $absPath); if ($storage) { + /** + * @var \OC\Files\Storage\Storage $storage + */ $scanner = $storage->getScanner($internalPath); // get data directly - $info = $scanner->getData($internalPath); + $data = $scanner->getData($internalPath); + $info = new FileInfo($absPath, $storage, $internalPath, $data); + } else { + $info = null; } - } - else { + } else { // read from cache - $info = $this->getFileView()->getFileInfo($path); + $info = $this->fileView->getFileInfo($path); } if (!$info) { throw new \Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located'); } - if ($info['mimetype'] === 'httpd/unix-directory') { - $node = new \OC_Connector_Sabre_Directory($path); + if ($info->getType() === 'dir') { + $node = new \OC_Connector_Sabre_Directory($this->fileView, $info); } else { - $node = new \OC_Connector_Sabre_File($path); + $node = new \OC_Connector_Sabre_File($this->fileView, $info); } - $node->setFileinfoCache($info); - $this->cache[$path] = $node; return $node; @@ -75,10 +98,15 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { * * @param string $sourcePath The path to the file which should be moved * @param string $destinationPath The full destination path, so not just the destination parent node + * @throws \Sabre_DAV_Exception_BadRequest + * @throws \Sabre_DAV_Exception_ServiceUnavailable * @throws \Sabre_DAV_Exception_Forbidden * @return int */ public function move($sourcePath, $destinationPath) { + if (!$this->fileView) { + throw new \Sabre_DAV_Exception_ServiceUnavailable('filesystem not setup'); + } $sourceNode = $this->getNodeForPath($sourcePath); if ($sourceNode instanceof \Sabre_DAV_ICollection and $this->nodeExists($destinationPath)) { @@ -87,20 +115,28 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { list($sourceDir,) = \Sabre_DAV_URLUtil::splitPath($sourcePath); list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destinationPath); + $isShareMountPoint = false; + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath( '/' . \OCP\User::getUser() . '/files/' . $sourcePath); + if ($storage instanceof \OC\Files\Storage\Shared && !$internalPath) { + $isShareMountPoint = true; + } + // check update privileges - $fs = $this->getFileView(); - if (!$fs->isUpdatable($sourcePath)) { + if (!$this->fileView->isUpdatable($sourcePath) && !$isShareMountPoint) { throw new \Sabre_DAV_Exception_Forbidden(); } if ($sourceDir !== $destinationDir) { // for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir - if (!$fs->isUpdatable($sourceDir)) { + if (ltrim($destinationDir, '/') === '') { + throw new \Sabre_DAV_Exception_Forbidden(); + } + if (!$this->fileView->isUpdatable($sourceDir)) { throw new \Sabre_DAV_Exception_Forbidden(); } - if (!$fs->isUpdatable($destinationDir)) { + if (!$this->fileView->isUpdatable($destinationDir)) { throw new \Sabre_DAV_Exception_Forbidden(); } - if (!$fs->isDeletable($sourcePath)) { + if (!$this->fileView->isDeletable($sourcePath)) { throw new \Sabre_DAV_Exception_Forbidden(); } } @@ -110,15 +146,15 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { throw new \Sabre_DAV_Exception_BadRequest(); } - $renameOkay = $fs->rename($sourcePath, $destinationPath); + $renameOkay = $this->fileView->rename($sourcePath, $destinationPath); if (!$renameOkay) { throw new \Sabre_DAV_Exception_Forbidden(''); } // update properties - $query = \OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?' - .' WHERE `userid` = ? AND `propertypath` = ?' ); - $query->execute( array( $destinationPath, \OC_User::getUser(), $sourcePath )); + $query = \OC_DB::prepare('UPDATE `*PREFIX*properties` SET `propertypath` = ?' + . ' WHERE `userid` = ? AND `propertypath` = ?'); + $query->execute(array(\OC\Files\Filesystem::normalizePath($destinationPath), \OC_User::getUser(), \OC\Files\Filesystem::normalizePath($sourcePath))); $this->markDirty($sourceDir); $this->markDirty($destinationDir); @@ -133,20 +169,24 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { * * @param string $source * @param string $destination + * @throws \Sabre_DAV_Exception_ServiceUnavailable * @return void */ public function copy($source, $destination) { + if (!$this->fileView) { + throw new \Sabre_DAV_Exception_ServiceUnavailable('filesystem not setup'); + } - if (Filesystem::is_file($source)) { - Filesystem::copy($source, $destination); + if ($this->fileView->is_file($source)) { + $this->fileView->copy($source, $destination); } else { - Filesystem::mkdir($destination); - $dh = Filesystem::opendir($source); - if(is_resource($dh)) { - while (($subnode = readdir($dh)) !== false) { + $this->fileView->mkdir($destination); + $dh = $this->fileView->opendir($source); + if (is_resource($dh)) { + while (($subNode = readdir($dh)) !== false) { - if ($subnode == '.' || $subnode == '..') continue; - $this->copy($source . '/' . $subnode, $destination . '/' . $subnode); + if ($subNode == '.' || $subNode == '..') continue; + $this->copy($source . '/' . $subNode, $destination . '/' . $subNode); } } @@ -155,14 +195,4 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destination); $this->markDirty($destinationDir); } - - /** - * @return \OC\Files\View - */ - public function getFileView() { - if (is_null($this->fileView)) { - $this->fileView = \OC\Files\Filesystem::getView(); - } - return $this->fileView; - } } diff --git a/lib/private/connector/sabre/quotaplugin.php b/lib/private/connector/sabre/quotaplugin.php index 8099794f670..1e73e1645c3 100644 --- a/lib/private/connector/sabre/quotaplugin.php +++ b/lib/private/connector/sabre/quotaplugin.php @@ -10,6 +10,11 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { /** + * @var \OC\Files\View + */ + private $view; + + /** * Reference to main server object * * @var Sabre_DAV_Server @@ -17,11 +22,11 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { private $server; /** - * is kept public to allow overwrite for unit testing - * - * @var \OC\Files\View + * @param \OC\Files\View $view */ - public $fileView; + public function __construct($view) { + $this->view = $view; + } /** * This initializes the plugin. @@ -45,27 +50,38 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { /** * This method is called before any HTTP method and validates there is enough free space to store the file * - * @throws Sabre_DAV_Exception * @param string $uri + * @param null $data + * @throws Sabre_DAV_Exception_InsufficientStorage * @return bool */ public function checkQuota($uri, $data = null) { $length = $this->getLength(); if ($length) { - if (substr($uri, 0, 1)!=='/') { - $uri='/'.$uri; + if (substr($uri, 0, 1) !== '/') { + $uri = '/' . $uri; } list($parentUri, $newName) = Sabre_DAV_URLUtil::splitPath($uri); + $req = $this->server->httpRequest; + if ($req->getHeader('OC-Chunked')) { + $info = OC_FileChunking::decodeName($newName); + $chunkHandler = new OC_FileChunking($info); + // subtract the already uploaded size to see whether + // there is still enough space for the remaining chunks + $length -= $chunkHandler->getCurrentSize(); + } $freeSpace = $this->getFreeSpace($parentUri); if ($freeSpace !== \OC\Files\SPACE_UNKNOWN && $length > $freeSpace) { + if (isset($chunkHandler)) { + $chunkHandler->cleanup(); + } throw new Sabre_DAV_Exception_InsufficientStorage(); } } return true; } - public function getLength() - { + public function getLength() { $req = $this->server->httpRequest; $length = $req->getHeader('X-Expected-Entity-Length'); if (!$length) { @@ -84,14 +100,8 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { * @param $parentUri * @return mixed */ - public function getFreeSpace($parentUri) - { - if (is_null($this->fileView)) { - // initialize fileView - $this->fileView = \OC\Files\Filesystem::getView(); - } - - $freeSpace = $this->fileView->free_space($parentUri); + public function getFreeSpace($parentUri) { + $freeSpace = $this->view->free_space($parentUri); return $freeSpace; } } diff --git a/lib/private/contactsmanager.php b/lib/private/contactsmanager.php index fc6745b4505..4299d88017a 100644 --- a/lib/private/contactsmanager.php +++ b/lib/private/contactsmanager.php @@ -37,7 +37,12 @@ namespace OC { $result = array(); foreach($this->address_books as $address_book) { $r = $address_book->search($pattern, $searchProperties, $options); - $result = array_merge($result, $r); + $contacts = array(); + foreach($r as $c){ + $c['addressbook-key'] = $address_book->getKey(); + $contacts[] = $c; + } + $result = array_merge($result, $contacts); } return $result; @@ -47,7 +52,7 @@ namespace OC { * This function can be used to delete the contact identified by the given id * * @param object $id the unique identifier to a contact - * @param $address_book_key + * @param string $address_book_key identifier of the address book in which the contact shall be deleted * @return bool successful or not */ public function delete($id, $address_book_key) { @@ -66,7 +71,7 @@ namespace OC { * 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 $address_book_key string to identify the address book in which the contact shall be created or updated + * @param string $address_book_key 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, $address_book_key) { diff --git a/lib/private/davclient.php b/lib/private/davclient.php index afa4e1103b4..916dc11d17a 100644 --- a/lib/private/davclient.php +++ b/lib/private/davclient.php @@ -29,6 +29,8 @@ class OC_DAVClient extends \Sabre_DAV_Client { protected $requestTimeout; + protected $verifyHost; + /** * @brief Sets the request timeout or 0 to disable timeout. * @param integer $timeout in seconds or 0 to disable @@ -37,10 +39,21 @@ class OC_DAVClient extends \Sabre_DAV_Client { $this->requestTimeout = (int)$timeout; } + /** + * @brief Sets the CURLOPT_SSL_VERIFYHOST setting + * @param integer $value value to set CURLOPT_SSL_VERIFYHOST to + */ + public function setVerifyHost($value) { + $this->verifyHost = $value; + } + protected function curlRequest($url, $settings) { if ($this->requestTimeout > 0) { $settings[CURLOPT_TIMEOUT] = $this->requestTimeout; } + if (!is_null($this->verifyHost)) { + $settings[CURLOPT_SSL_VERIFYHOST] = $this->verifyHost; + } return parent::curlRequest($url, $settings); } } diff --git a/lib/private/db.php b/lib/private/db.php index cfdac766bff..322a13642ae 100644 --- a/lib/private/db.php +++ b/lib/private/db.php @@ -313,9 +313,8 @@ class OC_DB { /** * @brief Insert a row if a matching row doesn't exists. - * @param string $table. The table to insert into in the form '*PREFIX*tableName' - * @param array $input. An array of fieldname/value pairs - * @param string $table + * @param string $table The table to insert into in the form '*PREFIX*tableName' + * @param array $input An array of fieldname/value pairs * @return boolean number of updated rows */ public static function insertIfNotExist($table, $input) { diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index c050d47b499..aaf2ea543b9 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -82,6 +82,9 @@ class MDB2SchemaManager { $platform = $this->conn->getDatabasePlatform(); foreach($schemaDiff->changedTables as $tableDiff) { $tableDiff->name = $platform->quoteIdentifier($tableDiff->name); + foreach($tableDiff->changedColumns as $column) { + $column->oldColumnName = $platform->quoteIdentifier($column->oldColumnName); + } } if ($generateSql) { diff --git a/lib/private/db/mdb2schemareader.php b/lib/private/db/mdb2schemareader.php index f9a76786c3e..1c16d03eab2 100644 --- a/lib/private/db/mdb2schemareader.php +++ b/lib/private/db/mdb2schemareader.php @@ -41,7 +41,9 @@ class MDB2SchemaReader { */ public function loadSchemaFromFile($file) { $schema = new \Doctrine\DBAL\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 diff --git a/lib/private/db/statementwrapper.php b/lib/private/db/statementwrapper.php index eaf215c7231..492209b883b 100644 --- a/lib/private/db/statementwrapper.php +++ b/lib/private/db/statementwrapper.php @@ -8,6 +8,11 @@ /** * 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(); */ class OC_DB_StatementWrapper { /** @@ -161,6 +166,8 @@ class OC_DB_StatementWrapper { /** * provide an alias for fetch + * + * @return mixed */ public function fetchRow() { return $this->statement->fetch(); @@ -168,12 +175,13 @@ class OC_DB_StatementWrapper { /** * Provide a simple fetchOne. + * * fetch single column from the next row - * @param int $colnum the column number to fetch + * @param int $column the column number to fetch * @return string */ - public function fetchOne($colnum = 0) { - return $this->statement->fetchColumn($colnum); + public function fetchOne($column = 0) { + return $this->statement->fetchColumn($column); } /** diff --git a/lib/private/defaults.php b/lib/private/defaults.php index 0b97497baa1..fca798568c5 100644 --- a/lib/private/defaults.php +++ b/lib/private/defaults.php @@ -21,6 +21,7 @@ class OC_Defaults { private $defaultDocBaseUrl; private $defaultSlogan; private $defaultLogoClaim; + private $defaultMailHeaderColor; function __construct() { $this->l = OC_L10N::get('core'); @@ -28,11 +29,12 @@ class OC_Defaults { $this->defaultEntity = "ownCloud"; /* e.g. company name, used for footers and copyright notices */ $this->defaultName = "ownCloud"; /* short name, used when referring to the software */ $this->defaultTitle = "ownCloud"; /* can be a longer name, for titles */ - $this->defaultBaseUrl = "http://owncloud.org"; - $this->defaultSyncClientUrl = " http://owncloud.org/sync-clients/"; + $this->defaultBaseUrl = "https://owncloud.org"; + $this->defaultSyncClientUrl = "https://owncloud.org/sync-clients/"; $this->defaultDocBaseUrl = "http://doc.owncloud.org"; $this->defaultSlogan = $this->l->t("web services under your control"); $this->defaultLogoClaim = ""; + $this->defaultMailHeaderColor = "#1d2d44"; /* header color of mail notifications */ if (class_exists("OC_Theme")) { $this->theme = new OC_Theme(); @@ -174,4 +176,23 @@ class OC_Defaults { return $footer; } + public function buildDocLinkToKey($key) { + if ($this->themeExist('buildDocLinkToKey')) { + return $this->theme->buildDocLinkToKey($key); + } + return $this->getDocBaseUrl() . '/server/6.0/go.php?to=' . $key; + } + + /** + * Returns mail header color + * @return mail header color + */ + public function getMailHeaderColor() { + if ($this->themeExist('getMailHeaderColor')) { + return $this->theme->getMailHeaderColor(); + } else { + return $this->defaultMailHeaderColor; + } + } + } diff --git a/lib/private/eventsource.php b/lib/private/eventsource.php index 4df0bc2e7cd..5a41ddd8b37 100644 --- a/lib/private/eventsource.php +++ b/lib/private/eventsource.php @@ -63,8 +63,9 @@ class OC_EventSource{ $type=null; } if($this->fallback) { + $fallBackId = OC_Util::sanitizeHTML($this->fallBackId); $response='<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack(' - .$this->fallBackId.',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL; + .$fallBackId.',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL; echo $response; }else{ if($type) { diff --git a/lib/private/filechunking.php b/lib/private/filechunking.php index be7f4e14a11..990499e40b4 100644 --- a/lib/private/filechunking.php +++ b/lib/private/filechunking.php @@ -64,20 +64,46 @@ class OC_FileChunking { return $parts == $this->info['chunkcount']; } + /** + * Assembles the chunks into the file specified by the path. + * Chunks are deleted afterwards. + * + * @param string $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++) { + 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); } - $this->cleanup(); 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() { @@ -128,7 +154,15 @@ class OC_FileChunking { } /** - * @param string $path + * Assembles the chunks into the file specified by the path. + * Also triggers the relevant hooks and proxies. + * + * @param string $path target path + * + * @return boolean assembled file size or false if file could not be created + * + * @throws \OC\InsufficientStorageException when file could not be fully + * assembled due to lack of free space */ public function file_assemble($path) { $absolutePath = \OC\Files\Filesystem::normalizePath(\OC\Files\Filesystem::getView()->getAbsolutePath($path)); @@ -182,5 +216,6 @@ class OC_FileChunking { return false; } } + return false; } } diff --git a/lib/private/files.php b/lib/private/files.php index 656d6f044ca..c435f8d1684 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -21,22 +21,39 @@ * */ +// TODO: get rid of this using proper composer packages +require_once 'mcnetic/phpzipstreamer/ZipStreamer.php'; + +class GET_TYPE { + const FILE = 1; + const ZIP_FILES = 2; + const ZIP_DIR = 3; +} + /** - * Class for fileserver access + * Class for file server access * */ class OC_Files { - static $tmpFiles = array(); - - static public function getFileInfo($path, $includeMountPoints = true){ - return \OC\Files\Filesystem::getFileInfo($path, $includeMountPoints); - } /** - * @param string $path + * @param string $filename + * @param string $name + * @param bool $zip */ - static public function getDirectoryContent($path){ - return \OC\Files\Filesystem::getDirectoryContent($path); + private static function sendHeaders($filename, $name, $zip = false) { + OC_Response::setContentDispositionHeader($name, 'attachment'); + header('Content-Transfer-Encoding: binary'); + OC_Response::disableCaching(); + if ($zip) { + header('Content-Type: application/zip'); + } else { + $filesize = \OC\Files\Filesystem::filesize($filename); + header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); + if ($filesize > -1) { + header("Content-Length: ".$filesize); + } + } } /** @@ -54,97 +71,50 @@ class OC_Files { $xsendfile = true; } - if (is_array($files) && count($files) == 1) { + if (is_array($files) && count($files) === 1) { $files = $files[0]; } if (is_array($files)) { - self::validateZipDownload($dir, $files); - $executionTime = intval(ini_get('max_execution_time')); - set_time_limit(0); - $zip = new ZipArchive(); - $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { - $l = OC_L10N::get('lib'); - throw new Exception($l->t('cannot open "%s"', array($filename))); - } - foreach ($files as $file) { - $file = $dir . '/' . $file; - if (\OC\Files\Filesystem::is_file($file)) { - $tmpFile = \OC\Files\Filesystem::toTmpFile($file); - self::$tmpFiles[] = $tmpFile; - $zip->addFile($tmpFile, basename($file)); - } elseif (\OC\Files\Filesystem::is_dir($file)) { - self::zipAddDir($file, $zip); - } - } - $zip->close(); - if ($xsendfile) { - $filename = OC_Helper::moveToNoClean($filename); - } + $get_type = GET_TYPE::ZIP_FILES; $basename = basename($dir); if ($basename) { $name = $basename . '.zip'; } else { $name = 'download.zip'; } - - set_time_limit($executionTime); - } elseif (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { - self::validateZipDownload($dir, $files); - $executionTime = intval(ini_get('max_execution_time')); - set_time_limit(0); - $zip = new ZipArchive(); - $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { - $l = OC_L10N::get('lib'); - throw new Exception($l->t('cannot open "%s"', array($filename))); - } - $file = $dir . '/' . $files; - self::zipAddDir($file, $zip); - $zip->close(); - if ($xsendfile) { - $filename = OC_Helper::moveToNoClean($filename); - } - // downloading root ? - if ($files === '') { - $name = 'download.zip'; + + $filename = $dir . '/' . $name; + } else { + $filename = $dir . '/' . $files; + if (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { + $get_type = GET_TYPE::ZIP_DIR; + // downloading root ? + if ($files === '') { + $name = 'download.zip'; + } else { + $name = $files . '.zip'; + } + } else { - $name = $files . '.zip'; + $get_type = GET_TYPE::FILE; + $name = $files; } - set_time_limit($executionTime); - } else { + } + + if ($get_type === GET_TYPE::FILE) { $zip = false; - $filename = $dir . '/' . $files; - $name = $files; if ($xsendfile && OC_App::isEnabled('files_encryption')) { $xsendfile = false; } + } else { + self::validateZipDownload($dir, $files); + $zip = new ZipStreamer(false); } OC_Util::obEnd(); if ($zip or \OC\Files\Filesystem::isReadable($filename)) { - OC_Response::setContentDispositionHeader($name, 'attachment'); - header('Content-Transfer-Encoding: binary'); - OC_Response::disableCaching(); - if ($zip) { - ini_set('zlib.output_compression', 'off'); - header('Content-Type: application/zip'); - header('Content-Length: ' . filesize($filename)); - self::addSendfileHeader($filename); - }else{ - $filesize = \OC\Files\Filesystem::filesize($filename); - header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); - if ($filesize > -1) { - header("Content-Length: ".$filesize); - } - if ($xsendfile) { - list($storage) = \OC\Files\Filesystem::resolvePath(\OC\Files\Filesystem::getView()->getAbsolutePath($filename)); - if ($storage->isLocal()) { - self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); - } - } - } - } elseif ($zip or !\OC\Files\Filesystem::file_exists($filename)) { + self::sendHeaders($filename, $name, $zip); + } elseif (!\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OC_Template('', '404', 'guest'); $tmpl->assign('file', $name); @@ -157,23 +127,37 @@ class OC_Files { return ; } if ($zip) { - $handle = fopen($filename, 'r'); - if ($handle) { - $chunkSize = 8 * 1024; // 1 MB chunks - while (!feof($handle)) { - echo fread($handle, $chunkSize); - flush(); + $executionTime = intval(ini_get('max_execution_time')); + set_time_limit(0); + if ($get_type === GET_TYPE::ZIP_FILES) { + foreach ($files as $file) { + $file = $dir . '/' . $file; + if (\OC\Files\Filesystem::is_file($file)) { + $fh = \OC\Files\Filesystem::fopen($file, 'r'); + $zip->addFileFromStream($fh, basename($file)); + fclose($fh); + } elseif (\OC\Files\Filesystem::is_dir($file)) { + self::zipAddDir($file, $zip); + } } + } elseif ($get_type === GET_TYPE::ZIP_DIR) { + $file = $dir . '/' . $files; + self::zipAddDir($file, $zip); } - if (!$xsendfile) { - unlink($filename); - } - }else{ - \OC\Files\Filesystem::readfile($filename); - } - foreach (self::$tmpFiles as $tmpFile) { - if (file_exists($tmpFile) and is_file($tmpFile)) { - unlink($tmpFile); + $zip->finalize(); + set_time_limit($executionTime); + } else { + if ($xsendfile) { + $view = \OC\Files\Filesystem::getView(); + /** @var $storage \OC\Files\Storage\Storage */ + list($storage) = $view->resolvePath($filename); + if ($storage->isLocal()) { + self::addSendfileHeader($filename); + } else { + \OC\Files\Filesystem::readfile($filename); + } + } else { + \OC\Files\Filesystem::readfile($filename); } } } @@ -183,13 +167,15 @@ class OC_Files { */ private static function addSendfileHeader($filename) { if (isset($_SERVER['MOD_X_SENDFILE_ENABLED'])) { + $filename = \OC\Files\Filesystem::getLocalFile($filename); header("X-Sendfile: " . $filename); } if (isset($_SERVER['MOD_X_SENDFILE2_ENABLED'])) { - if (isset($_SERVER['HTTP_RANGE']) && + $filename = \OC\Files\Filesystem::getLocalFile($filename); + if (isset($_SERVER['HTTP_RANGE']) && preg_match("/^bytes=([0-9]+)-([0-9]*)$/", $_SERVER['HTTP_RANGE'], $range)) { $filelength = filesize($filename); - if ($range[2] == "") { + if ($range[2] === "") { $range[2] = $filelength - 1; } header("Content-Range: bytes $range[1]-$range[2]/" . $filelength); @@ -199,30 +185,36 @@ class OC_Files { header("X-Sendfile: " . $filename); } } - + if (isset($_SERVER['MOD_X_ACCEL_REDIRECT_ENABLED'])) { + $filename = \OC::$WEBROOT . '/data' . \OC\Files\Filesystem::getRoot() . $filename; header("X-Accel-Redirect: " . $filename); } } /** * @param string $dir - * @param ZipArchive $zip + * @param ZipStreamer $zip + * @param string $internalDir */ public static function zipAddDir($dir, $zip, $internalDir='') { $dirname=basename($dir); - $zip->addEmptyDir($internalDir.$dirname); + $rootDir = $internalDir.$dirname; + if (!empty($rootDir)) { + $zip->addEmptyDir($rootDir); + } $internalDir.=$dirname.='/'; // prevent absolute dirs $internalDir = ltrim($internalDir, '/'); - $files=OC_Files::getDirectoryContent($dir); + + $files=\OC\Files\Filesystem::getDirectoryContent($dir); foreach($files as $file) { $filename=$file['name']; $file=$dir.'/'.$filename; if(\OC\Files\Filesystem::is_file($file)) { - $tmpFile=\OC\Files\Filesystem::toTmpFile($file); - OC_Files::$tmpFiles[]=$tmpFile; - $zip->addFile($tmpFile, $internalDir.$filename); + $fh = \OC\Files\Filesystem::fopen($file, 'r'); + $zip->addFileFromStream($fh, $internalDir.$filename); + fclose($fh); }elseif(\OC\Files\Filesystem::is_dir($file)) { self::zipAddDir($file, $zip, $internalDir); } @@ -232,8 +224,8 @@ class OC_Files { /** * checks if the selected files are within the size constraint. If not, outputs an error page. * - * @param string $dir - * @param files $files + * @param string $dir + * @param array | string $files */ static function validateZipDownload($dir, $files) { if (!OC_Config::getValue('allowZipDownload', true)) { @@ -242,7 +234,7 @@ class OC_Files { OC_Template::printErrorPage( $l->t('ZIP download is turned off.'), $l->t('Files need to be downloaded one by one.') - . '<br/><a href="javascript:history.back()">' . $l->t('Back to Files') . '</a>' + . '<br/><a href="'.OCP\Util::linkTo('files', 'index.php', array('dir' => $dir)).'">' . $l->t('Back to Files') . '</a>' ); exit; } @@ -269,8 +261,7 @@ class OC_Files { OC_Template::printErrorPage( $l->t('Selected files too large to generate zip file.'), $l->t('Please download the files separately in smaller chunks or kindly ask your administrator.') - .'<br/><a href="javascript:history.back()">' - . $l->t('Back to Files') . '</a>' + . '<br/><a href="'.OCP\Util::linkTo('files', 'index.php', array('dir' => $dir)).'">' . $l->t('Back to Files') . '</a>' ); exit; } @@ -280,8 +271,8 @@ class OC_Files { /** * set the maximum upload size limit for apache hosts using .htaccess * - * @param int size filesisze in bytes - * @return false on failure, size on success + * @param int $size file size in bytes + * @return bool false on failure, size on success */ static function setUploadLimit($size) { //don't allow user to break his config -- upper boundary @@ -291,17 +282,16 @@ class OC_Files { return false; $size -= 1; } else { - $size = OC_Helper::humanFileSize($size); - $size = substr($size, 0, -1); //strip the B - $size = str_replace(' ', '', $size); //remove the space between the size and the postfix + $size = OC_Helper::phpFileSize($size); } //don't allow user to break his config -- broken or malicious size input - if (intval($size) == 0) { + if (intval($size) === 0) { return false; } - $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess'); //supress errors in case we don't have permissions for + //suppress errors in case we don't have permissions for + $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess'); if (!$htaccess) { return false; } @@ -319,7 +309,7 @@ class OC_Files { if ($content !== null) { $htaccess = $content; } - if ($hasReplaced == 0) { + if ($hasReplaced === 0) { $htaccess .= "\n" . $setting; } } diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 4cab4619149..1c9de56f8c5 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -166,6 +166,16 @@ class Cache { */ 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 array + */ + public function getFolderContentsById($fileId) { if ($fileId > -1) { $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `unencrypted_size`, `etag` @@ -488,9 +498,10 @@ class Cache { * 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) { - $this->calculateFolderSize($path); + public function correctFolderSize($path, $data = null) { + $this->calculateFolderSize($path, $data); if ($path !== '') { $parent = dirname($path); if ($parent === '.' or $parent === '/') { @@ -504,11 +515,14 @@ class 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) { + public function calculateFolderSize($path, $entry = null) { $totalSize = 0; - $entry = $this->get($path); + if (is_null($entry) or !isset($entry['fileid'])) { + $entry = $this->get($path); + } if ($entry && $entry['mimetype'] === 'httpd/unix-directory') { $id = $entry['fileid']; $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2, ' . @@ -530,7 +544,7 @@ class Cache { if ($entry['size'] !== $totalSize) { $update['size'] = $totalSize; } - if ($entry['unencrypted_size'] !== $unencryptedSum) { + if (!isset($entry['unencrypted_size']) or $entry['unencrypted_size'] !== $unencryptedSum) { $update['unencrypted_size'] = $unencryptedSum; } if (count($update) > 0) { @@ -580,7 +594,25 @@ class Cache { } /** + * get the path of a file on this storage by it's id + * + * @param int $id + * @return string | null + */ + public function getPathById($id) { + $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?'; + $result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId())); + if ($row = $result->fetchRow()) { + 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 * @return array, first element holding the storage id, second the path diff --git a/lib/private/files/cache/homecache.php b/lib/private/files/cache/homecache.php index a7c310a3782..2326c46e8d0 100644 --- a/lib/private/files/cache/homecache.php +++ b/lib/private/files/cache/homecache.php @@ -13,26 +13,37 @@ 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) { - if ($path !== '/' and $path !== '' and $path !== 'files') { - return parent::calculateFolderSize($path); + public function calculateFolderSize($path, $entry = null) { + if ($path !== '/' and $path !== '' and $path !== 'files' and $path !== 'files_trashbin') { + 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; - $entry = $this->get($path); + if (is_null($entry)) { + $entry = $this->get($path); + } if ($entry && $entry['mimetype'] === 'httpd/unix-directory') { $id = $entry['fileid']; - $sql = 'SELECT SUM(`size`) FROM `*PREFIX*filecache` ' . + $sql = 'SELECT SUM(`size`) AS f1, ' . + 'SUM(`unencrypted_size`) AS f2 FROM `*PREFIX*filecache` ' . 'WHERE `parent` = ? AND `storage` = ? AND `size` >= 0'; $result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId())); if ($row = $result->fetchRow()) { - list($sum) = array_values($row); + list($sum, $unencryptedSum) = array_values($row); $totalSize = (int)$sum; + $unencryptedSize = (int)$unencryptedSum; if ($entry['size'] !== $totalSize) { $this->update($id, array('size' => $totalSize)); } + if ($entry['unencrypted_size'] !== $unencryptedSize) { + $this->update($id, array('unencrypted_size' => $unencryptedSize)); + } } } return $totalSize; @@ -40,6 +51,7 @@ class HomeCache extends Cache { /** * @param string $path + * @return array */ public function get($path) { $data = parent::get($path); diff --git a/lib/private/files/cache/legacy.php b/lib/private/files/cache/legacy.php deleted file mode 100644 index 4d5f58741e9..00000000000 --- a/lib/private/files/cache/legacy.php +++ /dev/null @@ -1,139 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -namespace OC\Files\Cache; - -/** - * Provide read only support for the old filecache - */ -class Legacy { - private $user; - - private $cacheHasItems = null; - - /** - * @param string $user - */ - public function __construct($user) { - $this->user = $user; - } - - /** - * get the numbers of items in the legacy cache - * - * @return int - */ - function getCount() { - $sql = 'SELECT COUNT(`id`) AS `count` FROM `*PREFIX*fscache` WHERE `user` = ?'; - $result = \OC_DB::executeAudited($sql, array($this->user)); - if ($row = $result->fetchRow()) { - return $row['count']; - } else { - return 0; - } - } - - /** - * check if a legacy cache is present and holds items - * - * @return bool - */ - function hasItems() { - if (!is_null($this->cacheHasItems)) { - return $this->cacheHasItems; - } - try { - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*fscache` WHERE `user` = ?',1); - } catch (\Exception $e) { - $this->cacheHasItems = false; - return false; - } - try { - $result = $query->execute(array($this->user)); - } catch (\Exception $e) { - $this->cacheHasItems = false; - return false; - } - - if ($result === false || property_exists($result, 'error_message_prefix')) { - $this->cacheHasItems = false; - return false; - } - - $this->cacheHasItems = (bool)$result->fetchRow(); - return $this->cacheHasItems; - } - - /** - * get an item from the legacy cache - * - * @param string $path - * @return array - */ - function get($path) { - if (is_numeric($path)) { - $sql = 'SELECT * FROM `*PREFIX*fscache` WHERE `id` = ?'; - } else { - $sql = 'SELECT * FROM `*PREFIX*fscache` WHERE `path` = ?'; - } - $result = \OC_DB::executeAudited($sql, array($path)); - $data = $result->fetchRow(); - $data['etag'] = $this->getEtag($data['path'], $data['user']); - return $data; - } - - /** - * Get the ETag for the given path - * - * @param type $path - * @return string - */ - function getEtag($path, $user = null) { - static $query = null; - - $pathDetails = explode('/', $path, 4); - if((!$user) && !isset($pathDetails[1])) { - //no user!? Too odd, return empty string. - return ''; - } else if(!$user) { - //guess user from path, if no user passed. - $user = $pathDetails[1]; - } - - if(!isset($pathDetails[3]) || is_null($pathDetails[3])) { - $relativePath = ''; - } else { - $relativePath = $pathDetails[3]; - } - - if(is_null($query)){ - $query = \OC_DB::prepare('SELECT `propertyvalue` FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = \'{DAV:}getetag\''); - } - $result = \OC_DB::executeAudited($query,array($user, '/' . $relativePath)); - if ($row = $result->fetchRow()) { - return trim($row['propertyvalue'], '"'); - } else { - return ''; - } - } - - /** - * get all child items of an item from the legacy cache - * - * @param int $id - * @return array - */ - function getChildren($id) { - $result = \OC_DB::executeAudited('SELECT * FROM `*PREFIX*fscache` WHERE `parent` = ?', array($id)); - $data = $result->fetchAll(); - foreach ($data as $i => $item) { - $data[$i]['etag'] = $this->getEtag($item['path'], $item['user']); - } - return $data; - } -} diff --git a/lib/private/files/cache/scanner.php b/lib/private/files/cache/scanner.php index 92a4c01841b..c0bdde06a75 100644 --- a/lib/private/files/cache/scanner.php +++ b/lib/private/files/cache/scanner.php @@ -115,11 +115,12 @@ class Scanner extends BasicEmitter { } if ($reuseExisting) { // prevent empty etag - $etag = $cacheData['etag']; - $propagateETagChange = false; - if (empty($etag)) { + if (empty($cacheData['etag'])) { $etag = $data['etag']; $propagateETagChange = true; + } else { + $etag = $cacheData['etag']; + $propagateETagChange = false; } // 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']) { @@ -155,7 +156,7 @@ class Scanner extends BasicEmitter { } } if (!empty($newData)) { - $this->cache->put($file, $newData); + $data['fileid'] = $this->cache->put($file, $newData); $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)); } @@ -173,14 +174,16 @@ class Scanner extends BasicEmitter { * @param string $path * @param bool $recursive * @param int $reuse - * @return int the size of the scanned folder or -1 if the size is unknown at this stage + * @return array with the meta data of the scanned file or folder */ public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1) { if ($reuse === -1) { $reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : 0; } - $this->scanFile($path, $reuse); - return $this->scanChildren($path, $recursive, $reuse); + $data = $this->scanFile($path, $reuse); + $size = $this->scanChildren($path, $recursive, $reuse); + $data['size'] = $size; + return $data; } /** diff --git a/lib/private/files/cache/updater.php b/lib/private/files/cache/updater.php index 7a45b9e9e96..199ce5dee78 100644 --- a/lib/private/files/cache/updater.php +++ b/lib/private/files/cache/updater.php @@ -38,8 +38,8 @@ class Updater { if ($storage) { $cache = $storage->getCache($internalPath); $scanner = $storage->getScanner($internalPath); - $scanner->scan($internalPath, Scanner::SCAN_SHALLOW); - $cache->correctFolderSize($internalPath); + $data = $scanner->scan($internalPath, Scanner::SCAN_SHALLOW); + $cache->correctFolderSize($internalPath, $data); self::correctFolder($path, $storage->filemtime($internalPath)); self::correctParentStorageMtime($storage, $internalPath); } @@ -119,6 +119,9 @@ class Updater { if ($uid != \OCP\User::getUser()) { $info = \OC\Files\Filesystem::getFileInfo($filename); + if (!$info) { + return array($uid, '/files/' . $filename); + } $ownerView = new \OC\Files\View('/' . $uid . '/files'); $filename = $ownerView->getPath($info['fileid']); } @@ -150,7 +153,7 @@ class Updater { $cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath))); if ($realPath !== '') { $realPath = dirname($realPath); - if($realPath === DIRECTORY_SEPARATOR ) { + if ($realPath === DIRECTORY_SEPARATOR) { $realPath = ""; } // check storage for parent in case we change the storage in this step diff --git a/lib/private/files/cache/upgrade.php b/lib/private/files/cache/upgrade.php deleted file mode 100644 index e3a46896cbf..00000000000 --- a/lib/private/files/cache/upgrade.php +++ /dev/null @@ -1,235 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -namespace OC\Files\Cache; - -class Upgrade { - /** - * @var Legacy $legacy - */ - private $legacy; - - private $numericIds = array(); - - private $mimeTypeIds = array(); - - /** - * @param Legacy $legacy - */ - public function __construct($legacy) { - $this->legacy = $legacy; - } - - /** - * Preform a upgrade a path and it's childs - * - * @param string $path - * @param bool $mode - */ - function upgradePath($path, $mode = Scanner::SCAN_RECURSIVE) { - if (!$this->legacy->hasItems()) { - return; - } - \OC_Hook::emit('\OC\Files\Cache\Upgrade', 'migrate_path', $path); - if ($row = $this->legacy->get($path)) { - $data = $this->getNewData($row); - if ($data) { - $this->insert($data); - $this->upgradeChilds($data['id'], $mode); - } - } - } - - /** - * upgrade all child elements of an item - * - * @param int $id - * @param bool $mode - */ - function upgradeChilds($id, $mode = Scanner::SCAN_RECURSIVE) { - $children = $this->legacy->getChildren($id); - foreach ($children as $child) { - $childData = $this->getNewData($child); - \OC_Hook::emit('\OC\Files\Cache\Upgrade', 'migrate_path', $child['path']); - if ($childData) { - $this->insert($childData); - if ($mode == Scanner::SCAN_RECURSIVE) { - $this->upgradeChilds($child['id']); - } - } - } - } - - /** - * insert data into the new cache - * - * @param array $data the data for the new cache - */ - function insert($data) { - static $insertQuery = null; - if(is_null($insertQuery)) { - $insertQuery = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache` - ( `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` ) - VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); - } - if (!$this->inCache($data['storage'], $data['path_hash'], $data['id'])) { - \OC_DB::executeAudited($insertQuery, array($data['id'], $data['storage'], - $data['path'], $data['path_hash'], $data['parent'], $data['name'], - $data['mimetype'], $data['mimepart'], $data['size'], $data['mtime'], $data['encrypted'], $data['etag'])); - } - } - - /** - * check if an item is already in the new cache - * - * @param string $storage - * @param string $pathHash - * @param string $id - * @return bool - */ - function inCache($storage, $pathHash, $id) { - static $query = null; - if(is_null($query)) { - $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE (`storage` = ? AND `path_hash` = ?) OR `fileid` = ?'); - } - $result = \OC_DB::executeAudited($query, array($storage, $pathHash, $id)); - return (bool)$result->fetchRow(); - } - - /** - * get the new data array from the old one - * - * @param array $data the data from the old cache - * Example data array - * Array - * ( - * [id] => 418 - * [path] => /tina/files/picture.jpg //relative to datadir - * [path_hash] => 66d4547e372888deed80b24fec9b192b - * [parent] => 234 - * [name] => picture.jpg - * [user] => tina - * [size] => 1265283 - * [ctime] => 1363909709 - * [mtime] => 1363909709 - * [mimetype] => image/jpeg - * [mimepart] => image - * [encrypted] => 0 - * [versioned] => 0 - * [writable] => 1 - * ) - * - * @return array - */ - function getNewData($data) { - //Make sure there is a path, otherwise we can do nothing. - if(!isset($data['path'])) { - return false; - } - $newData = $data; - /** - * @var \OC\Files\Storage\Storage $storage - * @var string $internalPath; - */ - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($data['path']); - if ($storage) { - $newData['etag'] = $data['etag']; - $newData['path_hash'] = md5($internalPath); - $newData['path'] = $internalPath; - $newData['storage'] = $this->getNumericId($storage); - $newData['parent'] = ($internalPath === '') ? -1 : $data['parent']; - $newData['permissions'] = ($data['writable']) ? \OCP\PERMISSION_ALL : \OCP\PERMISSION_READ; - $newData['storage_object'] = $storage; - $newData['mimetype'] = $this->getMimetypeId($newData['mimetype'], $storage); - $newData['mimepart'] = $this->getMimetypeId($newData['mimepart'], $storage); - return $newData; - } else { - \OC_Log::write('core', 'Unable to migrate data from old cache for '.$data['path'].' because the storage was not found', \OC_Log::ERROR); - return false; - } - } - - /** - * get the numeric storage id - * - * @param \OC\Files\Storage\Storage $storage - * @return int - */ - function getNumericId($storage) { - $storageId = $storage->getId(); - if (!isset($this->numericIds[$storageId])) { - $cache = $storage->getCache(); - $this->numericIds[$storageId] = $cache->getNumericStorageId(); - } - return $this->numericIds[$storageId]; - } - - /** - * get the numeric id for a mimetype - * - * @param string $mimetype - * @param \OC\Files\Storage\Storage $storage - * @return int - */ - function getMimetypeId($mimetype, $storage) { - if (!isset($this->mimeTypeIds[$mimetype])) { - $cache = new Cache($storage); - $this->mimeTypeIds[$mimetype] = $cache->getMimetypeId($mimetype); - } - return $this->mimeTypeIds[$mimetype]; - } - - /** - * check if a cache upgrade is required for $user - * - * @param string $user - * @return bool - */ - static function needUpgrade($user) { - $cacheVersion = (int)\OCP\Config::getUserValue($user, 'files', 'cache_version', 4); - if ($cacheVersion < 5) { - $legacy = new \OC\Files\Cache\Legacy($user); - if ($legacy->hasItems()) { - return true; - } - self::upgradeDone($user); - } - - return false; - } - - /** - * mark the filecache as upgrade - * - * @param string $user - */ - static function upgradeDone($user) { - \OCP\Config::setUserValue($user, 'files', 'cache_version', 5); - } - - /** - * Does a "silent" upgrade, i.e. without an Event-Source as triggered - * on User-Login via Ajax. This method is called within the regular - * ownCloud upgrade. - * - * @param string $user a User ID - */ - public static function doSilentUpgrade($user) { - if(!self::needUpgrade($user)) { - return; - } - $legacy = new \OC\Files\Cache\Legacy($user); - if ($legacy->hasItems()) { - \OC_DB::beginTransaction(); - $upgrade = new \OC\Files\Cache\Upgrade($legacy); - $upgrade->upgradePath('/' . $user . '/files'); - \OC_DB::commit(); - } - \OC\Files\Cache\Upgrade::upgradeDone($user); - } -} diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 2dbdd80a26b..d6940f50bf1 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -53,7 +53,13 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { } public function offsetGet($offset) { - return $this->data[$offset]; + if ($offset === 'type') { + return $this->getType(); + } elseif (isset($this->data[$offset])) { + return $this->data[$offset]; + } else { + return null; + } } /** @@ -144,10 +150,14 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return \OCP\Files\FileInfo::TYPE_FILE | \OCP\Files\FileInfo::TYPE_FOLDER */ public function getType() { - return $this->data['type']; + if (isset($this->data['type'])) { + return $this->data['type']; + } else { + return $this->getMimetype() === 'httpd/unix-directory' ? self::TYPE_FOLDER : self::TYPE_FILE; + } } - public function getData(){ + public function getData() { return $this->data; } diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php index 952f9f9febf..52df1bec611 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -119,6 +119,22 @@ class Filesystem { 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 @@ -320,78 +336,34 @@ class Filesystem { else { self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user); } - $mount_file = \OC_Config::getValue("mount_file", \OC::$SERVERROOT . "/data/mount.json"); - //move config file to it's new position - if (is_file(\OC::$SERVERROOT . '/config/mount.json')) { - rename(\OC::$SERVERROOT . '/config/mount.json', $mount_file); - } - // Load system mount points - if (is_file(\OC::$SERVERROOT . '/config/mount.php') or is_file($mount_file)) { - if (is_file($mount_file)) { - $mountConfig = json_decode(file_get_contents($mount_file), true); - } elseif (is_file(\OC::$SERVERROOT . '/config/mount.php')) { - $mountConfig = $parser->parsePHP(file_get_contents(\OC::$SERVERROOT . '/config/mount.php')); - } - if (isset($mountConfig['global'])) { - foreach ($mountConfig['global'] as $mountPoint => $options) { - self::mount($options['class'], $options['options'], $mountPoint); - } - } - if (isset($mountConfig['group'])) { - foreach ($mountConfig['group'] as $group => $mounts) { - if (\OC_Group::inGroup($user, $group)) { - foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($user, $mountPoint); - foreach ($options as &$option) { - $option = self::setUserVars($user, $option); - } - self::mount($options['class'], $options['options'], $mountPoint); - } - } - } - } - if (isset($mountConfig['user'])) { - foreach ($mountConfig['user'] as $mountUser => $mounts) { - if ($mountUser === 'all' or strtolower($mountUser) === strtolower($user)) { - foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($user, $mountPoint); - foreach ($options as &$option) { - $option = self::setUserVars($user, $option); - } - self::mount($options['class'], $options['options'], $mountPoint); - } - } - } - } - } - // Load personal mount points - if (is_file($root . '/mount.php') or is_file($root . '/mount.json')) { - if (is_file($root . '/mount.json')) { - $mountConfig = json_decode(file_get_contents($root . '/mount.json'), true); - } elseif (is_file($root . '/mount.php')) { - $mountConfig = $parser->parsePHP(file_get_contents($root . '/mount.php')); - } - if (isset($mountConfig['user'][$user])) { - foreach ($mountConfig['user'][$user] as $mountPoint => $options) { - self::mount($options['class'], $options['options'], $mountPoint); - } - } - } + self::mountCacheDir($user); // Chance to mount for other storages \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user, 'user_dir' => $root)); } /** - * fill in the correct values for $user - * - * @param string $user - * @param string $input - * @return string + * Mounts the cache directory + * @param string $user user name */ - private static function setUserVars($user, $input) { - return str_replace('$user', $user, $input); + private static function mountCacheDir($user) { + $cacheBaseDir = \OC_Config::getValue('cache_path', ''); + if ($cacheBaseDir === '') { + // use local cache dir relative to the user's home + $subdir = 'cache'; + $view = new \OC\Files\View('/' . $user); + if(!$view->file_exists($subdir)) { + $view->mkdir($subdir); + } + } else { + $cacheDir = rtrim($cacheBaseDir, '/') . '/' . $user; + if (!file_exists($cacheDir)) { + mkdir($cacheDir, 0770, true); + } + // mount external cache dir to "/$user/cache" mount point + self::mount('\OC\Files\Storage\Local', array('datadir' => $cacheDir), '/' . $user . '/cache'); + } } /** @@ -418,6 +390,9 @@ class Filesystem { * Returns path like /admin/files */ static public function getRoot() { + if (!self::$defaultInstance) { + return null; + } return self::$defaultInstance->getRoot(); } @@ -760,7 +735,7 @@ class Filesystem { * * @param string $directory path under datadirectory * @param string $mimetype_filter limit returned content to this mimetype or mimepart - * @return array + * @return \OC\Files\FileInfo[] */ public static function getDirectoryContent($directory, $mimetype_filter = '') { return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); diff --git a/lib/private/files/mount/manager.php b/lib/private/files/mount/manager.php index ff4a336f347..91460b72730 100644 --- a/lib/private/files/mount/manager.php +++ b/lib/private/files/mount/manager.php @@ -24,6 +24,13 @@ class Manager { } /** + * @param string $mountPoint + */ + public function removeMount($mountPoint) { + unset($this->mounts[$mountPoint]); + } + + /** * Find the mount for $path * * @param $path diff --git a/lib/private/files/mount/mount.php b/lib/private/files/mount/mount.php index 0ce2f5975c7..08d5ddf348b 100644 --- a/lib/private/files/mount/mount.php +++ b/lib/private/files/mount/mount.php @@ -66,6 +66,13 @@ class Mount { } /** + * @param string $mountPoint new mount point + */ + public function setMountPoint($mountPoint) { + $this->mountPoint = $mountPoint; + } + + /** * create the storage that is mounted * * @return \OC\Files\Storage\Storage diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index d4dca780ff3..8a263d4ce1e 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -27,6 +27,11 @@ abstract class Common implements \OC\Files\Storage\Storage { protected $watcher; protected $storageCache; + /** + * @var string[] + */ + protected $cachedFiles = array(); + public function __construct($parameters) { } @@ -113,20 +118,22 @@ abstract class Common implements \OC\Files\Storage\Storage { if (!$handle) { return false; } - $size = $this->filesize($path); - if ($size == 0) { - return ''; - } - return fread($handle, $size); + $data = stream_get_contents($handle); + fclose($handle); + return $data; } public function file_put_contents($path, $data) { $handle = $this->fopen($path, "w"); - return fwrite($handle, $data); + $this->removeCachedFile($path); + $count = fwrite($handle, $data); + fclose($handle); + return $count; } public function rename($path1, $path2) { if ($this->copy($path1, $path2)) { + $this->removeCachedFile($path1); return $this->unlink($path1); } else { return false; @@ -137,46 +144,10 @@ abstract class Common implements \OC\Files\Storage\Storage { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); list($count, $result) = \OC_Helper::streamCopy($source, $target); + $this->removeCachedFile($path2); return $result; } - /** - * @brief Deletes all files and folders recursively within a directory - * @param string $directory The directory whose contents will be deleted - * @param bool $empty Flag indicating whether directory will be emptied - * @returns bool - * - * @note By default the directory specified by $directory will be - * deleted together with its contents. To avoid this set $empty to true - */ - public function deleteAll($directory, $empty = false) { - $directory = trim($directory, '/'); - if (!$this->is_dir($directory) || !$this->isReadable($directory)) { - return false; - } else { - $directoryHandle = $this->opendir($directory); - if (is_resource($directoryHandle)) { - while (($contents = readdir($directoryHandle)) !== false) { - if (!\OC\Files\Filesystem::isIgnoredDir($contents)) { - $path = $directory . '/' . $contents; - if ($this->is_dir($path)) { - $this->deleteAll($path); - } else { - $this->unlink($path); - } - } - } - } - if ($empty === false) { - if (!$this->rmdir($directory)) { - return false; - } - } - return true; - } - - } - public function getMimeType($path) { if ($this->is_dir($path)) { return 'httpd/unix-directory'; @@ -188,10 +159,11 @@ abstract class Common implements \OC\Files\Storage\Storage { } public function hash($type, $path, $raw = false) { - $tmpFile = $this->getLocalFile($path); - $hash = hash($type, $tmpFile, $raw); - unlink($tmpFile); - return $hash; + $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) { @@ -199,13 +171,14 @@ abstract class Common implements \OC\Files\Storage\Storage { } public function getLocalFile($path) { - return $this->toTmpFile($path); + return $this->getCachedFile($path); } /** * @param string $path + * @return string */ - private function toTmpFile($path) { //no longer in the storage api, still useful here + protected function toTmpFile($path) { //no longer in the storage api, still useful here $source = $this->fopen($path, 'r'); if (!$source) { return false; @@ -389,4 +362,18 @@ abstract class Common implements \OC\Files\Storage\Storage { // default, which is not local return false; } + + /** + * @param string $path + */ + protected function getCachedFile($path) { + if (!isset($this->cachedFiles[$path])) { + $this->cachedFiles[$path] = $this->toTmpFile($path); + } + return $this->cachedFiles[$path]; + } + + protected function removeCachedFile($path) { + unset($this->cachedFiles[$path]); + } } diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php index a62230bdba5..de940fc7cdb 100644 --- a/lib/private/files/storage/local.php +++ b/lib/private/files/storage/local.php @@ -35,7 +35,7 @@ if (\OC_Util::runningOnWindows()) { } public function mkdir($path) { - return @mkdir($this->datadir . $path); + return @mkdir($this->datadir . $path, 0777, true); } public function rmdir($path) { @@ -44,17 +44,26 @@ if (\OC_Util::runningOnWindows()) { new \RecursiveDirectoryIterator($this->datadir . $path), \RecursiveIteratorIterator::CHILD_FIRST ); - foreach ($it as $file) { + /** + * 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->datadir . $path); } catch (\UnexpectedValueException $e) { @@ -256,7 +265,7 @@ if (\OC_Util::runningOnWindows()) { return 0; } - public function hash($path, $type, $raw = false) { + public function hash($type, $path, $raw = false) { return hash_file($type, $this->datadir . $path, $raw); } @@ -305,7 +314,11 @@ if (\OC_Util::runningOnWindows()) { * @return bool */ public function hasUpdated($path, $time) { - return $this->filemtime($path) > $time; + if ($this->file_exists($path)) { + return $this->filemtime($path) > $time; + } else { + return true; + } } /** diff --git a/lib/private/files/storage/mappedlocal.php b/lib/private/files/storage/mappedlocal.php index 1bab3489a28..07691661644 100644 --- a/lib/private/files/storage/mappedlocal.php +++ b/lib/private/files/storage/mappedlocal.php @@ -31,7 +31,7 @@ class MappedLocal extends \OC\Files\Storage\Common{ return 'local::'.$this->datadir; } public function mkdir($path) { - return @mkdir($this->buildPath($path)); + return @mkdir($this->buildPath($path), 0777, true); } public function rmdir($path) { try { @@ -39,17 +39,26 @@ class MappedLocal extends \OC\Files\Storage\Common{ new \RecursiveDirectoryIterator($this->buildPath($path)), \RecursiveIteratorIterator::CHILD_FIRST ); - foreach ($it as $file) { + /** + * 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(); } if ($result = @rmdir($this->buildPath($path))) { $this->cleanMapper($path); @@ -276,7 +285,7 @@ class MappedLocal extends \OC\Files\Storage\Common{ return 0; } - public function hash($path, $type, $raw=false) { + public function hash($type, $path, $raw=false) { return hash_file($type, $this->buildPath($path), $raw); } @@ -360,6 +369,9 @@ class MappedLocal extends \OC\Files\Storage\Common{ $this->mapper->copy($fullPath1, $fullPath2); } + /** + * @param string $path + */ private function stripLeading($path) { if(strpos($path, '/') === 0) { $path = substr($path, 1); diff --git a/lib/private/files/storage/wrapper/quota.php b/lib/private/files/storage/wrapper/quota.php index 26c952e694a..a878b2c5cf6 100644 --- a/lib/private/files/storage/wrapper/quota.php +++ b/lib/private/files/storage/wrapper/quota.php @@ -30,12 +30,24 @@ class Quota extends Wrapper { } /** + * @return quota value + */ + public function getQuota() { + return $this->quota; + } + + /** * @param string $path */ protected function getSize($path) { $cache = $this->getCache(); $data = $cache->get($path); if (is_array($data) and isset($data['size'])) { + if (isset($data['unencrypted_size']) + && $data['unencrypted_size'] > 0 + ) { + return $data['unencrypted_size']; + } return $data['size']; } else { return \OC\Files\SPACE_NOT_COMPUTED; @@ -57,7 +69,14 @@ class Quota extends Wrapper { return \OC\Files\SPACE_NOT_COMPUTED; } else { $free = $this->storage->free_space($path); - return min($free, (max($this->quota - $used, 0))); + $quotaFree = max($this->quota - $used, 0); + // if free space is known + if ($free >= 0) { + $free = min($free, $quotaFree); + } else { + $free = $quotaFree; + } + return $free; } } } diff --git a/lib/private/files/type/detection.php b/lib/private/files/type/detection.php index 11e439032ce..8ee53917814 100644 --- a/lib/private/files/type/detection.php +++ b/lib/private/files/type/detection.php @@ -17,24 +17,40 @@ namespace OC\Files\Type; */ class Detection { protected $mimetypes = array(); + protected $secureMimeTypes = array(); /** - * add an extension -> mimetype mapping + * 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) { - $this->mimetypes[$extension] = $mimetype; + 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 + * 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]] = $mimeType[1] ?: $mimeType[0]; + } } /** @@ -48,7 +64,9 @@ class Detection { //try to guess the type by the file extension $extension = strtolower(strrchr(basename($path), ".")); $extension = substr($extension, 1); //remove leading . - return (isset($this->mimetypes[$extension])) ? $this->mimetypes[$extension] : 'application/octet-stream'; + return (isset($this->mimetypes[$extension]) && isset($this->mimetypes[$extension][0])) + ? $this->mimetypes[$extension][0] + : 'application/octet-stream'; } else { return 'application/octet-stream'; } @@ -123,4 +141,16 @@ class Detection { return $mime; } } + + /** + * Get a secure mimetype that won't expose potential XSS. + * + * @param string $mimeType + * @return string + */ + public function getSecureMimeType($mimeType) { + return isset($this->secureMimeTypes[$mimeType]) + ? $this->secureMimeTypes[$mimeType] + : 'application/octet-stream'; + } } diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 530aa8f7514..47fc04c937d 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -25,6 +25,8 @@ namespace OC\Files; +use OC\Files\Cache\Updater; + class View { private $fakeRoot = ''; private $internal_path_cache = array(); @@ -269,6 +271,39 @@ class View { return $this->basicOperation('file_get_contents', $path, array('read')); } + 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, + )); + } + + 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), + )); + } + 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)); @@ -280,24 +315,7 @@ class View { $exists = $this->file_exists($path); $run = true; if ($this->shouldEmitHooks($path)) { - if (!$exists) { - \OC_Hook::emit( - Filesystem::CLASSNAME, - Filesystem::signal_create, - 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 - ) - ); + $this->emit_file_hooks_pre($exists, $path, $run); } if (!$run) { return false; @@ -308,18 +326,10 @@ class View { fclose($target); fclose($data); if ($this->shouldEmitHooks($path) && $result !== false) { - if (!$exists) { - \OC_Hook::emit( - Filesystem::CLASSNAME, - Filesystem::signal_post_create, - 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)) - ); + Updater::writeHook(array( + 'path' => $this->getHookPath($path) + )); + $this->emit_file_hooks_post($exists, $path); } \OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count); return $result; @@ -330,7 +340,7 @@ class View { return false; } } else { - $hooks = ($this->file_exists($path)) ? array('write') : array('create', 'write'); + $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write'); return $this->basicOperation('file_put_contents', $path, $hooks, $data); } } @@ -343,7 +353,8 @@ class View { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); - if (!$internalPath || $internalPath === '' || $internalPath === '/') { + if (!($storage instanceof \OC\Files\Storage\Shared) && + (!$internalPath || $internalPath === '' || $internalPath === '/')) { // 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 @@ -372,6 +383,7 @@ class View { ) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); + $exists = $this->file_exists($path2); if ($path1 == null or $path2 == null) { return false; @@ -379,13 +391,7 @@ class View { $run = true; if ($this->shouldEmitHooks() && (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 - \OC_Hook::emit( - Filesystem::CLASSNAME, Filesystem::signal_write, - array( - Filesystem::signal_param_path => $this->getHookPath($path2), - Filesystem::signal_param_run => &$run - ) - ); + $this->emit_file_hooks_pre($exists, $path2, $run); } elseif ($this->shouldEmitHooks()) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_rename, @@ -399,11 +405,21 @@ class View { if ($run) { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); - if ($mp1 == $mp2) { - list($storage, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); - list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2); - if ($storage) { - $result = $storage->rename($internalPath1, $internalPath2); + list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); + list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2); + // if source and target are on the same storage we can call the rename operation from the + // storage. If it is a "Shared" file/folder we call always the rename operation of the + // shared storage to handle mount point renaming, etc correctly + if ($storage1 instanceof \OC\Files\Storage\Shared) { + if ($storage1) { + $result = $storage1->rename($absolutePath1, $absolutePath2); + \OC_FileProxy::runPostProxies('rename', $absolutePath1, $absolutePath2); + } else { + $result = false; + } + } elseif ($mp1 == $mp2) { + if ($storage1) { + $result = $storage1->rename($internalPath1, $internalPath2); \OC_FileProxy::runPostProxies('rename', $absolutePath1, $absolutePath2); } else { $result = false; @@ -412,8 +428,7 @@ class View { if ($this->is_dir($path1)) { $result = $this->copy($path1, $path2); if ($result === true) { - list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); - $result = $storage1->deleteAll($internalPath1); + $result = $storage1->unlink($internalPath1); } } else { $source = $this->fopen($path1 . $postFix1, 'r'); @@ -426,21 +441,19 @@ class View { fclose($target); if ($result !== false) { - list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); $storage1->unlink($internalPath1); } } } if ($this->shouldEmitHooks() && (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 - \OC_Hook::emit( - Filesystem::CLASSNAME, - Filesystem::signal_post_write, - array( - Filesystem::signal_param_path => $this->getHookPath($path2), - ) - ); + Updater::writeHook(array('path' => $this->getHookPath($path2))); + $this->emit_file_hooks_post($exists, $path2); } elseif ($this->shouldEmitHooks() && $result !== false) { + Updater::renameHook(array( + 'oldpath' => $this->getHookPath($path1), + 'newpath' => $this->getHookPath($path2) + )); \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_post_rename, @@ -488,26 +501,7 @@ class View { Filesystem::signal_param_run => &$run ) ); - if ($run and !$exists) { - \OC_Hook::emit( - Filesystem::CLASSNAME, - Filesystem::signal_create, - array( - Filesystem::signal_param_path => $this->getHookPath($path2), - Filesystem::signal_param_run => &$run - ) - ); - } - if ($run) { - \OC_Hook::emit( - Filesystem::CLASSNAME, - Filesystem::signal_write, - array( - Filesystem::signal_param_path => $this->getHookPath($path2), - Filesystem::signal_param_run => &$run - ) - ); - } + $this->emit_file_hooks_pre($exists, $path2, $run); } if ($run) { $mp1 = $this->getMountPoint($path1 . $postFix1); @@ -534,6 +528,8 @@ class View { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); list($count, $result) = \OC_Helper::streamCopy($source, $target); + fclose($source); + fclose($target); } } if ($this->shouldEmitHooks() && $result !== false) { @@ -545,18 +541,7 @@ class View { Filesystem::signal_param_newpath => $this->getHookPath($path2) ) ); - if (!$exists) { - \OC_Hook::emit( - Filesystem::CLASSNAME, - Filesystem::signal_post_create, - array(Filesystem::signal_param_path => $this->getHookPath($path2)) - ); - } - \OC_Hook::emit( - Filesystem::CLASSNAME, - Filesystem::signal_post_write, - array(Filesystem::signal_param_path => $this->getHookPath($path2)) - ); + $this->emit_file_hooks_post($exists, $path2); } return $result; } else { @@ -617,10 +602,21 @@ class View { } public function fromTmpFile($tmpFile, $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)) { + $this->mkdir($filePath); + } + if (!$tmpFile) { debug_print_backtrace(); } + $source = fopen($tmpFile, 'r'); if ($source) { $this->file_put_contents($path, $source); @@ -739,7 +735,10 @@ class View { } /** + * @param string[] $hooks * @param string $path + * @param bool $post + * @return bool */ private function runHooks($hooks, $path, $post = false) { $path = $this->getHookPath($path); @@ -747,6 +746,16 @@ class View { $run = true; if ($this->shouldEmitHooks($path)) { foreach ($hooks as $hook) { + // manually triger updater hooks to ensure they are called first + if ($post) { + if ($hook == 'write') { + Updater::writeHook(array('path' => $path)); + } elseif ($hook == 'touch') { + Updater::touchHook(array('path' => $path)); + } else if ($hook == 'delete') { + Updater::deleteHook(array('path' => $path)); + } + } if ($hook != 'read') { \OC_Hook::emit( Filesystem::CLASSNAME, @@ -807,6 +816,9 @@ class View { $user = \OC_User::getUser(); if (!$cache->inCache($internalPath)) { + if (!$storage->file_exists($internalPath)) { + return false; + } $scanner = $storage->getScanner($internalPath); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } else { @@ -818,7 +830,7 @@ class View { $data = $cache->get($internalPath); } - if ($data and $data['fileid']) { + if ($data and isset($data['fileid'])) { if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { //add the sizes of other mountpoints to the folder $mountPoints = Filesystem::getMountPoints($path); @@ -880,12 +892,13 @@ class View { $watcher->checkUpdate($internalPath); } + $folderId = $cache->getId($internalPath); $files = array(); - $contents = $cache->getFolderContents($internalPath); //TODO: mimetype_filter + $contents = $cache->getFolderContents($internalPath, $folderId); //TODO: mimetype_filter foreach ($contents as $content) { $files[] = new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content); } - $permissions = $permissionsCache->getDirectoryPermissions($cache->getId($internalPath), $user); + $permissions = $permissionsCache->getDirectoryPermissions($folderId, $user); $ids = array(); foreach ($files as $i => $file) { @@ -932,8 +945,13 @@ class View { $permissions = $subStorage->getPermissions($rootEntry['path']); $subPermissionsCache->set($rootEntry['fileid'], $user, $permissions); } - // do not allow renaming/deleting the mount point - $rootEntry['permissions'] = $permissions & (\OCP\PERMISSION_ALL - (\OCP\PERMISSION_UPDATE | \OCP\PERMISSION_DELETE)); + // 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 ($subStorage instanceof \OC\Files\Storage\Shared) { + $rootEntry['permissions'] = $permissions; + } else { + $rootEntry['permissions'] = $permissions & (\OCP\PERMISSION_ALL - (\OCP\PERMISSION_UPDATE | \OCP\PERMISSION_DELETE)); + } //remove any existing entry with the same name foreach ($files as $i => $file) { @@ -942,6 +960,7 @@ class View { break; } } + $rootEntry['path'] = substr($path . '/' . $rootEntry['name'], strlen($user) + 2); // full path without /$user/ $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry); } } @@ -1055,8 +1074,9 @@ class View { if ($results) { foreach ($results as $result) { $internalPath = $result['path']; - $result['path'] = $relativeMountPoint . $result['path']; - $files[] = new FileInfo($mountPoint . $result['path'], $storage, $internalPath, $result); + $result['path'] = rtrim($relativeMountPoint . $result['path'], '/'); + $path = rtrim($mountPoint . $internalPath, '/'); + $files[] = new FileInfo($path, $storage, $internalPath, $result); } } } @@ -1103,15 +1123,23 @@ class View { * @return string */ public function getPath($id) { - list($storage, $internalPath) = Cache\Cache::getById($id); - $mounts = Filesystem::getMountByStorageId($storage); + $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 $mount + * @var \OC\Files\Mount\Mount $mount */ - $fullPath = $mount->getMountPoint() . $internalPath; - if (!is_null($path = $this->getRelativePath($fullPath))) { - return $path; + $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; + } } } return null; diff --git a/lib/private/forbiddenexception.php b/lib/private/forbiddenexception.php new file mode 100644 index 00000000000..14a4cd14984 --- /dev/null +++ b/lib/private/forbiddenexception.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC; + +/** + * Exception thrown whenever access to a resource has + * been forbidden or whenever a user isn't authenticated. + */ +class ForbiddenException extends \Exception { +} diff --git a/lib/private/geo.php b/lib/private/geo.php index 7094d885af6..cd62511f0c1 100644 --- a/lib/private/geo.php +++ b/lib/private/geo.php @@ -6,15 +6,11 @@ * See the COPYING-README file. */ class OC_Geo{ - /* - * @brief returns the closest timezone to coordinates - * @param (string) $latitude - Latitude - * @param (string) $longitude - Longitude - * @return (string) $timezone - closest timezone - */ /** - * @param integer $latitude - * @param integer $longitude + * @brief returns the closest timezone to coordinates + * @param $latitude + * @param $longitude + * @return mixed Closest timezone */ public static function timezone($latitude, $longitude) { $alltimezones = DateTimeZone::listIdentifiers(); diff --git a/lib/private/group.php b/lib/private/group.php index 4c187b538af..ea6384bae3e 100644 --- a/lib/private/group.php +++ b/lib/private/group.php @@ -200,6 +200,9 @@ class OC_Group { /** * @brief get a list of all groups + * @param string $search + * @param int|null $limit + * @param int|null $offset * @returns array with group names * * Returns a list with all groups @@ -225,6 +228,10 @@ class OC_Group { /** * @brief get a list of all users in a group + * @param string $gid + * @param string $search + * @param int $limit + * @param int $offset * @returns array with user ids */ public static function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { @@ -260,20 +267,14 @@ class OC_Group { /** * @brief get a list of all display names in a group + * @param string $gid + * @param string $search + * @param int $limit + * @param int $offset * @returns array with display names (value) and user ids(key) */ public static function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) { - $group = self::getManager()->get($gid); - if ($group) { - $users = $group->searchDisplayName($search, $limit, $offset); - $displayNames = array(); - foreach ($users as $user) { - $displayNames[$user->getUID()] = $user->getDisplayName(); - } - return $displayNames; - } else { - return array(); - } + return self::getManager()->displayNamesInGroup($gid, $search, $limit, $offset); } /** diff --git a/lib/private/group/backend.php b/lib/private/group/backend.php index 2e17b5d0b7f..cc61fce1615 100644 --- a/lib/private/group/backend.php +++ b/lib/private/group/backend.php @@ -33,7 +33,8 @@ define('OC_GROUP_BACKEND_CREATE_GROUP', 0x00000001); define('OC_GROUP_BACKEND_DELETE_GROUP', 0x00000010); define('OC_GROUP_BACKEND_ADD_TO_GROUP', 0x00000100); define('OC_GROUP_BACKEND_REMOVE_FROM_GOUP', 0x00001000); -define('OC_GROUP_BACKEND_GET_DISPLAYNAME', 0x00010000); +define('OC_GROUP_BACKEND_GET_DISPLAYNAME', 0x00010000); //OBSOLETE +define('OC_GROUP_BACKEND_COUNT_USERS', 0x00100000); /** * Abstract base class for user management @@ -44,7 +45,7 @@ abstract class OC_Group_Backend implements OC_Group_Interface { OC_GROUP_BACKEND_DELETE_GROUP => 'deleteGroup', OC_GROUP_BACKEND_ADD_TO_GROUP => 'addToGroup', OC_GROUP_BACKEND_REMOVE_FROM_GOUP => 'removeFromGroup', - OC_GROUP_BACKEND_GET_DISPLAYNAME => 'displayNamesInGroup', + OC_GROUP_BACKEND_COUNT_USERS => 'countUsersInGroup', ); /** @@ -135,23 +136,4 @@ abstract class OC_Group_Backend implements OC_Group_Interface { public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { return array(); } - - /** - * @brief get a list of all display names in a group - * @param string $gid - * @param string $search - * @param int $limit - * @param int $offset - * @return array with display names (value) and user ids (key) - */ - public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) { - $displayNames = array(); - $users = $this->usersInGroup($gid, $search, $limit, $offset); - foreach ($users as $user) { - $displayNames[$user] = $user; - } - - return $displayNames; - } - } diff --git a/lib/private/group/database.php b/lib/private/group/database.php index d0974685ff6..df0d84d0d2a 100644 --- a/lib/private/group/database.php +++ b/lib/private/group/database.php @@ -212,28 +212,17 @@ class OC_Group_Database extends OC_Group_Backend { } /** - * @brief get a list of all display names in a group + * @brief get the number of all users matching the search string in a group * @param string $gid * @param string $search * @param int $limit * @param int $offset - * @return array with display names (value) and user ids (key) + * @return int | false */ - public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) { - $displayNames = array(); - - $stmt = OC_DB::prepare('SELECT `*PREFIX*users`.`uid`, `*PREFIX*users`.`displayname`' - .' FROM `*PREFIX*users`' - .' INNER JOIN `*PREFIX*group_user` ON `*PREFIX*group_user`.`uid` = `*PREFIX*users`.`uid`' - .' WHERE `gid` = ? AND `*PREFIX*group_user`.`uid` LIKE ?', - $limit, - $offset); + public function countUsersInGroup($gid, $search = '') { + $stmt = OC_DB::prepare('SELECT COUNT(`uid`) AS `count` FROM `*PREFIX*group_user` WHERE `gid` = ? AND `uid` LIKE ?'); $result = $stmt->execute(array($gid, $search.'%')); - $users = array(); - while ($row = $result->fetchRow()) { - $displayName = trim($row['displayname'], ' '); - $displayNames[$row['uid']] = empty($displayName) ? $row['uid'] : $displayName; - } - return $displayNames; + return $result->fetchOne(); } + } diff --git a/lib/private/group/dummy.php b/lib/private/group/dummy.php index da26e1b910e..94cbb607ad1 100644 --- a/lib/private/group/dummy.php +++ b/lib/private/group/dummy.php @@ -157,4 +157,14 @@ class OC_Group_Dummy extends OC_Group_Backend { } } + /** + * @brief get the number of all users in a group + * @returns int | bool + */ + public function countUsersInGroup($gid, $search = '', $limit = -1, $offset = 0) { + if(isset($this->groups[$gid])) { + return count($this->groups[$gid]); + } + } + } diff --git a/lib/private/group/group.php b/lib/private/group/group.php index 8d2aa87a788..7593bb68d1a 100644 --- a/lib/private/group/group.php +++ b/lib/private/group/group.php @@ -172,12 +172,6 @@ class Group { $users = array(); foreach ($this->backends as $backend) { $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); - if (!is_null($limit)) { - $limit -= count($userIds); - } - if (!is_null($offset)) { - $offset -= count($userIds); - } $users += $this->getVerifiedUsers($userIds); if (!is_null($limit) and $limit <= 0) { return array_values($users); @@ -187,6 +181,27 @@ class Group { } /** + * 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 @@ -197,17 +212,7 @@ class Group { public function searchDisplayName($search, $limit = null, $offset = null) { $users = array(); foreach ($this->backends as $backend) { - if ($backend->implementsActions(OC_GROUP_BACKEND_GET_DISPLAYNAME)) { - $userIds = array_keys($backend->displayNamesInGroup($this->gid, $search, $limit, $offset)); - } else { - $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); - } - if (!is_null($limit)) { - $limit -= count($userIds); - } - if (!is_null($offset)) { - $offset -= count($userIds); - } + $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); $users = $this->getVerifiedUsers($userIds); if (!is_null($limit) and $limit <= 0) { return array_values($users); diff --git a/lib/private/group/manager.php b/lib/private/group/manager.php index 151b185dbf7..40c6074b2ac 100644 --- a/lib/private/group/manager.php +++ b/lib/private/group/manager.php @@ -153,12 +153,6 @@ class Manager extends PublicEmitter { $groups = array(); foreach ($this->backends as $backend) { $groupIds = $backend->getGroups($search, $limit, $offset); - if (!is_null($limit)) { - $limit -= count($groupIds); - } - if (!is_null($offset)) { - $offset -= count($groupIds); - } foreach ($groupIds as $groupId) { $groups[$groupId] = $this->get($groupId); } @@ -188,4 +182,38 @@ class Manager extends PublicEmitter { $this->cachedUserGroups[$uid] = array_values($groups); return $this->cachedUserGroups[$uid]; } + + /** + * @brief get a list of all display names in a group + * @param string $gid + * @param string $search + * @param int $limit + * @param int $offset + * @return array with 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(); + } + // only user backends have the capability to do a complex search for users + $groupUsers = $group->searchUsers('', $limit, $offset); + $search = trim($search); + if(!empty($search)) { + //TODO: for OC 7 earliest: user backend should get a method to check selected users against a pattern + $filteredUsers = $this->userManager->search($search); + $testUsers = true; + } else { + $filteredUsers = array(); + $testUsers = false; + } + + $matchingUsers = array(); + foreach($groupUsers as $user) { + if(!$testUsers || isset($filteredUsers[$user->getUID()])) { + $matchingUsers[$user->getUID()] = $user->getDisplayName(); + } + } + return $matchingUsers; + } } diff --git a/lib/private/helper.php b/lib/private/helper.php index 1aab2f296e1..64da1f6fb12 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -36,7 +36,7 @@ class OC_Helper { * @param array $parameters * @return * @internal param array $args with param=>value, will be appended to the returned url - * @returns the url + * @returns string the url * * Returns a url to the given app and file. */ @@ -64,7 +64,7 @@ class OC_Helper { */ public static function linkToDocs($key) { $theme = new OC_Defaults(); - return $theme->getDocBaseUrl() . '/server/6.0/go.php?to=' . $key; + return $theme->buildDocLinkToKey($key); } /** @@ -78,8 +78,9 @@ class OC_Helper { * Returns a absolute url to the given app and file. */ public static function linkToAbsolute($app, $file, $args = array()) { - $urlLinkTo = self::linkTo($app, $file, $args); - return self::makeURLAbsolute($urlLinkTo); + return OC::$server->getURLGenerator()->getAbsoluteURL( + self::linkTo($app, $file, $args) + ); } /** @@ -113,8 +114,10 @@ class OC_Helper { * Returns a absolute url to the given service. */ public static function linkToRemote($service, $add_slash = true) { - return self::makeURLAbsolute(self::linkToRemoteBase($service)) - . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : ''); + return OC::$server->getURLGenerator()->getAbsoluteURL( + self::linkToRemoteBase($service) + . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : '') + ); } /** @@ -126,8 +129,12 @@ class OC_Helper { * Returns a absolute url to the given service. */ public static function linkToPublic($service, $add_slash = false) { - return self::linkToAbsolute('', 'public.php') . '?service=' . $service - . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : ''); + return OC::$server->getURLGenerator()->getAbsoluteURL( + self::linkTo( + '', 'public.php') . '?service=' . $service + . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : '' + ) + ); } /** @@ -307,8 +314,34 @@ class OC_Helper { } /** + * @brief 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"; + } + + /** * @brief Make a computer file size - * @param string $str file size in a fancy format + * @param string $str file size in human readable format * @return int a file size in bytes * * Makes 2kB to 2048. @@ -338,41 +371,12 @@ class OC_Helper { $bytes *= $bytes_array[$matches[1]]; } - $bytes = round($bytes, 2); + $bytes = round($bytes); return $bytes; } /** - * @brief Recursive editing of file permissions - * @param string $path path to file or folder - * @param int $filemode unix style file permissions - * @return bool - */ - static function chmodr($path, $filemode) { - if (!is_dir($path)) - return chmod($path, $filemode); - $dh = opendir($path); - if(is_resource($dh)) { - while (($file = readdir($dh)) !== false) { - if ($file != '.' && $file != '..') { - $fullpath = $path . '/' . $file; - if (is_link($fullpath)) - return false; - elseif (!is_dir($fullpath) && !@chmod($fullpath, $filemode)) - return false; elseif (!self::chmodr($fullpath, $filemode)) - return false; - } - } - closedir($dh); - } - if (@chmod($path, $filemode)) - return true; - else - return false; - } - - /** * @brief Recursive copying of folders * @param string $src source folder * @param string $dest target folder @@ -461,6 +465,16 @@ class OC_Helper { } /** + * Get a secure mimetype that won't expose potential XSS. + * + * @param string $mimeType + * @return string + */ + static function getSecureMimeType($mimeType) { + return self::getMimetypeDetector()->getSecureMimeType($mimeType); + } + + /** * get the mimetype form a data string * * @param string $data @@ -839,7 +853,7 @@ class OC_Helper { * @return int number of bytes representing */ public static function maxUploadFilesize($dir, $freeSpace = null) { - if (is_null($freeSpace)){ + if (is_null($freeSpace) || $freeSpace < 0){ $freeSpace = self::freeSpace($dir); } return min($freeSpace, self::uploadLimit()); @@ -905,22 +919,34 @@ class OC_Helper { * Calculate the disc space for the given path * * @param string $path + * @param \OCP\Files\FileInfo $rootInfo (optional) * @return array */ - public static function getStorageInfo($path) { + public static function getStorageInfo($path, $rootInfo = null) { // return storage info without adding mount points - $rootInfo = \OC\Files\Filesystem::getFileInfo($path, false); - $used = $rootInfo['size']; + if (is_null($rootInfo)) { + $rootInfo = \OC\Files\Filesystem::getFileInfo($path, false); + } + $used = $rootInfo->getSize(); if ($used < 0) { $used = 0; } - $free = \OC\Files\Filesystem::free_space($path); + $quota = 0; + // TODO: need a better way to get total space from storage + $storage = $rootInfo->getStorage(); + if ($storage instanceof \OC\Files\Storage\Wrapper\Quota) { + $quota = $storage->getQuota(); + } + $free = $storage->free_space(''); 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 { diff --git a/lib/private/image.php b/lib/private/image.php index 17caaa012f5..14aa64d12da 100644 --- a/lib/private/image.php +++ b/lib/private/image.php @@ -34,25 +34,25 @@ class OC_Image { /** * @brief Get mime type for an image file. - * @param string|null $filepath The path to a local image file. - * @returns string The mime type if the it could be determined, otherwise an empty string. + * @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 (filesize($filePath) > 11) { $imageType = exif_imagetype($filePath); - } - else { + } else { $imageType = false; } return $imageType ? image_type_to_mime_type($imageType) : ''; } /** - * @brief Constructor. - * @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function. - * @returns bool False on error - */ + * @brief Constructor. + * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by + * an imagecreate* function. + * @return \OC_Image False on error + */ public function __construct($imageRef = null) { //OC_Log::write('core',__METHOD__.'(): start', OC_Log::DEBUG); if(!extension_loaded('gd') || !function_exists('gd_info')) { @@ -71,7 +71,7 @@ class OC_Image { /** * @brief Determine whether the object contains an image resource. - * @returns bool + * @return bool */ public function valid() { // apparently you can't name a method 'empty'... return is_resource($this->resource); @@ -79,7 +79,7 @@ class OC_Image { /** * @brief Returns the MIME type of the image or an empty string if no image is loaded. - * @returns int + * @return string */ public function mimeType() { return $this->valid() ? $this->mimeType : ''; @@ -87,7 +87,7 @@ class OC_Image { /** * @brief Returns the width of the image or -1 if no image is loaded. - * @returns int + * @return int */ public function width() { return $this->valid() ? imagesx($this->resource) : -1; @@ -95,7 +95,7 @@ class OC_Image { /** * @brief Returns the height of the image or -1 if no image is loaded. - * @returns int + * @return int */ public function height() { return $this->valid() ? imagesy($this->resource) : -1; @@ -103,7 +103,7 @@ class OC_Image { /** * @brief Returns the width when the image orientation is top-left. - * @returns int + * @return int */ public function widthTopLeft() { $o = $this->getOrientation(); @@ -115,20 +115,18 @@ class OC_Image { case 3: case 4: // Not tested return $this->width(); - break; case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->height(); - break; } return $this->width(); } /** * @brief Returns the height when the image orientation is top-left. - * @returns int + * @return int */ public function heightTopLeft() { $o = $this->getOrientation(); @@ -140,46 +138,56 @@ class OC_Image { case 3: case 4: // Not tested return $this->height(); - break; case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->width(); - break; } return $this->height(); } /** - * @brief Outputs the image. - * @returns bool - */ - public function show() { - header('Content-Type: '.$this->mimeType()); - return $this->_output(); + * @brief 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); } /** - * @brief Saves the image. - * @returns bool - * @param string $filePath - */ + * @brief Saves the image. + * @param string $filePath + * @param string $mimeType + * @return bool + */ - public function save($filePath=null) { + public function save($filePath=null, $mimeType=null) { + if($mimeType === null) { + $mimeType = $this->mimeType(); + } if($filePath === null && $this->filePath === null) { OC_Log::write('core', __METHOD__.'(): called with no path.', OC_Log::ERROR); return false; } elseif($filePath === null && $this->filePath !== null) { $filePath = $this->filePath; } - return $this->_output($filePath); + return $this->_output($filePath, $mimeType); } /** - * @brief Outputs/saves the image. - */ - private function _output($filePath=null) { + * @brief 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); @@ -197,8 +205,30 @@ class OC_Image { return false; } - $retVal = false; - switch($this->imageType) { + $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': + $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; @@ -209,7 +239,12 @@ class OC_Image { $retVal = imagepng($this->resource, $filePath); break; case IMAGETYPE_XBM: - $retVal = imagexbm($this->resource, $filePath); + 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); @@ -231,14 +266,14 @@ class OC_Image { } /** - * @returns resource Returns the image resource in any. + * @return resource Returns the image resource in any. */ public function resource() { return $this->resource; } /** - * @returns Returns the raw image data. + * @return string Returns the raw image data. */ function data() { ob_start(); @@ -264,8 +299,8 @@ class OC_Image { } /** - * @returns Returns a base64 encoded string suitable for embedding in a VCard. - */ + * @return string - base64 encoded, which is suitable for embedding in a VCard. + */ function __toString() { return base64_encode($this->data()); } @@ -273,7 +308,7 @@ class OC_Image { /** * (I'm open for suggestions on better method name ;) * @brief Get the orientation based on EXIF data. - * @returns The orientation or -1 if no EXIF data is available. + * @return int The orientation or -1 if no EXIF data is available. */ public function getOrientation() { if(!is_callable('exif_read_data')) { @@ -301,53 +336,43 @@ class OC_Image { /** * (I'm open for suggestions on better method name ;) * @brief Fixes orientation based on EXIF data. - * @returns bool. + * @return bool. */ public function fixOrientation() { $o = $this->getOrientation(); OC_Log::write('core', 'OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG); $rotate = 0; - $flip = false; switch($o) { case -1: return false; //Nothing to fix - break; case 1: $rotate = 0; - $flip = false; break; case 2: // Not tested $rotate = 0; - $flip = true; break; case 3: $rotate = 180; - $flip = false; break; case 4: // Not tested $rotate = 180; - $flip = true; break; case 5: // Not tested $rotate = 90; - $flip = true; break; case 6: //$rotate = 90; $rotate = 270; - $flip = false; break; case 7: // Not tested $rotate = 270; - $flip = true; break; case 8: $rotate = 90; - $flip = false; break; } if($rotate) { - $res = imagerotate($this->resource, $rotate, -1); + $res = imagerotate($this->resource, $rotate, 0); if($res) { if(imagealphablending($res, true)) { if(imagesavealpha($res, true)) { @@ -367,13 +392,14 @@ class OC_Image { return false; } } + return false; } /** - * @brief Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function. - * @param $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 ). - * @returns An image resource or false on error - */ + * @brief 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') { @@ -382,10 +408,10 @@ class OC_Image { } elseif(in_array(get_resource_type($imageRef), array('file', 'stream'))) { return $this->loadFromFileHandle($imageRef); } - } elseif($this->loadFromFile($imageRef) !== false) { - return $this->resource; } elseif($this->loadFromBase64($imageRef) !== false) { return $this->resource; + } elseif($this->loadFromFile($imageRef) !== false) { + return $this->resource; } elseif($this->loadFromData($imageRef) !== false) { return $this->resource; } else { @@ -398,7 +424,7 @@ class OC_Image { * @brief 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 - * @returns An image resource or false on error + * @return An image resource or false on error */ public function loadFromFileHandle($handle) { OC_Log::write('core', __METHOD__.'(): Trying', OC_Log::DEBUG); @@ -410,13 +436,12 @@ class OC_Image { /** * @brief Loads an image from a local file. - * @param $imagePath The path to a local file. - * @returns An image resource or false on error + * @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)) { - // Debug output disabled because this method is tried before loadFromBase64? OC_Log::write('core', 'OC_Image->loadFromFile, couldn\'t load: ' . (string) urlencode($imagePath), OC_Log::DEBUG); return false; } @@ -512,8 +537,8 @@ class OC_Image { /** * @brief Loads an image from a string of data. - * @param $str A string of image data as read from a file. - * @returns An image resource or false on error + * @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)) { @@ -537,8 +562,8 @@ class OC_Image { /** * @brief Loads an image from a base64 encoded string. - * @param $str A string base64 encoded string of image data. - * @returns An image resource or false on error + * @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)) { @@ -567,7 +592,7 @@ class OC_Image { * @param string $fileName <p> * Path to the BMP image. * </p> - * @return resource an image resource identifier on success, <b>FALSE</b> on errors. + * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors. */ private function imagecreatefrombmp($fileName) { if (!($fh = fopen($fileName, 'rb'))) { @@ -599,9 +624,9 @@ class OC_Image { $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']; + $meta['imagesize'] = @filesize($fileName) - $meta['offset']; if ($meta['imagesize'] < 1) { - trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING); + trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $fileName . '!', E_USER_WARNING); return false; } } @@ -706,7 +731,7 @@ class OC_Image { /** * @brief Resizes the image preserving ratio. * @param integer $maxSize The maximum size of either the width or height. - * @returns bool + * @return bool */ public function resize($maxSize) { if(!$this->valid()) { @@ -729,6 +754,11 @@ class OC_Image { return true; } + /** + * @param int $width + * @param int $height + * @return bool + */ public function preciseResize($width, $height) { if (!$this->valid()) { OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR); @@ -764,8 +794,8 @@ class OC_Image { /** * @brief Crops the image to the middle square. If the image is already square it just returns. - * @param int maximum size for the result (optional) - * @returns bool for success or failure + * @param int $size maximum size for the result (optional) + * @return bool for success or failure */ public function centerCrop($size=0) { if(!$this->valid()) { @@ -823,11 +853,11 @@ class OC_Image { /** * @brief Crops the image from point $x$y with dimension $wx$h. - * @param $x Horizontal position - * @param $y Vertical position - * @param $w Width - * @param $h Height - * @returns bool for success or failure + * @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()) { @@ -855,7 +885,7 @@ class OC_Image { * @brief Resizes the image to fit within a boundry while preserving ratio. * @param integer $maxWidth * @param integer $maxHeight - * @returns bool + * @return bool */ public function fitIn($maxWidth, $maxHeight) { if(!$this->valid()) { @@ -947,7 +977,7 @@ if ( ! function_exists( 'imagebmp') ) { $index = imagecolorat($im, $i, $j); if ($index !== $lastIndex || $sameNum > 255) { if ($sameNum != 0) { - $bmpData .= chr($same_num) . chr($lastIndex); + $bmpData .= chr($sameNum) . chr($lastIndex); } $lastIndex = $index; $sameNum = 1; diff --git a/lib/private/installer.php b/lib/private/installer.php index 11633a4d4a1..64e8e3a5e7a 100644 --- a/lib/private/installer.php +++ b/lib/private/installer.php @@ -464,7 +464,7 @@ class OC_Installer{ // is the code checker enabled? if(OC_Config::getValue('appcodechecker', true)) { // check if grep is installed - $grep = exec('which grep'); + $grep = exec('command -v grep'); if($grep=='') { OC_Log::write('core', 'grep not installed. So checking the code of the app "'.$appname.'" was not possible', diff --git a/lib/private/json.php b/lib/private/json.php index 4ccdb490a6c..4634d7adfea 100644 --- a/lib/private/json.php +++ b/lib/private/json.php @@ -43,8 +43,7 @@ class OC_JSON{ } /** - * @brief Check an ajax get/post call if the request token is valid. - * @return json Error msg if not valid. + * Check an ajax get/post call if the request token is valid, send json error msg if not. */ public static function callCheck() { if( !OC_Util::isCallRegistered()) { @@ -55,7 +54,7 @@ class OC_JSON{ } /** - * Check if the user is a admin, send json error msg if not + * Check if the user is a admin, send json error msg if not. */ public static function checkAdminUser() { if( !OC_User::isAdminUser(OC_User::getUser())) { @@ -119,8 +118,6 @@ class OC_JSON{ * Encode and print $data in json format */ public static function encodedPrint($data, $setContentType=true) { - // Disable mimesniffing, don't move this to setContentTypeHeader! - header( 'X-Content-Type-Options: nosniff' ); if($setContentType) { self::setContentTypeHeader(); } diff --git a/lib/private/l10n.php b/lib/private/l10n.php index ad979a92870..d6680d63445 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -73,9 +73,9 @@ class OC_L10N implements \OCP\IL10N { /** * get an L10N instance - * @param $app string - * @param $lang string|null - * @return OC_L10N + * @param string $app + * @param string|null $lang + * @return \OC_L10N */ public static function get($app, $lang=null) { if (is_null($lang)) { @@ -87,9 +87,8 @@ class OC_L10N implements \OCP\IL10N { /** * @brief The constructor - * @param $app string app requesting l10n - * @param $lang string default: null Language - * @returns OC_L10N-Object + * @param string $app app requesting l10n + * @param string $lang default: null Language * * If language is not set, the constructor tries to find the right * language. @@ -237,7 +236,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Translating - * @param $text String The text we need a translation for + * @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 * @@ -250,9 +249,9 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Translating - * @param $text_singular String the string to translate for exactly one object - * @param $text_plural String the string to translate for n objects - * @param $count Integer Number of objects + * @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 * @@ -268,43 +267,18 @@ class OC_L10N implements \OCP\IL10N { $identifier = "_${text_singular}_::_${text_plural}_"; if( array_key_exists($identifier, $this->translations)) { return new OC_L10N_String( $this, $identifier, $parameters, $count ); - } - else{ + }else{ if($count === 1) { return new OC_L10N_String($this, $text_singular, $parameters, $count); - } - else{ + }else{ return new OC_L10N_String($this, $text_plural, $parameters, $count); } } } /** - * @brief Translating - * @param $textArray The text array we need a translation for - * @returns Translation or the same text - * - * Returns the translation. If no translation is found, $textArray will be - * returned. - * - * - * @deprecated deprecated since ownCloud version 5.0 - * This method will probably be removed with ownCloud 6.0 - * - * - */ - public function tA($textArray) { - OC_Log::write('core', 'DEPRECATED: the method tA is deprecated and will be removed soon.', OC_Log::WARN); - $result = array(); - foreach($textArray as $key => $text) { - $result[$key] = (string)$this->t($text); - } - return $result; - } - - /** * @brief getTranslations - * @returns Fetch all translations + * @returns array Fetch all translations * * Returns an associative array with all translations */ @@ -340,7 +314,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief get localizations - * @returns Fetch all localizations + * @returns array Fetch all localizations * * Returns an associative array with all localizations */ @@ -351,8 +325,8 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Localization - * @param $type Type of localization - * @param $params parameters for this localization + * @param string $type Type of localization + * @param array|int|string $data parameters for this localization * @returns String or false * * Returns the localized data. @@ -406,7 +380,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Choose a language - * @param $texts Associative Array with possible strings + * @param array $text Associative Array with possible strings * @returns String * * $text is an array 'de' => 'hallo welt', 'en' => 'hello world', ... @@ -420,8 +394,16 @@ class OC_L10N implements \OCP\IL10N { } /** + * The given language is forced to be used while executing the current request + * @param string $lang + */ + public static function forceLanguage($lang) { + self::$language = $lang; + } + + /** * @brief find the best language - * @param $app Array or string, details below + * @param array|string $app details below * @returns string language * * If $app is an array, ownCloud assumes that these are the available @@ -494,7 +476,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief find the l10n directory - * @param $app App that needs to be translated + * @param string $app App that needs to be translated * @returns directory */ protected static function findI18nDir($app) { @@ -514,7 +496,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief find all available languages for an app - * @param $app App that needs to be translated + * @param string $app App that needs to be translated * @returns array an array of available languages */ public static function findAvailableLanguages($app=null) { @@ -533,7 +515,9 @@ class OC_L10N implements \OCP\IL10N { } /** + * @param string $app * @param string $lang + * @returns bool */ public static function languageExists($app, $lang) { if ($lang == 'en') {//english is always available diff --git a/lib/private/legacy/appconfig.php b/lib/private/legacy/appconfig.php index b6c3542a673..cb5cef7e350 100644 --- a/lib/private/legacy/appconfig.php +++ b/lib/private/legacy/appconfig.php @@ -116,8 +116,6 @@ class OC_Appconfig { /** * get multiply values, either the app or key can be used as wildcard by setting it to false * - * @param app - * @param key * @param string|false $app * @param string|false $key * @return array diff --git a/lib/private/legacy/config.php b/lib/private/legacy/config.php index ab67c8d3020..6c2103179ab 100644 --- a/lib/private/legacy/config.php +++ b/lib/private/legacy/config.php @@ -63,8 +63,8 @@ class OC_Config { /** * @brief Gets a value from config.php * @param string $key key - * @param string $default = null default value - * @return string the value or $default + * @param mixed $default = null default value + * @return mixed the value or $default * * This function gets the value from config.php. If it does not exist, * $default will be returned. @@ -76,7 +76,7 @@ class OC_Config { /** * @brief Sets a value * @param string $key key - * @param string $value value + * @param mixed $value value * * This function sets the value and writes the config.php. * diff --git a/lib/private/mail.php b/lib/private/mail.php index 90c3e343199..f9083cc4e64 100644 --- a/lib/private/mail.php +++ b/lib/private/mail.php @@ -72,11 +72,9 @@ class OC_Mail { $mailo->From = $fromaddress; $mailo->FromName = $fromname;; $mailo->Sender = $fromaddress; - $a=explode(' ', $toaddress); try { - foreach($a as $ad) { - $mailo->AddAddress($ad, $toname); - } + $toaddress = self::buildAsciiEmail($toaddress); + $mailo->AddAddress($toaddress, $toname); if($ccaddress<>'') $mailo->AddCC($ccaddress, $ccname); if($bcc<>'') $mailo->AddBCC($bcc); @@ -127,7 +125,26 @@ class OC_Mail { * @param string $emailAddress a given email address to be validated * @return bool */ - public static function ValidateAddress($emailAddress) { + public static function validateAddress($emailAddress) { + $emailAddress = self::buildAsciiEmail($emailAddress); return PHPMailer::ValidateAddress($emailAddress); } + + /** + * IDN domains will be properly converted to ascii domains. + * + * @param string $emailAddress + * @return string + */ + public static function buildAsciiEmail($emailAddress) { + if (!function_exists('idn_to_ascii')) { + return $emailAddress; + } + + list($name, $domain) = explode('@', $emailAddress, 2); + $domain = idn_to_ascii($domain); + + return "$name@$domain"; + } + } diff --git a/lib/private/migrate.php b/lib/private/migrate.php index 3fb3e334ea2..5bcc11b061b 100644 --- a/lib/private/migrate.php +++ b/lib/private/migrate.php @@ -69,9 +69,9 @@ class OC_Migrate{ /** * @brief exports a user, or owncloud instance - * @param optional $uid string user id of user to export if export type is user, defaults to current - * @param ootional $type string type of export, defualts to user - * @param otional $path string path to zip output folder + * @param string $uid user id of user to export if export type is user, defaults to current + * @param string $type type of export, defualts to user + * @param string $path path to zip output folder * @return string on error, path to zip on success */ public static function export( $uid=null, $type='user', $path=null ) { @@ -192,11 +192,12 @@ class OC_Migrate{ } /** - * @brief imports a user, or owncloud instance - * @param $path string path to zip - * @param optional $type type of import (user or instance) - * @param optional $uid userid of new user - */ + * @brief imports a user, or owncloud instance + * @param string $path path to zip + * @param string $type type of import (user or instance) + * @param string|null|int $uid userid of new user + * @return string + */ public static function import( $path, $type='user', $uid=null ) { $datadir = OC_Config::getValue( 'datadirectory' ); @@ -307,8 +308,8 @@ class OC_Migrate{ /** * @brief recursively deletes a directory - * @param string $dir string path of dir to delete - * $param optional $deleteRootToo bool delete the root directory + * @param string $dir path of dir to delete + * @param bool $deleteRootToo delete the root directory * @return bool */ private static function unlink_r( $dir, $deleteRootToo=true ) { @@ -406,7 +407,7 @@ class OC_Migrate{ /** * @brief generates json containing export info, and merges any data supplied - * @param optional $array array of data to include in the returned json + * @param array $array of data to include in the returned json * @return string */ static private function getExportInfo( $array=array() ) { @@ -430,8 +431,7 @@ class OC_Migrate{ /** * @brief connects to migration.db, or creates if not found - * @param $db optional path to migration.db, defaults to user data dir - * @param string $path + * @param string $path to migration.db, defaults to user data dir * @return bool whether the operation was successful */ static private function connectDB( $path=null ) { @@ -461,7 +461,7 @@ class OC_Migrate{ /** * @brief creates the tables in migration.db from an apps database.xml - * @param string $appid string id of the app + * @param string $appid id of the app * @return bool whether the operation was successful */ static private function createAppTables( $appid ) { @@ -499,7 +499,6 @@ class OC_Migrate{ /** * @brief tries to create the zip - * @param $path string path to zip destination * @return bool */ static private function createZip() { @@ -538,7 +537,7 @@ class OC_Migrate{ * @brief imports a new user * @param string $db string path to migration.db * @param $info object of migration info - * @param $uid optional uid to use + * @param string|null|int $uid uid to use * @return array of apps with import statuses, or false on failure. */ public static function importAppData( $db, $info, $uid=null ) { @@ -601,10 +600,10 @@ class OC_Migrate{ } - /* - * @brief creates a new user in the database - * @param $uid string user_id of the user to be created - * @param $hash string hash of the user to be created + /** + * creates a new user in the database + * @param string $uid user_id of the user to be created + * @param string $hash hash of the user to be created * @return bool result of user creation */ public static function createUser( $uid, $hash ) { diff --git a/lib/private/migration/content.php b/lib/private/migration/content.php index 43eba89b8d5..b0e7a4e9528 100644 --- a/lib/private/migration/content.php +++ b/lib/private/migration/content.php @@ -36,7 +36,7 @@ class OC_Migration_Content{ * @brief sets up the * @param ZipArchive $zip ZipArchive object * @param $db a database object (required for exporttype user) - * @return boolean|null + * @return bool|null */ public function __construct( $zip, $db=null ) { diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php index 9bd07b89023..3fe454f41f1 100644 --- a/lib/private/mimetypes.list.php +++ b/lib/private/mimetypes.list.php @@ -22,109 +22,116 @@ /** * Array mapping file extensions to mimetypes (in alphabetical order). + * + * The first index in the mime type array is the assumed correct mimetype + * and the second is either a secure alternative or null if the correct + * is considered secure. */ return array( - '7z' => 'application/x-7z-compressed', - 'accdb' => 'application/msaccess', - 'ai' => 'application/illustrator', - 'avi' => 'video/x-msvideo', - 'bash' => 'text/x-shellscript', - 'blend' => 'application/x-blender', - 'bin' => 'application/x-bin', - 'cb7' => 'application/x-cbr', - 'cba' => 'application/x-cbr', - 'cbr' => 'application/x-cbr', - 'cbt' => 'application/x-cbr', - 'cbtc' => 'application/x-cbr', - 'cbz' => 'application/x-cbr', - 'cc' => 'text/x-c', - 'cdr' => 'application/coreldraw', - 'cpp' => 'text/x-c++src', - 'css' => 'text/css', - 'csv' => 'text/csv', - 'cvbdl' => 'application/x-cbr', - 'c' => 'text/x-c', - 'c++' => 'text/x-c++src', - 'deb' => 'application/x-deb', - 'doc' => 'application/msword', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dot' => 'application/msword', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'dv' => 'video/dv', - 'eot' => 'application/vnd.ms-fontobject', - 'epub' => 'application/epub+zip', - 'exe' => 'application/x-ms-dos-executable', - 'flac' => 'audio/flac', - 'gif' => 'image/gif', - 'gz' => 'application/x-gzip', - 'gzip' => 'application/x-gzip', - 'html' => 'text/html', - 'htm' => 'text/html', - 'ical' => 'text/calendar', - 'ics' => 'text/calendar', - 'impress' => 'text/impress', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'js' => 'application/javascript', - 'json' => 'application/json', - 'keynote' => 'application/x-iwork-keynote-sffkey', - 'kra' => 'application/x-krita', - 'm2t' => 'video/mp2t', - 'm4v' => 'video/mp4', - 'markdown' => 'text/markdown', - 'mdown' => 'text/markdown', - 'md' => 'text/markdown', - 'mdb' => 'application/msaccess', - 'mdwn' => 'text/markdown', - 'mobi' => 'application/x-mobipocket-ebook', - 'mov' => 'video/quicktime', - 'mp3' => 'audio/mpeg', - 'mp4' => 'video/mp4', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'msi' => 'application/x-msi', - 'numbers' => 'application/x-iwork-numbers-sffnumbers', - 'odg' => 'application/vnd.oasis.opendocument.graphics', - 'odp' => 'application/vnd.oasis.opendocument.presentation', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'odt' => 'application/vnd.oasis.opendocument.text', - 'oga' => 'audio/ogg', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'otf' => 'font/opentype', - 'pages' => 'application/x-iwork-pages-sffpages', - 'pdf' => 'application/pdf', - 'php' => 'application/x-php', - 'pl' => 'application/x-perl', - 'png' => 'image/png', - 'ppt' => 'application/mspowerpoint', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'psd' => 'application/x-photoshop', - 'py' => 'text/x-python', - 'rar' => 'application/x-rar-compressed', - 'reveal' => 'text/reveal', - 'sgf' => 'application/sgf', - 'sh-lib' => 'text/x-shellscript', - 'sh' => 'text/x-shellscript', - 'svg' => 'image/svg+xml', - 'swf' => 'application/x-shockwave-flash', - 'tar' => 'application/x-tar', - 'tar.gz' => 'application/x-compressed', - 'tex' => 'application/x-tex', - 'tgz' => 'application/x-compressed', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'ttf' => 'application/x-font-ttf', - 'txt' => 'text/plain', - 'vcard' => 'text/vcard', - 'vcf' => 'text/vcard', - 'wav' => 'audio/wav', - 'webm' => 'video/webm', - 'woff' => 'application/font-woff', - 'wmv' => 'video/x-ms-asf', - 'xcf' => 'application/x-gimp', - 'xls' => 'application/msexcel', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xml' => 'application/xml', - 'zip' => 'application/zip', + '7z' => array('application/x-7z-compressed', null), + 'accdb' => array('application/msaccess', null), + 'ai' => array('application/illustrator', null), + 'avi' => array('video/x-msvideo', null), + 'bash' => array('text/x-shellscript', null), + 'blend' => array('application/x-blender', null), + 'bin' => array('application/x-bin', null), + 'bmp' => array('image/bmp', null), + 'cb7' => array('application/x-cbr', null), + 'cba' => array('application/x-cbr', null), + 'cbr' => array('application/x-cbr', null), + 'cbt' => array('application/x-cbr', null), + 'cbtc' => array('application/x-cbr', null), + 'cbz' => array('application/x-cbr', null), + 'cc' => array('text/x-c', null), + 'cdr' => array('application/coreldraw', null), + 'cpp' => array('text/x-c++src', null), + 'css' => array('text/css', null), + 'csv' => array('text/csv', null), + 'cvbdl' => array('application/x-cbr', null), + 'c' => array('text/x-c', null), + 'c++' => array('text/x-c++src', null), + 'deb' => array('application/x-deb', null), + 'doc' => array('application/msword', null), + 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', null), + 'dot' => array('application/msword', null), + 'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.template', null), + 'dv' => array('video/dv', null), + 'eot' => array('application/vnd.ms-fontobject', null), + 'epub' => array('application/epub+zip', null), + 'exe' => array('application/x-ms-dos-executable', null), + 'flac' => array('audio/flac', null), + 'flv' => array('video/x-flv', null), + 'gif' => array('image/gif', null), + 'gz' => array('application/x-gzip', null), + 'gzip' => array('application/x-gzip', null), + 'html' => array('text/html', 'text/plain'), + 'htm' => array('text/html', 'text/plain'), + 'ical' => array('text/calendar', null), + 'ics' => array('text/calendar', null), + 'impress' => array('text/impress', null), + 'jpeg' => array('image/jpeg', null), + 'jpg' => array('image/jpeg', null), + 'js' => array('application/javascript', 'text/plain'), + 'json' => array('application/json', 'text/plain'), + 'keynote' => array('application/x-iwork-keynote-sffkey', null), + 'kra' => array('application/x-krita', null), + 'm2t' => array('video/mp2t', null), + 'm4v' => array('video/mp4', null), + 'markdown' => array('text/markdown', null), + 'mdown' => array('text/markdown', null), + 'md' => array('text/markdown', null), + 'mdb' => array('application/msaccess', null), + 'mdwn' => array('text/markdown', null), + 'mkv' => array('video/x-matroska', null), + 'mobi' => array('application/x-mobipocket-ebook', null), + 'mov' => array('video/quicktime', null), + 'mp3' => array('audio/mpeg', null), + 'mp4' => array('video/mp4', null), + 'mpeg' => array('video/mpeg', null), + 'mpg' => array('video/mpeg', null), + 'msi' => array('application/x-msi', null), + 'numbers' => array('application/x-iwork-numbers-sffnumbers', null), + 'odg' => array('application/vnd.oasis.opendocument.graphics', null), + 'odp' => array('application/vnd.oasis.opendocument.presentation', null), + 'ods' => array('application/vnd.oasis.opendocument.spreadsheet', null), + 'odt' => array('application/vnd.oasis.opendocument.text', null), + 'oga' => array('audio/ogg', null), + 'ogg' => array('audio/ogg', null), + 'ogv' => array('video/ogg', null), + 'otf' => array('font/opentype', null), + 'pages' => array('application/x-iwork-pages-sffpages', null), + 'pdf' => array('application/pdf', null), + 'php' => array('application/x-php', null), + 'pl' => array('application/x-perl', null), + 'png' => array('image/png', null), + 'ppt' => array('application/mspowerpoint', null), + 'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', null), + 'psd' => array('application/x-photoshop', null), + 'py' => array('text/x-python', null), + 'rar' => array('application/x-rar-compressed', null), + 'reveal' => array('text/reveal', null), + 'sgf' => array('application/sgf', null), + 'sh-lib' => array('text/x-shellscript', null), + 'sh' => array('text/x-shellscript', null), + 'svg' => array('image/svg+xml', 'text/plain'), + 'swf' => array('application/x-shockwave-flash', 'application/octet-stream'), + 'tar' => array('application/x-tar', null), + 'tar.gz' => array('application/x-compressed', null), + 'tex' => array('application/x-tex', null), + 'tgz' => array('application/x-compressed', null), + 'tiff' => array('image/tiff', null), + 'tif' => array('image/tiff', null), + 'ttf' => array('application/x-font-ttf', null), + 'txt' => array('text/plain', null), + 'vcard' => array('text/vcard', null), + 'vcf' => array('text/vcard', null), + 'wav' => array('audio/wav', null), + 'webm' => array('video/webm', null), + 'woff' => array('application/font-woff', null), + 'wmv' => array('video/x-ms-asf', null), + 'xcf' => array('application/x-gimp', null), + 'xls' => array('application/msexcel', null), + 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null), + 'xml' => array('application/xml', 'text/plain'), + 'zip' => array('application/zip', null), ); diff --git a/lib/private/ocs.php b/lib/private/ocs.php index bbe965ce561..211e8222145 100644 --- a/lib/private/ocs.php +++ b/lib/private/ocs.php @@ -39,6 +39,7 @@ class OC_OCS { * @return string Data or if the key is not found and no default is set it will exit with a 400 Bad request */ public static function readData($method, $key, $type = 'raw', $default = null) { + $data = false; if ($method == 'get') { if (isset($_GET[$key])) { $data = $_GET[$key]; @@ -107,19 +108,19 @@ class OC_OCS { /** - * generates the xml or json response for the API call from an multidimenional data array. - * @param string $format - * @param string $status - * @param string $statuscode - * @param string $message - * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension - * @param int $itemscount - * @param int $itemsperpage - * @return string xml/json - */ + * generates the xml or json response for the API call from an multidimenional data array. + * @param string $format + * @param string $status + * @param string $statuscode + * @param string $message + * @param array $data + * @param string $tag + * @param string $tagattribute + * @param int $dimension + * @param int|string $itemscount + * @param int|string $itemsperpage + * @return string xml/json + */ private static function generateXml($format, $status, $statuscode, $message, $data=array(), $tag='', $tagattribute='', $dimension=-1, $itemscount='', $itemsperpage='') { if($format=='json') { @@ -212,6 +213,8 @@ class OC_OCS { } /** + * @param $writer + * @param $data * @param string $node */ public static function toXml($writer, $data, $node) { diff --git a/lib/private/ocs/cloud.php b/lib/private/ocs/cloud.php index cbbf3b626f5..c8bb9425f1a 100644 --- a/lib/private/ocs/cloud.php +++ b/lib/private/ocs/cloud.php @@ -61,17 +61,29 @@ class OC_OCS_Cloud { * the user from whom the information will be returned */ public static function getUser($parameters) { + $return = array(); // Check if they are viewing information on themselves if($parameters['userid'] === OC_User::getUser()) { // Self lookup $storage = OC_Helper::getStorageInfo('/'); - $quota = array( + $return['quota'] = array( 'free' => $storage['free'], 'used' => $storage['used'], 'total' => $storage['total'], 'relative' => $storage['relative'], ); - return new OC_OCS_Result(array('quota' => $quota)); + } + if(OC_User::isAdminUser(OC_User::getUser()) + || OC_Subadmin::isUserAccessible(OC_User::getUser(), $parameters['userid'])) { + if(OC_User::userExists($parameters['userid'])) { + // Is an admin/subadmin so can see display name + $return['displayname'] = OC_User::getDisplayName($parameters['userid']); + } else { + return new OC_OCS_Result(null, 101); + } + } + if(count($return)) { + return new OC_OCS_Result($return); } else { // No permission to view this user data return new OC_OCS_Result(null, 997); @@ -87,31 +99,4 @@ class OC_OCS_Cloud { ); return new OC_OCS_Result($data); } - - public static function getUserPublickey($parameters) { - - if(OC_User::userExists($parameters['user'])) { - // calculate the disc space - // TODO - return new OC_OCS_Result(array()); - } else { - return new OC_OCS_Result(null, 300); - } - } - - public static function getUserPrivatekey($parameters) { - $user = OC_User::getUser(); - if(OC_User::isAdminUser($user) or ($user==$parameters['user'])) { - - if(OC_User::userExists($user)) { - // calculate the disc space - $txt = 'this is the private key of '.$parameters['user']; - echo($txt); - } else { - return new OC_OCS_Result(null, 300, 'User does not exist'); - } - } else { - return new OC_OCS_Result('null', 300, 'You don´t have permission to access this ressource.'); - } - } } diff --git a/lib/private/ocs/result.php b/lib/private/ocs/result.php index 9f14e8da7e8..0e3b85d5905 100644 --- a/lib/private/ocs/result.php +++ b/lib/private/ocs/result.php @@ -96,7 +96,7 @@ class OC_OCS_Result{ * @return bool */ public function succeeded() { - return (substr($this->statusCode, 0, 1) === '1'); + return ($this->statusCode == 100); } diff --git a/lib/private/ocsclient.php b/lib/private/ocsclient.php index fa6e3fac1bb..b0480caf028 100644 --- a/lib/private/ocsclient.php +++ b/lib/private/ocsclient.php @@ -72,7 +72,9 @@ class OC_OCSClient{ if($xml==false) { return null; } - $data=simplexml_load_string($xml); + $loadEntities = libxml_disable_entity_loader(true); + $data = simplexml_load_string($xml); + libxml_disable_entity_loader($loadEntities); $tmp=$data->data; $cats=array(); @@ -93,7 +95,8 @@ class OC_OCSClient{ * @returns array with application data * * This function returns a list of all the applications on the OCS server - * @param integer $page + * @param $categories + * @param int $page * @param string $filter */ public static function getApplications($categories, $page, $filter) { @@ -117,7 +120,9 @@ class OC_OCSClient{ if($xml==false) { return null; } - $data=simplexml_load_string($xml); + $loadEntities = libxml_disable_entity_loader(true); + $data = simplexml_load_string($xml); + libxml_disable_entity_loader($loadEntities); $tmp=$data->data->content; for($i = 0; $i < count($tmp); $i++) { @@ -144,6 +149,7 @@ class OC_OCSClient{ /** * @brief Get an the applications from the OCS server + * @param string $id * @returns array with application data * * This function returns an applications from the OCS server @@ -159,7 +165,9 @@ class OC_OCSClient{ OC_Log::write('core', 'Unable to parse OCS content', OC_Log::FATAL); return null; } - $data=simplexml_load_string($xml); + $loadEntities = libxml_disable_entity_loader(true); + $data = simplexml_load_string($xml); + libxml_disable_entity_loader($loadEntities); $tmp=$data->data->content; $app=array(); @@ -183,12 +191,13 @@ class OC_OCSClient{ } /** - * @brief Get the download url for an application from the OCS server - * @returns array with application data - * - * This function returns an download url for an applications from the OCS server - * @param integer $item - */ + * @brief Get the download url for an application from the OCS server + * @returns array with application data + * + * This function returns an download url for an applications from the OCS server + * @param string $id + * @param integer $item + */ public static function getApplicationDownload($id, $item) { if(OC_Config::getValue('appstoreenabled', true)==false) { return null; @@ -200,7 +209,9 @@ class OC_OCSClient{ OC_Log::write('core', 'Unable to parse OCS content', OC_Log::FATAL); return null; } - $data=simplexml_load_string($xml); + $loadEntities = libxml_disable_entity_loader(true); + $data = simplexml_load_string($xml); + libxml_disable_entity_loader($loadEntities); $tmp=$data->data->content; $app=array(); diff --git a/lib/private/preview.php b/lib/private/preview.php index 80fd003ed8d..2964b83c508 100755 --- a/lib/private/preview.php +++ b/lib/private/preview.php @@ -13,6 +13,8 @@ */ namespace OC; +use OC\Preview\Provider; + require_once 'preview/image.php'; require_once 'preview/movies.php'; require_once 'preview/mp3.php'; @@ -39,11 +41,17 @@ class Preview { private $file; private $maxX; private $maxY; - private $scalingup; - private $mimetype; + private $scalingUp; + private $mimeType; + private $keepAspect = false; + + //filemapper used for deleting previews + // index is path, value is fileinfo + static public $deleteFileMapper = array(); - //preview images object /** + * preview images object + * * @var \OC_Image */ private $preview; @@ -53,6 +61,11 @@ class Preview { static private $registeredProviders = array(); /** + * @var \OCP\Files\FileInfo + */ + protected $info; + + /** * @brief check if thumbnail or bigger version of thumbnail of file is cached * @param string $user userid - if no user is given, OC_User::getUser will be used * @param string $root path of root @@ -60,13 +73,14 @@ class Preview { * @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 + * @throws \Exception * @return mixed (bool / string) - * false if thumbnail does not exist - * path to thumbnail if thumbnail exists - */ - public function __construct($user='', $root='/', $file='', $maxX=1, $maxY=1, $scalingUp=true) { + * false if thumbnail does not exist + * path to thumbnail if thumbnail exists + */ + public function __construct($user = '', $root = '/', $file = '', $maxX = 1, $maxY = 1, $scalingUp = true) { //init fileviews - if($user === ''){ + if ($user === '') { $user = \OC_User::getUser(); } $this->fileView = new \OC\Files\View('/' . $user . '/' . $root); @@ -86,11 +100,11 @@ class Preview { $this->preview = null; //check if there are preview backends - if(empty(self::$providers)) { + if (empty(self::$providers)) { self::initProviders(); } - if(empty(self::$providers)) { + if (empty(self::$providers)) { \OC_Log::write('core', 'No preview providers exist', \OC_Log::ERROR); throw new \Exception('No preview providers'); } @@ -99,15 +113,15 @@ class Preview { /** * @brief returns the path of the file you want a thumbnail from * @return string - */ - public function getFile() { + */ + public function getFile() { return $this->file; } /** * @brief returns the max width of the preview * @return integer - */ + */ public function getMaxX() { return $this->maxX; } @@ -115,7 +129,7 @@ class Preview { /** * @brief returns the max height of the preview * @return integer - */ + */ public function getMaxY() { return $this->maxY; } @@ -123,15 +137,15 @@ class Preview { /** * @brief returns whether or not scalingup is enabled * @return bool - */ + */ public function getScalingUp() { - return $this->scalingup; + return $this->scalingUp; } /** * @brief returns the name of the thumbnailfolder * @return string - */ + */ public function getThumbnailsFolder() { return self::THUMBNAILS_FOLDER; } @@ -139,7 +153,7 @@ class Preview { /** * @brief returns the max scale factor * @return string - */ + */ public function getMaxScaleFactor() { return $this->maxScaleFactor; } @@ -147,7 +161,7 @@ class Preview { /** * @brief returns the max width set in ownCloud's config * @return string - */ + */ public function getConfigMaxX() { return $this->configMaxX; } @@ -155,44 +169,63 @@ class Preview { /** * @brief returns the max height set in ownCloud's config * @return string - */ + */ public function getConfigMaxY() { return $this->configMaxY; } /** + * @return false|Files\FileInfo|\OCP\Files\FileInfo + */ + protected function getFileInfo() { + $absPath = $this->fileView->getAbsolutePath($this->file); + $absPath = Files\Filesystem::normalizePath($absPath); + if(array_key_exists($absPath, self::$deleteFileMapper)) { + $this->info = self::$deleteFileMapper[$absPath]; + } else if (!$this->info) { + $this->info = $this->fileView->getFileInfo($this->file); + } + return $this->info; + } + + /** * @brief set the path of the file you want a thumbnail from * @param string $file * @return $this - */ + */ public function setFile($file) { $this->file = $file; + $this->info = null; if ($file !== '') { - $this->mimetype = $this->fileView->getMimeType($this->file); + $this->getFileInfo(); + if($this->info !== null && $this->info !== false) { + $this->mimeType = $this->info->getMimetype(); + } } return $this; } /** - * @brief set mimetype explicitely - * @param string $mimetype + * @brief set mime type explicitly + * @param string $mimeType */ - public function setMimetype($mimetype) { - $this->mimetype = $mimetype; + public function setMimetype($mimeType) { + $this->mimeType = $mimeType; } /** * @brief set the the max width of the preview * @param int $maxX + * @throws \Exception * @return $this - */ - public function setMaxX($maxX=1) { - if($maxX <= 0) { + */ + public function setMaxX($maxX = 1) { + if ($maxX <= 0) { throw new \Exception('Cannot set width of 0 or smaller!'); } $configMaxX = $this->getConfigMaxX(); - if(!is_null($configMaxX)) { - if($maxX > $configMaxX) { + if (!is_null($configMaxX)) { + if ($maxX > $configMaxX) { \OC_Log::write('core', 'maxX reduced from ' . $maxX . ' to ' . $configMaxX, \OC_Log::DEBUG); $maxX = $configMaxX; } @@ -204,15 +237,16 @@ class Preview { /** * @brief set the the max height of the preview * @param int $maxY + * @throws \Exception * @return $this - */ - public function setMaxY($maxY=1) { - if($maxY <= 0) { + */ + public function setMaxY($maxY = 1) { + if ($maxY <= 0) { throw new \Exception('Cannot set height of 0 or smaller!'); } $configMaxY = $this->getConfigMaxY(); - if(!is_null($configMaxY)) { - if($maxY > $configMaxY) { + if (!is_null($configMaxY)) { + if ($maxY > $configMaxY) { \OC_Log::write('core', 'maxX reduced from ' . $maxY . ' to ' . $configMaxY, \OC_Log::DEBUG); $maxY = $configMaxY; } @@ -225,27 +259,32 @@ class Preview { * @brief set whether or not scalingup is enabled * @param bool $scalingUp * @return $this - */ + */ public function setScalingup($scalingUp) { - if($this->getMaxScaleFactor() === 1) { + if ($this->getMaxScaleFactor() === 1) { $scalingUp = false; } - $this->scalingup = $scalingUp; + $this->scalingUp = $scalingUp; + return $this; + } + + public function setKeepAspect($keepAspect) { + $this->keepAspect = $keepAspect; return $this; } /** * @brief check if all parameters are valid * @return bool - */ + */ public function isFileValid() { $file = $this->getFile(); - if($file === '') { + if ($file === '') { \OC_Log::write('core', 'No filename passed', \OC_Log::DEBUG); return false; } - if(!$this->fileView->file_exists($file)) { + if (!$this->fileView->file_exists($file)) { \OC_Log::write('core', 'File:"' . $file . '" not found', \OC_Log::DEBUG); return false; } @@ -256,127 +295,169 @@ class Preview { /** * @brief deletes previews of a file with specific x and y * @return bool - */ + */ public function deletePreview() { $file = $this->getFile(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $fileInfo = $this->getFileInfo($file); + if($fileInfo !== null && $fileInfo !== false) { + $fileId = $fileInfo->getId(); - $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/' . $this->getMaxX() . '-' . $this->getMaxY() . '.png'; - $this->userView->unlink($previewPath); - return !$this->userView->file_exists($previewPath); + $previewPath = $this->buildCachePath($fileId); + return $this->userView->unlink($previewPath); + } + return false; } /** * @brief deletes all previews of a file * @return bool - */ + */ public function deleteAllPreviews() { $file = $this->getFile(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $fileInfo = $this->getFileInfo($file); + if($fileInfo !== null && $fileInfo !== false) { + $fileId = $fileInfo->getId(); - $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; - $this->userView->deleteAll($previewPath); - $this->userView->rmdir($previewPath); - return !$this->userView->is_dir($previewPath); + $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; + $this->userView->deleteAll($previewPath); + return $this->userView->rmdir($previewPath); + } + return false; } /** * @brief check if thumbnail or bigger version of thumbnail of file is cached - * @return mixed (bool / string) - * false if thumbnail does not exist - * path to thumbnail if thumbnail exists - */ - private function isCached() { - $file = $this->getFile(); - $maxX = $this->getMaxX(); - $maxY = $this->getMaxY(); - $scalingUp = $this->getScalingUp(); - $maxScaleFactor = $this->getMaxScaleFactor(); + * @param int $fileId fileId of the original image + * @return string|false path to thumbnail if it exists or false + */ + public function isCached($fileId) { + if (is_null($fileId)) { + return false; + } - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $preview = $this->buildCachePath($fileId); - if(is_null($fileId)) { + //does a preview with the wanted height and width already exist? + if ($this->userView->file_exists($preview)) { + return $preview; + } + + return $this->isCachedBigger($fileId); + } + + /** + * @brief check if a bigger version of thumbnail of file is cached + * @param int $fileId fileId of the original image + * @return string|false path to bigger thumbnail if it exists or false + */ + private function isCachedBigger($fileId) { + + if (is_null($fileId)) { return false; } - $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; - if(!$this->userView->is_dir($previewPath)) { + // in order to not loose quality we better generate aspect preserving previews from the original file + if ($this->keepAspect) { return false; } - //does a preview with the wanted height and width already exist? - if($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) { - return $previewPath . $maxX . '-' . $maxY . '.png'; + $maxX = $this->getMaxX(); + + //array for usable cached thumbnails + $possibleThumbnails = $this->getPossibleThumbnails($fileId); + + foreach ($possibleThumbnails as $width => $path) { + if ($width < $maxX) { + continue; + } else { + return $path; + } + } + + return false; + } + + /** + * @brief get possible bigger thumbnails of the given image + * @param int $fileId fileId of the original image + * @return array of paths to bigger thumbnails + */ + private function getPossibleThumbnails($fileId) { + + if (is_null($fileId)) { + return array(); } - $wantedAspectRatio = (float) ($maxX / $maxY); + $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; + + $wantedAspectRatio = (float) ($this->getMaxX() / $this->getMaxY()); //array for usable cached thumbnails $possibleThumbnails = array(); $allThumbnails = $this->userView->getDirectoryContent($previewPath); - foreach($allThumbnails as $thumbnail) { + foreach ($allThumbnails as $thumbnail) { $name = rtrim($thumbnail['name'], '.png'); - $size = explode('-', $name); - $x = (int) $size[0]; - $y = (int) $size[1]; + list($x, $y, $aspectRatio) = $this->getDimensionsFromFilename($name); - $aspectRatio = (float) ($x / $y); - if($aspectRatio !== $wantedAspectRatio) { + if (abs($aspectRatio - $wantedAspectRatio) >= 0.000001 + || $this->unscalable($x, $y) + ) { continue; } - - if($x < $maxX || $y < $maxY) { - if($scalingUp) { - $scalefactor = $maxX / $x; - if($scalefactor > $maxScaleFactor) { - continue; - } - }else{ - continue; - } - } $possibleThumbnails[$x] = $thumbnail['path']; } - if(count($possibleThumbnails) === 0) { - return false; - } - - if(count($possibleThumbnails) === 1) { - return current($possibleThumbnails); - } - ksort($possibleThumbnails); - if(key(reset($possibleThumbnails)) > $maxX) { - return current(reset($possibleThumbnails)); - } + return $possibleThumbnails; + } - if(key(end($possibleThumbnails)) < $maxX) { - return current(end($possibleThumbnails)); - } + /** + * @param string $name + * @return array + */ + private function getDimensionsFromFilename($name) { + $size = explode('-', $name); + $x = (int) $size[0]; + $y = (int) $size[1]; + $aspectRatio = (float) ($x / $y); + return array($x, $y, $aspectRatio); + } - foreach($possibleThumbnails as $width => $path) { - if($width < $maxX) { - continue; - }else{ - return $path; + /** + * @param int $x + * @param int $y + * @return bool + */ + private function unscalable($x, $y) { + + $maxX = $this->getMaxX(); + $maxY = $this->getMaxY(); + $scalingUp = $this->getScalingUp(); + $maxScaleFactor = $this->getMaxScaleFactor(); + + if ($x < $maxX || $y < $maxY) { + if ($scalingUp) { + $scalefactor = $maxX / $x; + if ($scalefactor > $maxScaleFactor) { + return true; + } + } else { + return true; } } + return false; } /** * @brief return a preview of a file * @return \OC_Image - */ + */ public function getPreview() { - if(!is_null($this->preview) && $this->preview->valid()){ + if (!is_null($this->preview) && $this->preview->valid()) { return $this->preview; } @@ -386,30 +467,37 @@ class Preview { $maxY = $this->getMaxY(); $scalingUp = $this->getScalingUp(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; - - $cached = $this->isCached(); + $fileInfo = $this->getFileInfo($file); + if($fileInfo === null || $fileInfo === false) { + return new \OC_Image(); + } + $fileId = $fileInfo->getId(); - if($cached) { - $image = new \OC_Image($this->userView->file_get_contents($cached, 'r')); + $cached = $this->isCached($fileId); + if ($cached) { + $stream = $this->userView->fopen($cached, 'r'); + $image = new \OC_Image(); + $image->loadFromFileHandle($stream); $this->preview = $image->valid() ? $image : null; + $this->resizeAndCrop(); + fclose($stream); } - if(is_null($this->preview)) { + if (is_null($this->preview)) { $preview = null; - foreach(self::$providers as $supportedMimetype => $provider) { - if(!preg_match($supportedMimetype, $this->mimetype)) { + foreach (self::$providers as $supportedMimeType => $provider) { + if (!preg_match($supportedMimeType, $this->mimeType)) { continue; } \OC_Log::write('core', 'Generating preview for "' . $file . '" with "' . get_class($provider) . '"', \OC_Log::DEBUG); + /** @var $provider Provider */ $preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingUp, $this->fileView); - if(!($preview instanceof \OC_Image)) { + if (!($preview instanceof \OC_Image)) { continue; } @@ -417,13 +505,13 @@ class Preview { $this->resizeAndCrop(); $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; - $cachePath = $previewPath . $maxX . '-' . $maxY . '.png'; + $cachePath = $this->buildCachePath($fileId); - if($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) { + if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) { $this->userView->mkdir($this->getThumbnailsFolder() . '/'); } - if($this->userView->is_dir($previewPath) === false) { + if ($this->userView->is_dir($previewPath) === false) { $this->userView->mkdir($previewPath); } @@ -433,7 +521,7 @@ class Preview { } } - if(is_null($this->preview)) { + if (is_null($this->preview)) { $this->preview = new \OC_Image(); } @@ -443,85 +531,80 @@ class Preview { /** * @brief show preview * @return void - */ - public function showPreview() { + */ + public function showPreview($mimeType = null) { \OCP\Response::enableCaching(3600 * 24); // 24 hours - if(is_null($this->preview)) { + if (is_null($this->preview)) { $this->getPreview(); } - $this->preview->show(); - return; - } - - /** - * @brief show preview - * @return void - */ - public function show() { - $this->showPreview(); - return; + $this->preview->show($mimeType); } /** * @brief resize, crop and fix orientation * @return void - */ + */ private function resizeAndCrop() { $image = $this->preview; $x = $this->getMaxX(); $y = $this->getMaxY(); $scalingUp = $this->getScalingUp(); - $maxscalefactor = $this->getMaxScaleFactor(); + $maxScaleFactor = $this->getMaxScaleFactor(); - if(!($image instanceof \OC_Image)) { + if (!($image instanceof \OC_Image)) { \OC_Log::write('core', '$this->preview is not an instance of OC_Image', \OC_Log::DEBUG); return; } $image->fixOrientation(); - $realx = (int) $image->width(); - $realy = (int) $image->height(); + $realX = (int)$image->width(); + $realY = (int)$image->height(); + + // compute $maxY using the aspect of the generated preview + if ($this->keepAspect) { + $y = $x / ($realX / $realY); + } - if($x === $realx && $y === $realy) { + if ($x === $realX && $y === $realY) { $this->preview = $image; return; } - $factorX = $x / $realx; - $factorY = $y / $realy; + $factorX = $x / $realX; + $factorY = $y / $realY; - if($factorX >= $factorY) { + if ($factorX >= $factorY) { $factor = $factorX; - }else{ + } else { $factor = $factorY; } - if($scalingUp === false) { - if($factor > 1) { + if ($scalingUp === false) { + if ($factor > 1) { $factor = 1; } } - if(!is_null($maxscalefactor)) { - if($factor > $maxscalefactor) { - \OC_Log::write('core', 'scalefactor reduced from ' . $factor . ' to ' . $maxscalefactor, \OC_Log::DEBUG); - $factor = $maxscalefactor; + if (!is_null($maxScaleFactor)) { + if ($factor > $maxScaleFactor) { + \OC_Log::write('core', 'scale factor reduced from ' . $factor . ' to ' . $maxScaleFactor, \OC_Log::DEBUG); + $factor = $maxScaleFactor; } } - $newXsize = (int) ($realx * $factor); - $newYsize = (int) ($realy * $factor); + $newXSize = (int)($realX * $factor); + $newYSize = (int)($realY * $factor); - $image->preciseResize($newXsize, $newYsize); + $image->preciseResize($newXSize, $newYSize); - if($newXsize === $x && $newYsize === $y) { + if ($newXSize === $x && $newYSize === $y) { $this->preview = $image; return; } - if($newXsize >= $x && $newYsize >= $y) { - $cropX = floor(abs($x - $newXsize) * 0.5); + if ($newXSize >= $x && $newYSize >= $y) { + $cropX = floor(abs($x - $newXSize) * 0.5); //don't crop previews on the Y axis, this sucks if it's a document. //$cropY = floor(abs($y - $newYsize) * 0.5); $cropY = 0; @@ -532,36 +615,36 @@ class Preview { return; } - if($newXsize < $x || $newYsize < $y) { - if($newXsize > $x) { - $cropX = floor(($newXsize - $x) * 0.5); - $image->crop($cropX, 0, $x, $newYsize); + if (($newXSize < $x || $newYSize < $y) && $scalingUp) { + if ($newXSize > $x) { + $cropX = floor(($newXSize - $x) * 0.5); + $image->crop($cropX, 0, $x, $newYSize); } - if($newYsize > $y) { - $cropY = floor(($newYsize - $y) * 0.5); - $image->crop(0, $cropY, $newXsize, $y); + if ($newYSize > $y) { + $cropY = floor(($newYSize - $y) * 0.5); + $image->crop(0, $cropY, $newXSize, $y); } - $newXsize = (int) $image->width(); - $newYsize = (int) $image->height(); + $newXSize = (int)$image->width(); + $newYSize = (int)$image->height(); //create transparent background layer - $backgroundlayer = imagecreatetruecolor($x, $y); - $white = imagecolorallocate($backgroundlayer, 255, 255, 255); - imagefill($backgroundlayer, 0, 0, $white); + $backgroundLayer = imagecreatetruecolor($x, $y); + $white = imagecolorallocate($backgroundLayer, 255, 255, 255); + imagefill($backgroundLayer, 0, 0, $white); $image = $image->resource(); - $mergeX = floor(abs($x - $newXsize) * 0.5); - $mergeY = floor(abs($y - $newYsize) * 0.5); + $mergeX = floor(abs($x - $newXSize) * 0.5); + $mergeY = floor(abs($y - $newYSize) * 0.5); - imagecopy($backgroundlayer, $image, $mergeX, $mergeY, 0, 0, $newXsize, $newYsize); + imagecopy($backgroundLayer, $image, $mergeX, $mergeY, 0, 0, $newXSize, $newYSize); //$black = imagecolorallocate(0,0,0); //imagecolortransparent($transparentlayer, $black); - $image = new \OC_Image($backgroundlayer); + $image = new \OC_Image($backgroundLayer); $this->preview = $image; return; @@ -573,8 +656,8 @@ class Preview { * @param array $options * @return void */ - public static function registerProvider($class, $options=array()) { - self::$registeredProviders[]=array('class'=>$class, 'options'=>$options); + public static function registerProvider($class, $options = array()) { + self::$registeredProviders[] = array('class' => $class, 'options' => $options); } /** @@ -582,20 +665,21 @@ class Preview { * @return void */ private static function initProviders() { - if(!\OC_Config::getValue('enable_previews', true)) { + if (!\OC_Config::getValue('enable_previews', true)) { $provider = new Preview\Unknown(array()); self::$providers = array($provider->getMimeType() => $provider); return; } - if(count(self::$providers)>0) { + if (count(self::$providers) > 0) { return; } - foreach(self::$registeredProviders as $provider) { - $class=$provider['class']; - $options=$provider['options']; + foreach (self::$registeredProviders as $provider) { + $class = $provider['class']; + $options = $provider['options']; + /** @var $object Provider */ $object = new $class($options); self::$providers[$object->getMimeType()] = $object; @@ -606,38 +690,79 @@ class Preview { } public static function post_write($args) { - self::post_delete($args); + self::post_delete($args, 'files/'); } - public static function post_delete($args) { + public static function prepare_delete_files($args) { + self::prepare_delete($args, 'files/'); + } + + public static function prepare_delete($args, $prefix='') { $path = $args['path']; - if(substr($path, 0, 1) === '/') { + if (substr($path, 0, 1) === '/') { $path = substr($path, 1); } - $preview = new Preview(\OC_User::getUser(), 'files/', $path); + + $view = new \OC\Files\View('/' . \OC_User::getUser() . '/' . $prefix); + $info = $view->getFileInfo($path); + + \OC\Preview::$deleteFileMapper = array_merge( + \OC\Preview::$deleteFileMapper, + array( + Files\Filesystem::normalizePath($view->getAbsolutePath($path)) => $info, + ) + ); + } + + public static function post_delete_files($args) { + self::post_delete($args, 'files/'); + } + + public static function post_delete($args, $prefix='') { + $path = Files\Filesystem::normalizePath($args['path']); + + $preview = new Preview(\OC_User::getUser(), $prefix, $path); $preview->deleteAllPreviews(); } /** - * @param string $mimetype + * @param string $mimeType + * @return bool */ - public static function isMimeSupported($mimetype) { - if(!\OC_Config::getValue('enable_previews', true)) { + public static function isMimeSupported($mimeType) { + if (!\OC_Config::getValue('enable_previews', true)) { return false; } //check if there are preview backends - if(empty(self::$providers)) { + if (empty(self::$providers)) { self::initProviders(); } //remove last element because it has the mimetype * $providers = array_slice(self::$providers, 0, -1); - foreach($providers as $supportedMimetype => $provider) { - if(preg_match($supportedMimetype, $mimetype)) { + foreach ($providers as $supportedMimeType => $provider) { + if (preg_match($supportedMimeType, $mimeType)) { return true; } } return false; } + + /** + * @param $fileId + * @return string + */ + private function buildCachePath($fileId) { + $maxX = $this->getMaxX(); + $maxY = $this->getMaxY(); + + $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; + $preview = $previewPath . $maxX . '-' . $maxY . '.png'; + if ($this->keepAspect) { + $preview = $previewPath . $maxX . '-with-aspect.png'; + return $preview; + } + return $preview; + } } diff --git a/lib/private/preview/image.php b/lib/private/preview/image.php index 84343df2608..59aaa27ef34 100644 --- a/lib/private/preview/image.php +++ b/lib/private/preview/image.php @@ -31,6 +31,7 @@ class Image extends Provider { return $image->valid() ? $image : false; } + } -\OC\Preview::registerProvider('OC\Preview\Image');
\ No newline at end of file +\OC\Preview::registerProvider('OC\Preview\Image'); diff --git a/lib/private/preview/movies.php b/lib/private/preview/movies.php index 71cd3bae057..72ccfadc6e9 100644 --- a/lib/private/preview/movies.php +++ b/lib/private/preview/movies.php @@ -9,7 +9,7 @@ namespace OC\Preview; function findBinaryPath($program) { - exec('which ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode); + exec('command -v ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode); if ($returnCode === 0 && count($output) > 0) { return escapeshellcmd($output[0]); } @@ -42,7 +42,6 @@ if (!\OC_Util::runningOnWindows()) { public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { // TODO: use proc_open() and stream the source file ? $absPath = \OC_Helper::tmpFile(); - $tmpPath = \OC_Helper::tmpFile(); $handle = $fileview->fopen($path, 'rb'); @@ -51,14 +50,39 @@ if (!\OC_Util::runningOnWindows()) { $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); + } + } + + unlink($absPath); + + + return $result; + } + + /** + * @param int $maxX + * @param int $maxY + * @param string $absPath + * @param string $tmpPath + * @param int $second + * @return bool|\OC_Image + */ + private function generateThumbNail($maxX, $maxY, $absPath, $second) + { + $tmpPath = \OC_Helper::tmpFile(); + if (self::$avconvBinary) { - $cmd = self::$avconvBinary . ' -an -y -ss 5'. + $cmd = self::$avconvBinary . ' -an -y -ss ' . escapeshellarg($second) . ' -i ' . escapeshellarg($absPath) . ' -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) . ' > /dev/null 2>&1'; - } - else { - $cmd = self::$ffmpegBinary . ' -y -ss 5' . + } else { + $cmd = self::$ffmpegBinary . ' -y -ss ' . escapeshellarg($second) . ' -i ' . escapeshellarg($absPath) . ' -f mjpeg -vframes 1' . ' -s ' . escapeshellarg($maxX) . 'x' . escapeshellarg($maxY) . @@ -68,14 +92,13 @@ if (!\OC_Util::runningOnWindows()) { exec($cmd, $output, $returnCode); - unlink($absPath); - if ($returnCode === 0) { $image = new \OC_Image(); $image->loadFromFile($tmpPath); unlink($tmpPath); return $image->valid() ? $image : false; } + unlink($tmpPath); return false; } } diff --git a/lib/private/preview/mp3.php b/lib/private/preview/mp3.php index 3fc0ab0490c..21f160fd50f 100644 --- a/lib/private/preview/mp3.php +++ b/lib/private/preview/mp3.php @@ -47,4 +47,4 @@ class MP3 extends Provider { } -\OC\Preview::registerProvider('OC\Preview\MP3');
\ No newline at end of file +\OC\Preview::registerProvider('OC\Preview\MP3'); diff --git a/lib/private/preview/office-cl.php b/lib/private/preview/office-cl.php index b11fed13ba1..6e4d4321eb7 100644 --- a/lib/private/preview/office-cl.php +++ b/lib/private/preview/office-cl.php @@ -64,12 +64,12 @@ if (!\OC_Util::runningOnWindows()) { $cmd = \OC_Config::getValue('preview_libreoffice_path', null); } - $whichLibreOffice = shell_exec('which libreoffice'); + $whichLibreOffice = shell_exec('command -v libreoffice'); if($cmd === '' && !empty($whichLibreOffice)) { $cmd = 'libreoffice'; } - $whichOpenOffice = shell_exec('which openoffice'); + $whichOpenOffice = shell_exec('command -v openoffice'); if($cmd === '' && !empty($whichOpenOffice)) { $cmd = 'openoffice'; } diff --git a/lib/private/preview/office-fallback.php b/lib/private/preview/office-fallback.php deleted file mode 100644 index f184b3149d5..00000000000 --- a/lib/private/preview/office-fallback.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php -/** - * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ -namespace OC\Preview; - -/* //There is no (good) php-only solution for converting 2003 word documents to pdfs / pngs ... -class DOC extends Provider { - - public function getMimeType() { - return '/application\/msword/'; - } - - public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - require_once(''); - } - -} - -\OC\Preview::registerProvider('OC\Preview\DOC'); -*/ - -class DOCX extends Provider { - - public function getMimeType() { - return '/application\/vnd.openxmlformats-officedocument.wordprocessingml.document/'; - } - - public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - require_once('phpdocx/classes/TransformDoc.inc'); - - $tmpDoc = $fileview->toTmpFile($path); - - $transformdoc = new \TransformDoc(); - $transformdoc->setStrFile($tmpDoc); - $transformdoc->generatePDF($tmpDoc); - - $pdf = new \imagick($tmpDoc . '[0]'); - $pdf->setImageFormat('jpg'); - - unlink($tmpDoc); - - $image = new \OC_Image($pdf); - - return $image->valid() ? $image : false; - } - -} - -\OC\Preview::registerProvider('OC\Preview\DOCX'); - -class MSOfficeExcel extends Provider { - - public function getMimeType() { - return null; - } - - public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - require_once('PHPExcel/Classes/PHPExcel.php'); - require_once('PHPExcel/Classes/PHPExcel/IOFactory.php'); - - $absPath = $fileview->toTmpFile($path); - $tmpPath = \OC_Helper::tmpFile(); - - $rendererName = \PHPExcel_Settings::PDF_RENDERER_DOMPDF; - $rendererLibraryPath = \OC::$THIRDPARTYROOT . '/3rdparty/dompdf'; - - \PHPExcel_Settings::setPdfRenderer($rendererName, $rendererLibraryPath); - - $phpexcel = new \PHPExcel($absPath); - $excel = \PHPExcel_IOFactory::createWriter($phpexcel, 'PDF'); - $excel->save($tmpPath); - - $pdf = new \imagick($tmpPath . '[0]'); - $pdf->setImageFormat('jpg'); - - unlink($absPath); - unlink($tmpPath); - - $image = new \OC_Image(); - $image->loadFromData($pdf); - - return $image->valid() ? $image : false; - } - -} - -class XLS extends MSOfficeExcel { - - public function getMimeType() { - return '/application\/vnd.ms-excel/'; - } - -} - -\OC\Preview::registerProvider('OC\Preview\XLS'); - -class XLSX extends MSOfficeExcel { - - public function getMimeType() { - return '/application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet/'; - } - -} - -\OC\Preview::registerProvider('OC\Preview\XLSX'); - -/* //There is no (good) php-only solution for converting powerpoint documents to pdfs / pngs ... -class MSOfficePowerPoint extends Provider { - - public function getMimeType() { - return null; - } - - public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - return false; - } - -} - -class PPT extends MSOfficePowerPoint { - - public function getMimeType() { - return '/application\/vnd.ms-powerpoint/'; - } - -} - -\OC\Preview::registerProvider('OC\Preview\PPT'); - -class PPTX extends MSOfficePowerPoint { - - public function getMimeType() { - return '/application\/vnd.openxmlformats-officedocument.presentationml.presentation/'; - } - -} - -\OC\Preview::registerProvider('OC\Preview\PPTX'); -*/
\ No newline at end of file diff --git a/lib/private/preview/office.php b/lib/private/preview/office.php index 02bb22e9b94..16e395cb1a8 100644 --- a/lib/private/preview/office.php +++ b/lib/private/preview/office.php @@ -6,24 +6,23 @@ * See the COPYING-README file. */ //both, libreoffice backend and php fallback, need imagick -if (extension_loaded('imagick') && count(@\Imagick::queryFormats("PDF")) === 1) { - $isShellExecEnabled = \OC_Helper::is_function_enabled('shell_exec'); +if (extension_loaded('imagick')) { - // LibreOffice preview is currently not supported on Windows - if (!\OC_Util::runningOnWindows()) { - $whichLibreOffice = ($isShellExecEnabled ? shell_exec('which libreoffice') : ''); - $isLibreOfficeAvailable = !empty($whichLibreOffice); - $whichOpenOffice = ($isShellExecEnabled ? shell_exec('which libreoffice') : ''); - $isOpenOfficeAvailable = !empty($whichOpenOffice); - //let's see if there is libreoffice or openoffice on this machine - if($isShellExecEnabled && ($isLibreOfficeAvailable || $isOpenOfficeAvailable || is_string(\OC_Config::getValue('preview_libreoffice_path', null)))) { - require_once('office-cl.php'); - }else{ - //in case there isn't, use our fallback - require_once('office-fallback.php'); + $checkImagick = new Imagick(); + + if(count($checkImagick->queryFormats('PDF')) === 1) { + $isShellExecEnabled = \OC_Helper::is_function_enabled('shell_exec'); + + // LibreOffice preview is currently not supported on Windows + if (!\OC_Util::runningOnWindows()) { + $whichLibreOffice = ($isShellExecEnabled ? shell_exec('command -v libreoffice') : ''); + $isLibreOfficeAvailable = !empty($whichLibreOffice); + $whichOpenOffice = ($isShellExecEnabled ? shell_exec('command -v libreoffice') : ''); + $isOpenOfficeAvailable = !empty($whichOpenOffice); + //let's see if there is libreoffice or openoffice on this machine + if($isShellExecEnabled && ($isLibreOfficeAvailable || $isOpenOfficeAvailable || is_string(\OC_Config::getValue('preview_libreoffice_path', null)))) { + require_once('office-cl.php'); + } } - } else { - //in case there isn't, use our fallback - require_once('office-fallback.php'); } } diff --git a/lib/private/preview/pdf.php b/lib/private/preview/pdf.php index d390b4fc677..4b88b1a31e2 100644 --- a/lib/private/preview/pdf.php +++ b/lib/private/preview/pdf.php @@ -7,34 +7,42 @@ */ namespace OC\Preview; -if (extension_loaded('imagick') && count(@\Imagick::queryFormats("PDF")) === 1) { +use Imagick; - class PDF extends Provider { +if (extension_loaded('imagick')) { - public function getMimeType() { - return '/application\/pdf/'; - } + $checkImagick = new Imagick(); + + if(count($checkImagick->queryFormats('PDF')) === 1) { - public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - $tmpPath = $fileview->toTmpFile($path); + class PDF extends Provider { - //create imagick object from pdf - try{ - $pdf = new \imagick($tmpPath . '[0]'); - $pdf->setImageFormat('jpg'); - } catch (\Exception $e) { - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - return false; + public function getMimeType() { + return '/application\/pdf/'; } - unlink($tmpPath); + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $tmpPath = $fileview->toTmpFile($path); + + //create imagick object from pdf + try{ + $pdf = new Imagick($tmpPath . '[0]'); + $pdf->setImageFormat('jpg'); + } catch (\Exception $e) { + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; + } + + unlink($tmpPath); + + //new image object + $image = new \OC_Image($pdf); + //check if image object is valid + return $image->valid() ? $image : false; + } - //new image object - $image = new \OC_Image($pdf); - //check if image object is valid - return $image->valid() ? $image : false; } - } - \OC\Preview::registerProvider('OC\Preview\PDF'); + \OC\Preview::registerProvider('OC\Preview\PDF'); + } } diff --git a/lib/private/preview/provider.php b/lib/private/preview/provider.php index 88337d64e4b..f769823f6e6 100644 --- a/lib/private/preview/provider.php +++ b/lib/private/preview/provider.php @@ -22,4 +22,5 @@ abstract class Provider { * OC_Image object of the preview */ abstract public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview); + } diff --git a/lib/private/preview/svg.php b/lib/private/preview/svg.php index 9a73fff9467..82ef3cdebf6 100644 --- a/lib/private/preview/svg.php +++ b/lib/private/preview/svg.php @@ -7,40 +7,47 @@ */ namespace OC\Preview; -if (extension_loaded('imagick') && count(@\Imagick::queryFormats("SVG")) === 1) { +use Imagick; - class SVG extends Provider { +if (extension_loaded('imagick')) { - public function getMimeType() { - return '/image\/svg\+xml/'; - } + $checkImagick = new Imagick(); - public function getThumbnail($path,$maxX,$maxY,$scalingup,$fileview) { - try{ - $svg = new \Imagick(); - $svg->setBackgroundColor(new \ImagickPixel('transparent')); + if(count($checkImagick->queryFormats('SVG')) === 1) { - $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; - } + class SVG extends Provider { - $svg->readImageBlob($content); - $svg->setImageFormat('png32'); - } catch (\Exception $e) { - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - return false; + public function getMimeType() { + return '/image\/svg\+xml/'; } + 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; + } + + $svg->readImageBlob($content); + $svg->setImageFormat('png32'); + } catch (\Exception $e) { + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; + } - //new image object - $image = new \OC_Image(); - $image->loadFromData($svg); - //check if image object is valid - return $image->valid() ? $image : false; - } - } - \OC\Preview::registerProvider('OC\Preview\SVG'); + //new image object + $image = new \OC_Image(); + $image->loadFromData($svg); + //check if image object is valid + return $image->valid() ? $image : false; + } + + } -}
\ No newline at end of file + \OC\Preview::registerProvider('OC\Preview\SVG'); + } +} diff --git a/lib/private/request.php b/lib/private/request.php index d0128f95d96..90f7488eea5 100755 --- a/lib/private/request.php +++ b/lib/private/request.php @@ -13,6 +13,8 @@ class OC_Request { 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)(:[0-9]+|)$/'; + /** * @brief Check overwrite condition * @param string $type @@ -25,39 +27,97 @@ class OC_Request { } /** - * @brief Returns the server host + * @brief 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 $domain + * @return bool true if the given domain is trusted or if no trusted domains + * have been configured + */ + public static function isTrustedDomain($domain) { + $trustedList = \OC_Config::getValue('trusted_domains', array()); + if (empty($trustedList)) { + return true; + } + if (preg_match(self::REGEX_LOCALHOST, $domain) === 1) { + return true; + } + return in_array($domain, $trustedList); + } + + /** + * @brief Returns the unverified server host from the headers without checking + * whether it is a trusted domain * @returns string the server host * * Returns the server host, even if the website uses one or more * reverse proxies */ - public static function serverHost() { - if(OC::$CLI) { - return 'localhost'; - } - if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) { - return OC_Config::getValue('overwritehost'); - } + public static function insecureServerHost() { + $host = null; if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) { - $host = trim(array_pop(explode(",", $_SERVER['HTTP_X_FORWARDED_HOST']))); - } - else{ - $host=$_SERVER['HTTP_X_FORWARDED_HOST']; + $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); + $host = trim(current($parts)); + } else { + $host = $_SERVER['HTTP_X_FORWARDED_HOST']; } - } - else{ + } else { if (isset($_SERVER['HTTP_HOST'])) { - return $_SERVER['HTTP_HOST']; + $host = $_SERVER['HTTP_HOST']; + } else if (isset($_SERVER['SERVER_NAME'])) { + $host = $_SERVER['SERVER_NAME']; } - if (isset($_SERVER['SERVER_NAME'])) { - return $_SERVER['SERVER_NAME']; - } - return 'localhost'; } return $host; } + /** + * 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 + */ + public static function getOverwriteHost() { + if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) { + return OC_Config::getValue('overwritehost'); + } + return null; + } + + /** + * @brief Returns the server host from the headers, or the first configured + * trusted domain if the host isn't in the trusted list + * @returns string the server host + * + * Returns the server host, even if the website uses one or more + * reverse proxies + */ + public static function serverHost() { + if(OC::$CLI) { + return 'localhost'; + } + + // overwritehost is always trusted + $host = self::getOverwriteHost(); + if ($host !== null) { + return $host; + } + + // get the host from the headers + $host = self::insecureServerHost(); + + // 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 + if (self::isTrustedDomain($host)) { + return $host; + } else { + $trustedList = \OC_Config::getValue('trusted_domains', array('')); + return $trustedList[0]; + } + } /** * @brief Returns the server protocol @@ -71,14 +131,14 @@ class OC_Request { } if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { $proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']); - }else{ - if(isset($_SERVER['HTTPS']) and !empty($_SERVER['HTTPS']) and ($_SERVER['HTTPS']!='off')) { - $proto = 'https'; - }else{ - $proto = 'http'; - } + // 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($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') { + return 'https'; } - return $proto; + return 'http'; } /** @@ -106,10 +166,11 @@ class OC_Request { */ public static function scriptName() { $name = $_SERVER['SCRIPT_NAME']; - if (OC_Config::getValue('overwritewebroot', '') !== '' and self::isOverwriteCondition()) { + $overwriteWebRoot = OC_Config::getValue('overwritewebroot', ''); + if ($overwriteWebRoot !== '' and self::isOverwriteCondition()) { $serverroot = str_replace("\\", '/', substr(__DIR__, 0, -strlen('lib/private/'))); $suburi = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen($serverroot))); - $name = OC_Config::getValue('overwritewebroot', '') . $suburi; + $name = '/' . ltrim($overwriteWebRoot . $suburi, '/'); } return $name; } @@ -140,6 +201,7 @@ class OC_Request { /** * @brief get Path info from request, not urldecoded + * @throws Exception * @return string Path info or false when not found */ public static function getRawPathInfo() { diff --git a/lib/private/response.php b/lib/private/response.php index 71c538fb311..1aa5e629b8b 100644 --- a/lib/private/response.php +++ b/lib/private/response.php @@ -12,6 +12,7 @@ class OC_Response { const STATUS_TEMPORARY_REDIRECT = 307; const STATUS_NOT_FOUND = 404; const STATUS_INTERNAL_SERVER_ERROR = 500; + const STATUS_SERVICE_UNAVAILABLE = 503; /** * @brief Enable response caching by sending correct HTTP headers @@ -49,7 +50,7 @@ class OC_Response { /** * @brief Set response status - * @param $status a HTTP status code, see also the STATUS constants + * @param int $status a HTTP status code, see also the STATUS constants */ static public function setStatus($status) { $protocol = $_SERVER['SERVER_PROTOCOL']; @@ -74,6 +75,9 @@ class OC_Response { 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); } diff --git a/lib/private/route/cachingrouter.php b/lib/private/route/cachingrouter.php new file mode 100644 index 00000000000..7a21c523435 --- /dev/null +++ b/lib/private/route/cachingrouter.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Route; + +class CachingRouter extends Router { + /** + * @var \OCP\ICache + */ + protected $cache; + + /** + * @param \OCP\ICache $cache + */ + public function __construct($cache) { + $this->cache = $cache; + parent::__construct(); + } + + /** + * 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 . json_encode($parameters) . intval($absolute); + if ($this->cache->hasKey($key)) { + return $this->cache->get($key); + } else { + $url = parent::generate($name, $parameters, $absolute); + $this->cache->set($key, $url, 3600); + return $url; + } + } +} diff --git a/lib/private/route.php b/lib/private/route/route.php index fb7da456b62..df80facf9c1 100644 --- a/lib/private/route.php +++ b/lib/private/route/route.php @@ -6,13 +6,17 @@ * See the COPYING-README file. */ -use Symfony\Component\Routing\Route; +namespace OC\Route; -class OC_Route extends 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->setRequirement('_method', strtoupper($method)); @@ -21,6 +25,7 @@ class OC_Route extends Route { /** * Specify POST as the method to use with this route + * @return \OC\Route\Route */ public function post() { $this->method('POST'); @@ -29,6 +34,7 @@ class OC_Route extends Route { /** * Specify GET as the method to use with this route + * @return \OC\Route\Route */ public function get() { $this->method('GET'); @@ -37,6 +43,7 @@ class OC_Route extends Route { /** * Specify PUT as the method to use with this route + * @return \OC\Route\Route */ public function put() { $this->method('PUT'); @@ -45,6 +52,7 @@ class OC_Route extends Route { /** * Specify DELETE as the method to use with this route + * @return \OC\Route\Route */ public function delete() { $this->method('DELETE'); @@ -53,6 +61,7 @@ class OC_Route extends Route { /** * Specify PATCH as the method to use with this route + * @return \OC\Route\Route */ public function patch() { $this->method('PATCH'); @@ -63,6 +72,7 @@ class OC_Route extends Route { * Defaults to use for this route * * @param array $defaults The defaults + * @return \OC\Route\Route */ public function defaults($defaults) { $action = $this->getDefault('action'); @@ -78,6 +88,7 @@ class OC_Route extends Route { * Requirements for this route * * @param array $requirements The requirements + * @return \OC\Route\Route */ public function requirements($requirements) { $method = $this->getRequirement('_method'); @@ -93,8 +104,10 @@ class OC_Route extends Route { /** * 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 @@ -112,6 +125,7 @@ class OC_Route extends Route { * The action to execute when this route matches, includes a file like * it is called directly * @param $file + * @return void */ public function actionInclude($file) { $function = create_function('$param', diff --git a/lib/private/route/router.php b/lib/private/route/router.php new file mode 100644 index 00000000000..f7900362bec --- /dev/null +++ b/lib/private/route/router.php @@ -0,0 +1,248 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Route; + +use OCP\Route\IRouter; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\Generator\UrlGenerator; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; + +class Router implements IRouter { + /** + * @var \Symfony\Component\Routing\RouteCollection[] + */ + protected $collections = array(); + + /** + * @var \Symfony\Component\Routing\RouteCollection + */ + protected $collection = null; + + /** + * @var \Symfony\Component\Routing\RouteCollection + */ + protected $root = null; + + /** + * @var \Symfony\Component\Routing\Generator\UrlGenerator + */ + protected $generator = null; + + /** + * @var string[] + */ + protected $routingFiles; + + /** + * @var string + */ + protected $cacheKey; + + protected $loaded = false; + + protected $loadedApps = array(); + + public function __construct() { + $baseUrl = \OC_Helper::linkTo('', 'index.php'); + if (!\OC::$CLI) { + $method = $_SERVER['REQUEST_METHOD']; + } else { + $method = 'GET'; + } + $host = \OC_Request::serverHost(); + $schema = \OC_Request::serverProtocol(); + $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 = array(); + foreach (\OC_APP::getEnabledApps() as $app) { + $file = \OC_App::getAppPath($app) . '/appinfo/routes.php'; + if (file_exists($file)) { + $this->routingFiles[$app] = $file; + } + } + } + return $this->routingFiles; + } + + /** + * @return string + */ + public function getCacheKey() { + if (!isset($this->cacheKey)) { + $files = $this->getRoutingFiles(); + $files[] = 'settings/routes.php'; + $files[] = 'core/routes.php'; + $files[] = 'ocs/routes.php'; + $this->cacheKey = \OC_Cache::generateCacheKeyFromFiles($files); + } + return $this->cacheKey; + } + + /** + * loads the api routes + * @return void + */ + public function loadRoutes($app = null) { + 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_exists($file)) { + $routingFiles = array($app => $file); + } else { + $routingFiles = array(); + } + } + foreach ($routingFiles as $app => $file) { + if (!isset($this->loadedApps[$app])) { + $this->loadedApps[$app] = true; + $this->useCollection($app); + $this->requireRouteFile($file); + $collection = $this->getCollection($app); + $collection->addPrefix('/apps/' . $app); + $this->root->addCollection($collection); + } + } + if (!isset($this->loadedApps['core'])) { + $this->loadedApps['core'] = true; + $this->useCollection('root'); + require_once 'settings/routes.php'; + require_once 'core/routes.php'; + + // include ocs routes + require_once 'ocs/routes.php'; + $collection = $this->getCollection('ocs'); + $collection->addPrefix('/ocs'); + $this->root->addCollection($collection); + } + } + + /** + * @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); + } + + /** + * 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(), array $requirements = array()) { + $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); + $this->loadRoutes($app); + } else if (substr($url, 0, 6) === '/core/' or substr($url, 0, 10) === '/settings/') { + $this->loadRoutes('core'); + } else { + $this->loadRoutes(); + } + $matcher = new UrlMatcher($this->root, $this->context); + $parameters = $matcher->match($url); + if (isset($parameters['action'])) { + $action = $parameters['action']; + if (!is_callable($action)) { + var_dump($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'); + } + } + + /** + * 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 = array(), $absolute = false) { + $this->loadRoutes(); + return $this->getGenerator()->generate($name, $parameters, $absolute); + } + + /** + * To isolate the variable scope used inside the $file it is required in it's own method + * @param $file + */ + private function requireRouteFile($file) { + require_once $file; + } + +} diff --git a/lib/private/router.php b/lib/private/router.php deleted file mode 100644 index 19c1e4473ec..00000000000 --- a/lib/private/router.php +++ /dev/null @@ -1,185 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -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\Route; - -class OC_Router { - protected $collections = array(); - protected $collection = null; - protected $root = null; - - protected $generator = null; - protected $routing_files; - protected $cache_key; - - public function __construct() { - $baseUrl = OC_Helper::linkTo('', 'index.php'); - if ( !OC::$CLI) { - $method = $_SERVER['REQUEST_METHOD']; - }else{ - $method = 'GET'; - } - $host = OC_Request::serverHost(); - $schema = OC_Request::serverProtocol(); - $this->context = new RequestContext($baseUrl, $method, $host, $schema); - // TODO cache - $this->root = $this->getCollection('root'); - } - - public function getRoutingFiles() { - if (!isset($this->routing_files)) { - $this->routing_files = array(); - foreach(OC_APP::getEnabledApps() as $app) { - $file = OC_App::getAppPath($app).'/appinfo/routes.php'; - if(file_exists($file)) { - $this->routing_files[$app] = $file; - } - } - } - return $this->routing_files; - } - - public function getCacheKey() { - if (!isset($this->cache_key)) { - $files = $this->getRoutingFiles(); - $files[] = 'settings/routes.php'; - $files[] = 'core/routes.php'; - $files[] = 'ocs/routes.php'; - $this->cache_key = OC_Cache::generateCacheKeyFromFiles($files); - } - return $this->cache_key; - } - - /** - * loads the api routes - */ - public function loadRoutes() { - foreach($this->getRoutingFiles() as $app => $file) { - $this->useCollection($app); - require_once $file; - $collection = $this->getCollection($app); - $collection->addPrefix('/apps/'.$app); - $this->root->addCollection($collection); - } - $this->useCollection('root'); - require_once 'settings/routes.php'; - require_once 'core/routes.php'; - - // include ocs routes - require_once 'ocs/routes.php'; - $collection = $this->getCollection('ocs'); - $collection->addPrefix('/ocs'); - $this->root->addCollection($collection); - } - - 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 colletion to use. - */ - public function useCollection($name) { - $this->collection = $this->getCollection($name); - } - - /** - * Create a OC_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) - */ - public function create($name, $pattern, array $defaults = array(), array $requirements = array()) { - $route = new OC_Route($pattern, $defaults, $requirements); - $this->collection->add($name, $route); - return $route; - } - - /** - * Find the route matching $url. - * - * @param string $url The url to find - */ - public function match($url) { - $matcher = new UrlMatcher($this->root, $this->context); - $parameters = $matcher->match($url); - if (isset($parameters['action'])) { - $action = $parameters['action']; - if (!is_callable($action)) { - var_dump($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'); - } - } - - /** - * Get the url generator - * - */ - 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 - */ - public function generate($name, $parameters = array(), $absolute = false) - { - return $this->getGenerator()->generate($name, $parameters, $absolute); - } - - /** - * Generate JSON response for routing in javascript - */ - public static function JSRoutes() - { - $router = OC::getRouter(); - - $etag = $router->getCacheKey(); - OC_Response::enableCaching(); - OC_Response::setETagHeader($etag); - - $root = $router->getCollection('root'); - $routes = array(); - foreach($root->all() as $name => $route) { - $compiled_route = $route->compile(); - $defaults = $route->getDefaults(); - unset($defaults['action']); - $routes[$name] = array( - 'tokens' => $compiled_route->getTokens(), - 'defaults' => $defaults, - ); - } - OCP\JSON::success ( array( 'data' => $routes ) ); - } -} diff --git a/lib/private/search.php b/lib/private/search.php index 70d670e048e..3f540090fdd 100644 --- a/lib/private/search.php +++ b/lib/private/search.php @@ -45,7 +45,7 @@ class OC_Search{ /** * search all provider for $query - * @param string query + * @param string $query * @return array An array of OC_Search_Result's */ public static function search($query) { diff --git a/lib/private/server.php b/lib/private/server.php index 7696fc207fd..4c29092cf44 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -35,6 +35,13 @@ class Server extends SimpleContainer implements IServerContainer { $requesttoken = false; } + if (defined('PHPUNIT_RUN') && PHPUNIT_RUN + && in_array('fakeinput', stream_get_wrappers())) { + $stream = 'fakeinput://data'; + } else { + $stream = 'php://input'; + } + return new Request( array( 'get' => $_GET, @@ -48,7 +55,7 @@ class Server extends SimpleContainer implements IServerContainer { : null, 'urlParams' => $urlParams, 'requesttoken' => $requesttoken, - ) + ), $stream ); }); $this->registerService('PreviewManager', function($c) { @@ -158,6 +165,18 @@ class Server extends SimpleContainer implements IServerContainer { $config = $c->getConfig(); return new \OC\BackgroundJob\JobList($c->getDatabaseConnection(), $config); }); + $this->registerService('Router', function ($c){ + /** + * @var Server $c + */ + $cacheFactory = $c->getMemCacheFactory(); + if ($cacheFactory->isAvailable()) { + $router = new \OC\Route\CachingRouter($cacheFactory->create('route')); + } else { + $router = new \OC\Route\Router(); + } + return $router; + }); } /** @@ -221,15 +240,23 @@ class Server extends SimpleContainer implements IServerContainer { * @return \OCP\Files\Folder */ function getUserFolder() { - - $dir = '/files'; + $dir = '/' . \OCP\User::getUser(); $root = $this->getRootFolder(); $folder = null; + if(!$root->nodeExists($dir)) { $folder = $root->newFolder($dir); } else { $folder = $root->get($dir); } + + $dir = '/files'; + if(!$folder->nodeExists($dir)) { + $folder = $folder->newFolder($dir); + } else { + $folder = $folder->get($dir); + } + return $folder; } @@ -290,7 +317,7 @@ class Server extends SimpleContainer implements IServerContainer { /** * get an L10N instance - * @param $app string appid + * @param string $app appid * @return \OC_L10N */ function getL10N($app) { @@ -323,7 +350,7 @@ class Server extends SimpleContainer implements IServerContainer { /** * Returns an \OCP\CacheFactory instance * - * @return \OCP\CacheFactory + * @return \OCP\ICacheFactory */ function getMemCacheFactory() { return $this->query('MemCacheFactory'); @@ -364,4 +391,13 @@ class Server extends SimpleContainer implements IServerContainer { function getJobList(){ return $this->query('JobList'); } + + /** + * Returns a router for generating and matching urls + * + * @return \OCP\Route\IRouter + */ + function getRouter(){ + return $this->query('Router'); + } } diff --git a/lib/private/session/internal.php b/lib/private/session/internal.php index a7c9e2fdefd..42ec9606dc9 100644 --- a/lib/private/session/internal.php +++ b/lib/private/session/internal.php @@ -26,8 +26,7 @@ class Internal extends Memory { } public function __destruct() { - $_SESSION = array_merge($_SESSION, $this->data); - session_write_close(); + $this->close(); } /** @@ -47,4 +46,15 @@ class Internal extends Memory { @session_start(); $this->data = $_SESSION = array(); } + + public function close() { + $_SESSION = array_merge($_SESSION, $this->data); + session_write_close(); + + parent::close(); + } + + public function reopen() { + throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.'); + } } diff --git a/lib/private/session/memory.php b/lib/private/session/memory.php index 1b9ac452575..1497c0f8928 100644 --- a/lib/private/session/memory.php +++ b/lib/private/session/memory.php @@ -28,6 +28,7 @@ class Memory extends Session { * @param integer $value */ public function set($key, $value) { + $this->validateSession(); $this->data[$key] = $value; } @@ -54,10 +55,29 @@ class Memory extends Session { * @param string $key */ public function remove($key) { + $this->validateSession(); unset($this->data[$key]); } public function clear() { $this->data = array(); } + + /** + * 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 as allowed'); + } + } } diff --git a/lib/private/session/session.php b/lib/private/session/session.php index fe160faa267..6f6c804f384 100644 --- a/lib/private/session/session.php +++ b/lib/private/session/session.php @@ -13,6 +13,11 @@ 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 @@ -49,4 +54,11 @@ abstract class Session implements \ArrayAccess, ISession { public function offsetUnset($offset) { $this->remove($offset); } + + /** + * Close the session and release the lock + */ + public function close() { + $this->sessionClosed = true; + } } diff --git a/lib/private/setup.php b/lib/private/setup.php index 7bf75be0165..4889f603332 100644 --- a/lib/private/setup.php +++ b/lib/private/setup.php @@ -65,6 +65,7 @@ class OC_Setup { OC_Config::setValue('passwordsalt', $salt); //write the config file + OC_Config::setValue('trusted_domains', array(OC_Request::serverHost())); OC_Config::setValue('datadirectory', $datadir); OC_Config::setValue('dbtype', $dbtype); OC_Config::setValue('version', implode('.', OC_Util::getVersion())); @@ -105,9 +106,14 @@ class OC_Setup { //guess what this does OC_Installer::installShippedApps(); - //create htaccess files for apache hosts + // create empty file in data dir, so we can later find + // out that this is indeed an ownCloud data directory + file_put_contents(OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data').'/.ocdata', ''); + + // Update htaccess files for apache hosts if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) { - self::createHtaccess(); + self::updateHtaccess(); + self::protectDataDirectory(); } //and we are done @@ -118,52 +124,13 @@ class OC_Setup { } /** - * create .htaccess files for apache hosts + * Append the correct ErrorDocument path for Apache hosts */ - private static function createHtaccess() { - $content = "<IfModule mod_fcgid.c>\n"; - $content.= "<IfModule mod_setenvif.c>\n"; - $content.= "<IfModule mod_headers.c>\n"; - $content.= "SetEnvIfNoCase ^Authorization$ \"(.+)\" XAUTHORIZATION=$1\n"; - $content.= "RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION\n"; - $content.= "</IfModule>\n"; - $content.= "</IfModule>\n"; - $content.= "</IfModule>\n"; + public static function updateHtaccess() { + $content = "\n"; $content.= "ErrorDocument 403 ".OC::$WEBROOT."/core/templates/403.php\n";//custom 403 error page - $content.= "ErrorDocument 404 ".OC::$WEBROOT."/core/templates/404.php\n";//custom 404 error page - $content.= "<IfModule mod_php5.c>\n"; - $content.= "php_value upload_max_filesize 512M\n";//upload limit - $content.= "php_value post_max_size 512M\n"; - $content.= "php_value memory_limit 512M\n"; - $content.= "php_value mbstring.func_overload 0\n"; - $content.= "<IfModule env_module>\n"; - $content.= " SetEnv htaccessWorking true\n"; - $content.= "</IfModule>\n"; - $content.= "</IfModule>\n"; - $content.= "<IfModule mod_rewrite.c>\n"; - $content.= "RewriteEngine on\n"; - $content.= "RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]\n"; - $content.= "RewriteRule ^.well-known/host-meta /public.php?service=host-meta [QSA,L]\n"; - $content.= "RewriteRule ^.well-known/carddav /remote.php/carddav/ [R]\n"; - $content.= "RewriteRule ^.well-known/caldav /remote.php/caldav/ [R]\n"; - $content.= "RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L]\n"; - $content.= "RewriteRule ^remote/(.*) remote.php [QSA,L]\n"; - $content.= "</IfModule>\n"; - $content.= "<IfModule mod_mime.c>\n"; - $content.= "AddType image/svg+xml svg svgz\n"; - $content.= "AddEncoding gzip svgz\n"; - $content.= "</IfModule>\n"; - $content.= "<IfModule dir_module>\n"; - $content.= "DirectoryIndex index.php index.html\n"; - $content.= "</IfModule>\n"; - $content.= "AddDefaultCharset utf-8\n"; - $content.= "Options -Indexes\n"; - $content.= "<IfModule pagespeed_module>\n"; - $content.= "ModPagespeed Off\n"; - $content.= "</IfModule>\n"; - @file_put_contents(OC::$SERVERROOT.'/.htaccess', $content); //supress errors in case we don't have permissions for it - - self::protectDataDirectory(); + $content.= "ErrorDocument 404 ".OC::$WEBROOT."/core/templates/404.php";//custom 404 error page + @file_put_contents(OC::$SERVERROOT.'/.htaccess', $content, FILE_APPEND); //suppress errors in case we don't have permissions for it } public static function protectDataDirectory() { diff --git a/lib/private/setup/postgresql.php b/lib/private/setup/postgresql.php index 89d328ada19..4d0c9b52a4d 100644 --- a/lib/private/setup/postgresql.php +++ b/lib/private/setup/postgresql.php @@ -10,13 +10,20 @@ class PostgreSQL extends AbstractDatabase { $e_user = addslashes($this->dbuser); $e_password = addslashes($this->dbpassword); + // Fix database with port connection + if(strpos($e_host, ':')) { + list($e_host, $port)=explode(':', $e_host, 2); + } else { + $port=false; + } + //check if the database user has admin rights - $connection_string = "host='$e_host' dbname=postgres user='$e_user' password='$e_password'"; + $connection_string = "host='$e_host' dbname=postgres user='$e_user' port='$port' password='$e_password'"; $connection = @pg_connect($connection_string); if(!$connection) { // Try if we can connect to the DB with the specified name $e_dbname = addslashes($this->dbname); - $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' password='$e_password'"; + $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' port='$port' password='$e_password'"; $connection = @pg_connect($connection_string); if(!$connection) @@ -63,7 +70,14 @@ class PostgreSQL extends AbstractDatabase { $e_user = addslashes($this->dbuser); $e_password = addslashes($this->dbpassword); - $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' password='$e_password'"; + // Fix database with port connection + if(strpos($e_host, ':')) { + list($e_host, $port)=explode(':', $e_host, 2); + } else { + $port=false; + } + + $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' port='$port' password='$e_password'"; $connection = @pg_connect($connection_string); if(!$connection) { throw new \DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'), diff --git a/lib/private/share/constants.php b/lib/private/share/constants.php new file mode 100644 index 00000000000..4c398c43c2d --- /dev/null +++ b/lib/private/share/constants.php @@ -0,0 +1,44 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle + * @copyright 2014 Bjoern Schiessle <schiessle@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/>. + */ + +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; // ToDo Check if it is still in use otherwise remove it + const SHARE_TYPE_CONTACT = 5; // ToDo Check if it is still in use otherwise remove it + const SHARE_TYPE_REMOTE = 6; // ToDo Check if it is still in use otherwise remove it + + const FORMAT_NONE = -1; + const FORMAT_STATUSES = -2; + const FORMAT_SOURCES = -3; // ToDo Check if it is still in use otherwise remove it + + const TOKEN_LENGTH = 32; // see db_structure.xml + + protected static $shareTypeUserAndGroups = -1; + protected static $shareTypeGroupUserUnique = 2; + protected static $backends = array(); + protected static $backendTypes = array(); + protected static $isResharingAllowed; +} diff --git a/lib/private/share/helper.php b/lib/private/share/helper.php new file mode 100644 index 00000000000..515ec85909a --- /dev/null +++ b/lib/private/share/helper.php @@ -0,0 +1,251 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle + * @copyright 2014 Bjoern Schiessle <schiessle@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/>. + */ + +namespace OC\Share; + +class Helper extends \OC\Share\Constants { + + /** + * Generate a unique target for the item + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param string User that is the owner of shared item + * @param string The suggested target originating from a reshare (optional) + * @param int The id of the parent group share (optional) + * @return string Item target + */ + public static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $suggestedTarget = null, $groupParent = null) { + $backend = \OC\Share\Share::getBackend($itemType); + if ($shareType == self::SHARE_TYPE_LINK) { + if (isset($suggestedTarget)) { + return $suggestedTarget; + } + return $backend->generateTarget($itemSource, false); + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_target'; + $columnSource = 'file_source'; + } else { + $column = 'item_target'; + $columnSource = 'item_source'; + } + if ($shareType == self::SHARE_TYPE_USER) { + // Share with is a user, so set share type to user and groups + $shareType = self::$shareTypeUserAndGroups; + $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); + } else { + $userAndGroups = false; + } + $exclude = null; + // Backend has 3 opportunities to generate a unique target + for ($i = 0; $i < 2; $i++) { + // Check if suggested target exists first + if ($i == 0 && isset($suggestedTarget)) { + $target = $suggestedTarget; + } else { + if ($shareType == self::SHARE_TYPE_GROUP) { + $target = $backend->generateTarget($itemSource, false, $exclude); + } else { + $target = $backend->generateTarget($itemSource, $shareWith, $exclude); + } + if (is_array($exclude) && in_array($target, $exclude)) { + break; + } + } + // Check if target already exists + $checkTarget = \OC\Share\Share::getItems($itemType, $target, $shareType, $shareWith); + if (!empty($checkTarget)) { + foreach ($checkTarget as $item) { + // Skip item if it is the group parent row + if (isset($groupParent) && $item['id'] == $groupParent) { + if (count($checkTarget) == 1) { + return $target; + } else { + continue; + } + } + if ($item['uid_owner'] == $uidOwner) { + if ($itemType == 'file' || $itemType == 'folder') { + $meta = \OC\Files\Filesystem::getFileInfo($itemSource); + if ($item['file_source'] == $meta['fileid']) { + return $target; + } + } else if ($item['item_source'] == $itemSource) { + return $target; + } + } + } + if (!isset($exclude)) { + $exclude = array(); + } + // Find similar targets to improve backend's chances to generate a unqiue target + if ($userAndGroups) { + if ($column == 'file_target') { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' + .' WHERE `item_type` IN (\'file\', \'folder\')' + .' AND `share_type` IN (?,?,?)' + .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); + $result = $checkTargets->execute(array(self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, + self::$shareTypeGroupUserUnique)); + } else { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' + .' WHERE `item_type` = ? AND `share_type` IN (?,?,?)' + .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); + $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER, + self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique)); + } + } else { + if ($column == 'file_target') { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' + .' WHERE `item_type` IN (\'file\', \'folder\')' + .' AND `share_type` = ? AND `share_with` = ?'); + $result = $checkTargets->execute(array(self::SHARE_TYPE_GROUP, $shareWith)); + } else { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' + .' WHERE `item_type` = ? AND `share_type` = ? AND `share_with` = ?'); + $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith)); + } + } + while ($row = $result->fetchRow()) { + $exclude[] = $row[$column]; + } + } else { + return $target; + } + } + } + $message = 'Sharing backend registered for '.$itemType.' did not generate a unique target for '.$itemSource; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + /** + * Delete all reshares of an item + * @param int Id of item to delete + * @param bool If true, exclude the parent from the delete (optional) + * @param string The user that the parent was shared with (optinal) + */ + public static function delete($parent, $excludeParent = false, $uidOwner = null) { + $ids = array($parent); + $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 + if (count($ids) == 1 && isset($uidOwner)) { + $query = \OC_DB::prepare('SELECT `id`, `uid_owner`, `item_type`, `item_target`, `parent`' + .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.') AND `uid_owner` = ?'); + $result = $query->execute(array($uidOwner)); + } else { + $query = \OC_DB::prepare('SELECT `id`, `item_type`, `item_target`, `parent`, `uid_owner`' + .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.')'); + $result = $query->execute(); + } + // Reset parents array, only go through loop again if items are found + $parents = array(); + while ($item = $result->fetchRow()) { + // Search for a duplicate parent share, this occurs when an + // item is shared to the same user through a group and user or the + // same item is shared by different users + $userAndGroups = array_merge(array($item['uid_owner']), \OC_Group::getUserGroups($item['uid_owner'])); + $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' + .' WHERE `item_type` = ?' + .' AND `item_target` = ?' + .' AND `share_type` IN (?,?,?)' + .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')' + .' AND `uid_owner` != ? AND `id` != ?'); + $duplicateParent = $query->execute(array($item['item_type'], $item['item_target'], + self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique, + $item['uid_owner'], $item['parent']))->fetchRow(); + if ($duplicateParent) { + // Change the parent to the other item id if share permission is granted + if ($duplicateParent['permissions'] & \OCP\PERMISSION_SHARE) { + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` = ?'); + $query->execute(array($duplicateParent['id'], $item['id'])); + continue; + } + } + $ids[] = $item['id']; + $parents[] = $item['id']; + } + } + if ($excludeParent) { + unset($ids[0]); + } + if (!empty($ids)) { + $ids = "'".implode("','", $ids)."'"; + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$ids.')'); + $query->execute(); + } + } + + /** + * @brief get default expire settings defined by the admin + * @return array contains 'defaultExpireDateSet', 'enforceExpireDate', 'expireAfterDays' + */ + public static function getDefaultExpireSetting() { + + $defaultExpireSettings = array('defaultExpireDateSet' => false); + + // get default expire settings + $defaultExpireDate = \OC_Appconfig::getValue('core', 'shareapi_default_expire_date', 'no'); + if ($defaultExpireDate === 'yes') { + $enforceExpireDate = \OC_Appconfig::getValue('core', 'shareapi_enforce_expire_date', 'no'); + $defaultExpireSettings['defaultExpireDateSet'] = true; + $defaultExpireSettings['expireAfterDays'] = (int)\OC_Appconfig::getValue('core', 'shareapi_expire_after_n_days', '7'); + $defaultExpireSettings['enforceExpireDate'] = $enforceExpireDate === 'yes' ? true : false; + } + + return $defaultExpireSettings; + } + + /** + * @brief 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; + + if (isset($defaultExpireSettings['defaultExpireDateSet']) && $defaultExpireSettings['defaultExpireDateSet']) { + $expires = $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 ($expires && isset($defaultExpireSettings['enforceExpireDate']) && $defaultExpireSettings['enforceExpireDate']) { + $expires = ($userExpireDate < $expires) ? $userExpireDate : $expires; + } else { + $expires = $userExpireDate; + } + } + + return $expires; + } +} diff --git a/lib/private/share/hooks.php b/lib/private/share/hooks.php new file mode 100644 index 00000000000..a33c71eedd2 --- /dev/null +++ b/lib/private/share/hooks.php @@ -0,0 +1,108 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle + * @copyright 2014 Bjoern Schiessle <schiessle@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/>. + */ + +namespace OC\Share; + +class Hooks extends \OC\Share\Constants { + /** + * Function that is called after a user is deleted. Cleans up the shares of that user. + * @param array arguments + */ + public static function post_deleteUser($arguments) { + // Delete any items shared with the deleted user + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`' + .' WHERE `share_with` = ? AND `share_type` = ? OR `share_type` = ?'); + $result = $query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique)); + // Delete any items the deleted user shared + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?'); + $result = $query->execute(array($arguments['uid'])); + while ($item = $result->fetchRow()) { + Helper::delete($item['id']); + } + } + + /** + * Function that is called after a user is added to a group. + * TODO what does it do? + * @param array arguments + */ + public static function post_addToGroup($arguments) { + // Find the group shares and check if the user needs a unique target + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'); + $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid'])); + $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 (?,?,?,?,?,?,?,?,?,?,?)'); + while ($item = $result->fetchRow()) { + if ($item['item_type'] == 'file' || $item['item_type'] == 'file') { + $itemTarget = null; + } else { + $itemTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, + $arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']); + } + if (isset($item['file_source'])) { + $fileTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, + $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']); + } else { + $fileTarget = null; + } + // Insert an extra row for the group share if the item or file target is unique for this user + if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) { + $query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'], + self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'], + $item['stime'], $item['file_source'], $fileTarget)); + \OC_DB::insertid('*PREFIX*share'); + } + } + } + + /** + * Function that is called after a user is removed from a group. Shares are cleaned up. + * @param array arguments + */ + public static function post_removeFromGroup($arguments) { + $sql = 'SELECT `id`, `share_type` FROM `*PREFIX*share`' + .' WHERE (`share_type` = ? AND `share_with` = ?) OR (`share_type` = ? AND `share_with` = ?)'; + $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'], + self::$shareTypeGroupUserUnique, $arguments['uid'])); + while ($item = $result->fetchRow()) { + if ($item['share_type'] == self::SHARE_TYPE_GROUP) { + // Delete all reshares by this user of the group share + Helper::delete($item['id'], true, $arguments['uid']); + } else { + Helper::delete($item['id']); + } + } + } + + /** + * Function that is called after a group is removed. Cleans up the shares to that group. + * @param array arguments + */ + public static function post_deleteGroup($arguments) { + $sql = 'SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'; + $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'])); + while ($item = $result->fetchRow()) { + Helper::delete($item['id']); + } + } + +} diff --git a/lib/private/share/mailnotifications.php b/lib/private/share/mailnotifications.php index 360376294cc..4799db52330 100644 --- a/lib/private/share/mailnotifications.php +++ b/lib/private/share/mailnotifications.php @@ -30,7 +30,6 @@ class MailNotifications { /** * - * @param string $recipient user id * @param string $sender user id (if nothing is set we use the currently logged-in user) */ public function __construct($sender = null) { @@ -97,7 +96,7 @@ class MailNotifications { try { \OCP\Util::sendMail($to, $recipientDisplayName, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail); } catch (\Exception $e) { - \OCP\Util::writeLog('sharing', "Can't send mail to inform the user abaut an internal share: " . $e->getMessage() , \OCP\Util::ERROR); + \OCP\Util::writeLog('sharing', "Can't send mail to inform the user about an internal share: " . $e->getMessage() , \OCP\Util::ERROR); $noMail[] = $recipientDisplayName; } } @@ -109,23 +108,26 @@ class MailNotifications { /** * @brief inform recipient about public link share * - * @param string recipient recipient email address + * @param string $recipient recipient email address * @param string $filename the shared file * @param string $link the public link * @param int $expiration expiration date (timestamp) - * @return mixed $result true or error message + * @return array $result of failed recipients */ public function sendLinkShareMail($recipient, $filename, $link, $expiration) { $subject = (string)$this->l->t('%s shared »%s« with you', array($this->senderDisplayName, $filename)); list($htmlMail, $alttextMail) = $this->createMailBody($filename, $link, $expiration); - try { - \OCP\Util::sendMail($recipient, $recipient, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail); - } catch (\Exception $e) { - \OCP\Util::writeLog('sharing', "Can't send mail with public link: " . $e->getMessage(), \OCP\Util::ERROR); - return $e->getMessage(); + $rs = explode(' ', $recipient); + $failed = array(); + foreach ($rs as $r) { + try { + \OCP\Util::sendMail($r, $r, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail); + } catch (\Exception $e) { + \OCP\Util::writeLog('sharing', "Can't send mail with public link to $r: " . $e->getMessage(), \OCP\Util::ERROR); + $failed[] = $r; + } } - - return true; + return $failed; } /** diff --git a/lib/private/share/share.php b/lib/private/share/share.php new file mode 100644 index 00000000000..b12d62e8439 --- /dev/null +++ b/lib/private/share/share.php @@ -0,0 +1,1746 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle, Michael Gapczynski + * @copyright 2012 Michael Gapczynski <mtgap@owncloud.com> + * 2014 Bjoern Schiessle <schiessle@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/>. + */ + +namespace OC\Share; + +/** + * 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 \OC\Share\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 Item type + * @param string Backend class + * @param string (optional) Depends on item type + * @param array (optional) List of supported file extensions if this item type depends on files + * @return Returns 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) { + \OC_Util::addScript('core', 'share'); + \OC_Util::addStyle('core', 'share'); + } + return true; + } + \OC_Log::write('OCP\Share', + 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class'] + .' is already registered for '.$itemType, + \OC_Log::WARN); + } + return false; + } + + /** + * Check if the Share API is enabled + * @return Returns true if enabled or false + * + * The Share API is enabled by default if not configured + */ + public static function isEnabled() { + if (\OC_Appconfig::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 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 + * @return array + * @note $path needs to be relative to user data dir, e.g. 'file.txt' + * not '/admin/data/file.txt' + */ + public static function getUsersSharingFile($path, $ownerUser, $includeOwner = false, $returnUserPaths = false) { + + $shares = $sharePaths = $fileTargets = array(); + $publicShare = false; + $source = -1; + $cache = false; + + $view = new \OC\Files\View('/' . $ownerUser . '/files'); + if ($view->file_exists($path)) { + $meta = $view->getFileInfo($path); + $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']); + } + + while ($source !== -1) { + // Fetch all shares with another user + $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)); + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::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($result), \OC_Log::ERROR); + } else { + while ($row = $result->fetchRow()) { + $usersInGroup = \OC_Group::usersInGroup($row['share_with']); + $shares = array_merge($shares, $usersInGroup); + if ($returnUserPaths) { + foreach ($usersInGroup as $user) { + $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` = ? AND `item_type` IN (\'file\', \'folder\')' + ); + + $result = $query->execute(array($source, self::SHARE_TYPE_LINK)); + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); + } else { + if ($result->fetchRow()) { + $publicShare = true; + } + } + } + + // let's get the parent for the next round + $meta = $cache->get((int)$source); + if($meta !== false) { + $source = (int)$meta['parent']; + } else { + $source = -1; + } + } + + // Include owner in list of users, if requested + if ($includeOwner) { + $shares[] = $ownerUser; + if ($returnUserPaths) { + $sharePaths[$ownerUser] = $path; + } + } + + 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($result), \OC_Log::ERROR); + } else { + while ($row = $result->fetchRow()) { + foreach ($fileTargets[$row['fileid']] as $uid => $shareData) { + $sharedPath = $shareData['file_target']; + $sharedPath .= substr($path, strlen($row['path']) -5); + $sharePaths[$uid] = $sharedPath; + } + } + } + } + + return $sharePaths; + } + + return array("users" => array_unique($shares), "public" => $publicShare); + } + + /** + * Get the items of item type shared with the current user + * @param string Item type + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters (optional) + * @param int Number of items to return (optional) Returns all by default + * @param bool include collections (optional) + * @return 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 Item type + * @param sting 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 Number of items to return (optional) Returns all by default + * @param bool include collections (optional) + * @return 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 bool include collections (optional) + * @return 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 user to whom the item was shared + * @return array Return list of items with file_target, permissions and expiration + */ + public static function getItemSharedWithUser($itemType, $itemSource, $user) { + + $shares = array(); + + // first check if there is a db entry for the specific user + $query = \OC_DB::prepare( + 'SELECT `file_target`, `permissions`, `expiration` + FROM + `*PREFIX*share` + WHERE + `item_source` = ? AND `item_type` = ? AND `share_with` = ?' + ); + + $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, $user)); + + while ($row = $result->fetchRow()) { + $shares[] = $row; + } + + //if didn't found a result than let's look for a group share. + if(empty($shares)) { + $groups = \OC_Group::getUserGroups($user); + + $query = \OC_DB::prepare( + 'SELECT `file_target`, `permissions`, `expiration` + FROM + `*PREFIX*share` + WHERE + `item_source` = ? AND `item_type` = ? AND `share_with` in (?)' + ); + + $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, implode(',', $groups))); + + while ($row = $result->fetchRow()) { + $shares[] = $row; + } + } + + return $shares; + + } + + /** + * Get the item of item type shared with the current user by source + * @param string Item type + * @param string Item source + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters + * @param bool include collections + * @return Return depends on format + */ + public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, + $parameters = null, $includeCollections = false) { + return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, + $parameters, 1, $includeCollections, true); + } + + /** + * Get the item of item type shared by a link + * @param string Item type + * @param string Item source + * @param string Owner of link + * @return Item + */ + 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 + * @return array | bool 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 (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::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 $linkItem + * @return $fileOwner + */ + 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 Item type + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters + * @param int Number of items to return (optional) Returns all by default + * @param bool include collections + * @return 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 Item type + * @param string Item source + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters + * @param bool include collections + * @return 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 Item type + * @param string Item source + * @param string Owner + * @param bool Include collections + * @praram bool check expire date + * @return 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) { + $users = array_merge($users, \OC_Group::usersInGroup($item['share_with'])); + } + } + } + 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 null $itemSourceName + * @throws \Exception + * @internal param \OCP\Item $string type + * @internal param \OCP\Item $string source + * @internal param \OCP\SHARE_TYPE_USER $int , SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @internal param \OCP\User $string or group the item is being shared with + * @internal param \OCP\CRUDS $int permissions + * @return bool|string Returns true on success or false on failure, Returns token on success for links + */ + public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null) { + $uidOwner = \OC_User::getUser(); + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + $l = \OC_L10N::get('lib'); + + if (is_null($itemSourceName)) { + $itemSourceName = $itemSource; + } + + + // verify that the file exists before we try to share it + if ($itemType === 'file' or $itemType === 'folder') { + $path = \OC\Files\Filesystem::getPath($itemSource); + 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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR); + 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->getAll(); + foreach ($mounts as $mountPoint => $mount) { + if ($mount->getStorage() instanceof \OC\Files\Storage\Shared && strpos($mountPoint, $path) === 0) { + $message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + } + } + + // Verify share type and sharing conditions are met + if ($shareType === self::SHARE_TYPE_USER) { + if ($shareWith == $uidOwner) { + $message = 'Sharing %s failed, because the user %s is the item owner'; + $message_t = $l->t('Sharing %s failed, because the user %s is the item owner', array($itemSourceName, $shareWith)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR); + 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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR); + throw new \Exception($message_t); + } + if ($sharingPolicy == 'groups_only') { + $inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith)); + 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($itemSourceName, $shareWith, $uidOwner)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith, $uidOwner), \OC_Log::ERROR); + 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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR); + throw new \Exception($message_t); + } + } + } else if ($shareType === self::SHARE_TYPE_GROUP) { + if (!\OC_Group::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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR); + throw new \Exception($message_t); + } + if ($sharingPolicy == 'groups_only' && !\OC_Group::inGroup($uidOwner, $shareWith)) { + $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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OC_Log::ERROR); + 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)) { + // Only allow the same share to occur again if it is the same + // owner and is not a group 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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR); + throw new \Exception($message_t); + } + } + // Convert share with into an array with the keys group and users + $group = $shareWith; + $shareWith = array(); + $shareWith['group'] = $group; + $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner)); + } else if ($shareType === self::SHARE_TYPE_LINK) { + if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') { + // when updating a link share + 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']); + } + + // Generate hash of password - same method as user passwords + if (isset($shareWith)) { + $forcePortable = (CRYPT_BLOWFISH != 1); + $hasher = new \PasswordHash(8, $forcePortable); + $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', '')); + } 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']; + } + } + + // Generate token + if (isset($oldToken)) { + $token = $oldToken; + } else { + $token = \OC_Util::generateRandomBytes(self::TOKEN_LENGTH); + } + $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, + null, $token, $itemSourceName); + 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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR); + throw new \Exception($message_t); + return false; + } 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)); + \OC_Log::write('OCP\Share', sprintf($message, $shareType, $itemSource), \OC_Log::ERROR); + throw new \Exception($message_t); + } + // Put the item into the database + return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName); + } + + /** + * Unshare an item from a user, group, or delete a private link + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @return Returns true on success or false on failure + */ + public static function unshare($itemType, $itemSource, $shareType, $shareWith) { + $item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(),self::FORMAT_NONE, null, 1); + if (!empty($item)) { + self::unshareItem($item); + return true; + } + return false; + } + + /** + * Unshare an item from all users, groups, and remove all links + * @param string Item type + * @param string Item source + * @return Returns 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 Item type + * @param string Item target + * @return Returns true on success or false on failure + * + * Unsharing from self is not allowed for items inside collections + */ + public static function unshareFromSelf($itemType, $itemTarget) { + $item = self::getItemSharedWith($itemType, $itemTarget); + if (!empty($item)) { + if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) { + // Insert an extra row for the group share and set permission + // to 0 to prevent it from showing up for the user + $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($item['item_type'], $item['item_source'], $item['item_target'], + $item['id'], self::$shareTypeGroupUserUnique, + \OC_User::getUser(), $item['uid_owner'], 0, $item['stime'], $item['file_source'], + $item['file_target'])); + \OC_DB::insertid('*PREFIX*share'); + // Delete all reshares by this user of the group share + Helper::delete($item['id'], true, \OC_User::getUser()); + } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) { + // Set permission to 0 to prevent it from showing up for the user + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); + $query->execute(array(0, $item['id'])); + Helper::delete($item['id'], true); + } else { + Helper::delete($item['id']); + } + return true; + } + return false; + } + /** + * 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 bool $status + */ + public static function setSendMailStatus($itemType, $itemSource, $shareType, $status) { + $status = $status ? 1 : 0; + + $query = \OC_DB::prepare( + 'UPDATE `*PREFIX*share` + SET `mail_send` = ? + WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ?'); + + $result = $query->execute(array($status, $itemType, $itemSource, $shareType)); + + if($result === false) { + \OC_Log::write('OCP\Share', 'Couldn\'t set send mail status', \OC_Log::ERROR); + } + } + + /** + * Set the permissions of an item for a specific user or group + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param int CRUDS permissions + * @return Returns true on success or false on failure + */ + public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) { + $l = \OC_L10N::get('lib'); + if ($item = 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($item['parent'])) { + $query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*share` WHERE `id` = ?', 1); + $result = $query->execute(array($item['parent']))->fetchRow(); + 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())); + \OC_Log::write('OCP\Share', sprintf($message, $itemSource, \OC_User::getUser()), \OC_Log::ERROR); + throw new \Exception($message_t); + } + } + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); + $query->execute(array($permissions, $item['id'])); + 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' => $item['path'], + )); + } + // Check if permissions were removed + if ($item['permissions'] & ~$permissions) { + // If share permission is removed all reshares must be deleted + if (($item['permissions'] & \OCP\PERMISSION_SHARE) && (~$permissions & \OCP\PERMISSION_SHARE)) { + Helper::delete($item['id'], true); + } else { + $ids = array(); + $parents = array($item['id']); + while (!empty($parents)) { + $parents = "'".implode("','", $parents)."'"; + $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' + .' WHERE `parent` IN ('.$parents.')'); + $result = $query->execute(); + // Reset parents array, only go through loop again if + // items are found that need permissions removed + $parents = array(); + while ($item = $result->fetchRow()) { + // 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']; + } + } + } + // 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_Config::getValue( "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)); + } + } + } + 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)); + + \OC_Log::write('OCP\Share', sprintf($message, $itemSource), \OC_Log::ERROR); + throw new \Exception($message_t); + } + + /** + * Set expiration date for a share + * @param string $itemType + * @param string $itemSource + * @param string $date expiration date + * @return \OCP\Share_Backend + */ + public static function setExpirationDate($itemType, $itemSource, $date) { + $user = \OC_User::getUser(); + $items = self::getItems($itemType, $itemSource, null, null, $user, self::FORMAT_NONE, null, -1, false); + if (!empty($items)) { + if ($date == '') { + $date = null; + } else { + $date = new \DateTime($date); + } + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `id` = ?'); + $query->bindValue(1, $date, 'datetime'); + foreach ($items as $item) { + $query->bindValue(2, (int) $item['id']); + $query->execute(); + \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'date' => $date, + 'uidOwner' => $user + )); + } + return true; + } + return false; + } + + /** + * Checks whether a share has expired, calls unshareItem() if yes. + * @param array $item Share data (usually database row) + * @return bool True if item was expired, false otherwise. + */ + protected static function expireItem(array $item) { + + // get default expire settings + $defaultSettings = Helper::getDefaultExpireSetting(); + // calculate expire date + if (!empty($item['expiration'])) { + $userDefinedExpire = new \DateTime($item['expiration']); + $userDefinedExpireTimestamp = $userDefinedExpire->getTimestamp(); + } else { + $userDefinedExpireTimestamp = null; + } + $expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $userDefinedExpireTimestamp); + + if (is_int($expires)) { + $now = time(); + if ($now > $expires) { + self::unshareItem($item); + return true; + } + } + return false; + } + + /** + * Unshares a share given a share data array + * @param array $item Share data (usually database row) + * @return null + */ + protected static function unshareItem(array $item) { + // Pass all the vars we have for now, they may be useful + $hookParams = array( + 'itemType' => $item['item_type'], + 'itemSource' => $item['item_source'], + 'fileSource' => $item['file_source'], + 'shareType' => $item['share_type'], + 'shareWith' => $item['share_with'], + 'itemParent' => $item['parent'], + 'uidOwner' => $item['uid_owner'], + ); + + \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams + array( + 'fileSource' => $item['file_source'], + )); + Helper::delete($item['id']); + \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams); + } + + /** + * Get the backend class for the specified item type + * @param string $itemType + * @return \OCP\Share_Backend + */ + public static function getBackend($itemType) { + $l = \OC_L10N::get('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)); + \OC_Log::write('OCP\Share', sprintf($message, $class), \OC_Log::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)); + \OC_Log::write('OCP\Share', sprintf($message, $class), \OC_Log::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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemType), \OC_Log::ERROR); + throw new \Exception($message_t); + } + + /** + * Check if resharing is allowed + * @return Returns true if allowed or false + * + * Resharing is allowed by default if not configured + */ + private static function isResharingAllowed() { + if (!isset(self::$isResharingAllowed)) { + if (\OC_Appconfig::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 Item type + * @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 (!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 shared items from the database + * @param string Item type + * @param string Item source or target (optional) + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique + * @param string User or group the item is being shared with + * @param string User that is the owner of shared items (optional) + * @param int Format to convert items to with formatItems() + * @param mixed Parameters to pass to formatItems() + * @param int Number of items to return, -1 to return all matches (optional) + * @param bool Include collection item types (optional) + * @param bool TODO (optional) + * @prams bool check expire date + * @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 .= ' WHERE `file_target` IS NOT NULL'; + } + $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_Appconfig::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 (?,?,?)'; + $queryArgs[] = self::SHARE_TYPE_USER; + $queryArgs[] = self::SHARE_TYPE_GROUP; + $queryArgs[] = self::$shareTypeGroupUserUnique; + $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); + $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); + $where .= ' AND `share_with` IN ('.$placeholders.')'; + $queryArgs = array_merge($queryArgs, $userAndGroups); + // 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) { + $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) { + $placeholders = join(',', array_fill(0, count($collectionTypes), '?')); + $where .= ' OR `item_type` IN ('.$placeholders.'))'; + $queryArgs = array_merge($queryArgs, $collectionTypes); + } + } + if ($limit != -1 && !$includeCollections) { + if ($shareType == self::$shareTypeUserAndGroups) { + // 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'; + } + // 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 (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', + \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, + \OC_Log::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 ($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']; + // 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[$column]])) { + // Check if the same owner shared with the user twice + // through a group and user share - this is allowed + $id = $targets[$row[$column]]; + 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\PERMISSION_SHARE + && (int)$row['permissions'] & \OCP\PERMISSION_SHARE) { + $items[$row['id']] = $items[$id]; + $switchedItems[$id] = $row['id']; + unset($items[$id]); + $id = $row['id']; + } + // Combine the permissions for the item + $items[$id]['permissions'] |= (int)$row['permissions']; + continue; + } + } else { + $targets[$row[$column]] = $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 (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', 'Can\'t select parent: ' . + \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, + \OC_Log::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 ($mounts[$row['storage']]) { + $path = $mounts[$row['storage']]->getMountPoint().$row['path']; + $row['path'] = substr($path, $root); + } + } + } + if($checkExpireDate) { + if (self::expireItem($row)) { + continue; + } + } + // Check if resharing is allowed, if not remove share permission + if (isset($row['permissions']) && !self::isResharingAllowed()) { + $row['permissions'] &= ~\OCP\PERMISSION_SHARE; + } + // Add display names to result + if ( isset($row['share_with']) && $row['share_with'] != '') { + $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']); + } + if ( isset($row['uid_owner']) && $row['uid_owner'] != '') { + $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']); + } + + $items[$row['id']] = $row; + } + 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 && 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]); + } + } + if (!empty($collectionItems)) { + $items = array_merge($items, $collectionItems); + } + + return self::formatResult($items, $column, $backend, $format, $parameters); + } + + return array(); + } + + /** + * Put shared item into the database + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param string User that is the owner of shared item + * @param int CRUDS permissions + * @param bool|array Parent folder target (optional) + * @param string token (optional) + * @param string name of the source item (optional) + * @return bool Returns true on success or false on failure + */ + private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $permissions, $parentFolder = null, $token = null, $itemSourceName = null) { + $backend = self::getBackend($itemType); + $l = \OC_L10N::get('lib'); + // Check if this is a reshare + if ($checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true)) { + + // 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 %s failed, because the user %s is the original sharer', array($itemSourceName, $shareWith)); + + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR); + throw new \Exception($message_t); + } + // Check if share permissions is granted + if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\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)); + + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OC_Log::ERROR); + throw new \Exception($message_t); + } else { + // TODO Don't check if inside folder + $parent = $checkReshare['id']; + $itemSource = $checkReshare['item_source']; + $fileSource = $checkReshare['file_source']; + $suggestedItemTarget = $checkReshare['item_target']; + $suggestedFileTarget = $checkReshare['file_target']; + $filePath = $checkReshare['file_target']; + } + } else { + $message = 'Sharing %s failed, because resharing is not allowed'; + $message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName)); + + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR); + throw new \Exception($message_t); + } + } else { + $parent = null; + $suggestedItemTarget = null; + $suggestedFileTarget = null; + 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)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSource, $itemType), \OC_Log::ERROR); + throw new \Exception($message_t); + } + if ($backend instanceof \OCP\Share_Backend_File_Dependent) { + $filePath = $backend->getFilePath($itemSource, $uidOwner); + if ($itemType == 'file' || $itemType == 'folder') { + $fileSource = $itemSource; + } else { + $meta = \OC\Files\Filesystem::getFileInfo($filePath); + $fileSource = $meta['fileid']; + } + if ($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)); + + \OC_Log::write('OCP\Share', sprintf($message, $itemSource), \OC_Log::ERROR); + throw new \Exception($message_t); + } + } else { + $filePath = null; + $fileSource = null; + } + } + $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`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); + // Share with a group + if ($shareType == self::SHARE_TYPE_GROUP) { + $groupItemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], + $uidOwner, $suggestedItemTarget); + $run = true; + $error = ''; + \OC_Hook::emit('OCP\Share', 'pre_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $groupItemTarget, + 'shareType' => $shareType, + 'shareWith' => $shareWith['group'], + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'token' => $token, + 'run' => &$run, + 'error' => &$error + )); + + if ($run === false) { + throw new \Exception($error); + } + + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, + $shareWith['group'], $uidOwner, $suggestedFileTarget); + // Set group default file target for future use + $parentFolders[0]['folder'] = $groupFileTarget; + } else { + // Get group default file target + $groupFileTarget = $parentFolder[0]['folder'].$itemSource; + $parent = $parentFolder[0]['id']; + } + } else { + $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith['group'], + $uidOwner, $suggestedFileTarget); + } + } else { + $groupFileTarget = null; + } + $queriesToExecute = array(); + $queriesToExecute['groupShare'] = array($itemType, $itemSource, $groupItemTarget, $shareType, + $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget, $token, $parent); + // Loop through all users of this group in case we need to add an extra row + foreach ($shareWith['users'] as $uid) { + $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid, + $uidOwner, $suggestedItemTarget, $parent); + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, + $uidOwner, $suggestedFileTarget, $parent); + if ($fileTarget != $groupFileTarget) { + $parentFolders[$uid]['folder'] = $fileTarget; + } + } else if (isset($parentFolder[$uid])) { + $fileTarget = $parentFolder[$uid]['folder'].$itemSource; + $parent = $parentFolder[$uid]['id']; + } + } else { + $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, + $uid, $uidOwner, $suggestedFileTarget, $parent); + } + } else { + $fileTarget = null; + } + // Insert an extra row for the group share if the item or file target is unique for this user + if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) { + $queriesToExecute[] = array($itemType, $itemSource, $itemTarget, + self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), + $fileSource, $fileTarget, $token); + $id = \OC_DB::insertid('*PREFIX*share'); + } + } + $query->execute($queriesToExecute['groupShare']); + // Save this id, any extra rows for this group share will need to reference it + $parent = \OC_DB::insertid('*PREFIX*share'); + unset($queriesToExecute['groupShare']); + foreach ($queriesToExecute as $qe) { + $qe[] = $parent; + $query->execute($qe); + } + + \OC_Hook::emit('OCP\Share', 'post_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $groupItemTarget, + 'parent' => $parent, + 'shareType' => $shareType, + 'shareWith' => $shareWith['group'], + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'fileTarget' => $groupFileTarget, + 'id' => $parent, + 'token' => $token + )); + + if ($parentFolder === true) { + // Return parent folders to preserve file target paths for potential children + return $parentFolders; + } + } else { + $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $suggestedItemTarget); + $run = true; + $error = ''; + \OC_Hook::emit('OCP\Share', 'pre_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $itemTarget, + 'shareType' => $shareType, + 'shareWith' => $shareWith, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'token' => $token, + 'run' => &$run, + 'error' => &$error + )); + + if ($run === false) { + throw new \Exception($error); + } + + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, + $uidOwner, $suggestedFileTarget); + $parentFolders['folder'] = $fileTarget; + } else { + $fileTarget = $parentFolder['folder'].$itemSource; + $parent = $parentFolder['id']; + } + } else { + $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, + $suggestedFileTarget); + } + } else { + $fileTarget = null; + } + $query->execute(array($itemType, $itemSource, $itemTarget, $shareType, $shareWith, $uidOwner, + $permissions, time(), $fileSource, $fileTarget, $token, $parent)); + $id = \OC_DB::insertid('*PREFIX*share'); + \OC_Hook::emit('OCP\Share', 'post_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $itemTarget, + 'parent' => $parent, + 'shareType' => $shareType, + 'shareWith' => $shareWith, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'fileTarget' => $fileTarget, + 'id' => $id, + 'token' => $token + )); + if ($parentFolder === true) { + $parentFolders['id'] = $id; + // Return parent folder to preserve file target paths for potential children + return $parentFolders; + } + } + return true; + } + + /** + * 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 bool + */ + 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::$session->exists('public_link_authenticated') + && \OC::$session->get('public_link_authenticated') === $linkItem['id'] ) { + return true; + } + + return false; + } + + /** + * @breif construct select statement + * @param int $format + * @param bool $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`'; + } else { + $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`'; + } + } else { + if (isset($uidOwner)) { + if ($fileDependent) { + $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,' + . ' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,' + . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`'; + } else { + $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,' + . ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`'; + } + } else { + if ($fileDependent) { + if ($format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS || $format == \OC_Share_Backend_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`, ' + . '`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, ' + . '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `unencrypted_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`, `permissions`, `stime`, `expiration`, `token`, `storage`, `mail_send`'; + } + } + } + } + return $select; + } + + + /** + * @brief 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']; + } + } + + /** + * @brief format result + * @param array $items result + * @prams string $column is it a file share or a general share ('file_target' or 'item_target') + * @params \OCP\Share_Backend $backend sharing backend + * @param int $format + * @param array additional format parameters + * @return array formate 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) { + $statuses[$item[$column]]['link'] = true; + } else if (!isset($statuses[$item[$column]])) { + $statuses[$item[$column]]['link'] = false; + } + if ('file_target') { + $statuses[$item[$column]]['path'] = $item['path']; + } + } + return $statuses; + } else { + return $backend->formatItems($items, $format, $parameters); + } + } +} diff --git a/lib/private/subadmin.php b/lib/private/subadmin.php index 8cda7240ac9..5b6072987ad 100644 --- a/lib/private/subadmin.php +++ b/lib/private/subadmin.php @@ -32,8 +32,8 @@ class OC_SubAdmin{ /** * @brief add a SubAdmin - * @param $uid uid of the SubAdmin - * @param $gid gid of the group + * @param string $uid uid of the SubAdmin + * @param string $gid gid of the group * @return boolean */ public static function createSubAdmin($uid, $gid) { @@ -45,8 +45,8 @@ class OC_SubAdmin{ /** * @brief delete a SubAdmin - * @param $uid uid of the SubAdmin - * @param $gid gid of the group + * @param string $uid uid of the SubAdmin + * @param string $gid gid of the group * @return boolean */ public static function deleteSubAdmin($uid, $gid) { @@ -58,7 +58,7 @@ class OC_SubAdmin{ /** * @brief get groups of a SubAdmin - * @param $uid uid of the SubAdmin + * @param string $uid uid of the SubAdmin * @return array */ public static function getSubAdminsGroups($uid) { @@ -73,7 +73,7 @@ class OC_SubAdmin{ /** * @brief get SubAdmins of a group - * @param $gid gid of the group + * @param string $gid gid of the group * @return array */ public static function getGroupsSubAdmins($gid) { @@ -102,8 +102,8 @@ class OC_SubAdmin{ /** * @brief checks if a user is a SubAdmin of a group - * @param $uid uid of the subadmin - * @param $gid gid of the group + * @param string $uid uid of the subadmin + * @param string $gid gid of the group * @return bool */ public static function isSubAdminofGroup($uid, $gid) { @@ -118,7 +118,7 @@ class OC_SubAdmin{ /** * @brief checks if a user is a SubAdmin - * @param $uid uid of the subadmin + * @param string $uid uid of the subadmin * @return bool */ public static function isSubAdmin($uid) { @@ -138,8 +138,8 @@ class OC_SubAdmin{ /** * @brief checks if a user is a accessible by a subadmin - * @param $subadmin uid of the subadmin - * @param $user uid of the user + * @param string $subadmin uid of the subadmin + * @param string $user uid of the user * @return bool */ public static function isUserAccessible($subadmin, $user) { diff --git a/lib/private/template.php b/lib/private/template.php index c6851c6cc8d..3d18b52bac9 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -64,29 +64,6 @@ class OC_Template extends \OC\Template\Base { $this->path = $path; parent::__construct($template, $requesttoken, $l10n, $themeDefaults); - - // Some headers to enhance security - 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 - - // iFrame Restriction Policy - $xFramePolicy = OC_Config::getValue('xframe_restriction', true); - if($xFramePolicy) { - header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains - } - - // Content Security Policy - // If you change the standard policy, please also change it in config.sample.php - $policy = OC_Config::getValue('custom_csp_policy', - 'default-src \'self\'; ' - .'script-src \'self\' \'unsafe-eval\'; ' - .'style-src \'self\' \'unsafe-inline\'; ' - .'frame-src *; ' - .'img-src *; ' - .'font-src \'self\' data:; ' - .'media-src *'); - header('Content-Security-Policy:'.$policy); // Standard - } /** @@ -159,6 +136,7 @@ class OC_Template extends \OC\Template\Base { * @param string $theme * @param string $app * @param string $fext + * @return array */ protected function findTemplate($theme, $app, $name, $fext) { // Check if it is a app template or not. @@ -255,7 +233,7 @@ class OC_Template extends \OC\Template\Base { * @brief 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 string $parameters Parameters for the template + * @param array|string $parameters Parameters for the template * @return bool */ public static function printGuestPage( $application, $name, $parameters = array() ) { @@ -284,7 +262,6 @@ class OC_Template extends \OC\Template\Base { * print error page using Exception details * @param Exception $exception */ - public static function printExceptionErrorPage(Exception $exception) { $error_msg = $exception->getMessage(); if ($exception->getCode()) { diff --git a/lib/private/template/base.php b/lib/private/template/base.php index 232a29939cc..3d7c685c1cf 100644 --- a/lib/private/template/base.php +++ b/lib/private/template/base.php @@ -61,7 +61,7 @@ class Base { /** * @brief Assign variables * @param string $key key - * @param string $value value + * @param array|bool|integer|string $value value * @return bool * * This function assigns a variable. It can be accessed via $_[$key] in @@ -77,7 +77,7 @@ class Base { /** * @brief Appends a variable * @param string $key key - * @param string $value value + * @param mixed $value value * @return boolean|null * * This function assigns a variable in an array context. If the key already diff --git a/lib/private/template/functions.php b/lib/private/template/functions.php index a72d41f72da..3c42d441efa 100644 --- a/lib/private/template/functions.php +++ b/lib/private/template/functions.php @@ -7,16 +7,17 @@ */ /** - * Prints an XSS escaped string - * @param string $string the string which will be escaped and printed + * Prints a sanitized string + * @param string|array $string the string which will be escaped and printed */ function p($string) { print(OC_Util::sanitizeHTML($string)); } /** - * Prints an unescaped string - * @param string $string the string which will be printed as it is + * 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); diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index af17adb11c6..b7ac02a753d 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -1,8 +1,6 @@ <?php use Assetic\Asset\AssetCollection; use Assetic\Asset\FileAsset; -use Assetic\Asset\GlobAsset; -use Assetic\AssetManager; use Assetic\AssetWriter; use Assetic\Filter\CssRewriteFilter; @@ -66,7 +64,7 @@ class OC_TemplateLayout extends OC_Template { } $versionParameter = '?v=' . md5(implode(OC_Util::getVersion())); - $useAssetPipeline = OC_Config::getValue('asset-pipeline.enabled', false); + $useAssetPipeline = $this->isAssetPipelineEnabled(); if ($useAssetPipeline) { $this->append( 'jsfiles', OC_Helper::linkToRoute('js_config') . $versionParameter); @@ -99,6 +97,10 @@ class OC_TemplateLayout extends OC_Template { } } + /** + * @param $styles + * @return array + */ static public function findStylesheetFiles($styles) { // Read the selected theme from the config file $theme = OC_Util::getTheme(); @@ -113,6 +115,10 @@ class OC_TemplateLayout extends OC_Template { return $locator->getResources(); } + /** + * @param $scripts + * @return array + */ static public function findJavascriptFiles($scripts) { // Read the selected theme from the config file $theme = OC_Util::getTheme(); @@ -168,6 +174,10 @@ class OC_TemplateLayout extends OC_Template { $this->append('cssfiles', OC_Helper::linkTo('assets', "$cssHash.css")); } + /** + * @param $files + * @return string + */ private static function hashScriptNames($files) { $files = array_map(function ($item) { @@ -179,4 +189,33 @@ class OC_TemplateLayout extends OC_Template { sort($files); return hash('md5', implode('', $files)); } + + /** + * @return bool + */ + private function isAssetPipelineEnabled() { + // asset management enabled? + $useAssetPipeline = OC_Config::getValue('asset-pipeline.enabled', false); + if (!$useAssetPipeline) { + return false; + } + + // assets folder exists? + $assetDir = \OC::$SERVERROOT . '/assets'; + if (!is_dir($assetDir)) { + if (!mkdir($assetDir)) { + \OCP\Util::writeLog('assets', + "Folder <$assetDir> does not exist and/or could not be generated.", \OCP\Util::ERROR); + return false; + } + } + + // assets folder can be accessed? + if (!touch($assetDir."/.oc")) { + \OCP\Util::writeLog('assets', + "Folder <$assetDir> could not be accessed.", \OCP\Util::ERROR); + return false; + } + return $useAssetPipeline; + } } diff --git a/lib/private/updater.php b/lib/private/updater.php index 764a0f14120..7b09f580176 100644 --- a/lib/private/updater.php +++ b/lib/private/updater.php @@ -16,9 +16,6 @@ use OC\Hooks\BasicEmitter; * - maintenanceStart() * - maintenanceEnd() * - dbUpgrade() - * - filecacheStart() - * - filecacheProgress(int $percentage) - * - filecacheDone() * - failure(string $message) */ class Updater extends BasicEmitter { @@ -76,7 +73,9 @@ class Updater extends BasicEmitter { if ($xml == false) { return array(); } + $loadEntities = libxml_disable_entity_loader(true); $data = @simplexml_load_string($xml); + libxml_disable_entity_loader($loadEntities); $tmp = array(); $tmp['version'] = $data->version; @@ -92,6 +91,7 @@ class Updater extends BasicEmitter { /** * runs the update actions in maintenance mode, does not upgrade the source files + * except the main .htaccess file */ public function upgrade() { \OC_DB::enableCaching(false); @@ -102,13 +102,34 @@ class Updater extends BasicEmitter { $this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core')); } $this->emit('\OC\Updater', 'maintenanceStart'); + + // Update htaccess files for apache hosts + if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) { + \OC_Setup::updateHtaccess(); + } + + // 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(\OC_Config::getValue('datadirectory', \OC::$SERVERROOT.'/data').'/.ocdata', ''); + + /* + * START CONFIG CHANGES FOR OLDER VERSIONS + */ + if (!\OC::$CLI && version_compare($installedVersion, '6.90.1', '<')) { + // Add the trusted_domains config if it is not existant + // This is added to prevent host header poisoning + \OC_Config::setValue('trusted_domains', \OC_Config::getValue('trusted_domains', array(\OC_Request::serverHost()))); + } + /* + * STOP CONFIG CHANGES FOR OLDER VERSIONS + */ + + try { \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml'); $this->emit('\OC\Updater', 'dbUpgrade'); - // do a file cache upgrade for users with files - // this can take loooooooooooooooooooooooong - $this->upgradeFileCache(); } catch (\Exception $exception) { $this->emit('\OC\Updater', 'failure', array($exception->getMessage())); } @@ -120,45 +141,11 @@ class Updater extends BasicEmitter { $repair = new Repair(); $repair->run(); + //Invalidate update feed + \OC_Appconfig::setValue('core', 'lastupdatedat', 0); \OC_Config::setValue('maintenance', false); $this->emit('\OC\Updater', 'maintenanceEnd'); } - private function upgradeFileCache() { - try { - $query = \OC_DB::prepare(' - SELECT DISTINCT `user` - FROM `*PREFIX*fscache` - '); - $result = $query->execute(); - } catch (\Exception $e) { - return; - } - $users = $result->fetchAll(); - if (count($users) == 0) { - return; - } - $step = 100 / count($users); - $percentCompleted = 0; - $lastPercentCompletedOutput = 0; - $startInfoShown = false; - foreach ($users as $userRow) { - $user = $userRow['user']; - \OC\Files\Filesystem::initMountPoints($user); - \OC\Files\Cache\Upgrade::doSilentUpgrade($user); - if (!$startInfoShown) { - //We show it only now, because otherwise Info about upgraded apps - //will appear between this and progress info - $this->emit('\OC\Updater', 'filecacheStart'); - $startInfoShown = true; - } - $percentCompleted += $step; - $out = floor($percentCompleted); - if ($out != $lastPercentCompletedOutput) { - $this->emit('\OC\Updater', 'filecacheProgress', array($out)); - $lastPercentCompletedOutput = $out; - } - } - $this->emit('\OC\Updater', 'filecacheDone'); - } } + diff --git a/lib/private/urlgenerator.php b/lib/private/urlgenerator.php index 60da34f2d6e..a56b0fe3378 100644 --- a/lib/private/urlgenerator.php +++ b/lib/private/urlgenerator.php @@ -32,14 +32,13 @@ class URLGenerator implements IURLGenerator { * @brief Creates an url using a defined route * @param $route * @param array $parameters - * @return * @internal param array $args with param=>value, will be appended to the returned url - * @returns string the url + * @return string the url * * Returns a url to the given app and file. */ public function linkToRoute($route, $parameters = array()) { - $urlLinkTo = \OC::getRouter()->generate($route, $parameters); + $urlLinkTo = \OC::$server->getRouter()->generate($route, $parameters); return $urlLinkTo; } @@ -60,7 +59,7 @@ class URLGenerator implements IURLGenerator { $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' || substr($file, -3) == 'css') { + if (substr($file, -3) == 'php') { $urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $app; if ($frontControllerActive) { @@ -96,6 +95,7 @@ class URLGenerator implements IURLGenerator { * @brief 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. @@ -148,6 +148,12 @@ class URLGenerator implements IURLGenerator { */ public function getAbsoluteURL($url) { $separator = $url[0] === '/' ? '' : '/'; - return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . $separator . $url; + + // The ownCloud web root can already be prepended. + $webRoot = substr($url, 0, strlen(\OC::$WEBROOT)) === \OC::$WEBROOT + ? '' + : \OC::$WEBROOT; + + return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost(). $webRoot . $separator . $url; } } diff --git a/lib/private/user.php b/lib/private/user.php index a89b7286c10..7106d664aca 100644 --- a/lib/private/user.php +++ b/lib/private/user.php @@ -37,6 +37,10 @@ * logout() */ class OC_User { + + /** + * @return \OC\User\Session + */ public static function getUserSession() { return OC::$server->getUserSession(); } @@ -220,8 +224,8 @@ class OC_User { /** * @brief Try to login a user - * @param $uid The username of the user to log in - * @param $password The password of the user + * @param string $uid The username of the user to log in + * @param string $password The password of the user * @return boolean|null * * Log in a user and regenerate a new session - if the password is ok @@ -291,6 +295,8 @@ class OC_User { /** * @brief Sets user display name for session * @param string $uid + * @param null $displayName + * @return bool Whether the display name could get set */ public static function setDisplayName($uid, $displayName = null) { if (is_null($displayName)) { @@ -321,8 +327,6 @@ class OC_User { */ public static function isLoggedIn() { if (\OC::$session->get('user_id') && self::$incognitoMode === false) { - OC_App::loadApps(array('authentication')); - self::setupBackends(); return self::userExists(\OC::$session->get('user_id')); } return false; @@ -516,6 +520,7 @@ class OC_User { * @returns array with all uids * * Get a list of all users. + * @param string $search * @param integer $limit * @param integer $offset */ diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 15e6643dfb3..681f03981f5 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -42,7 +42,9 @@ class OC_User_Database extends OC_User_Backend { /** * @var PasswordHash */ - static private $hasher = null; + private static $hasher = null; + + private $cache = array(); private function getHasher() { if (!self::$hasher) { @@ -51,7 +53,6 @@ class OC_User_Database extends OC_User_Backend { self::$hasher = new PasswordHash(8, $forcePortable); } return self::$hasher; - } /** @@ -64,9 +65,7 @@ class OC_User_Database extends OC_User_Backend { * itself, not in its subclasses. */ public function createUser($uid, $password) { - if ($this->userExists($uid)) { - return false; - } else { + if (!$this->userExists($uid)) { $hasher = $this->getHasher(); $hash = $hasher->HashPassword($password . OC_Config::getValue('passwordsalt', '')); $query = OC_DB::prepare('INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )'); @@ -74,6 +73,8 @@ class OC_User_Database extends OC_User_Backend { return $result ? true : false; } + + return false; } /** @@ -86,8 +87,13 @@ class OC_User_Database extends OC_User_Backend { public function deleteUser($uid) { // Delete user-group-relation $query = OC_DB::prepare('DELETE FROM `*PREFIX*users` WHERE `uid` = ?'); - $query->execute(array($uid)); - return true; + $result = $query->execute(array($uid)); + + if (isset($this->cache[$uid])) { + unset($this->cache[$uid]); + } + + return $result ? true : false; } /** @@ -103,12 +109,12 @@ class OC_User_Database extends OC_User_Backend { $hasher = $this->getHasher(); $hash = $hasher->HashPassword($password . OC_Config::getValue('passwordsalt', '')); $query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `password` = ? WHERE `uid` = ?'); - $query->execute(array($hash, $uid)); + $result = $query->execute(array($hash, $uid)); - return true; - } else { - return false; + return $result ? true : false; } + + return false; } /** @@ -121,12 +127,14 @@ class OC_User_Database extends OC_User_Backend { */ public function setDisplayName($uid, $displayName) { if ($this->userExists($uid)) { - $query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `displayname` = ? WHERE LOWER(`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; - } else { - return false; } + + return false; } /** @@ -135,14 +143,8 @@ class OC_User_Database extends OC_User_Backend { * @return string display name */ public function getDisplayName($uid) { - $query = OC_DB::prepare('SELECT `displayname` FROM `*PREFIX*users` WHERE `uid` = ?'); - $result = $query->execute(array($uid))->fetchAll(); - $displayName = trim($result[0]['displayname'], ' '); - if (!empty($displayName)) { - return $displayName; - } else { - return $uid; - } + $this->loadUser($uid); + return empty($this->cache[$uid]['displayname']) ? $uid : $this->cache[$uid]['displayname']; } /** @@ -185,21 +187,41 @@ class OC_User_Database extends OC_User_Backend { $hasher = $this->getHasher(); if ($hasher->CheckPassword($password . OC_Config::getValue('passwordsalt', ''), $storedHash)) { return $row['uid']; - } else { - return false; - } - } else { //old sha1 based hashing - if (sha1($password) == $storedHash) { - //upgrade to new hashing - $this->setPassword($row['uid'], $password); - return $row['uid']; - } else { - return false; } + + //old sha1 based hashing + } elseif (sha1($password) == $storedHash) { + //upgrade to new hashing + $this->setPassword($row['uid'], $password); + return $row['uid']; } - } else { - return false; } + + return false; + } + + /** + * @brief Load an user in the cache + * @param string $uid the username + * @returns boolean + */ + private function loadUser($uid) { + if (empty($this->cache[$uid])) { + $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)'); + $result = $query->execute(array($uid)); + + if (OC_DB::isError($result)) { + OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + + while ($row = $result->fetchRow()) { + $this->cache[$uid]['uid'] = $row['uid']; + $this->cache[$uid]['displayname'] = $row['displayname']; + } + } + + return true; } /** @@ -224,13 +246,8 @@ class OC_User_Database extends OC_User_Backend { * @return boolean */ public function userExists($uid) { - $query = OC_DB::prepare('SELECT COUNT(*) FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)'); - $result = $query->execute(array($uid)); - if (OC_DB::isError($result)) { - OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - return $result->fetchOne() > 0; + $this->loadUser($uid); + return !empty($this->cache[$uid]); } /** @@ -241,9 +258,9 @@ class OC_User_Database extends OC_User_Backend { public function getHome($uid) { if ($this->userExists($uid)) { return OC_Config::getValue("datadirectory", OC::$SERVERROOT . "/data") . '/' . $uid; - } else { - return false; } + + return false; } /** @@ -256,7 +273,7 @@ class OC_User_Database extends OC_User_Backend { /** * counts the users in the database * - * @return false|string | bool + * @return int | bool */ public function countUsers() { $query = OC_DB::prepare('SELECT COUNT(*) FROM `*PREFIX*users`'); diff --git a/lib/private/user/manager.php b/lib/private/user/manager.php index 8583a451f2d..6f6fd80a8ef 100644 --- a/lib/private/user/manager.php +++ b/lib/private/user/manager.php @@ -174,19 +174,12 @@ class Manager extends PublicEmitter { $backendUsers = $backend->getUsers($pattern, $limit, $offset); if (is_array($backendUsers)) { foreach ($backendUsers as $uid) { - $users[] = $this->getUserObject($uid, $backend); - if (!is_null($limit)) { - $limit--; - } - if (!is_null($offset) and $offset > 0) { - $offset--; - } - + $users[$uid] = $this->getUserObject($uid, $backend); } } } - usort($users, function ($a, $b) { + uasort($users, function ($a, $b) { /** * @var \OC\User\User $a * @var \OC\User\User $b @@ -211,13 +204,6 @@ class Manager extends PublicEmitter { if (is_array($backendUsers)) { foreach ($backendUsers as $uid => $displayName) { $users[] = $this->getUserObject($uid, $backend); - if (!is_null($limit)) { - $limit--; - } - if (!is_null($offset) and $offset > 0) { - $offset--; - } - } } } @@ -239,24 +225,25 @@ class Manager extends PublicEmitter { * @return bool | \OC\User\User the created user of false */ public function createUser($uid, $password) { + $l = \OC_L10N::get('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 \Exception('Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-"'); + throw new \Exception($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 \Exception('A valid username must be provided'); + throw new \Exception($l->t('A valid username must be provided')); } // No empty password if (trim($password) == '') { - throw new \Exception('A valid password must be provided'); + throw new \Exception($l->t('A valid password must be provided')); } // Check if user already exists if ($this->userExists($uid)) { - throw new \Exception('The username is already being used'); + throw new \Exception($l->t('The username is already being used')); } $this->emit('\OC\User', 'preCreateUser', array($uid, $password)); diff --git a/lib/private/util.php b/lib/private/util.php index 920161949ae..e2005d31c38 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -30,9 +30,7 @@ class OC_Util { } // load all filesystem apps before, so no setup-hook gets lost - if(!isset($RUNTIME_NOAPPS) || !$RUNTIME_NOAPPS) { - OC_App::loadApps(array('filesystem')); - } + 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 @@ -89,7 +87,9 @@ class OC_Util { } /** + * Get the quota of a user * @param string $user + * @return int Quota bytes */ public static function getUserQuota($user){ $config = \OC::$server->getConfig(); @@ -290,14 +290,18 @@ class OC_Util { * @return array arrays with error messages and hints */ public static function checkServer() { - // Assume that if checkServer() succeeded before in this session, then all is fine. - if(\OC::$session->exists('checkServer_suceeded') && \OC::$session->get('checkServer_suceeded')) { - return array(); - } - $errors = array(); + $CONFIG_DATADIRECTORY = OC_Config::getValue('datadirectory', OC::$SERVERROOT . '/data'); - $defaults = new \OC_Defaults(); + if (!\OC::needUpgrade() && OC_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::$session->exists('checkServer_succeeded') && \OC::$session->get('checkServer_succeeded')) { + return $errors; + } $webServerRestart = false; //check for database drivers @@ -341,7 +345,6 @@ class OC_Util { ); } } - $CONFIG_DATADIRECTORY = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); // Create root dir. if(!is_dir($CONFIG_DATADIRECTORY)) { $success=@mkdir($CONFIG_DATADIRECTORY); @@ -485,12 +488,47 @@ class OC_Util { ); } + $errors = array_merge($errors, self::checkDatabaseVersion()); + // Cache the result of this function - \OC::$session->set('checkServer_suceeded', count($errors) == 0); + \OC::$session->set('checkServer_succeeded', count($errors) == 0); + + return $errors; + } + /** + * Check the database version + * @return array errors array + */ + public static function checkDatabaseVersion() { + $errors = array(); + $dbType = \OC_Config::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' => 'PostgreSQL >= 9 required', + 'hint' => 'Please upgrade your database version' + ); + } + } + } catch (\Doctrine\DBAL\DBALException $e) { + \OCP\Util::logException('core', $e); + $errors[] = array( + 'error' => 'Error occurred while checking PostgreSQL version', + 'hint' => 'Please make sure you have PostgreSQL >= 9 or check the logs for more information about the error' + ); + } + } return $errors; } + /** * @brief check if there are still some encrypted files stored * @return boolean @@ -526,7 +564,7 @@ class OC_Util { .' cannot be listed by other users.'; $perms = substr(decoct(@fileperms($dataDirectory)), -3); if (substr($perms, -1) != '0') { - OC_Helper::chmodr($dataDirectory, 0770); + chmod($dataDirectory, 0770); clearstatcache(); $perms = substr(decoct(@fileperms($dataDirectory)), -3); if (substr($perms, 2, 1) != '0') { @@ -541,11 +579,30 @@ class OC_Util { } /** - * @return void + * Check that the data directory exists and is valid by + * checking the existence of the ".ocdata" file. + * + * @param string $dataDirectory data directory path + * @return bool true if the data directory is valid, false otherwise + */ + public static function checkDataDirectoryValidity($dataDirectory) { + $errors = array(); + if (!file_exists($dataDirectory.'/.ocdata')) { + $errors[] = array( + 'error' => 'Data directory (' . $dataDirectory . ') is invalid', + 'hint' => 'Please check that the data directory contains a file' . + ' ".ocdata" in its root.' + ); + } + return $errors; + } + + /** + * @param array $errors */ public static function displayLoginPage($errors = array()) { $parameters = array(); - foreach( $errors as $key => $value ) { + foreach( $errors as $value ) { $parameters[$value] = true; } if (!empty($_POST['user'])) { @@ -644,17 +701,18 @@ class OC_Util { * @return void */ public static function redirectToDefaultPage() { + $urlGenerator = \OC::$server->getURLGenerator(); if(isset($_REQUEST['redirect_url'])) { - $location = OC_Helper::makeURLAbsolute(urldecode($_REQUEST['redirect_url'])); + $location = urldecode($_REQUEST['redirect_url']); } else if (isset(OC::$REQUESTEDAPP) && !empty(OC::$REQUESTEDAPP)) { - $location = OC_Helper::linkToAbsolute( OC::$REQUESTEDAPP, 'index.php' ); + $location = $urlGenerator->getAbsoluteURL('/index.php/apps/'.OC::$REQUESTEDAPP.'/index.php'); } else { $defaultPage = OC_Appconfig::getValue('core', 'defaultpage'); if ($defaultPage) { - $location = OC_Helper::makeURLAbsolute(OC::$WEBROOT.'/'.$defaultPage); + $location = $urlGenerator->getAbsoluteURL($defaultPage); } else { - $location = OC_Helper::linkToAbsolute( 'files', 'index.php' ); + $location = $urlGenerator->getAbsoluteURL('/index.php/files/index.php'); } } OC_Log::write('core', 'redirectToDefaultPage: '.$location, OC_Log::DEBUG); @@ -690,7 +748,7 @@ class OC_Util { /** * @brief Register an get/post call. Important to prevent CSRF attacks. * @todo Write howto: CSRF protection guide - * @return $token Generated token. + * @return string Generated token. * @description * Creates a 'request token' (random) and stores it inside the session. * Ever subsequent (ajax) request must use such a valid token to succeed, @@ -723,7 +781,7 @@ class OC_Util { } /** - * @brief Check an ajax get/post call if the request token is valid. exit if not. + * @brief Check an ajax get/post call if the request token is valid. Exit if not. * @todo Write howto * @return void */ @@ -747,7 +805,7 @@ class OC_Util { array_walk_recursive($value, 'OC_Util::sanitizeHTML'); } else { //Specify encoding for PHP<5.4 - $value = htmlentities((string)$value, ENT_QUOTES, 'UTF-8'); + $value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); } return $value; } @@ -769,12 +827,13 @@ class OC_Util { } /** - * @brief Check if the htaccess file is working + * @brief Check if the .htaccess file is working + * @throws OC\HintException If the testfile can't get written. * @return bool - * @description Check if the htaccess file is working by creating a test + * @description Check if the .htaccess file is working by creating a test * file in the data directory and trying to access via http */ - public static function isHtAccessWorking() { + public static function isHtaccessWorking() { if (!\OC_Config::getValue("check_for_working_htaccess", true)) { return true; } @@ -844,6 +903,8 @@ class OC_Util { // for this self test we don't care if the ssl certificate is self signed and the peer cannot be verified. $client->setVerifyPeer(false); + // also don't care if the host can't be verified + $client->setVerifyHost(0); $return = true; try { @@ -1015,13 +1076,13 @@ class OC_Util { public static function getUrlContent($url) { if (function_exists('curl_init')) { $curl = curl_init(); + $max_redirects = 10; curl_setopt($curl, CURLOPT_HEADER, 0); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_MAXREDIRS, 10); + curl_setopt($curl, CURLOPT_USERAGENT, "ownCloud Server Crawler"); if(OC_Config::getValue('proxy', '') != '') { @@ -1030,9 +1091,50 @@ class OC_Util { if(OC_Config::getValue('proxyuserpwd', '') != '') { curl_setopt($curl, CURLOPT_PROXYUSERPWD, OC_Config::getValue('proxyuserpwd')); } - $data = curl_exec($curl); + + if (ini_get('open_basedir') === '' && ini_get('safe_mode' === 'Off')) { + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_MAXREDIRS, $max_redirects); + $data = curl_exec($curl); + } else { + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false); + $mr = $max_redirects; + if ($mr > 0) { + $newURL = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); + + $rcurl = curl_copy_handle($curl); + curl_setopt($rcurl, CURLOPT_HEADER, true); + curl_setopt($rcurl, CURLOPT_NOBODY, true); + curl_setopt($rcurl, CURLOPT_FORBID_REUSE, false); + curl_setopt($rcurl, CURLOPT_RETURNTRANSFER, true); + do { + curl_setopt($rcurl, CURLOPT_URL, $newURL); + $header = curl_exec($rcurl); + if (curl_errno($rcurl)) { + $code = 0; + } else { + $code = curl_getinfo($rcurl, CURLINFO_HTTP_CODE); + if ($code == 301 || $code == 302) { + preg_match('/Location:(.*?)\n/', $header, $matches); + $newURL = trim(array_pop($matches)); + } else { + $code = 0; + } + } + } while ($code && --$mr); + curl_close($rcurl); + if ($mr > 0) { + curl_setopt($curl, CURLOPT_URL, $newURL); + } + } + + if($mr == 0 && $max_redirects > 0) { + $data = false; + } else { + $data = curl_exec($curl); + } + } curl_close($curl); - } else { $contextArray = null; @@ -1061,13 +1163,22 @@ class OC_Util { } /** - * @return bool - well are we running on windows or not + * Checks whether the server is running on Windows + * @return bool true if running on Windows, false otherwise */ public static function runningOnWindows() { return (substr(PHP_OS, 0, 3) === "WIN"); } /** + * 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'); + } + + /** * 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 diff --git a/lib/private/vobject.php b/lib/private/vobject.php index 267176ebc07..a3e9f7ef790 100644 --- a/lib/private/vobject.php +++ b/lib/private/vobject.php @@ -36,8 +36,8 @@ class OC_VObject{ /** * @brief Parses the VObject - * @param string VObject as string - * @returns Sabre_VObject or null + * @param string $data VObject as string + * @returns Sabre\VObject\Reader|null */ public static function parse($data) { try { @@ -55,7 +55,7 @@ class OC_VObject{ /** * @brief Escapes semicolons - * @param string $value + * @param array $value * @return string */ public static function escapeSemicolons($value) { @@ -88,7 +88,7 @@ class OC_VObject{ } /** - * Constuctor + * Constructor * @param Sabre\VObject\Component or string */ public function __construct($vobject_or_name) { @@ -99,6 +99,11 @@ class OC_VObject{ } } + /** + * @todo Write documentation + * @param $item + * @param null $itemValue + */ public function add($item, $itemValue = null) { if ($item instanceof OC_VObject) { $item = $item->getVObject(); @@ -110,8 +115,8 @@ class OC_VObject{ * @brief Add property to vobject * @param object $name of property * @param object $value of property - * @param object $parameters of property - * @returns Sabre_VObject_Property newly created + * @param array|object $parameters of property + * @returns Sabre\VObject\Property newly created */ public function addProperty($name, $value, $parameters=array()) { if(is_array($value)) { @@ -131,6 +136,11 @@ class OC_VObject{ $this->vobject->add('UID', $uid); } + /** + * @todo Write documentation + * @param mixed $name + * @param string $string + */ public function setString($name, $string) { if ($string != '') { $string = strtr($string, array("\r\n"=>"\n")); @@ -145,7 +155,7 @@ class OC_VObject{ * When $datetime is set to 'now', use the current time * When $datetime is null, unset the property * - * @param string property name + * @param string $name * @param DateTime $datetime * @param int $dateType * @return void @@ -163,12 +173,22 @@ class OC_VObject{ } } + /** + * @todo Write documentation + * @param $name + * @return string + */ public function getAsString($name) { return $this->vobject->__isset($name) ? $this->vobject->__get($name)->value : ''; } + /** + * @todo Write documentation + * @param $name + * @return array + */ public function getAsArray($name) { $values = array(); if ($this->vobject->__isset($name)) { @@ -178,6 +198,11 @@ class OC_VObject{ return $values; } + /** + * @todo Write documentation + * @param $name + * @return array|OC_VObject|\Sabre\VObject\Property + */ public function &__get($name) { if ($name == 'children') { return $this->vobject->children; @@ -189,18 +214,38 @@ class OC_VObject{ return $return; } + /** + * @todo Write documentation + * @param string $name + * @param string $value + */ public function __set($name, $value) { return $this->vobject->__set($name, $value); } + /** + * @todo Write documentation + * @param string $name + */ public function __unset($name) { return $this->vobject->__unset($name); } + /** + * @todo Write documentation + * @param string $name + * @return bool + */ public function __isset($name) { return $this->vobject->__isset($name); } + /** + * @todo Write documentation + * @param $function + * @param $arguments + * @return mixed + */ public function __call($function, $arguments) { return call_user_func_array(array($this->vobject, $function), $arguments); } diff --git a/lib/public/appframework/app.php b/lib/public/appframework/app.php index 90150245c41..21612327879 100644 --- a/lib/public/appframework/app.php +++ b/lib/public/appframework/app.php @@ -67,7 +67,7 @@ class App { * $a = new TasksApp(); * $a->registerRoutes($this, $routes); * - * @param \OC_Router $router + * @param \OCP\Route\IRouter $router * @param array $routes */ public function registerRoutes($router, $routes) { diff --git a/lib/public/appframework/controller.php b/lib/public/appframework/controller.php index 7c2219bd046..758f0a80083 100644 --- a/lib/public/appframework/controller.php +++ b/lib/public/appframework/controller.php @@ -38,10 +38,10 @@ use OCP\IRequest; abstract class Controller { /** - * app container for dependency injection - * @var \OCP\AppFramework\IAppContainer + * app name + * @var string */ - protected $app; + protected $appName; /** * current request @@ -51,11 +51,11 @@ abstract class Controller { /** * constructor of the controller - * @param IAppContainer $app interface to the app + * @param string $appName the name of the app * @param IRequest $request an instance of the request */ - public function __construct(IAppContainer $app, IRequest $request){ - $this->app = $app; + public function __construct($appName, IRequest $request){ + $this->appName = $appName; $this->request = $request; } @@ -136,7 +136,7 @@ abstract class Controller { */ public function render($templateName, array $params=array(), $renderAs='user', array $headers=array()){ - $response = new TemplateResponse($this->app->getAppName(), $templateName); + $response = new TemplateResponse($this->appName, $templateName); $response->setParams($params); $response->renderAs($renderAs); diff --git a/lib/private/appframework/http/downloadresponse.php b/lib/public/appframework/http/downloadresponse.php index 67b9542dba6..d3c2818e828 100644 --- a/lib/private/appframework/http/downloadresponse.php +++ b/lib/public/appframework/http/downloadresponse.php @@ -22,7 +22,7 @@ */ -namespace OC\AppFramework\Http; +namespace OCP\AppFramework\Http; /** diff --git a/lib/public/appframework/http/jsonresponse.php b/lib/public/appframework/http/jsonresponse.php index b54b23a34e6..6d029b7464a 100644 --- a/lib/public/appframework/http/jsonresponse.php +++ b/lib/public/appframework/http/jsonresponse.php @@ -49,7 +49,6 @@ class JSONResponse extends Response { public function __construct($data=array(), $statusCode=Http::STATUS_OK) { $this->data = $data; $this->setStatus($statusCode); - $this->addHeader('X-Content-Type-Options', 'nosniff'); $this->addHeader('Content-type', 'application/json; charset=utf-8'); } @@ -66,9 +65,12 @@ class JSONResponse extends 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 */ public function setData($data){ $this->data = $data; + + return $this; } diff --git a/lib/private/appframework/http/redirectresponse.php b/lib/public/appframework/http/redirectresponse.php index 05353349065..416e1533635 100644 --- a/lib/private/appframework/http/redirectresponse.php +++ b/lib/public/appframework/http/redirectresponse.php @@ -22,7 +22,7 @@ */ -namespace OC\AppFramework\Http; +namespace OCP\AppFramework\Http; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http; diff --git a/lib/public/appframework/http/response.php b/lib/public/appframework/http/response.php index d223621d4fd..45402d9b3b3 100644 --- a/lib/public/appframework/http/response.php +++ b/lib/public/appframework/http/response.php @@ -80,6 +80,7 @@ class Response { $this->addHeader('Cache-Control', 'no-cache, must-revalidate'); } + return $this; } @@ -88,6 +89,7 @@ class Response { * function * @param string $name The name of the HTTP header * @param string $value The value, null will delete it + * @return Response Reference to this object */ public function addHeader($name, $value) { if(is_null($value)) { @@ -95,6 +97,8 @@ class Response { } else { $this->headers[$name] = $value; } + + return $this; } @@ -130,9 +134,12 @@ class Response { /** * Set response status * @param int $status a HTTP status code, see also the STATUS constants + * @return Response Reference to this object */ public function setStatus($status) { $this->status = $status; + + return $this; } @@ -165,18 +172,24 @@ class Response { /** * Set the ETag * @param string $ETag + * @return Response Reference to this object */ public function setETag($ETag) { $this->ETag = $ETag; + + return $this; } /** * Set "last modified" date * @param \DateTime $lastModified + * @return Response Reference to this object */ public function setLastModified($lastModified) { $this->lastModified = $lastModified; + + return $this; } diff --git a/lib/public/appframework/http/templateresponse.php b/lib/public/appframework/http/templateresponse.php index 2200a38beca..f5baf788ada 100644 --- a/lib/public/appframework/http/templateresponse.php +++ b/lib/public/appframework/http/templateresponse.php @@ -74,9 +74,12 @@ class TemplateResponse extends Response { * Sets template parameters * @param array $params an array with key => value structure which sets template * variables + * @return TemplateResponse Reference to this object */ public function setParams(array $params){ $this->params = $params; + + return $this; } @@ -104,9 +107,12 @@ class TemplateResponse extends Response { * 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 */ public function renderAs($renderAs){ $this->renderAs = $renderAs; + + return $this; } diff --git a/lib/public/appframework/iappcontainer.php b/lib/public/appframework/iappcontainer.php index e4f5f37cad2..a0b0c06881a 100644 --- a/lib/public/appframework/iappcontainer.php +++ b/lib/public/appframework/iappcontainer.php @@ -50,10 +50,10 @@ interface IAppContainer extends IContainer{ function getServer(); /** - * @param Middleware $middleWare + * @param string $middleWare * @return boolean */ - function registerMiddleWare(Middleware $middleWare); + function registerMiddleWare($middleWare); /** * @return boolean diff --git a/lib/public/config.php b/lib/public/config.php index bb973939f44..8e3791b449b 100644 --- a/lib/public/config.php +++ b/lib/public/config.php @@ -64,7 +64,7 @@ class Config { public static function setSystemValue( $key, $value ) { try { \OC_Config::setValue( $key, $value ); - } catch (Exception $e) { + } catch (\Exception $e) { return false; } return true; @@ -96,7 +96,7 @@ class Config { public static function setAppValue( $app, $key, $value ) { try { \OC_Appconfig::setValue( $app, $key, $value ); - } catch (Exception $e) { + } catch (\Exception $e) { return false; } return true; @@ -131,7 +131,7 @@ class Config { public static function setUserValue( $user, $app, $key, $value ) { try { \OC_Preferences::setValue( $user, $app, $key, $value ); - } catch (Exception $e) { + } catch (\Exception $e) { return false; } return true; diff --git a/lib/public/contacts/imanager.php b/lib/public/contacts/imanager.php index 5b9d64ecc41..005b71f298b 100644 --- a/lib/public/contacts/imanager.php +++ b/lib/public/contacts/imanager.php @@ -96,7 +96,7 @@ namespace OCP\Contacts { * This function can be used to delete the contact identified by the given id * * @param object $id the unique identifier to a contact - * @param $address_book_key + * @param string $address_book_key identifier of the address book in which the contact shall be deleted * @return bool successful or not */ function delete($id, $address_book_key); @@ -106,7 +106,7 @@ namespace OCP\Contacts { * 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 $address_book_key string to identify the address book in which the contact shall be created or updated + * @param string $address_book_key identifier of the address book in which the contact shall be created or updated * @return array representing the contact just created or updated */ function createOrUpdate($properties, $address_book_key); diff --git a/lib/public/files/fileinfo.php b/lib/public/files/fileinfo.php index 68ce45d3fa1..37162e09336 100644 --- a/lib/public/files/fileinfo.php +++ b/lib/public/files/fileinfo.php @@ -9,7 +9,7 @@ namespace OCP\Files; interface FileInfo { const TYPE_FILE = 'file'; - const TYPE_FOLDER = 'folder'; + const TYPE_FOLDER = 'dir'; /** * Get the Etag of the file or folder diff --git a/lib/public/iappconfig.php b/lib/public/iappconfig.php index 1f31898bf2c..2b014df2e42 100644 --- a/lib/public/iappconfig.php +++ b/lib/public/iappconfig.php @@ -55,9 +55,8 @@ interface IAppConfig { /** * get multiply values, either the app or key can be used as wildcard by setting it to false * - * @param app - * @param key - * @param string $key + * @param string|false $key + * @param string|false $app * @return array */ public function getValues($app, $key); diff --git a/lib/public/il10n.php b/lib/public/il10n.php index 1c025e7824f..1388274c21a 100644 --- a/lib/public/il10n.php +++ b/lib/public/il10n.php @@ -23,7 +23,7 @@ namespace OCP; interface IL10N { /** * Translating - * @param $text String The text we need a translation for + * @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 * @@ -34,9 +34,9 @@ interface IL10N { /** * Translating - * @param $text_singular String the string to translate for exactly one object - * @param $text_plural String the string to translate for n objects - * @param $count Integer Number of objects + * @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 * @@ -51,8 +51,8 @@ interface IL10N { /** * Localization - * @param $type Type of localization - * @param $params parameters for this localization + * @param string $type Type of localization + * @param array $data parameters for this localization * @return String or false * * Returns the localized data. diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 5fb51f9ecd5..600d81d83af 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -122,7 +122,7 @@ interface IServerContainer { /** * get an L10N instance - * @param $app string appid + * @param string $app appid * @return \OCP\IL10N */ function getL10N($app); @@ -190,4 +190,10 @@ interface IServerContainer { */ function getJobList(); + /** + * Returns a router for generating and matching urls + * + * @return \OCP\Route\IRouter + */ + function getRouter(); } diff --git a/lib/public/isession.php b/lib/public/isession.php index 20da712cda3..dc5719625cc 100644 --- a/lib/public/isession.php +++ b/lib/public/isession.php @@ -75,4 +75,9 @@ interface ISession { */ public function clear(); + /** + * Close the session and release the lock + */ + public function close(); + } diff --git a/lib/public/route/iroute.php b/lib/public/route/iroute.php new file mode 100644 index 00000000000..d5610e762a8 --- /dev/null +++ b/lib/public/route/iroute.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OCP\Route; + +interface IRoute { + /** + * Specify PATCH as the method to use with this route + * @return \OCP\Route\IRoute + */ + public function patch(); + + /** + * Specify the method when this route is to be used + * + * @param string $method HTTP method (uppercase) + * @return \OCP\Route\IRoute + */ + public function method($method); + + /** + * The action to execute when this route matches, includes a file like + * it is called directly + * + * @param $file + * @return void + */ + public function actionInclude($file); + + /** + * Specify GET as the method to use with this route + * @return \OCP\Route\IRoute + */ + public function get(); + + /** + * Specify POST as the method to use with this route + * @return \OCP\Route\IRoute + */ + public function post(); + + /** + * Specify DELETE as the method to use with this route + * @return \OCP\Route\IRoute + */ + 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 + */ + public function action($class, $function = null); + + /** + * Defaults to use for this route + * + * @param array $defaults The defaults + * @return \OCP\Route\IRoute + */ + public function defaults($defaults); + + /** + * Requirements for this route + * + * @param array $requirements The requirements + * @return \OCP\Route\IRoute + */ + public function requirements($requirements); + + /** + * Specify PUT as the method to use with this route + * @return \OCP\Route\IRoute + */ + public function put(); +} diff --git a/lib/public/route/irouter.php b/lib/public/route/irouter.php new file mode 100644 index 00000000000..1c003c7b4b9 --- /dev/null +++ b/lib/public/route/irouter.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCP\Route; + +interface IRouter { + + /** + * Get the files to load the routes from + * + * @return string[] + */ + public function getRoutingFiles(); + + /** + * @return string + */ + public function getCacheKey(); + + /** + * loads the api routes + * @return void + */ + public function loadRoutes($app = null); + + /** + * Sets the collection to use for adding routes + * + * @param string $name Name of the collection to use. + * @return void + */ + public function useCollection($name); + + /** + * 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 + */ + 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 + */ + public function match($url); + + /** + * Get the url generator + * + */ + 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 + */ + public function generate($name, $parameters = array(), $absolute = false); + +} diff --git a/lib/public/share.php b/lib/public/share.php index cc4bfb67bde..230a517b5ee 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -2,8 +2,9 @@ /** * ownCloud * - * @author Michael Gapczynski - * @copyright 2012 Michael Gapczynski mtgap@owncloud.com + * @author Bjoern Schiessle, Michael Gapczynski + * @copyright 2012 Michael Gapczynski <mtgap@owncloud.com> + * 2014 Bjoern Schiessle <schiessle@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 @@ -36,41 +37,7 @@ namespace OCP; * It provides the following hooks: * - post_shared */ -class Share { - - 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; - const SHARE_TYPE_REMOTE = 6; - - /** 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 - */ - - const FORMAT_NONE = -1; - const FORMAT_STATUSES = -2; - const FORMAT_SOURCES = -3; - - const TOKEN_LENGTH = 32; // see db_structure.xml - - private static $shareTypeUserAndGroups = -1; - private static $shareTypeGroupUserUnique = 2; - private static $backends = array(); - private static $backendTypes = array(); - private static $isResharingAllowed; +class Share extends \OC\Share\Constants { /** * Register a sharing backend class that implements OCP\Share_Backend for an item type @@ -78,215 +45,65 @@ class Share { * @param string Backend class * @param string (optional) Depends on item type * @param array (optional) List of supported file extensions if this item type depends on files - * @param string $itemType - * @param string $class - * @param string $collectionOf - * @return boolean true if backend is registered or false if error + * @return Returns 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) { - \OC_Util::addScript('core', 'share'); - \OC_Util::addStyle('core', 'share'); - } - return true; - } - \OC_Log::write('OCP\Share', - 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class'] - .' is already registered for '.$itemType, - \OC_Log::WARN); - } - return false; + return \OC\Share\Share::registerBackend($itemType, $class, $collectionOf, $supportedFileExtensions); } /** * Check if the Share API is enabled - * @return boolean true if enabled or false + * @return Returns true if enabled or false * * The Share API is enabled by default if not configured */ public static function isEnabled() { - if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') == 'yes') { - return true; - } - return false; - } - - /** - * Prepare a path to be passed to DB as file_target - * @param string $path path - * @return string Prepared path - */ - public static function prepFileTarget( $path ) { - - // Paths in DB are stored with leading slashes, so add one if necessary - if ( substr( $path, 0, 1 ) !== '/' ) { - - $path = '/' . $path; - - } - - return $path; - + return \OC\Share\Share::isEnabled(); } /** * Find which users can access a shared item * @param string $path to the file - * @param $user owner of the file - * @param include owner to the list of users with access 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 * @return array * @note $path needs to be relative to user data dir, e.g. 'file.txt' * not '/admin/data/file.txt' */ - public static function getUsersSharingFile($path, $user, $includeOwner = false) { - - $shares = array(); - $publicShare = false; - $source = -1; - $cache = false; - - $view = new \OC\Files\View('/' . $user . '/files'); - if ($view->file_exists($path)) { - $meta = $view->getFileInfo($path); - } 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']); - } - - while ($source !== -1) { - - // Fetch all shares with another user - $query = \OC_DB::prepare( - 'SELECT `share_with` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' - ); - - $result = $query->execute(array($source, self::SHARE_TYPE_USER)); - - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); - } else { - while ($row = $result->fetchRow()) { - $shares[] = $row['share_with']; - } - } - // We also need to take group shares into account - - $query = \OC_DB::prepare( - 'SELECT `share_with` - 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($result), \OC_Log::ERROR); - } else { - while ($row = $result->fetchRow()) { - $usersInGroup = \OC_Group::usersInGroup($row['share_with']); - $shares = array_merge($shares, $usersInGroup); - } - } - - //check for public link shares - if (!$publicShare) { - $query = \OC_DB::prepare( - 'SELECT `share_with` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' - ); - - $result = $query->execute(array($source, self::SHARE_TYPE_LINK)); - - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); - } else { - if ($result->fetchRow()) { - $publicShare = true; - } - } - } - - // let's get the parent for the next round - $meta = $cache->get((int)$source); - if($meta !== false) { - $source = (int)$meta['parent']; - } else { - $source = -1; - } - } - // Include owner in list of users, if requested - if ($includeOwner) { - $shares[] = $user; - } - - return array("users" => array_unique($shares), "public" => $publicShare); + public static function getUsersSharingFile($path, $ownerUser, $includeOwner = false, $returnUserPaths = false) { + return \OC\Share\Share::getUsersSharingFile($path, $ownerUser, $includeOwner, $returnUserPaths); } /** - * Check if the current user has files shared with - * @return boolean + * Get the items of item type shared with the current user + * @param string Item type + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters (optional) + * @param int Number of items to return (optional) Returns all by default + * @param bool include collections (optional) + * @return Return depends on format */ - public static function hasFilesSharedWith() { - if (!self::isEnabled()) { - return false; - } - $shareWith = \OC_User::getUser(); - $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid`'; - $where .= ' WHERE `file_target` IS NOT NULL'; - $queryArgs = array(); - $where .= ' AND `share_type` IN (?,?,?)'; - $queryArgs[] = self::SHARE_TYPE_USER; - $queryArgs[] = self::SHARE_TYPE_GROUP; - $queryArgs[] = self::$shareTypeGroupUserUnique; - $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); - $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); - $where .= ' AND `share_with` IN ('.$placeholders.')'; - $queryArgs = array_merge($queryArgs, $userAndGroups); - // Don't include own group shares - $where .= ' AND `uid_owner` != ?'; - $queryArgs[] = $shareWith; - $result = \OC_DB::executeAudited(array( - 'sql' => 'SELECT * FROM `*PREFIX*share` '.$where, - 'limit' => 1, - ), $queryArgs); - return (bool)$result->fetchOne(); + 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 the current user + * Get the items of item type shared with a user * @param string Item type + * @param sting 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 Number of items to return (optional) Returns all by default * @param bool include collections (optional) - * @param string $itemType * @return Return depends on format */ - public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE, + public static function getItemsSharedWithUser($itemType, $user, $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); + + return \OC\Share\Share::getItemsSharedWithUser($itemType, $user, $format, $parameters, $limit, $includeCollections); } /** @@ -296,12 +113,12 @@ class Share { * @param int $format (optional) Format type must be defined by the backend * @param mixed Parameters (optional) * @param bool include collections (optional) - * @return string depends on format + * @return 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); + + return \OC\Share\Share::getItemSharedWith($itemType, $itemTarget, $format, $parameters, $includeCollections); } /** @@ -312,45 +129,7 @@ class Share { * @return array Return list of items with file_target, permissions and expiration */ public static function getItemSharedWithUser($itemType, $itemSource, $user) { - - $shares = array(); - - // first check if there is a db entry for the specific user - $query = \OC_DB::prepare( - 'SELECT `file_target`, `permissions`, `expiration` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `item_type` = ? AND `share_with` = ?' - ); - - $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, $user)); - - while ($row = $result->fetchRow()) { - $shares[] = $row; - } - - //if didn't found a result than let's look for a group share. - if(empty($shares)) { - $groups = \OC_Group::getUserGroups($user); - - $query = \OC_DB::prepare( - 'SELECT `file_target`, `permissions`, `expiration` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `item_type` = ? AND `share_with` in (?)' - ); - - $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, implode(',', $groups))); - - while ($row = $result->fetchRow()) { - $shares[] = $row; - } - } - - return $shares; - + return \OC\Share\Share::getItemSharedWithUser($itemType, $itemSource, $user); } /** @@ -364,8 +143,7 @@ class Share { */ public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { - return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, - $parameters, 1, $includeCollections, true); + return \OC\Share\Share::getItemSharedWithBySource($itemType, $itemSource, $format, $parameters, $includeCollections); } /** @@ -376,8 +154,7 @@ class Share { * @return Item */ public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) { - return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, - null, 1); + return \OC\Share\Share::getItemSharedWithByLink($itemType, $itemSource, $uidOwner); } /** @@ -386,25 +163,7 @@ class Share { * @return array | bool 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 (\OC_DB::isError($result)) { - \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::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; + return \OC\Share\Share::getShareByToken($token, $checkPasswordProtection); } /** @@ -412,21 +171,8 @@ class Share { * @param $linkItem * @return $fileOwner */ - 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; + public static function resolveReShare($linkItem) { + return \OC\Share\Share::resolveReShare($linkItem); } @@ -437,13 +183,12 @@ class Share { * @param mixed Parameters * @param int Number of items to return (optional) Returns all by default * @param bool include collections - * @param string $itemType * @return 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); + + return \OC\Share\Share::getItemsShared($itemType, $format, $parameters, $limit, $includeCollections); } /** @@ -457,8 +202,8 @@ class Share { */ 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); + + return \OC\Share\Share::getItemShared($itemType, $itemSource, $format, $parameters, $includeCollections); } /** @@ -471,19 +216,7 @@ class Share { * @return 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) { - $users = array_merge($users, \OC_Group::usersInGroup($item['share_with'])); - } - } - } - return $users; + return \OC\Share\Share::getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections, $checkExpireDate); } /** @@ -503,176 +236,7 @@ class Share { * @return bool|string Returns true on success or false on failure, Returns token on success for links */ public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null) { - $uidOwner = \OC_User::getUser(); - $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); - - if (is_null($itemSourceName)) { - $itemSourceName = $itemSource; - } - - // Verify share type and sharing conditions are met - if ($shareType === self::SHARE_TYPE_USER) { - if ($shareWith == $uidOwner) { - $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' is the item owner'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - if (!\OC_User::userExists($shareWith)) { - $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' does not exist'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - if ($sharingPolicy == 'groups_only') { - $inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith)); - if (empty($inGroup)) { - $message = 'Sharing '.$itemSourceName.' failed, because the user ' - .$shareWith.' is not a member of any groups that '.$uidOwner.' is a member of'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - // 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 '.$itemSourceName.' failed, because this item is already shared with '.$shareWith; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - } else if ($shareType === self::SHARE_TYPE_GROUP) { - if (!\OC_Group::groupExists($shareWith)) { - $message = 'Sharing '.$itemSourceName.' failed, because the group '.$shareWith.' does not exist'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - if ($sharingPolicy == 'groups_only' && !\OC_Group::inGroup($uidOwner, $shareWith)) { - $message = 'Sharing '.$itemSourceName.' failed, because ' - .$uidOwner.' is not a member of the group '.$shareWith; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - // 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)) { - // Only allow the same share to occur again if it is the same - // owner and is not a group share, this use case is for increasing - // permissions for a specific user - if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { - $message = 'Sharing '.$itemSourceName.' failed, because this item is already shared with '.$shareWith; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - // Convert share with into an array with the keys group and users - $group = $shareWith; - $shareWith = array(); - $shareWith['group'] = $group; - $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner)); - } else if ($shareType === self::SHARE_TYPE_LINK) { - if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') { - // when updating a link share - 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 - self::delete($checkExists['id']); - } - - // Generate hash of password - same method as user passwords - if (isset($shareWith)) { - $forcePortable = (CRYPT_BLOWFISH != 1); - $hasher = new \PasswordHash(8, $forcePortable); - $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', '')); - } 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']; - } - } - - // Generate token - if (isset($oldToken)) { - $token = $oldToken; - } else { - $token = \OC_Util::generateRandomBytes(self::TOKEN_LENGTH); - } - $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, - null, $token, $itemSourceName); - if ($result) { - return $token; - } else { - return false; - } - } - $message = 'Sharing '.$itemSourceName.' failed, because sharing with links is not allowed'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - return false; -// } else if ($shareType === self::SHARE_TYPE_CONTACT) { -// if (!\OC_App::isEnabled('contacts')) { -// $message = 'Sharing '.$itemSource.' failed, because the contacts app is not enabled'; -// \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); -// return false; -// } -// $vcard = \OC_Contacts_App::getContactVCard($shareWith); -// if (!isset($vcard)) { -// $message = 'Sharing '.$itemSource.' failed, because the contact does not exist'; -// \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); -// throw new \Exception($message); -// } -// $details = \OC_Contacts_VCard::structureContact($vcard); -// // TODO Add ownCloud user to contacts vcard -// if (!isset($details['EMAIL'])) { -// $message = 'Sharing '.$itemSource.' failed, because no email address is associated with the contact'; -// \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); -// throw new \Exception($message); -// } -// return self::shareItem($itemType, $itemSource, self::SHARE_TYPE_EMAIL, $details['EMAIL'], $permissions); - } else { - // Future share types need to include their own conditions - $message = 'Share type '.$shareType.' is not valid for '.$itemSource; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - // If the item is a folder, scan through the folder looking for equivalent item types -// if ($itemType == 'folder') { -// $parentFolder = self::put('folder', $itemSource, $shareType, $shareWith, $uidOwner, $permissions, true); -// if ($parentFolder && $files = \OC\Files\Filesystem::getDirectoryContent($itemSource)) { -// for ($i = 0; $i < count($files); $i++) { -// $name = substr($files[$i]['name'], strpos($files[$i]['name'], $itemSource) - strlen($itemSource)); -// if ($files[$i]['mimetype'] == 'httpd/unix-directory' -// && $children = \OC\Files\Filesystem::getDirectoryContent($name, '/') -// ) { -// // Continue scanning into child folders -// array_push($files, $children); -// } else { -// // Check file extension for an equivalent item type to convert to -// $extension = strtolower(substr($itemSource, strrpos($itemSource, '.') + 1)); -// foreach (self::$backends as $type => $backend) { -// if (isset($backend->dependsOn) && $backend->dependsOn == 'file' && isset($backend->supportedFileExtensions) && in_array($extension, $backend->supportedFileExtensions)) { -// $itemType = $type; -// break; -// } -// } -// // Pass on to put() to check if this item should be converted, the item won't be inserted into the database unless it can be converted -// self::put($itemType, $name, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder); -// } -// } -// return true; -// } -// return false; -// } else { - // Put the item into the database - return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName); -// } + return \OC\Share\Share::shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName); } /** @@ -681,88 +245,32 @@ class Share { * @param string Item source * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string User or group the item is being shared with - * @return boolean true on success or false on failure + * @return Returns true on success or false on failure */ public static function unshare($itemType, $itemSource, $shareType, $shareWith) { - if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(), - self::FORMAT_NONE, null, 1)) { - self::unshareItem($item); - return true; - } - return false; + return \OC\Share\Share::unshare($itemType, $itemSource, $shareType, $shareWith); } /** * Unshare an item from all users, groups, and remove all links * @param string Item type * @param string Item source - * @param string $itemType - * @param string $itemSource - * @return boolean true on success or false on failure + * @return Returns 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; + return \OC\Share\Share::unshareAll($itemType, $itemSource); } /** * Unshare an item shared with the current user * @param string Item type * @param string Item target - * @param string $itemType - * @param string $itemTarget - * @return boolean true on success or false on failure + * @return Returns true on success or false on failure * * Unsharing from self is not allowed for items inside collections */ public static function unshareFromSelf($itemType, $itemTarget) { - if ($item = self::getItemSharedWith($itemType, $itemTarget)) { - if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) { - // Insert an extra row for the group share and set permission - // to 0 to prevent it from showing up for the user - $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($item['item_type'], $item['item_source'], $item['item_target'], - $item['id'], self::$shareTypeGroupUserUnique, - \OC_User::getUser(), $item['uid_owner'], 0, $item['stime'], $item['file_source'], - $item['file_target'])); - \OC_DB::insertid('*PREFIX*share'); - // Delete all reshares by this user of the group share - self::delete($item['id'], true, \OC_User::getUser()); - } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) { - // Set permission to 0 to prevent it from showing up for the user - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); - $query->execute(array(0, $item['id'])); - self::delete($item['id'], true); - } else { - self::delete($item['id']); - } - return true; - } - return false; + return \OC\Share\Share::unshareFromSelf($itemType, $itemTarget); } /** * sent status if users got informed by mail about share @@ -772,102 +280,20 @@ class Share { * @param bool $status */ public static function setSendMailStatus($itemType, $itemSource, $shareType, $status) { - $status = $status ? 1 : 0; - - $query = \OC_DB::prepare( - 'UPDATE `*PREFIX*share` - SET `mail_send` = ? - WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ?'); - - $result = $query->execute(array($status, $itemType, $itemSource, $shareType)); - - if($result === false) { - \OC_Log::write('OCP\Share', 'Couldn\'t set send mail status', \OC_Log::ERROR); - } + return \OC\Share\Share::setSendMailStatus($itemType, $itemSource, $shareType, $status); } /** * Set the permissions of an item for a specific user or group - * @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 integer|null $permissions CRUDS - * @return boolean true on success or false on failure + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param int CRUDS permissions + * @return Returns true on success or false on failure */ public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) { - if ($item = 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($item['parent'])) { - $query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*share` WHERE `id` = ?', 1); - $result = $query->execute(array($item['parent']))->fetchRow(); - if (~(int)$result['permissions'] & $permissions) { - $message = 'Setting permissions for '.$itemSource.' failed,' - .' because the permissions exceed permissions granted to '.\OC_User::getUser(); - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); - $query->execute(array($permissions, $item['id'])); - 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' => $item['path'], - )); - } - // Check if permissions were removed - if ($item['permissions'] & ~$permissions) { - // If share permission is removed all reshares must be deleted - if (($item['permissions'] & PERMISSION_SHARE) && (~$permissions & PERMISSION_SHARE)) { - self::delete($item['id'], true); - } else { - $ids = array(); - $parents = array($item['id']); - while (!empty($parents)) { - $parents = "'".implode("','", $parents)."'"; - $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' - .' WHERE `parent` IN ('.$parents.')'); - $result = $query->execute(); - // Reset parents array, only go through loop again if - // items are found that need permissions removed - $parents = array(); - while ($item = $result->fetchRow()) { - // 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']; - } - } - } - // 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_Config::getValue( "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)); - } - } - } - return true; - } - $message = 'Setting permissions for '.$itemSource.' failed, because the item was not found'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); + return \OC\Share\Share::setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions); } /** @@ -875,67 +301,10 @@ class Share { * @param string $itemType * @param string $itemSource * @param string $date expiration date - * @return boolean + * @return Share_Backend */ public static function setExpirationDate($itemType, $itemSource, $date) { - if ($items = self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), - self::FORMAT_NONE, null, -1, false)) { - if (!empty($items)) { - if ($date == '') { - $date = null; - } else { - $date = new \DateTime($date); - } - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `id` = ?'); - $query->bindValue(1, $date, 'datetime'); - foreach ($items as $item) { - $query->bindValue(2, (int) $item['id']); - $query->execute(); - } - return true; - } - } - return false; - } - - /** - * Checks whether a share has expired, calls unshareItem() if yes. - * @param array $item Share data (usually database row) - * @return bool True if item was expired, false otherwise. - */ - protected static function expireItem(array $item) { - if (!empty($item['expiration'])) { - $now = new \DateTime(); - $expires = new \DateTime($item['expiration']); - if ($now > $expires) { - self::unshareItem($item); - return true; - } - } - return false; - } - - /** - * Unshares a share given a share data array - * @param array $item Share data (usually database row) - * @return null - */ - protected static function unshareItem(array $item) { - // Pass all the vars we have for now, they may be useful - $hookParams = array( - 'itemType' => $item['item_type'], - 'itemSource' => $item['item_source'], - 'shareType' => $item['share_type'], - 'shareWith' => $item['share_with'], - 'itemParent' => $item['parent'], - 'uidOwner' => $item['uid_owner'], - ); - - \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams + array( - 'fileSource' => $item['file_source'], - )); - self::delete($item['id']); - \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams); + return \OC\Share\Share::setExpirationDate($itemType, $itemSource, $date); } /** @@ -944,997 +313,14 @@ class Share { * @return Share_Backend */ public static function getBackend($itemType) { - 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 Share_Backend)) { - $message = 'Sharing backend '.$class.' must implement the interface OCP\Share_Backend'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - return self::$backends[$itemType]; - } else { - $message = 'Sharing backend '.$class.' not found'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - $message = 'Sharing backend for '.$itemType.' not found'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - - /** - * Check if resharing is allowed - * @return boolean true if allowed or false - * - * Resharing is allowed by default if not configured - */ - private static function isResharingAllowed() { - if (!isset(self::$isResharingAllowed)) { - if (\OC_Appconfig::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 Item type - * @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 (!self::getBackend($itemType) instanceof 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 shared items from the database - * @param string Item type - * @param string Item source or target (optional) - * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique - * @param string User or group the item is being shared with - * @param string User that is the owner of shared items (optional) - * @param int Format to convert items to with formatItems() - * @param mixed Parameters to pass to formatItems() - * @param int Number of items to return, -1 to return all matches (optional) - * @param bool Include collection item types (optional) - * @param bool TODO (optional) - * @prams bool check expire date - * @return mixed - * - * See public functions getItem(s)... for parameter usage - * - */ - private 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()) { - if ($limit == 1 || (isset($uidOwner) && isset($item))) { - return false; - } else { - 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 .= ' WHERE `file_target` IS NOT NULL'; - } - $fileDependent = true; - $queryArgs = array(); - } else { - $fileDependent = false; - $root = ''; - if ($includeCollections && !isset($item) && ($collectionTypes = self::getCollectionItemTypes($itemType))) { - // 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_Appconfig::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 (?,?,?)'; - $queryArgs[] = self::SHARE_TYPE_USER; - $queryArgs[] = self::SHARE_TYPE_GROUP; - $queryArgs[] = self::$shareTypeGroupUserUnique; - $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); - $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); - $where .= ' AND `share_with` IN ('.$placeholders.')'; - $queryArgs = array_merge($queryArgs, $userAndGroups); - // 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 ($itemType == 'file' || $itemType == 'folder') { - $column = 'file_source'; - } else { - $column = 'item_source'; - } - } else { - if ($itemType == 'file' || $itemType == 'folder') { - $column = 'file_target'; - } else { - $column = 'item_target'; - } - } - if (isset($item)) { - if ($includeCollections && $collectionTypes = self::getCollectionItemTypes($itemType)) { - $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 ($itemType == 'file' || $itemType == 'folder') { - $where .= ' `file_source` = ?'; - $column = 'file_source'; - } else { - $where .= ' `item_source` = ?'; - $column = 'item_source'; - } - } else { - if ($itemType == 'file' || $itemType == 'folder') { - $where .= ' `file_target` = ?'; - $item = \OC\Files\Filesystem::normalizePath($item); - } else { - $where .= ' `item_target` = ?'; - } - } - $queryArgs[] = $item; - if ($includeCollections && $collectionTypes) { - $placeholders = join(',', array_fill(0, count($collectionTypes), '?')); - $where .= ' OR `item_type` IN ('.$placeholders.'))'; - $queryArgs = array_merge($queryArgs, $collectionTypes); - } - } - if ($limit != -1 && !$includeCollections) { - if ($shareType == self::$shareTypeUserAndGroups) { - // 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'; - } - // The limit must be at least 3, because filtering needs to be done - if ($limit < 3) { - $queryLimit = 3; - } else { - $queryLimit = $limit; - } - } else { - $queryLimit = null; - } - // TODO Optimize selects - if ($format == self::FORMAT_STATUSES) { - if ($itemType == 'file' || $itemType == 'folder') { - $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,' - .' `share_type`, `file_source`, `path`, `expiration`, `storage`, `share_with`, `mail_send`, `uid_owner`'; - } else { - $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `expiration`, `mail_send`, `uid_owner`'; - } - } else { - if (isset($uidOwner)) { - if ($itemType == 'file' || $itemType == 'folder') { - $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,' - .' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,' - .' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`'; - } else { - $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,' - .' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`'; - } - } else { - if ($fileDependent) { - if (($itemType == 'file' || $itemType == 'folder') - && $format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS - || $format == \OC_Share_Backend_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`, ' - .'`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, ' - .'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `unencrypted_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`, `permissions`, `stime`, `expiration`, `token`, `storage`, `mail_send`'; - } - } else { - $select = '*'; - } - } - } - $root = strlen($root); - $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit); - $result = $query->execute($queryArgs); - if (\OC_DB::isError($result)) { - \OC_Log::write('OCP\Share', - \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, - \OC_Log::ERROR); - } - $items = array(); - $targets = array(); - $switchedItems = array(); - $mounts = array(); - while ($row = $result->fetchRow()) { - 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']; - } - // Filter out duplicate group shares for users with unique targets - if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) { - $row['share_type'] = self::SHARE_TYPE_GROUP; - $row['share_with'] = $items[$row['parent']]['share_with']; - // 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[$column]])) { - // Check if the same owner shared with the user twice - // through a group and user share - this is allowed - $id = $targets[$row[$column]]; - 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'] & PERMISSION_SHARE - && (int)$row['permissions'] & PERMISSION_SHARE) { - $items[$row['id']] = $items[$id]; - $switchedItems[$id] = $row['id']; - unset($items[$id]); - $id = $row['id']; - } - // Combine the permissions for the item - $items[$id]['permissions'] |= (int)$row['permissions']; - continue; - } - } else { - $targets[$row[$column]] = $row['id']; - } - } - // Remove root from file source paths if retrieving own shared items - if (isset($uidOwner) && isset($row['path'])) { - if (isset($row['parent'])) { - $row['path'] = '/Shared/'.basename($row['path']); - } else { - if (!isset($mounts[$row['storage']])) { - $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']); - if (is_array($mountPoints)) { - $mounts[$row['storage']] = current($mountPoints); - } - } - if ($mounts[$row['storage']]) { - $path = $mounts[$row['storage']]->getMountPoint().$row['path']; - $row['path'] = substr($path, $root); - } - } - } - if($checkExpireDate) { - if (self::expireItem($row)) { - continue; - } - } - // Check if resharing is allowed, if not remove share permission - if (isset($row['permissions']) && !self::isResharingAllowed()) { - $row['permissions'] &= ~PERMISSION_SHARE; - } - // Add display names to result - if ( isset($row['share_with']) && $row['share_with'] != '') { - $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']); - } - if ( isset($row['uid_owner']) && $row['uid_owner'] != '') { - $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']); - } - - $items[$row['id']] = $row; - } - 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 && in_array($row['item_type'], $collectionTypes)) { - if (($collectionBackend = self::getBackend($row['item_type'])) - && $collectionBackend instanceof 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 Share_Backend_File_Dependent) { - if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { - $childItem['file_source'] = $child['source']; - } else { - $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]); - } - } - if (!empty($collectionItems)) { - $items = array_merge($items, $collectionItems); - } - if (empty($items) && $limit == 1) { - return false; - } - 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) { - $statuses[$item[$column]]['link'] = true; - } else if (!isset($statuses[$item[$column]])) { - $statuses[$item[$column]]['link'] = false; - } - if ($itemType == 'file' || $itemType == 'folder') { - $statuses[$item[$column]]['path'] = $item['path']; - } - } - return $statuses; - } else { - return $backend->formatItems($items, $format, $parameters); - } - } else if ($limit == 1 || (isset($uidOwner) && isset($item))) { - return false; - } - return array(); - } - - /** - * Put shared item into the database - * @param string $itemType Item type - * @param string $itemSource Item source - * @param integer $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 bool|array, $parentFolder Parent folder target (optional) - * @param string $token (optional) - * @param string $itemSourceName name of the source item (optional) - * @return bool Returns true on success or false on failure - */ - private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $permissions, $parentFolder = null, $token = null, $itemSourceName = null) { - $backend = self::getBackend($itemType); - - // Check if this is a reshare - if ($checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true)) { - - // Check if attempting to share back to owner - if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) { - $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' is the original sharer'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - // Check if share permissions is granted - if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & PERMISSION_SHARE) { - if (~(int)$checkReshare['permissions'] & $permissions) { - $message = 'Sharing '.$itemSourceName - .' failed, because the permissions exceed permissions granted to '.$uidOwner; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } else { - // TODO Don't check if inside folder - $parent = $checkReshare['id']; - $itemSource = $checkReshare['item_source']; - $fileSource = $checkReshare['file_source']; - $suggestedItemTarget = $checkReshare['item_target']; - $suggestedFileTarget = $checkReshare['file_target']; - $filePath = $checkReshare['file_target']; - } - } else { - $message = 'Sharing '.$itemSourceName.' failed, because resharing is not allowed'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } else { - $parent = null; - $suggestedItemTarget = null; - $suggestedFileTarget = null; - if (!$backend->isValidSource($itemSource, $uidOwner)) { - $message = 'Sharing '.$itemSource.' failed, because the sharing backend for ' - .$itemType.' could not find its source'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - $parent = null; - if ($backend instanceof Share_Backend_File_Dependent) { - $filePath = $backend->getFilePath($itemSource, $uidOwner); - if ($itemType == 'file' || $itemType == 'folder') { - $fileSource = $itemSource; - } else { - $meta = \OC\Files\Filesystem::getFileInfo($filePath); - $fileSource = $meta['fileid']; - } - if ($fileSource == -1) { - $message = 'Sharing '.$itemSource.' failed, because the file could not be found in the file cache'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } else { - $filePath = null; - $fileSource = null; - } - } - $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`, `token`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); - // Share with a group - if ($shareType == self::SHARE_TYPE_GROUP) { - $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], - $uidOwner, $suggestedItemTarget); - $run = true; - $error = ''; - \OC_Hook::emit('OCP\Share', 'pre_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $groupItemTarget, - 'shareType' => $shareType, - 'shareWith' => $shareWith['group'], - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'token' => $token, - 'run' => &$run, - 'error' => &$error - )); - - if ($run === false) { - throw new \Exception($error); - } - - if (isset($fileSource)) { - if ($parentFolder) { - if ($parentFolder === true) { - $groupFileTarget = self::generateTarget('file', $filePath, $shareType, - $shareWith['group'], $uidOwner, $suggestedFileTarget); - // Set group default file target for future use - $parentFolders[0]['folder'] = $groupFileTarget; - } else { - // Get group default file target - $groupFileTarget = $parentFolder[0]['folder'].$itemSource; - $parent = $parentFolder[0]['id']; - } - } else { - $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], - $uidOwner, $suggestedFileTarget); - } - } else { - $groupFileTarget = null; - } - $query->execute(array($itemType, $itemSource, $groupItemTarget, $parent, $shareType, - $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget, $token)); - // Save this id, any extra rows for this group share will need to reference it - $parent = \OC_DB::insertid('*PREFIX*share'); - // Loop through all users of this group in case we need to add an extra row - foreach ($shareWith['users'] as $uid) { - $itemTarget = self::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid, - $uidOwner, $suggestedItemTarget, $parent); - if (isset($fileSource)) { - if ($parentFolder) { - if ($parentFolder === true) { - $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, - $uidOwner, $suggestedFileTarget, $parent); - if ($fileTarget != $groupFileTarget) { - $parentFolders[$uid]['folder'] = $fileTarget; - } - } else if (isset($parentFolder[$uid])) { - $fileTarget = $parentFolder[$uid]['folder'].$itemSource; - $parent = $parentFolder[$uid]['id']; - } - } else { - $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, - $uid, $uidOwner, $suggestedFileTarget, $parent); - } - } else { - $fileTarget = null; - } - // Insert an extra row for the group share if the item or file target is unique for this user - if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) { - $query->execute(array($itemType, $itemSource, $itemTarget, $parent, - self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), - $fileSource, $fileTarget, $token)); - $id = \OC_DB::insertid('*PREFIX*share'); - } - } - \OC_Hook::emit('OCP\Share', 'post_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $groupItemTarget, - 'parent' => $parent, - 'shareType' => $shareType, - 'shareWith' => $shareWith['group'], - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'fileTarget' => $groupFileTarget, - 'id' => $parent, - 'token' => $token - )); - - if ($parentFolder === true) { - // Return parent folders to preserve file target paths for potential children - return $parentFolders; - } - } else { - $itemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $suggestedItemTarget); - $run = true; - $error = ''; - \OC_Hook::emit('OCP\Share', 'pre_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $itemTarget, - 'shareType' => $shareType, - 'shareWith' => $shareWith, - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'token' => $token, - 'run' => &$run, - 'error' => &$error - )); - - if ($run === false) { - throw new \Exception($error); - } - - if (isset($fileSource)) { - if ($parentFolder) { - if ($parentFolder === true) { - $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, - $uidOwner, $suggestedFileTarget); - $parentFolders['folder'] = $fileTarget; - } else { - $fileTarget = $parentFolder['folder'].$itemSource; - $parent = $parentFolder['id']; - } - } else { - $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, - $suggestedFileTarget); - } - } else { - $fileTarget = null; - } - $query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, - $permissions, time(), $fileSource, $fileTarget, $token)); - $id = \OC_DB::insertid('*PREFIX*share'); - \OC_Hook::emit('OCP\Share', 'post_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $itemTarget, - 'parent' => $parent, - 'shareType' => $shareType, - 'shareWith' => $shareWith, - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'fileTarget' => $fileTarget, - 'id' => $id, - 'token' => $token - )); - if ($parentFolder === true) { - $parentFolders['id'] = $id; - // Return parent folder to preserve file target paths for potential children - return $parentFolders; - } - } - return true; - } - - /** - * Generate a unique target for the item - * @param string Item type - * @param string Item source - * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK - * @param string User or group the item is being shared with - * @param string User that is the owner of shared item - * @param string The suggested target originating from a reshare (optional) - * @param int The id of the parent group share (optional) - * @param integer $shareType - * @return string Item target - */ - private static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $suggestedTarget = null, $groupParent = null) { - $backend = self::getBackend($itemType); - if ($shareType == self::SHARE_TYPE_LINK) { - if (isset($suggestedTarget)) { - return $suggestedTarget; - } - return $backend->generateTarget($itemSource, false); - } else { - if ($itemType == 'file' || $itemType == 'folder') { - $column = 'file_target'; - $columnSource = 'file_source'; - } else { - $column = 'item_target'; - $columnSource = 'item_source'; - } - if ($shareType == self::SHARE_TYPE_USER) { - // Share with is a user, so set share type to user and groups - $shareType = self::$shareTypeUserAndGroups; - $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); - } else { - $userAndGroups = false; - } - $exclude = null; - // Backend has 3 opportunities to generate a unique target - for ($i = 0; $i < 2; $i++) { - // Check if suggested target exists first - if ($i == 0 && isset($suggestedTarget)) { - $target = $suggestedTarget; - } else { - if ($shareType == self::SHARE_TYPE_GROUP) { - $target = $backend->generateTarget($itemSource, false, $exclude); - } else { - $target = $backend->generateTarget($itemSource, $shareWith, $exclude); - } - if (is_array($exclude) && in_array($target, $exclude)) { - break; - } - } - // Check if target already exists - $checkTarget = self::getItems($itemType, $target, $shareType, $shareWith); - if (!empty($checkTarget)) { - foreach ($checkTarget as $item) { - // Skip item if it is the group parent row - if (isset($groupParent) && $item['id'] == $groupParent) { - if (count($checkTarget) == 1) { - return $target; - } else { - continue; - } - } - if ($item['uid_owner'] == $uidOwner) { - if ($itemType == 'file' || $itemType == 'folder') { - $meta = \OC\Files\Filesystem::getFileInfo($itemSource); - if ($item['file_source'] == $meta['fileid']) { - return $target; - } - } else if ($item['item_source'] == $itemSource) { - return $target; - } - } - } - if (!isset($exclude)) { - $exclude = array(); - } - // Find similar targets to improve backend's chances to generate a unqiue target - if ($userAndGroups) { - if ($column == 'file_target') { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` IN (\'file\', \'folder\')' - .' AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); - $result = $checkTargets->execute(array(self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, - self::$shareTypeGroupUserUnique)); - } else { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` = ? AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); - $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER, - self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique)); - } - } else { - if ($column == 'file_target') { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` IN (\'file\', \'folder\')' - .' AND `share_type` = ? AND `share_with` = ?'); - $result = $checkTargets->execute(array(self::SHARE_TYPE_GROUP, $shareWith)); - } else { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` = ? AND `share_type` = ? AND `share_with` = ?'); - $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith)); - } - } - while ($row = $result->fetchRow()) { - $exclude[] = $row[$column]; - } - } else { - return $target; - } - } - } - $message = 'Sharing backend registered for '.$itemType.' did not generate a unique target for '.$itemSource; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - - /** - * Delete all reshares of an item - * @param int Id of item to delete - * @param bool If true, exclude the parent from the delete (optional) - * @param string The user that the parent was shared with (optinal) - */ - private static function delete($parent, $excludeParent = false, $uidOwner = null) { - $ids = array($parent); - $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 - if (count($ids) == 1 && isset($uidOwner)) { - $query = \OC_DB::prepare('SELECT `id`, `uid_owner`, `item_type`, `item_target`, `parent`' - .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.') AND `uid_owner` = ?'); - $result = $query->execute(array($uidOwner)); - } else { - $query = \OC_DB::prepare('SELECT `id`, `item_type`, `item_target`, `parent`, `uid_owner`' - .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.')'); - $result = $query->execute(); - } - // Reset parents array, only go through loop again if items are found - $parents = array(); - while ($item = $result->fetchRow()) { - // Search for a duplicate parent share, this occurs when an - // item is shared to the same user through a group and user or the - // same item is shared by different users - $userAndGroups = array_merge(array($item['uid_owner']), \OC_Group::getUserGroups($item['uid_owner'])); - $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' - .' WHERE `item_type` = ?' - .' AND `item_target` = ?' - .' AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')' - .' AND `uid_owner` != ? AND `id` != ?'); - $duplicateParent = $query->execute(array($item['item_type'], $item['item_target'], - self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique, - $item['uid_owner'], $item['parent']))->fetchRow(); - if ($duplicateParent) { - // Change the parent to the other item id if share permission is granted - if ($duplicateParent['permissions'] & PERMISSION_SHARE) { - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` = ?'); - $query->execute(array($duplicateParent['id'], $item['id'])); - continue; - } - } - $ids[] = $item['id']; - $parents[] = $item['id']; - } - } - if ($excludeParent) { - unset($ids[0]); - } - if (!empty($ids)) { - $ids = "'".implode("','", $ids)."'"; - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$ids.')'); - $query->execute(); - } + return \OC\Share\Share::getBackend($itemType); } /** * 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()) { - self::delete($item['id']); - } - } - - /** - * Hook Listeners - */ - - /** - * Function that is called after a user is deleted. Cleans up the shares of that user. - * @param array arguments - */ - public static function post_deleteUser($arguments) { - // Delete any items shared with the deleted user - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`' - .' WHERE `share_with` = ? AND `share_type` = ? OR `share_type` = ?'); - $result = $query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique)); - // Delete any items the deleted user shared - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?'); - $result = $query->execute(array($arguments['uid'])); - while ($item = $result->fetchRow()) { - self::delete($item['id']); - } - } - - /** - * Function that is called after a user is added to a group. - * TODO what does it do? - * @param array arguments - */ - public static function post_addToGroup($arguments) { - // Find the group shares and check if the user needs a unique target - $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'); - $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid'])); - $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 (?,?,?,?,?,?,?,?,?,?,?)'); - while ($item = $result->fetchRow()) { - if ($item['item_type'] == 'file' || $item['item_type'] == 'file') { - $itemTarget = null; - } else { - $itemTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, - $arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']); - } - if (isset($item['file_source'])) { - $fileTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, - $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']); - } else { - $fileTarget = null; - } - // Insert an extra row for the group share if the item or file target is unique for this user - if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) { - $query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'], - self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'], - $item['stime'], $item['file_source'], $fileTarget)); - \OC_DB::insertid('*PREFIX*share'); - } - } - } - - /** - * Function that is called after a user is removed from a group. Shares are cleaned up. - * @param array arguments - */ - public static function post_removeFromGroup($arguments) { - // TODO Don't call if user deleted? - $sql = 'SELECT `id`, `share_type` FROM `*PREFIX*share`' - .' WHERE (`share_type` = ? AND `share_with` = ?) OR (`share_type` = ? AND `share_with` = ?)'; - $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'], - self::$shareTypeGroupUserUnique, $arguments['uid'])); - while ($item = $result->fetchRow()) { - if ($item['share_type'] == self::SHARE_TYPE_GROUP) { - // Delete all reshares by this user of the group share - self::delete($item['id'], true, $arguments['uid']); - } else { - self::delete($item['id']); - } - } - } - - /** - * Function that is called after a group is removed. Cleans up the shares to that group. - * @param array arguments - */ - public static function post_deleteGroup($arguments) { - $sql = 'SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'; - $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'])); - while ($item = $result->fetchRow()) { - self::delete($item['id']); - } + return \OC\Share\Share::removeAllLinkShares(); } /** @@ -1944,26 +330,7 @@ class Share { * @return bool */ 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::$session->exists('public_link_authenticated') - && \OC::$session->get('public_link_authenticated') === $linkItem['id'] ) { - return true; - } - - return false; + return \OC\Share\Share::checkPasswordProtectedShare($linkItem); } } @@ -1976,9 +343,7 @@ interface Share_Backend { * Get the source of the item to be stored in the database * @param string Item source * @param string Owner of the item - * @param string $itemSource - * @param string $uidOwner - * @return boolean Source + * @return mixed|array|false Source * * Return an array if the item is file dependent, the array needs two keys: 'item' and 'file' * Return false if the item does not exist for the user @@ -2001,8 +366,8 @@ interface Share_Backend { /** * Converts the shared item sources back into the item in the specified format - * @param array $items Shared items - * @param integer $format + * @param array Shared items + * @param int Format * @return TODO * * The items array is a 3-dimensional array with the item_source as the @@ -2034,9 +399,6 @@ interface Share_Backend_File_Dependent extends Share_Backend { * Get the file path of the item * @param string Item source * @param string User that is the owner of shared item - * @param string $itemSource - * @param string $uidOwner - * @return boolean */ public function getFilePath($itemSource, $uidOwner); diff --git a/lib/public/template.php b/lib/public/template.php index 9a994c1bea8..6cc984b12d5 100644 --- a/lib/public/template.php +++ b/lib/public/template.php @@ -67,7 +67,7 @@ function preview_icon( $path ) { * Returns the path to the preview of the image. * @param string $path of file * @param string $token - * @return link to the preview + * @return string link to the preview */ function publicPreview_icon ( $path, $token ) { return(\publicPreview_icon( $path, $token )); diff --git a/lib/public/util.php b/lib/public/util.php index 585c5d22634..3b0fc09f7e5 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -57,7 +57,7 @@ class Util { * @param string $mailtext * @param string $fromaddress * @param string $fromname - * @param bool $html + * @param int $html * @param string $altbody * @param string $ccaddress * @param string $ccname @@ -85,25 +85,23 @@ class Util { * write exception into the log. Include the stack trace * if DEBUG mode is enabled * @param string $app app name - * @param Exception $ex exception to log + * @param \Exception $ex exception to log + * @param string $level log level, defaults to \OCP\Util::FATAL */ - public static function logException( $app, \Exception $ex ) { + public static function logException( $app, \Exception $ex, $level = \OCP\Util::FATAL ) { $class = get_class($ex); - if ($class !== 'Exception') { - $message = $class . ': '; - } - $message .= $ex->getMessage(); + $message = $class . ': ' . $ex->getMessage(); if ($ex->getCode()) { $message .= ' [' . $ex->getCode() . ']'; } - \OCP\Util::writeLog($app, 'Exception: ' . $message, \OCP\Util::FATAL); + \OCP\Util::writeLog($app, $message, $level); if (defined('DEBUG') and DEBUG) { // also log stack trace $stack = explode("\n", $ex->getTraceAsString()); // first element is empty array_shift($stack); foreach ($stack as $s) { - \OCP\Util::writeLog($app, 'Exception: ' . $s, \OCP\Util::FATAL); + \OCP\Util::writeLog($app, 'Exception: ' . $s, $level); } // include cause @@ -113,7 +111,7 @@ class Util { if ($ex->getCode()) { $message .= '[' . $ex->getCode() . '] '; } - \OCP\Util::writeLog($app, 'Exception: ' . $message, \OCP\Util::FATAL); + \OCP\Util::writeLog($app, 'Exception: ' . $message, $level); } } } @@ -159,6 +157,7 @@ class Util { * formats a timestamp in the "right" way * @param int $timestamp $timestamp * @param bool $dateOnly option to omit time from the result + * @return string timestamp */ public static function formatDate( $timestamp, $dateOnly=false) { return(\OC_Util::formatDate( $timestamp, $dateOnly )); @@ -206,9 +205,8 @@ class Util { * Creates an url using a defined route * @param $route * @param array $parameters - * @return * @internal param array $args with param=>value, will be appended to the returned url - * @return the url + * @return string the url */ public static function linkToRoute( $route, $parameters = array() ) { return \OC_Helper::linkToRoute($route, $parameters); @@ -269,7 +267,7 @@ class Util { $host_name = \OC_Config::getValue('mail_domain', $host_name); $defaultEmailAddress = $user_part.'@'.$host_name; - if (\OC_Mail::ValidateAddress($defaultEmailAddress)) { + if (\OC_Mail::validateAddress($defaultEmailAddress)) { return $defaultEmailAddress; } @@ -287,8 +285,7 @@ class Util { /** * Returns the request uri, even if the website uses one or more reverse proxies - * - * @return the request uri + * @return string the request uri */ public static function getRequestUri() { return(\OC_Request::requestUri()); @@ -296,8 +293,7 @@ class Util { /** * Returns the script name, even if the website uses one or more reverse proxies - * - * @return the script name + * @returns string the script name */ public static function getScriptName() { return(\OC_Request::scriptName()); @@ -353,7 +349,7 @@ class Util { * 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 string $params defautl: array() array with additional data + * @param array $params default: array() array with additional data * @return bool true if slots exists or false if not * * TODO: write example @@ -470,9 +466,8 @@ class Util { /** * Calculate free space left within user quota - * - * @param $dir the current folder where the user currently operates - * @return number of bytes representing + * @param string $dir the current folder where the user currently operates + * @return int number of bytes representing */ public static function freeSpace($dir) { return \OC_Helper::freeSpace($dir); @@ -495,4 +490,13 @@ class Util { public static function isValidFileName($file) { return \OC_Util::isValidFileName($file); } + + /** + * @brief Generates a cryptographic secure pseudo-random string + * @param Int $length of the random string + * @return String + */ + public static function generateRandomBytes($length = 30) { + return \OC_Util::generateRandomBytes($length); + } } |