diff options
Diffstat (limited to 'lib')
132 files changed, 3938 insertions, 3136 deletions
diff --git a/lib/base.php b/lib/base.php index 49cbb1279d1..15a3ec8bc8a 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/'; @@ -316,7 +313,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 +384,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'; @@ -537,6 +520,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,7 +538,8 @@ 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') + $basic_auth = OC_Config::getValue('basic_auth', true); + if ($basic_auth && 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']; @@ -564,16 +549,10 @@ class OC { 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 +640,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 +653,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'); } } @@ -693,6 +675,22 @@ 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( + 'You are accessing the server from an untrusted domain.', + 'Please contact your administrator' + ); + return; + } + $request = OC_Request::getPathInfo(); if (substr($request, -3) !== '.js') { // we need these files during the upgrade self::checkMaintenanceMode(); @@ -708,7 +706,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'); @@ -847,7 +845,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); } @@ -919,7 +917,7 @@ class OC { ) { 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(); @@ -930,11 +928,6 @@ class OC { } -// define runtime variables - unless this already has been done -if (!isset($RUNTIME_NOAPPS)) { - $RUNTIME_NOAPPS = false; -} - if (!function_exists('get_temp_dir')) { function get_temp_dir() { if ($temp = ini_get('upload_tmp_dir')) return $temp; @@ -953,4 +946,3 @@ if (!function_exists('get_temp_dir')) { } OC::init(); - 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..6287a53d619 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("","","","","",""), 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..88bb8ec9eee 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"), diff --git a/lib/l10n/cs_CZ.php b/lib/l10n/cs_CZ.php index df3a47b5ae3..6b5c8441ca1 100644 --- a/lib/l10n/cs_CZ.php +++ b/lib/l10n/cs_CZ.php @@ -11,7 +11,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 +31,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 +40,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 +55,7 @@ $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«", "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"), 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..c3166000727 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"), diff --git a/lib/l10n/de.php b/lib/l10n/de.php index b1045892fb1..d644582b91e 100644 --- a/lib/l10n/de.php +++ b/lib/l10n/de.php @@ -11,7 +11,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 +31,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 +40,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 +55,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." => "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", "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"), 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..fe1a519ccf7 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"), diff --git a/lib/l10n/de_DE.php b/lib/l10n/de_DE.php index 1a1c9783f42..85ed7e5c80a 100644 --- a/lib/l10n/de_DE.php +++ b/lib/l10n/de_DE.php @@ -11,7 +11,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 +21,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 +31,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 +40,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 +55,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 hat »%s« mit Ihnen geteilt", "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"), diff --git a/lib/l10n/el.php b/lib/l10n/el.php index 7f7797bbc7a..148b7fc1fe5 100644 --- a/lib/l10n/el.php +++ b/lib/l10n/el.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" => "Πίσω στα Αρχεία", @@ -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,11 @@ $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", +"MySQL/MariaDB user '%s'@'%%' already exists" => "Υπάρχει ήδη ο χρήστης '%s'@'%%' της 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 +53,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 λεπτά πριν"), diff --git a/lib/l10n/en_GB.php b/lib/l10n/en_GB.php index e2e8ee2e541..7d2246eb66b 100644 --- a/lib/l10n/en_GB.php +++ b/lib/l10n/en_GB.php @@ -11,7 +11,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 +31,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 +40,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 +55,7 @@ $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", "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"), 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..14d6435c891 100644 --- a/lib/l10n/es.php +++ b/lib/l10n/es.php @@ -11,12 +11,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 +31,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 +40,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 +55,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 compatido »%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"), diff --git a/lib/l10n/es_AR.php b/lib/l10n/es_AR.php index bc5fcd7e012..a925e22ba88 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"), 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_MX.php b/lib/l10n/es_MX.php index 7454d4966d8..2f0ed33f6b3 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"), diff --git a/lib/l10n/et_EE.php b/lib/l10n/et_EE.php index 96fceaa04ed..f6435320c6c 100644 --- a/lib/l10n/et_EE.php +++ b/lib/l10n/et_EE.php @@ -11,7 +11,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", @@ -40,13 +39,8 @@ $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", "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.", "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 +49,7 @@ $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«", "Could not find category \"%s\"" => "Ei leia kategooriat \"%s\"", "seconds ago" => "sekundit tagasi", "_%n minute ago_::_%n minutes ago_" => array("","%n minutit tagasi"), diff --git a/lib/l10n/eu.php b/lib/l10n/eu.php index e3f18fca47a..58c198cff14 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"), 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..7323ffe1918 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(""), diff --git a/lib/l10n/fi_FI.php b/lib/l10n/fi_FI.php index 573704da44c..018e4c04c4f 100644 --- a/lib/l10n/fi_FI.php +++ b/lib/l10n/fi_FI.php @@ -28,6 +28,7 @@ $TRANSLATIONS = array( "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 +36,19 @@ $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ä.", +"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", "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.", "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", "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"), diff --git a/lib/l10n/fr.php b/lib/l10n/fr.php index 75a4f277271..d866fb664ee 100644 --- a/lib/l10n/fr.php +++ b/lib/l10n/fr.php @@ -11,7 +11,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 +31,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 +40,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 +55,7 @@ $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", "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"), diff --git a/lib/l10n/gl.php b/lib/l10n/gl.php index 81a62021556..cc1351c2f15 100644 --- a/lib/l10n/gl.php +++ b/lib/l10n/gl.php @@ -11,7 +11,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 +31,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 +40,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,6 +55,7 @@ $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", "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"), diff --git a/lib/l10n/he.php b/lib/l10n/he.php index 5bbfffe9ae9..6a9020009e9 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 דקות"), diff --git a/lib/l10n/hu_HU.php b/lib/l10n/hu_HU.php index efaf2a2fd48..2a03f72a51e 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"), diff --git a/lib/l10n/id.php b/lib/l10n/id.php index 27d7843104b..76dda80cf70 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"), diff --git a/lib/l10n/it.php b/lib/l10n/it.php index cd2073bfd0a..ed1cbc2e31a 100644 --- a/lib/l10n/it.php +++ b/lib/l10n/it.php @@ -11,7 +11,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 +24,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 +40,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 +55,7 @@ $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", "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"), diff --git a/lib/l10n/ja.php b/lib/l10n/ja.php new file mode 100644 index 00000000000..015c885904e --- /dev/null +++ b/lib/l10n/ja.php @@ -0,0 +1,71 @@ +<?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." => "ファイルは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«を共有しました", +"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" => "年前" +); +$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/ka_GE.php b/lib/l10n/ka_GE.php index 0cf6ab333e8..e2a719d0746 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 იუზერნეიმი და/ან პაროლი არ არის სწორი", diff --git a/lib/l10n/km.php b/lib/l10n/km.php index e7b09649a24..51dd4f33a7e 100644 --- a/lib/l10n/km.php +++ b/lib/l10n/km.php @@ -1,8 +1,44 @@ <?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" => "ឆ្នាំមុន" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/lib/l10n/ko.php b/lib/l10n/ko.php index b33ad01546f..833476f6464 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" => "파일로 돌아가기", @@ -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." => "기존 계정이나 administrator(관리자)를 입력해야 합니다.", -"MySQL username and/or password not valid" => "MySQL 사용자 이름이나 암호가 잘못되었습니다.", "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에서 삭제하십시오.", "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." => "관리자의 암호를 설정합니다.", "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분 전 "), 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..dac8eed7633 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ų"), diff --git a/lib/l10n/lv.php b/lib/l10n/lv.php index 8ecee5bdae8..5461e077ec9 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"), 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..4b41e54ef44 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"), diff --git a/lib/l10n/nl.php b/lib/l10n/nl.php index 2f6205fcf1c..dcf893af630 100644 --- a/lib/l10n/nl.php +++ b/lib/l10n/nl.php @@ -11,7 +11,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 +31,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 +40,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 +55,7 @@ $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", "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"), diff --git a/lib/l10n/nn_NO.php b/lib/l10n/nn_NO.php index e8bf8dfdef4..db257f35dce 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"), diff --git a/lib/l10n/pl.php b/lib/l10n/pl.php index fe3e876916a..bc5e4a947c7 100644 --- a/lib/l10n/pl.php +++ b/lib/l10n/pl.php @@ -11,7 +11,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 +31,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 +40,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 +55,7 @@ $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ą", "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"), diff --git a/lib/l10n/pt_BR.php b/lib/l10n/pt_BR.php index cc20fb3cb02..6c8ea586de8 100644 --- a/lib/l10n/pt_BR.php +++ b/lib/l10n/pt_BR.php @@ -11,7 +11,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 +31,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 +40,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 +55,7 @@ $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ê", "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"), diff --git a/lib/l10n/pt_PT.php b/lib/l10n/pt_PT.php index bd9165ebb1a..e6dd459a7e1 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,8 @@ $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", "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", "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 +50,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« contigo", "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"), diff --git a/lib/l10n/ro.php b/lib/l10n/ro.php index 94ff7a4326a..6bff105a61f 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"), diff --git a/lib/l10n/ru.php b/lib/l10n/ru.php index 34d1730aaf2..214849721da 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 минут назад"), diff --git a/lib/l10n/sk_SK.php b/lib/l10n/sk_SK.php index 59c45e2b0bc..546e3017978 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", @@ -40,13 +39,8 @@ $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é", "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.", "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 +49,7 @@ $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«", "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"), diff --git a/lib/l10n/sl.php b/lib/l10n/sl.php index 3cc8dd130c8..411a14370ee 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,7 @@ $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«", "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"), diff --git a/lib/l10n/sq.php b/lib/l10n/sq.php index b36aa4ceefc..b22663f76df 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ë"), 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..a1f371210ac 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", @@ -40,13 +39,8 @@ $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", "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.", "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 +49,7 @@ $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", "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"), 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..2fce8b87bf4 100644 --- a/lib/l10n/tr.php +++ b/lib/l10n/tr.php @@ -11,7 +11,6 @@ $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", @@ -32,6 +31,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", @@ -40,13 +40,13 @@ $TRANSLATIONS = array( "%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", +"MySQL/MariaDB username and/or password not valid" => "MySQL/MariaDB kullanıcı adı ve/veya parolası geçersiz", "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", +"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. ", "Offending command was: \"%s\", name: %s, password: %s" => "Hatalı komut: \"%s\", ad: %s, parola: %s", @@ -55,6 +55,7 @@ $TRANSLATIONS = array( "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.", "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", "Could not find category \"%s\"" => "\"%s\" kategorisi bulunamadı", "seconds ago" => "saniye önce", "_%n minute ago_::_%n minutes ago_" => array("","%n dakika önce"), diff --git a/lib/l10n/uk.php b/lib/l10n/uk.php index 32e010f1d40..94163f0b6f5 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 хвилин тому"), 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..684ee17f98e 100644 --- a/lib/l10n/zh_CN.php +++ b/lib/l10n/zh_CN.php @@ -5,6 +5,7 @@ $TRANSLATIONS = array( "Settings" => "设置", "Users" => "用户", "Admin" => "管理", +"Failed to upgrade \"%s\"." => "\"%s\" 升级失败。", "Unknown filetype" => "未知的文件类型", "Invalid image" => "无效的图像", "web services under your control" => "您控制的web服务", @@ -13,6 +14,8 @@ $TRANSLATIONS = array( "Back to Files" => "回到文件", "Selected files too large to generate zip file." => "选择的文件太大,无法生成 zip 文件。", "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." => "Token 过期,请刷新页面。", @@ -24,13 +27,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 数据库中丢弃 Drop 此用户", -"MySQL user '%s'@'%%' already exists" => "MySQL 用户 '%s'@'%%' 已存在", -"Drop this user from MySQL." => "建议从 MySQL 数据库中丢弃 Drop 此用户。", "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 +37,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 分钟前"), diff --git a/lib/l10n/zh_TW.php b/lib/l10n/zh_TW.php index 35719c8b17e..1fbae6e2355 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 分鐘前"), diff --git a/lib/private/api.php b/lib/private/api.php index 3f96196e6df..b3b5eb1067b 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(); } @@ -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..58bf67c1d47 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); } diff --git a/lib/private/appframework/routing/routeconfig.php b/lib/private/appframework/routing/routeconfig.php index 716358444a2..35bee75cc4d 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() { diff --git a/lib/private/cache/file.php b/lib/private/cache/file.php index 8a6ef39f61b..be6805a9a57 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 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'); } } diff --git a/lib/private/config.php b/lib/private/config.php index 3649da84973..56f47256134 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, 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..3ed9e94d69b 100644 --- a/lib/private/connector/sabre/directory.php +++ b/lib/private/connector/sabre/directory.php @@ -50,7 +50,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function createFile($name, $data = null) { - if ($name === 'Shared' && empty($this->path)) { + if (strtolower($name) === 'shared' && empty($this->path)) { throw new \Sabre_DAV_Exception_Forbidden(); } @@ -86,7 +86,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function createDirectory($name) { - if ($name === 'Shared' && empty($this->path)) { + if (strtolower($name) === 'shared' && empty($this->path)) { throw new \Sabre_DAV_Exception_Forbidden(); } diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php index d2fa425b22c..accf020daa2 100644 --- a/lib/private/connector/sabre/objecttree.php +++ b/lib/private/connector/sabre/objecttree.php @@ -94,6 +94,9 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { } if ($sourceDir !== $destinationDir) { // for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir + if (ltrim($destinationDir, '/') === '' && strtolower($sourceNode->getName()) === 'shared') { + throw new \Sabre_DAV_Exception_Forbidden(); + } if (!$fs->isUpdatable($sourceDir)) { throw new \Sabre_DAV_Exception_Forbidden(); } 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 59630cda5c0..79be211b82f 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'); @@ -33,6 +34,7 @@ class OC_Defaults { $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(); @@ -181,4 +183,16 @@ class OC_Defaults { 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/files.php b/lib/private/files.php index 7e7a27f48dc..bfe6d3c02da 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -148,8 +148,9 @@ class OC_Files { set_time_limit($executionTime); } else { if ($xsendfile) { + $view = \OC\Files\Filesystem::getView(); /** @var $storage \OC\Files\Storage\Storage */ - list($storage) = \OC\Files\Filesystem::resolvePath($filename); + list($storage) = $view->resolvePath($filename); if ($storage->isLocal()) { self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); } else { diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 9b18257088c..abc11e76470 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -498,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 === '/') { @@ -514,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, ' . @@ -540,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) { 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..79159724d16 100644 --- a/lib/private/files/cache/scanner.php +++ b/lib/private/files/cache/scanner.php @@ -155,7 +155,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 +173,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 7f7b6f7f468..7e27650c557 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -320,79 +320,34 @@ class Filesystem { else { self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user); } - $datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data"); - $mount_file = \OC_Config::getValue("mount_file", $datadir . "/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'); + } } /** @@ -761,7 +716,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/storage/common.php b/lib/private/files/storage/common.php index 9e826dd6192..2b697141515 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) { } @@ -122,11 +127,13 @@ abstract class Common implements \OC\Files\Storage\Storage { public function file_put_contents($path, $data) { $handle = $this->fopen($path, "w"); + $this->removeCachedFile($path); return fwrite($handle, $data); } public function rename($path1, $path2) { if ($this->copy($path1, $path2)) { + $this->removeCachedFile($path1); return $this->unlink($path1); } else { return false; @@ -137,6 +144,7 @@ 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; } @@ -152,8 +160,7 @@ 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); + $hash = hash_file($type, $tmpFile, $raw); return $hash; } @@ -162,13 +169,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; @@ -352,4 +360,15 @@ abstract class Common implements \OC\Files\Storage\Storage { // default, which is not local return false; } + + 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..571bf7f97c1 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) { @@ -256,7 +256,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); } diff --git a/lib/private/files/storage/mappedlocal.php b/lib/private/files/storage/mappedlocal.php index 1bab3489a28..94ee28ca763 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 { @@ -276,7 +276,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); } 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/view.php b/lib/private/files/view.php index 2dbbf5b88c9..f06c2fcd66c 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(); @@ -308,6 +310,9 @@ class View { fclose($target); fclose($data); if ($this->shouldEmitHooks($path) && $result !== false) { + Updater::writeHook(array( + 'path' => $this->getHookPath($path) + )); if (!$exists) { \OC_Hook::emit( Filesystem::CLASSNAME, @@ -433,6 +438,7 @@ class View { } 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 + Updater::writeHook(array('path' => $this->getHookPath($path2))); \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_post_write, @@ -441,6 +447,10 @@ class View { ) ); } 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, @@ -741,7 +751,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); @@ -749,6 +762,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, @@ -820,7 +843,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); 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/helper.php b/lib/private/helper.php index d8c4650f666..d7ac0b5f4fa 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -78,8 +78,7 @@ 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 self::linkTo($app, $file, $args); } /** @@ -308,7 +307,7 @@ class OC_Helper { /** * @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 +337,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 @@ -839,7 +809,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()); @@ -914,13 +884,22 @@ class OC_Helper { 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 e0397ec8a00..f1b8acc41b7 100644 --- a/lib/private/image.php +++ b/lib/private/image.php @@ -35,24 +35,24 @@ 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. + * @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 int */ 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/l10n.php b/lib/private/l10n.php index f0b4f9a70f6..175360e27a3 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -405,7 +405,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Choose a language - * @param array $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', ... @@ -413,14 +413,14 @@ class OC_L10N implements \OCP\IL10N { * This function is useful to avoid loading thousands of files if only one * simple string is needed, for example in appinfo.php */ - public static function selectLanguage($texts) { - $lang = self::findLanguage(array_keys($texts)); - return $texts[$lang]; + public static function selectLanguage($text) { + $lang = self::findLanguage(array_keys($text)); + return $text[$lang]; } /** * @brief find the best language - * @param array|string $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 @@ -532,8 +532,9 @@ class OC_L10N implements \OCP\IL10N { } /** - * @param string $lang * @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/mail.php b/lib/private/mail.php index 90c3e343199..79f51609631 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,23 @@ 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) { + + list($name, $domain) = explode('@', $emailAddress, 2); + $domain = idn_to_ascii($domain); + + return "$name@$domain"; + } + } diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php index 9bd07b89023..91bcf584267 100644 --- a/lib/private/mimetypes.list.php +++ b/lib/private/mimetypes.list.php @@ -31,6 +31,7 @@ return array( 'bash' => 'text/x-shellscript', 'blend' => 'application/x-blender', 'bin' => 'application/x-bin', + 'bmp' => 'image/bmp', 'cb7' => 'application/x-cbr', 'cba' => 'application/x-cbr', 'cbr' => 'application/x-cbr', @@ -76,6 +77,7 @@ return array( 'md' => 'text/markdown', 'mdb' => 'application/msaccess', 'mdwn' => 'text/markdown', + 'mkv' => 'video/x-matroska', 'mobi' => 'application/x-mobipocket-ebook', 'mov' => 'video/quicktime', 'mp3' => 'audio/mpeg', diff --git a/lib/private/ocs/cloud.php b/lib/private/ocs/cloud.php index cbbf3b626f5..06d6a8eb4b0 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); diff --git a/lib/private/ocsclient.php b/lib/private/ocsclient.php index fa6e3fac1bb..68dc2c2d6ec 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(); @@ -117,7 +119,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++) { @@ -159,7 +163,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(); @@ -200,7 +206,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..0c1af3c9588 100755 --- a/lib/private/preview.php +++ b/lib/private/preview.php @@ -42,6 +42,10 @@ class Preview { private $scalingup; private $mimetype; + //filemapper used for deleting previews + // index is path, value is fileinfo + static public $deleteFileMapper = array(); + //preview images object /** * @var \OC_Image @@ -53,6 +57,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 @@ -61,12 +70,12 @@ class Preview { * @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 * @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 +95,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 +108,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 +124,7 @@ class Preview { /** * @brief returns the max height of the preview * @return integer - */ + */ public function getMaxY() { return $this->maxY; } @@ -123,7 +132,7 @@ class Preview { /** * @brief returns whether or not scalingup is enabled * @return bool - */ + */ public function getScalingUp() { return $this->scalingup; } @@ -131,7 +140,7 @@ class Preview { /** * @brief returns the name of the thumbnailfolder * @return string - */ + */ public function getThumbnailsFolder() { return self::THUMBNAILS_FOLDER; } @@ -139,7 +148,7 @@ class Preview { /** * @brief returns the max scale factor * @return string - */ + */ public function getMaxScaleFactor() { return $this->maxScaleFactor; } @@ -147,7 +156,7 @@ class Preview { /** * @brief returns the max width set in ownCloud's config * @return string - */ + */ public function getConfigMaxX() { return $this->configMaxX; } @@ -155,20 +164,35 @@ class Preview { /** * @brief returns the max height set in ownCloud's config * @return string - */ + */ public function getConfigMaxY() { return $this->configMaxY; } + 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; } @@ -185,14 +209,14 @@ class Preview { * @brief set the the max width of the preview * @param int $maxX * @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; } @@ -205,14 +229,14 @@ class Preview { * @brief set the the max height of the preview * @param int $maxY * @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,9 +249,9 @@ 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; @@ -237,15 +261,15 @@ class Preview { /** * @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,40 +280,44 @@ 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->getThumbnailsFolder() . '/' . $fileId . '/' . $this->getMaxX() . '-' . $this->getMaxY() . '.png'; + 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 - */ + * false if thumbnail does not exist + * path to thumbnail if thumbnail exists + */ private function isCached() { $file = $this->getFile(); $maxX = $this->getMaxX(); @@ -297,75 +325,75 @@ class Preview { $scalingUp = $this->getScalingUp(); $maxScaleFactor = $this->getMaxScaleFactor(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $fileInfo = $this->getFileInfo($file); + $fileId = $fileInfo->getId(); - if(is_null($fileId)) { + if (is_null($fileId)) { return false; } $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; - if(!$this->userView->is_dir($previewPath)) { + if (!$this->userView->is_dir($previewPath)) { return false; } //does a preview with the wanted height and width already exist? - if($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) { + if ($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) { return $previewPath . $maxX . '-' . $maxY . '.png'; } - $wantedAspectRatio = (float) ($maxX / $maxY); + $wantedAspectRatio = (float)($maxX / $maxY); //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]; + $x = (int)$size[0]; + $y = (int)$size[1]; - $aspectRatio = (float) ($x / $y); - if($aspectRatio !== $wantedAspectRatio) { + $aspectRatio = (float)($x / $y); + if ($aspectRatio !== $wantedAspectRatio) { continue; } - if($x < $maxX || $y < $maxY) { - if($scalingUp) { + if ($x < $maxX || $y < $maxY) { + if ($scalingUp) { $scalefactor = $maxX / $x; - if($scalefactor > $maxScaleFactor) { + if ($scalefactor > $maxScaleFactor) { continue; } - }else{ + } else { continue; } } $possibleThumbnails[$x] = $thumbnail['path']; } - if(count($possibleThumbnails) === 0) { + if (count($possibleThumbnails) === 0) { return false; } - if(count($possibleThumbnails) === 1) { + if (count($possibleThumbnails) === 1) { return current($possibleThumbnails); } ksort($possibleThumbnails); - if(key(reset($possibleThumbnails)) > $maxX) { + if (key(reset($possibleThumbnails)) > $maxX) { return current(reset($possibleThumbnails)); } - if(key(end($possibleThumbnails)) < $maxX) { + if (key(end($possibleThumbnails)) < $maxX) { return current(end($possibleThumbnails)); } - foreach($possibleThumbnails as $width => $path) { - if($width < $maxX) { + foreach ($possibleThumbnails as $width => $path) { + if ($width < $maxX) { continue; - }else{ + } else { return $path; } } @@ -374,9 +402,9 @@ class Preview { /** * @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,22 +414,28 @@ class Preview { $maxY = $this->getMaxY(); $scalingUp = $this->getScalingUp(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $fileInfo = $this->getFileInfo($file); + if($fileInfo === null || $fileInfo === false) { + return new \OC_Image(); + } + $fileId = $fileInfo->getId(); $cached = $this->isCached(); - if($cached) { - $image = new \OC_Image($this->userView->file_get_contents($cached, 'r')); + 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; } @@ -409,7 +443,7 @@ class Preview { $preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingUp, $this->fileView); - if(!($preview instanceof \OC_Image)) { + if (!($preview instanceof \OC_Image)) { continue; } @@ -419,11 +453,11 @@ class Preview { $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; $cachePath = $previewPath . $maxX . '-' . $maxY . '.png'; - 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 +467,7 @@ class Preview { } } - if(is_null($this->preview)) { + if (is_null($this->preview)) { $this->preview = new \OC_Image(); } @@ -443,20 +477,20 @@ class Preview { /** * @brief show preview * @return void - */ + */ public function showPreview() { \OCP\Response::enableCaching(3600 * 24); // 24 hours - if(is_null($this->preview)) { + if (is_null($this->preview)) { $this->getPreview(); } - $this->preview->show(); + $this->preview->show('image/png'); return; } /** * @brief show preview * @return void - */ + */ public function show() { $this->showPreview(); return; @@ -465,7 +499,7 @@ class Preview { /** * @brief resize, crop and fix orientation * @return void - */ + */ private function resizeAndCrop() { $image = $this->preview; $x = $this->getMaxX(); @@ -473,17 +507,17 @@ class Preview { $scalingUp = $this->getScalingUp(); $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(); - if($x === $realx && $y === $realy) { + if ($x === $realx && $y === $realy) { $this->preview = $image; return; } @@ -491,36 +525,36 @@ class Preview { $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) { + if (!is_null($maxscalefactor)) { + if ($factor > $maxscalefactor) { \OC_Log::write('core', 'scalefactor 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); - if($newXsize === $x && $newYsize === $y) { + if ($newXsize === $x && $newYsize === $y) { $this->preview = $image; return; } - if($newXsize >= $x && $newYsize >= $y) { + 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); @@ -532,19 +566,19 @@ class Preview { return; } - if($newXsize < $x || $newYsize < $y) { - if($newXsize > $x) { + if ($newXsize < $x || $newYsize < $y) { + if ($newXsize > $x) { $cropX = floor(($newXsize - $x) * 0.5); $image->crop($cropX, 0, $x, $newYsize); } - if($newYsize > $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); @@ -573,8 +607,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,19 +616,19 @@ 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']; $object = new $class($options); @@ -609,12 +643,35 @@ class Preview { self::post_delete($args); } - 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(); } @@ -622,19 +679,19 @@ class Preview { * @param string $mimetype */ public static function isMimeSupported($mimetype) { - if(!\OC_Config::getValue('enable_previews', true)) { + 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; } } diff --git a/lib/private/preview/movies.php b/lib/private/preview/movies.php index 71cd3bae057..7e0ff51ad2e 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]); } 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.php b/lib/private/preview/office.php index 02bb22e9b94..882c4426e6d 100644 --- a/lib/private/preview/office.php +++ b/lib/private/preview/office.php @@ -6,24 +6,29 @@ * 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{ + $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'); + } + } else { //in case there isn't, use our fallback require_once('office-fallback.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..064a5a3b3d1 100644 --- a/lib/private/preview/pdf.php +++ b/lib/private/preview/pdf.php @@ -7,34 +7,41 @@ */ 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/svg.php b/lib/private/preview/svg.php index 9a73fff9467..505122fddbf 100644 --- a/lib/private/preview/svg.php +++ b/lib/private/preview/svg.php @@ -7,40 +7,46 @@ */ 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; + } + } + \OC\Preview::registerProvider('OC\Preview\SVG'); + } }
\ No newline at end of file diff --git a/lib/private/request.php b/lib/private/request.php index 59a9e395e85..8041c4f0048 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,49 +27,91 @@ class OC_Request { } /** - * @brief Checks whether a domain is considered as trusted. This is used to prevent Host Header Poisoning. - * @param string $domain - * @return bool + * @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 $host + * @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('')); + $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 server host + * @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{ + $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); + $host = trim(current($parts)); + } else { $host = $_SERVER['HTTP_X_FORWARDED_HOST']; } } else { if (isset($_SERVER['HTTP_HOST'])) { $host = $_SERVER['HTTP_HOST']; - } - else if (isset($_SERVER['SERVER_NAME'])) { + } else if (isset($_SERVER['SERVER_NAME'])) { $host = $_SERVER['SERVER_NAME']; } } + return $host; + } + + /** + * Returns the overwritehost setting from the config if set and + * if the overwrite condition is met + * @return 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) || \OC_Config::getValue('trusted_domains', "") === "") { + if (self::isTrustedDomain($host)) { return $host; } else { $trustedList = \OC_Config::getValue('trusted_domains', array('')); diff --git a/lib/private/response.php b/lib/private/response.php index 71c538fb311..983c682bf3f 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 @@ -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..ad25372391f --- /dev/null +++ b/lib/private/route/cachingrouter.php @@ -0,0 +1,43 @@ +<?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) { + $key = $name . json_encode($parameters) . $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..6ade9ec15f6 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)); @@ -63,6 +67,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 +83,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 +99,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 diff --git a/lib/private/route/router.php b/lib/private/route/router.php new file mode 100644 index 00000000000..1f0a23ee124 --- /dev/null +++ b/lib/private/route/router.php @@ -0,0 +1,233 @@ +<?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; + } + + 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 + */ + 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); + require_once $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. + */ + 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 + */ + 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, 5) === '/ocs/' 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 + * + */ + 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); + } + +} 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/server.php b/lib/private/server.php index 3255713e12a..3517d7b3548 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -158,6 +158,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; + }); } /** @@ -323,7 +335,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 +376,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 3906204bda3..b1061b3a25b 100644 --- a/lib/private/setup.php +++ b/lib/private/setup.php @@ -106,6 +106,10 @@ class OC_Setup { //guess what this does OC_Installer::installShippedApps(); + // 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', ''); + //create htaccess files for apache hosts if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) { self::createHtaccess(); @@ -147,7 +151,7 @@ class OC_Setup { $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 ^apps/([^/]*)/(.*\.(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"; 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..7e4223d10fa --- /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; + const SHARE_TYPE_CONTACT = 5; + const SHARE_TYPE_REMOTE = 6; + + const FORMAT_NONE = -1; + const FORMAT_STATUSES = -2; + const FORMAT_SOURCES = -3; + + 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..fde55667281 --- /dev/null +++ b/lib/private/share/helper.php @@ -0,0 +1,202 @@ +<?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(); + } + } +} 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 afbf35aa397..4799db52330 100644 --- a/lib/private/share/mailnotifications.php +++ b/lib/private/share/mailnotifications.php @@ -96,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; } } @@ -108,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 string|boolean $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..8238797600e --- /dev/null +++ b/lib/private/share/share.php @@ -0,0 +1,1619 @@ +<?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 $path to the file + * @param $user owner of the file + * @param include owner to the list of users with access to the file + * @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); + } + + /** + * 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 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'); + + 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 + 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 '.$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 { + // 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); + } + // 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) { + 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'] & \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 '.$itemSource.' failed, because the item was not found'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + /** + * 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) { + 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'], + )); + 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) { + 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 '.$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 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['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'])) { + // FIXME: Doesn't always construct the correct path, example: + // Folder '/a/b', share '/a' and '/a/b' to user2 + // user2 reshares /Shared/b and ask for share status of /Shared/a/b + // expected result: path=/Shared/a/b; actual result /Shared/b because of the 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 = '/Shared' . $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)) { + $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); + + // 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'] & \OCP\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); + } + 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 '.$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 = 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; + } + $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 = 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)) { + $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 = 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, $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; + } + + /** + * 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`, `share_with`, `uid_owner`'; + } else { + $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`'; + } + } 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`, ' + . '`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/template/base.php b/lib/private/template/base.php index 8af2fce7737..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 mixed $value value + * @param array|bool|integer|string $value value * @return bool * * This function assigns a variable. It can be accessed via $_[$key] in diff --git a/lib/private/updater.php b/lib/private/updater.php index f05d5038b76..9f57aa0b6a0 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; @@ -103,10 +102,15 @@ class Updater extends BasicEmitter { } $this->emit('\OC\Updater', 'maintenanceStart'); + // 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 (version_compare($currentVersion, '6.90.1', '<')) { + if (!\OC::$CLI && version_compare($installedVersion, '6.90.1', '<')) { // Add the overwriteHost 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()))); @@ -120,9 +124,6 @@ class Updater extends BasicEmitter { \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())); } @@ -134,46 +135,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..0d238737dde 100644 --- a/lib/private/urlgenerator.php +++ b/lib/private/urlgenerator.php @@ -39,7 +39,7 @@ class URLGenerator implements IURLGenerator { * 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 +60,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) { @@ -148,6 +148,7 @@ class URLGenerator implements IURLGenerator { */ public function getAbsoluteURL($url) { $separator = $url[0] === '/' ? '' : '/'; - return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . $separator . $url; + + return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost(). \OC::$WEBROOT . $separator . $url; } } diff --git a/lib/private/user.php b/lib/private/user.php index a89b7286c10..dc4c7ec3b61 100644 --- a/lib/private/user.php +++ b/lib/private/user.php @@ -321,8 +321,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; diff --git a/lib/private/util.php b/lib/private/util.php index 920161949ae..731b7c97503 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 @@ -290,13 +288,19 @@ class OC_Util { * @return array arrays with error messages and hints */ public static function checkServer() { + $errors = array(); + $CONFIG_DATADIRECTORY = OC_Config::getValue('datadirectory', OC::$SERVERROOT . '/data'); + + 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_suceeded') && \OC::$session->get('checkServer_suceeded')) { - return array(); + return $errors; } - $errors = array(); - $defaults = new \OC_Defaults(); $webServerRestart = false; @@ -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,6 +488,8 @@ class OC_Util { ); } + $errors = array_merge($errors, self::checkDatabaseVersion()); + // Cache the result of this function \OC::$session->set('checkServer_suceeded', count($errors) == 0); @@ -492,6 +497,39 @@ class OC_Util { } /** + * 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,6 +579,25 @@ class OC_Util { } /** + * 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; + } + + /** * @return void */ public static function displayLoginPage($errors = array()) { @@ -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); @@ -1015,13 +1073,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 +1088,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 +1160,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/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/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/iservercontainer.php b/lib/public/iservercontainer.php index 0eb358816d2..600d81d83af 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -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..66fdb841821 --- /dev/null +++ b/lib/public/route/iroute.php @@ -0,0 +1,79 @@ +<?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 + */ + 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 + */ + public function actionInclude($file); + + /** + * Specify GET as the method to use with this route + */ + public function get(); + + /** + * Specify POST as the method to use with this route + */ + public function post(); + + /** + * Specify DELETE as the method to use with this route + */ + 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 + */ + public function put(); +} diff --git a/lib/public/route/irouter.php b/lib/public/route/irouter.php new file mode 100644 index 00000000000..125cd29e81b --- /dev/null +++ b/lib/public/route/irouter.php @@ -0,0 +1,69 @@ +<?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(); + + public function getCacheKey(); + + /** + * loads the api routes + */ + public function loadRoutes($app = null); + + /** + * Sets the collection to use for adding routes + * + * @param string $name Name of the collection to use. + */ + 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 + */ + 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 2fed41488ca..a08134b3837 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,67 +45,25 @@ 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 $path to the file * @param $user owner of the file * @param include owner to the list of users with access to the file * @return array @@ -146,101 +71,7 @@ class Share { * 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); + return \OC\Share\Share::getUsersSharingFile($path, $user, $includeOwner); } /** @@ -250,13 +81,12 @@ class Share { * @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, $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::getItemsSharedWith($itemType, $format, $parameters, $limit, $includeCollections); } /** @@ -266,12 +96,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); } /** @@ -282,45 +112,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); } /** @@ -334,8 +126,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); } /** @@ -346,8 +137,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); } /** @@ -356,25 +146,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); } /** @@ -382,21 +154,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); } @@ -407,13 +166,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); } /** @@ -427,8 +185,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); } /** @@ -441,19 +199,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); } /** @@ -473,176 +219,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); } /** @@ -651,88 +228,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 @@ -742,102 +263,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); } /** @@ -845,67 +284,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); } /** @@ -914,1011 +296,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'])) { - $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(); - $splitPath = explode('/', $row['path']); - $tmpPath = '/Shared' . $parentRow['file_target']; - 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)) { - $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(); } /** @@ -1928,26 +313,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); } } @@ -1960,9 +326,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 @@ -1985,8 +349,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 @@ -2018,9 +382,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/util.php b/lib/public/util.php index 585c5d22634..f02213f2446 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -89,14 +89,11 @@ class Util { */ public static function logException( $app, \Exception $ex ) { $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, \OCP\Util::FATAL); if (defined('DEBUG') and DEBUG) { // also log stack trace $stack = explode("\n", $ex->getTraceAsString()); @@ -269,7 +266,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; } @@ -495,4 +492,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); + } } |