diff --git a/.gitignore b/.gitignore index 9da4d97290..fe89c74ab3 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,10 @@ apps/* !apps/user_ldap !apps/user_webdavauth +# ignore themes except the README +themes/* +!themes/README + # just sane ignores .*.sw[po] *.bak @@ -72,4 +76,4 @@ nbproject data-autotest /tests/coverage* /tests/autoconfig* -/tests/autotest* +/tests/autotest* \ No newline at end of file diff --git a/.htaccess b/.htaccess index 463b49993e..08e2a82fac 100755 --- a/.htaccess +++ b/.htaccess @@ -12,6 +12,7 @@ ErrorDocument 404 /core/templates/404.php php_value upload_max_filesize 513M php_value post_max_size 513M php_value memory_limit 512M +php_value mbstring.func_overload 0 SetEnv htaccessWorking true @@ -32,5 +33,8 @@ RewriteRule ^remote/(.*) remote.php [QSA,L] AddType image/svg+xml svg svgz AddEncoding gzip svgz + +DirectoryIndex index.php index.html + AddDefaultCharset utf-8 Options -Indexes diff --git a/3rdparty b/3rdparty index a13af72fbe..e312294ef6 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit a13af72fbe8983686fc47489a750e60319f68ac2 +Subproject commit e312294ef62873df2b8c02e774f9dfe1b7fbc38d diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1de46ac8b8..fd87513ec2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,13 +3,21 @@ If you have questions about how to use ownCloud, please direct these to the [mailing list][mailinglist] or our [forum][forum]. We are also available on [IRC][irc]. ### Guidelines +* Please search the existing issues first, it's likely that your issue was already reported. * [Report the issue](https://github.com/owncloud/core/issues/new) using our [template][template], it includes all the informations we need to track down the issue. * This repository is *only* for issues within the ownCloud core code. Issues in other compontents should be reported in their own repositores: - [Android client](https://github.com/owncloud/android/issues) - [iOS client](https://github.com/owncloud/ios-issues/issues) - [Desktop client](https://github.com/owncloud/mirall/issues) - - [ownCloud apps](https://github.com/owncloud/apps/issues) (e.g. Calendar, Contacts...) -* Search the existing issues first, it's likely that your issue was already reported. + - Apps: + - [Bookmarks](https://github.com/owncloud/bookmarks/issues) + - [Calendar](https://github.com/owncloud/calendar/issues) + - [Contacts](https://github.com/owncloud/contacts/issues) + - [Mail](https://github.com/owncloud/mail/issues) + - [News](https://github.com/owncloud/news/issues) + - [Notes](https://github.com/owncloud/notes/issues) + - [Shorty](https://github.com/owncloud/shorty/issues) + - [other apps](https://github.com/owncloud/apps/issues) (e.g. Pictures, Music, Tasks, ...) If your issue appears to be a bug, and hasn't been reported, open a new issue. diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php index da7e9d6b2a..5f4856ec79 100644 --- a/apps/files/ajax/delete.php +++ b/apps/files/ajax/delete.php @@ -8,7 +8,7 @@ OCP\JSON::callCheck(); // Get data $dir = stripslashes($_POST["dir"]); -$files = isset($_POST["file"]) ? stripslashes($_POST["file"]) : stripslashes($_POST["files"]); +$files = isset($_POST["file"]) ? $_POST["file"] : $_POST["files"]; $files = json_decode($files); $filesWithError = ''; diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 38714f34a6..8548fc95dd 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -85,7 +85,7 @@ if($source) { }elseif(\OC\Files\Filesystem::touch($dir . '/' . $filename)) { $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename); $id = $meta['fileid']; - OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id))); + OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id, 'mime' => $meta['mimetype']))); exit(); } } diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index 1cd2944483..f568afad4d 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -15,6 +15,14 @@ $mimetype = isset($_GET['mimetype']) ? $_GET['mimetype'] : ''; // make filelist $files = array(); +// If a type other than directory is requested first load them. +if($mimetype && strpos($mimetype, 'httpd/unix-directory') === false) { + foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, 'httpd/unix-directory' ) as $i ) { + $i["date"] = OCP\Util::formatDate($i["mtime"] ); + $i['mimetype_icon'] = $i['type'] == 'dir' ? \mimetype_icon('dir'): \mimetype_icon($i['mimetype']); + $files[] = $i; + } +} foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, $mimetype ) as $i ) { $i["date"] = OCP\Util::formatDate($i["mtime"] ); $i['mimetype_icon'] = $i['type'] == 'dir' ? \mimetype_icon('dir'): \mimetype_icon($i['mimetype']); diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php index 9fd2ce3ad4..f455185828 100644 --- a/apps/files/ajax/rename.php +++ b/apps/files/ajax/rename.php @@ -1,26 +1,41 @@ . + * + */ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); -// Get data -$dir = stripslashes($_GET["dir"]); -$file = stripslashes($_GET["file"]); -$newname = stripslashes($_GET["newname"]); +$files = new \OCA\Files\App( + \OC\Files\Filesystem::getView(), + \OC_L10n::get('files') +); +$result = $files->rename( + $_GET["dir"], + $_GET["file"], + $_GET["newname"] +); -$l = OC_L10N::get('files'); - -if ( $newname !== '.' and ($dir != '' || $file != 'Shared') and $newname !== '.') { - $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname); - $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); - if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) { - OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname ))); - } else { - OCP\JSON::error(array("data" => array( "message" => $l->t("Unable to rename file") ))); - } -}else{ - OCP\JSON::error(array("data" => array( "message" => $l->t("Unable to rename file") ))); -} +if($result['success'] === true){ + OCP\JSON::success(array('data' => $result['data'])); +} else { + OCP\JSON::error(array('data' => $result['data'])); +} \ No newline at end of file diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php index 6535a9b7ba..99739cb4ce 100644 --- a/apps/files/appinfo/app.php +++ b/apps/files/appinfo/app.php @@ -12,3 +12,12 @@ OCP\App::addNavigationEntry( array( "id" => "files_index", "name" => $l->t("Files") )); OC_Search::registerProvider('OC_Search_Provider_File'); + +// cache hooks must be connected before all other apps. +// since 'files' is always loaded first the hooks need to be connected here +\OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Updater', 'writeHook'); +\OC_Hook::connect('OC_Filesystem', 'post_touch', '\OC\Files\Cache\Updater', 'touchHook'); +\OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Updater', 'deleteHook'); +\OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Updater', 'renameHook'); + +\OCP\BackgroundJob::addRegularTask('\OC\Files\Cache\BackgroundWatcher', 'checkNext'); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index ec323915b4..f788949b1b 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -5,7 +5,8 @@ /* FILE MENU */ .actions { padding:.3em; height:2em; width: 100%; } .actions input, .actions button, .actions .button { margin:0; float:left; } - +.actions .button a { color: #555; } +.actions .button a:hover, .actions .button a:active { color: #333; } #new { height:17px; margin:0 0 0 1em; z-index:1010; float:left; } @@ -34,6 +35,7 @@ background-image:url('%webroot%/core/img/actions/upload.svg'); background-repeat:no-repeat; background-position:7px 6px; + opacity:0.65; } .file_upload_target { display:none; } .file_upload_form { display:inline; float:left; margin:0; padding:0; cursor:pointer; overflow:visible; } @@ -148,7 +150,7 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } #scanning-message{ top:40%; left:40%; position:absolute; display:none; } -div.crumb a{ padding:0.9em 0 0.7em 0; } +div.crumb a{ padding:0.9em 0 0.7em 0; color:#555; } table.dragshadow { width:auto; diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index f3264da5a1..14fca6f148 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -22,18 +22,18 @@ var FileActions = { if (FileActions.actions.all) { actions = $.extend(actions, FileActions.actions.all); } - if (mime) { - if (FileActions.actions[mime]) { - actions = $.extend(actions, FileActions.actions[mime]); + if (type) {//type is 'dir' or 'file' + if (FileActions.actions[type]) { + actions = $.extend(actions, FileActions.actions[type]); } + } + if (mime) { var mimePart = mime.substr(0, mime.indexOf('/')); if (FileActions.actions[mimePart]) { actions = $.extend(actions, FileActions.actions[mimePart]); } - } - if (type) {//type is 'dir' or 'file' - if (FileActions.actions[type]) { - actions = $.extend(actions, FileActions.actions[type]); + if (FileActions.actions[mime]) { + actions = $.extend(actions, FileActions.actions[mime]); } } var filteredActions = {}; @@ -113,6 +113,7 @@ var FileActions = { } }); if(actions.Share && !($('#dir').val() === '/' && file === 'Shared')){ + // t('files', 'Share') addAction('Share', actions.Share); } diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 70c3332cca..c24d1fd824 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -191,6 +191,13 @@ var FileList={ td.children('a.name').hide(); td.append(form); input.focus(); + //preselect input + var len = input.val().lastIndexOf('.'); + if (len === -1) { + len = input.val().length; + } + input.selectRange(0,len); + form.submit(function(event){ event.stopPropagation(); event.preventDefault(); @@ -320,9 +327,9 @@ var FileList={ } for (var i=0; i'; - deleteAction[0].outerHTML = newHTML; + deleteAction.html(newHTML); } // Finish any existing actions if (FileList.lastAction) { @@ -344,7 +351,7 @@ var FileList={ } else { $.each(files,function(index,file) { var deleteAction = $('tr').filterAttr('data-file',file).children("td.date").children(".move2trash"); - deleteAction[0].outerHTML = oldHTML; + deleteAction.html(oldHTML); }); } }); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 7e3caf71a0..a79d34c9b2 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -115,6 +115,11 @@ $(document).ready(function() { return false; }); + // Trigger cancelling of file upload + $('#uploadprogresswrapper .stop').on('click', function() { + Files.cancelUploads(); + }); + // Show trash bin $('#trash a').live('click', function() { window.location=OC.filePath('files_trashbin', '', 'index.php'); @@ -506,9 +511,9 @@ $(document).ready(function() { var date=new Date(); FileList.addFile(name,0,date,false,hidden); var tr=$('tr').filterAttr('data-file',name); - tr.attr('data-mime','text/plain'); + tr.attr('data-mime',result.data.mime); tr.attr('data-id', result.data.id); - getMimeIcon('text/plain',function(path){ + getMimeIcon(result.data.mime,function(path){ tr.find('td.filename').attr('style','background-image:url('+path+')'); }); } else { @@ -703,14 +708,14 @@ function scanFiles(force, dir){ var scannerEventSource = new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir}); scanFiles.cancel = scannerEventSource.close.bind(scannerEventSource); scannerEventSource.listen('count',function(count){ - console.log(count + 'files scanned') + console.log(count + ' files scanned') }); scannerEventSource.listen('folder',function(path){ console.log('now scanning ' + path) }); scannerEventSource.listen('done',function(count){ scanFiles.scanning=false; - console.log('done after ' + count + 'files'); + console.log('done after ' + count + ' files'); }); } scanFiles.scanning=false; @@ -757,9 +762,9 @@ var createDragShadow = function(event){ var dir=$('#dir').val(); $(selectedFiles).each(function(i,elem){ - var newtr = $('' - +''+elem.name+''+humanFileSize(elem.size)+'' - +''); + var newtr = $('').attr('data-dir', dir).attr('data-filename', elem.name); + newtr.append($('').addClass('filename').text(elem.name)); + newtr.append($('').addClass('size').text(humanFileSize(elem.size))); tbody.append(newtr); if (elem.type === 'dir') { newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')'); diff --git a/apps/files/l10n/ar.php b/apps/files/l10n/ar.php index 41e6a225a2..ca198b7efe 100644 --- a/apps/files/l10n/ar.php +++ b/apps/files/l10n/ar.php @@ -1,7 +1,6 @@ "فشل في نقل الملف %s - يوجد ملف بنفس هذا الاسم", "Could not move %s" => "فشل في نقل %s", -"Unable to rename file" => "فشل في اعادة تسمية الملف", "No file was uploaded. Unknown error" => "لم يتم رفع أي ملف , خطأ غير معروف", "There is no error, the file uploaded with success" => "تم ترفيع الملفات بنجاح.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "حجم الملف المرفوع تجاوز قيمة upload_max_filesize الموجودة في ملف php.ini ", @@ -13,8 +12,9 @@ "Not enough storage available" => "لا يوجد مساحة تخزينية كافية", "Invalid directory." => "مسار غير صحيح.", "Files" => "الملفات", +"Share" => "شارك", "Delete permanently" => "حذف بشكل دائم", -"Delete" => "محذوف", +"Delete" => "إلغاء", "Rename" => "إعادة تسميه", "Pending" => "قيد الانتظار", "{new_name} already exists" => "{new_name} موجود مسبقا", @@ -37,14 +37,15 @@ "URL cannot be empty." => "عنوان ال URL لا يجوز أن يكون فارغا.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "إسم مجلد غير صحيح. استخدام مصطلح \"Shared\" محجوز للنظام", "Error" => "خطأ", -"Name" => "الاسم", +"Name" => "اسم", "Size" => "حجم", "Modified" => "معدل", "1 folder" => "مجلد عدد 1", "{count} folders" => "{count} مجلدات", "1 file" => "ملف واحد", "{count} files" => "{count} ملفات", -"Upload" => "إرفع", +"Unable to rename file" => "فشل في اعادة تسمية الملف", +"Upload" => "رفع", "File handling" => "التعامل مع الملف", "Maximum upload size" => "الحد الأقصى لحجم الملفات التي يمكن رفعها", "max. possible: " => "الحد الأقصى المسموح به", diff --git a/apps/files/l10n/bg_BG.php b/apps/files/l10n/bg_BG.php index c4bbca36f4..661bb5718a 100644 --- a/apps/files/l10n/bg_BG.php +++ b/apps/files/l10n/bg_BG.php @@ -1,8 +1,13 @@ "Файлът е качен успешно", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Файлът който се опитвате да качите надвишава стойностите в MAX_FILE_SIZE в HTML формата.", +"The uploaded file was only partially uploaded" => "Файлът е качен частично", +"No file was uploaded" => "Фахлът не бе качен", "Missing a temporary folder" => "Липсва временна папка", "Failed to write to disk" => "Възникна проблем при запис в диска", "Invalid directory." => "Невалидна директория.", "Files" => "Файлове", +"Share" => "Споделяне", "Delete permanently" => "Изтриване завинаги", "Delete" => "Изтриване", "Rename" => "Преименуване", @@ -29,5 +34,7 @@ "Cancel upload" => "Спри качването", "Nothing in here. Upload something!" => "Няма нищо тук. Качете нещо.", "Download" => "Изтегляне", -"Upload too large" => "Файлът който сте избрали за качване е прекалено голям" +"Upload too large" => "Файлът който сте избрали за качване е прекалено голям", +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файловете които се опитвате да качите са по-големи от позволеното за сървъра.", +"Files are being scanned, please wait." => "Файловете се претърсват, изчакайте." ); diff --git a/apps/files/l10n/bn_BD.php b/apps/files/l10n/bn_BD.php index 640430716f..83dd4dc36d 100644 --- a/apps/files/l10n/bn_BD.php +++ b/apps/files/l10n/bn_BD.php @@ -1,18 +1,18 @@ "%s কে স্থানান্তর করা সম্ভব হলো না - এই নামের ফাইল বিদ্যমান", "Could not move %s" => "%s কে স্থানান্তর করা সম্ভব হলো না", -"Unable to rename file" => "ফাইলের নাম পরিবর্তন করা সম্ভব হলো না", -"No file was uploaded. Unknown error" => "কোন ফাইল আপলোড করা হয় নি। সমস্যা অজ্ঞাত।", -"There is no error, the file uploaded with success" => "কোন সমস্যা নেই, ফাইল আপলোড সুসম্পন্ন হয়েছে", +"No file was uploaded. Unknown error" => "কোন ফাইল আপলোড করা হয় নি। সমস্যার কারণটি অজ্ঞাত।", +"There is no error, the file uploaded with success" => "কোন সমস্যা হয় নি, ফাইল আপলোড সুসম্পন্ন হয়েছে।", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "আপলোড করা ফাইলটি php.ini তে বর্ণিত upload_max_filesize নির্দেশিত আয়তন অতিক্রম করছেঃ", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "আপলোড করা ফাইলটি HTML ফর্মে নির্ধারিত MAX_FILE_SIZE নির্দেশিত সর্বোচ্চ আকার অতিক্রম করেছে ", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "আপলোড করা ফাইলটি HTML ফর্মে উল্লিখিত MAX_FILE_SIZE নির্ধারিত ফাইলের সর্বোচ্চ আকার অতিক্রম করতে চলেছে ", "The uploaded file was only partially uploaded" => "আপলোড করা ফাইলটি আংশিক আপলোড করা হয়েছে", "No file was uploaded" => "কোন ফাইল আপলোড করা হয় নি", -"Missing a temporary folder" => "অস্থায়ী ফোল্ডার খোয়া গিয়েছে", +"Missing a temporary folder" => "অস্থায়ী ফোল্ডারটি হারানো গিয়েছে", "Failed to write to disk" => "ডিস্কে লিখতে ব্যর্থ", "Invalid directory." => "ভুল ডিরেক্টরি", "Files" => "ফাইল", -"Delete" => "মুছে ফেল", +"Share" => "ভাগাভাগি কর", +"Delete" => "মুছে", "Rename" => "পূনঃনামকরণ", "Pending" => "মুলতুবি", "{new_name} already exists" => "{new_name} টি বিদ্যমান", @@ -32,13 +32,14 @@ "URL cannot be empty." => "URL ফাঁকা রাখা যাবে না।", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "ফোল্ডারের নামটি সঠিক নয়। 'ভাগাভাগি করা' শুধুমাত্র Owncloud এর জন্য সংরক্ষিত।", "Error" => "সমস্যা", -"Name" => "নাম", +"Name" => "রাম", "Size" => "আকার", "Modified" => "পরিবর্তিত", "1 folder" => "১টি ফোল্ডার", "{count} folders" => "{count} টি ফোল্ডার", "1 file" => "১টি ফাইল", "{count} files" => "{count} টি ফাইল", +"Unable to rename file" => "ফাইলের নাম পরিবর্তন করা সম্ভব হলো না", "Upload" => "আপলোড", "File handling" => "ফাইল হ্যার্ডলিং", "Maximum upload size" => "আপলোডের সর্বোচ্চ আকার", @@ -47,7 +48,7 @@ "Enable ZIP-download" => "ZIP ডাউনলোড সক্রিয় কর", "0 is unlimited" => "০ এর অর্থ অসীম", "Maximum input size for ZIP files" => "ZIP ফাইলের ইনপুটের সর্বোচ্চ আকার", -"Save" => "সংরক্ষন কর", +"Save" => "সংরক্ষণ", "New" => "নতুন", "Text file" => "টেক্সট ফাইল", "Folder" => "ফোল্ডার", diff --git a/apps/files/l10n/ca.php b/apps/files/l10n/ca.php index d92dbeef67..c1c94b9900 100644 --- a/apps/files/l10n/ca.php +++ b/apps/files/l10n/ca.php @@ -1,22 +1,22 @@ "No s'ha pogut moure %s - Ja hi ha un fitxer amb aquest nom", "Could not move %s" => " No s'ha pogut moure %s", -"Unable to rename file" => "No es pot canviar el nom del fitxer", "No file was uploaded. Unknown error" => "No s'ha carregat cap fitxer. Error desconegut", -"There is no error, the file uploaded with success" => "El fitxer s'ha pujat correctament", +"There is no error, the file uploaded with success" => "No hi ha errors, el fitxer s'ha carregat correctament", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "L’arxiu que voleu carregar supera el màxim definit en la directiva upload_max_filesize del php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El fitxer de pujada excedeix la directiva MAX_FILE_SIZE especificada al formulari HTML", -"The uploaded file was only partially uploaded" => "El fitxer només s'ha pujat parcialment", -"No file was uploaded" => "El fitxer no s'ha pujat", -"Missing a temporary folder" => "S'ha perdut un fitxer temporal", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El fitxer carregat supera la directiva MAX_FILE_SIZE especificada al formulari HTML", +"The uploaded file was only partially uploaded" => "El fitxer només s'ha carregat parcialment", +"No file was uploaded" => "No s'ha carregat cap fitxer", +"Missing a temporary folder" => "Falta un fitxer temporal", "Failed to write to disk" => "Ha fallat en escriure al disc", "Not enough storage available" => "No hi ha prou espai disponible", "Invalid directory." => "Directori no vàlid.", "Files" => "Fitxers", +"Share" => "Comparteix", "Delete permanently" => "Esborra permanentment", -"Delete" => "Suprimeix", +"Delete" => "Esborra", "Rename" => "Reanomena", -"Pending" => "Pendents", +"Pending" => "Pendent", "{new_name} already exists" => "{new_name} ja existeix", "replace" => "substitueix", "suggest name" => "sugereix un nom", @@ -46,6 +46,8 @@ "{count} folders" => "{count} carpetes", "1 file" => "1 fitxer", "{count} files" => "{count} fitxers", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nom de carpeta no vàlid. L'ús de 'Shared' està reservat per Owncloud", +"Unable to rename file" => "No es pot canviar el nom del fitxer", "Upload" => "Puja", "File handling" => "Gestió de fitxers", "Maximum upload size" => "Mida màxima de pujada", diff --git a/apps/files/l10n/cs_CZ.php b/apps/files/l10n/cs_CZ.php index 66c748fbaa..de6a154242 100644 --- a/apps/files/l10n/cs_CZ.php +++ b/apps/files/l10n/cs_CZ.php @@ -1,7 +1,6 @@ "Nelze přesunout %s - existuje soubor se stejným názvem", "Could not move %s" => "Nelze přesunout %s", -"Unable to rename file" => "Nelze přejmenovat soubor", "No file was uploaded. Unknown error" => "Soubor nebyl odeslán. Neznámá chyba", "There is no error, the file uploaded with success" => "Soubor byl odeslán úspěšně", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Odesílaný soubor přesahuje velikost upload_max_filesize povolenou v php.ini:", @@ -13,10 +12,11 @@ "Not enough storage available" => "Nedostatek dostupného úložného prostoru", "Invalid directory." => "Neplatný adresář", "Files" => "Soubory", +"Share" => "Sdílet", "Delete permanently" => "Trvale odstranit", "Delete" => "Smazat", "Rename" => "Přejmenovat", -"Pending" => "Čekající", +"Pending" => "Nevyřízené", "{new_name} already exists" => "{new_name} již existuje", "replace" => "nahradit", "suggest name" => "navrhnout název", @@ -32,7 +32,7 @@ "Your storage is full, files can not be updated or synced anymore!" => "Vaše úložiště je plné, nelze aktualizovat ani synchronizovat soubory.", "Your storage is almost full ({usedSpacePercent}%)" => "Vaše úložiště je téměř plné ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Vaše soubory ke stažení se připravují. Pokud jsou velké může to chvíli trvat.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Nelze odeslat Váš soubor, protože je to adresář nebo má velikost 0 bajtů", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nelze odeslat Váš soubor, protože je to adresář, nebo je jeho velikost 0 bajtů", "Not enough space available" => "Nedostatek dostupného místa", "Upload cancelled." => "Odesílání zrušeno.", "File upload is in progress. Leaving the page now will cancel the upload." => "Probíhá odesílání souboru. Opuštění stránky vyústí ve zrušení nahrávání.", @@ -41,11 +41,12 @@ "Error" => "Chyba", "Name" => "Název", "Size" => "Velikost", -"Modified" => "Změněno", +"Modified" => "Upraveno", "1 folder" => "1 složka", "{count} folders" => "{count} složky", "1 file" => "1 soubor", "{count} files" => "{count} soubory", +"Unable to rename file" => "Nelze přejmenovat soubor", "Upload" => "Odeslat", "File handling" => "Zacházení se soubory", "Maximum upload size" => "Maximální velikost pro odesílání", @@ -65,7 +66,7 @@ "Nothing in here. Upload something!" => "Žádný obsah. Nahrajte něco.", "Download" => "Stáhnout", "Unshare" => "Zrušit sdílení", -"Upload too large" => "Odeslaný soubor je příliš velký", +"Upload too large" => "Odesílaný soubor je příliš velký", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Soubory, které se snažíte odeslat, překračují limit velikosti odesílání na tomto serveru.", "Files are being scanned, please wait." => "Soubory se prohledávají, prosím čekejte.", "Current scanning" => "Aktuální prohledávání", diff --git a/apps/files/l10n/cy_GB.php b/apps/files/l10n/cy_GB.php new file mode 100644 index 0000000000..ae33948891 --- /dev/null +++ b/apps/files/l10n/cy_GB.php @@ -0,0 +1,74 @@ + "Methwyd symud %s - Mae ffeil gyda'r enw hwn eisoes yn bodoli", +"Could not move %s" => "Methwyd symud %s", +"No file was uploaded. Unknown error" => "Ni lwythwyd ffeil i fyny. Gwall anhysbys.", +"There is no error, the file uploaded with success" => "Does dim gwall, llwythodd y ffeil i fyny'n llwyddiannus", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Mae'r ffeil lwythwyd i fyny'n fwy na chyfarwyddeb upload_max_filesize yn php.ini:", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Mae'r ffeil lwythwyd i fyny'n fwy na chyfarwyddeb MAX_FILE_SIZE bennwyd yn y ffurflen HTML", +"The uploaded file was only partially uploaded" => "Dim ond yn rhannol y llwythwyd y ffeil i fyny", +"No file was uploaded" => "Ni lwythwyd ffeil i fyny", +"Missing a temporary folder" => "Plygell dros dro yn eisiau", +"Failed to write to disk" => "Methwyd ysgrifennu i'r ddisg", +"Not enough storage available" => "Dim digon o le storio ar gael", +"Invalid directory." => "Cyfeiriadur annilys.", +"Files" => "Ffeiliau", +"Share" => "Rhannu", +"Delete permanently" => "Dileu'n barhaol", +"Delete" => "Dileu", +"Rename" => "Ailenwi", +"Pending" => "I ddod", +"{new_name} already exists" => "{new_name} yn bodoli'n barod", +"replace" => "amnewid", +"suggest name" => "awgrymu enw", +"cancel" => "diddymu", +"replaced {new_name} with {old_name}" => "newidiwyd {new_name} yn lle {old_name}", +"undo" => "dadwneud", +"perform delete operation" => "cyflawni gweithred dileu", +"1 file uploading" => "1 ffeil yn llwytho i fyny", +"files uploading" => "ffeiliau'n llwytho i fyny", +"'.' is an invalid file name." => "Mae '.' yn enw ffeil annilys.", +"File name cannot be empty." => "Does dim hawl cael enw ffeil gwag.", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Enw annilys, ni chaniateir, '\\', '/', '<', '>', ':', '\"', '|', '?' na '*'.", +"Your storage is full, files can not be updated or synced anymore!" => "Mae eich storfa'n llawn, ni ellir diweddaru a chydweddu ffeiliau mwyach!", +"Your storage is almost full ({usedSpacePercent}%)" => "Mae eich storfa bron a bod yn llawn ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Wrthi'n paratoi i lwytho i lawr. Gall gymryd peth amser os yw'r ffeiliau'n fawr.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Methu llwytho'ch ffeil i fyny gan ei fod yn gyferiadur neu'n cynnwys 0 beit", +"Not enough space available" => "Dim digon o le ar gael", +"Upload cancelled." => "Diddymwyd llwytho i fyny.", +"File upload is in progress. Leaving the page now will cancel the upload." => "Mae ffeiliau'n cael eu llwytho i fyny. Bydd gadael y dudalen hon nawr yn diddymu'r broses.", +"URL cannot be empty." => "Does dim hawl cael URL gwag.", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Enw plygell annilys. Mae'r defnydd o 'Shared' yn cael ei gadw gan Owncloud", +"Error" => "Gwall", +"Name" => "Enw", +"Size" => "Maint", +"Modified" => "Addaswyd", +"1 folder" => "1 blygell", +"{count} folders" => "{count} plygell", +"1 file" => "1 ffeil", +"{count} files" => "{count} ffeil", +"Unable to rename file" => "Methu ailenwi ffeil", +"Upload" => "Llwytho i fyny", +"File handling" => "Trafod ffeiliau", +"Maximum upload size" => "Maint mwyaf llwytho i fyny", +"max. possible: " => "mwyaf. posib:", +"Needed for multi-file and folder downloads." => "Angen ar gyfer llwytho mwy nag un ffeil neu blygell i lawr yr un pryd.", +"Enable ZIP-download" => "Galluogi llwytho i lawr ZIP", +"0 is unlimited" => "0 yn ddiderfyn", +"Maximum input size for ZIP files" => "Maint mewnbynnu mwyaf ffeiliau ZIP", +"Save" => "Cadw", +"New" => "Newydd", +"Text file" => "Ffeil destun", +"Folder" => "Plygell", +"From link" => "Dolen o", +"Deleted files" => "Ffeiliau ddilewyd", +"Cancel upload" => "Diddymu llwytho i fyny", +"You don’t have write permissions here." => "Nid oes gennych hawliau ysgrifennu fan hyn.", +"Nothing in here. Upload something!" => "Does dim byd fan hyn. Llwythwch rhywbeth i fyny!", +"Download" => "Llwytho i lawr", +"Unshare" => "Dad-rannu", +"Upload too large" => "Maint llwytho i fyny'n rhy fawr", +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Mae'r ffeiliau rydych yn ceisio llwytho i fyny'n fwy na maint mwyaf llwytho ffeiliau i fyny ar y gweinydd hwn.", +"Files are being scanned, please wait." => "Arhoswch, mae ffeiliau'n cael eu sganio.", +"Current scanning" => "Sganio cyfredol", +"Upgrading filesystem cache..." => "Uwchraddio storfa system ffeiliau..." +); diff --git a/apps/files/l10n/da.php b/apps/files/l10n/da.php index 7c065952ae..879fbc8451 100644 --- a/apps/files/l10n/da.php +++ b/apps/files/l10n/da.php @@ -1,18 +1,18 @@ "Kunne ikke flytte %s - der findes allerede en fil med dette navn", "Could not move %s" => "Kunne ikke flytte %s", -"Unable to rename file" => "Kunne ikke omdøbe fil", "No file was uploaded. Unknown error" => "Ingen fil blev uploadet. Ukendt fejl.", -"There is no error, the file uploaded with success" => "Der er ingen fejl, filen blev uploadet med success", +"There is no error, the file uploaded with success" => "Der skete ingen fejl, filen blev succesfuldt uploadet", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Den uploadede fil overstiger upload_max_filesize direktivet i php.ini", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den uploadede fil overskrider MAX_FILE_SIZE -direktivet som er specificeret i HTML-formularen", -"The uploaded file was only partially uploaded" => "Den uploadede file blev kun delvist uploadet", -"No file was uploaded" => "Ingen fil blev uploadet", -"Missing a temporary folder" => "Mangler en midlertidig mappe", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den uploadede fil overstiger MAX_FILE_SIZE indstilingen, som specificeret i HTML formularen", +"The uploaded file was only partially uploaded" => "Filen blev kun delvist uploadet.", +"No file was uploaded" => "Ingen fil uploadet", +"Missing a temporary folder" => "Manglende midlertidig mappe.", "Failed to write to disk" => "Fejl ved skrivning til disk.", "Not enough storage available" => "Der er ikke nok plads til rådlighed", "Invalid directory." => "Ugyldig mappe.", "Files" => "Filer", +"Share" => "Del", "Delete permanently" => "Slet permanent", "Delete" => "Slet", "Rename" => "Omdøb", @@ -25,13 +25,15 @@ "undo" => "fortryd", "perform delete operation" => "udfør slet operation", "1 file uploading" => "1 fil uploades", +"files uploading" => "uploader filer", "'.' is an invalid file name." => "'.' er et ugyldigt filnavn.", "File name cannot be empty." => "Filnavnet kan ikke stå tomt.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldigt navn, '\\', '/', '<', '>', ':' | '?', '\"', '', og '*' er ikke tilladt.", "Your storage is full, files can not be updated or synced anymore!" => "Din opbevaringsplads er fyldt op, filer kan ikke opdateres eller synkroniseres længere!", "Your storage is almost full ({usedSpacePercent}%)" => "Din opbevaringsplads er næsten fyldt op ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Dit download forberedes. Dette kan tage lidt tid ved større filer.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Kunne ikke uploade din fil, da det enten er en mappe eller er tom", +"Unable to upload your file as it is a directory or has 0 bytes" => "Kan ikke uploade din fil - det er enten en mappe eller en fil med et indhold på 0 bytes.", +"Not enough space available" => "ikke nok tilgængelig ledig plads ", "Upload cancelled." => "Upload afbrudt.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.", "URL cannot be empty." => "URLen kan ikke være tom.", @@ -44,6 +46,7 @@ "{count} folders" => "{count} mapper", "1 file" => "1 fil", "{count} files" => "{count} filer", +"Unable to rename file" => "Kunne ikke omdøbe fil", "Upload" => "Upload", "File handling" => "Filhåndtering", "Maximum upload size" => "Maksimal upload-størrelse", @@ -63,7 +66,7 @@ "Nothing in here. Upload something!" => "Her er tomt. Upload noget!", "Download" => "Download", "Unshare" => "Fjern deling", -"Upload too large" => "Upload for stor", +"Upload too large" => "Upload er for stor", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerne, du prøver at uploade, er større end den maksimale størrelse for fil-upload på denne server.", "Files are being scanned, please wait." => "Filerne bliver indlæst, vent venligst.", "Current scanning" => "Indlæser", diff --git a/apps/files/l10n/de.php b/apps/files/l10n/de.php index 34f0233486..bcc3a4c6c9 100644 --- a/apps/files/l10n/de.php +++ b/apps/files/l10n/de.php @@ -1,38 +1,38 @@ "%s konnte nicht verschoben werden - eine Datei mit diesem Namen existiert bereits.", -"Could not move %s" => "%s konnte nicht verschoben werden", -"Unable to rename file" => "Die Datei konnte nicht umbenannt werden", +"Could not move %s - File with this name already exists" => "Konnte %s nicht verschieben. Eine Datei mit diesem Namen existiert bereits", +"Could not move %s" => "Konnte %s nicht verschieben", "No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler", -"There is no error, the file uploaded with success" => "Datei fehlerfrei hochgeladen.", +"There is no error, the file uploaded with success" => "Es ist kein Fehler aufgetreten. Die Datei wurde erfolgreich hochgeladen.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in php.ini", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Die Größe der hochzuladenden Datei überschreitet die MAX_FILE_SIZE-Richtlinie, die im HTML-Formular angegeben wurde", -"The uploaded file was only partially uploaded" => "Die Datei wurde nur teilweise hochgeladen.", -"No file was uploaded" => "Es wurde keine Datei hochgeladen.", -"Missing a temporary folder" => "Temporärer Ordner fehlt.", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Die Datei ist größer, als die MAX_FILE_SIZE Direktive erlaubt, die im HTML-Formular spezifiziert ist", +"The uploaded file was only partially uploaded" => "Die Datei konnte nur teilweise übertragen werden", +"No file was uploaded" => "Keine Datei konnte übertragen werden.", +"Missing a temporary folder" => "Kein temporärer Ordner vorhanden", "Failed to write to disk" => "Fehler beim Schreiben auf die Festplatte", -"Not enough storage available" => "Nicht genug Speicherplatz verfügbar", +"Not enough storage available" => "Nicht genug Speicher vorhanden.", "Invalid directory." => "Ungültiges Verzeichnis.", "Files" => "Dateien", -"Delete permanently" => "Permanent löschen", +"Share" => "Teilen", +"Delete permanently" => "Endgültig löschen", "Delete" => "Löschen", "Rename" => "Umbenennen", "Pending" => "Ausstehend", "{new_name} already exists" => "{new_name} existiert bereits", "replace" => "ersetzen", -"suggest name" => "Name vorschlagen", +"suggest name" => "Namen vorschlagen", "cancel" => "abbrechen", "replaced {new_name} with {old_name}" => "{old_name} ersetzt durch {new_name}", "undo" => "rückgängig machen", "perform delete operation" => "Löschvorgang ausführen", -"1 file uploading" => "Eine Datei wird hoch geladen", +"1 file uploading" => "1 Datei wird hochgeladen", "files uploading" => "Dateien werden hoch geladen", "'.' is an invalid file name." => "'.' ist kein gültiger Dateiname.", "File name cannot be empty." => "Der Dateiname darf nicht leer sein.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ungültiger Name, '\\', '/', '<', '>', ':', '\"', '|', '?' und '*' sind nicht zulässig.", -"Your storage is full, files can not be updated or synced anymore!" => "Ihr Speicherplatz ist voll, Dateien können nicht mehr aktualisiert oder synchronisiert werden!", -"Your storage is almost full ({usedSpacePercent}%)" => "Ihr Speicherplatz ist fast aufgebraucht ({usedSpacePercent}%)", +"Your storage is full, files can not be updated or synced anymore!" => "Dein Speicher ist voll, daher können keine Dateien mehr aktualisiert oder synchronisiert werden!", +"Your storage is almost full ({usedSpacePercent}%)" => "Dein Speicher ist fast voll ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Dein Download wird vorbereitet. Dies kann bei größeren Dateien etwas dauern.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Deine Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Deine Datei kann nicht hochgeladen werden, weil es sich um einen Ordner handelt oder 0 Bytes groß ist.", "Not enough space available" => "Nicht genug Speicherplatz verfügbar", "Upload cancelled." => "Upload abgebrochen.", "File upload is in progress. Leaving the page now will cancel the upload." => "Dateiupload läuft. Wenn Du die Seite jetzt verlässt, wird der Upload abgebrochen.", @@ -41,11 +41,12 @@ "Error" => "Fehler", "Name" => "Name", "Size" => "Größe", -"Modified" => "Bearbeitet", +"Modified" => "Geändert", "1 folder" => "1 Ordner", "{count} folders" => "{count} Ordner", "1 file" => "1 Datei", "{count} files" => "{count} Dateien", +"Unable to rename file" => "Konnte Datei nicht umbenennen", "Upload" => "Hochladen", "File handling" => "Dateibehandlung", "Maximum upload size" => "Maximale Upload-Größe", @@ -61,11 +62,11 @@ "From link" => "Von einem Link", "Deleted files" => "Gelöschte Dateien", "Cancel upload" => "Upload abbrechen", -"You don’t have write permissions here." => "Du besitzt hier keine Schreib-Berechtigung.", +"You don’t have write permissions here." => "Du hast hier keine Schreib-Berechtigung.", "Nothing in here. Upload something!" => "Alles leer. Lade etwas hoch!", "Download" => "Herunterladen", -"Unshare" => "Nicht mehr freigeben", -"Upload too large" => "Upload zu groß", +"Unshare" => "Freigabe aufheben", +"Upload too large" => "Der Upload ist zu groß", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.", "Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.", "Current scanning" => "Scanne", diff --git a/apps/files/l10n/de_DE.php b/apps/files/l10n/de_DE.php index 8fc1c106d0..3c06c1ac83 100644 --- a/apps/files/l10n/de_DE.php +++ b/apps/files/l10n/de_DE.php @@ -1,51 +1,53 @@ "Konnte %s nicht verschieben. Eine Datei mit diesem Namen existiert bereits", "Could not move %s" => "Konnte %s nicht verschieben", -"Unable to rename file" => "Konnte Datei nicht umbenennen", "No file was uploaded. Unknown error" => "Keine Datei hochgeladen. Unbekannter Fehler", -"There is no error, the file uploaded with success" => "Es sind keine Fehler aufgetreten. Die Datei wurde erfolgreich hochgeladen.", -"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in der php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Die Größe der hochzuladenden Datei überschreitet die MAX_FILE_SIZE-Richtlinie, die im HTML-Formular angegeben wurde", -"The uploaded file was only partially uploaded" => "Die Datei wurde nur teilweise hochgeladen.", -"No file was uploaded" => "Es wurde keine Datei hochgeladen.", -"Missing a temporary folder" => "Der temporäre Ordner fehlt.", +"There is no error, the file uploaded with success" => "Es ist kein Fehler aufgetreten. Die Datei wurde erfolgreich hochgeladen.", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Die hochgeladene Datei überschreitet die upload_max_filesize Vorgabe in php.ini", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Die Datei ist größer, als die MAX_FILE_SIZE Vorgabe erlaubt, die im HTML-Formular spezifiziert ist", +"The uploaded file was only partially uploaded" => "Die Datei konnte nur teilweise übertragen werden", +"No file was uploaded" => "Keine Datei konnte übertragen werden.", +"Missing a temporary folder" => "Kein temporärer Ordner vorhanden", "Failed to write to disk" => "Fehler beim Schreiben auf die Festplatte", "Not enough storage available" => "Nicht genug Speicher vorhanden.", "Invalid directory." => "Ungültiges Verzeichnis.", "Files" => "Dateien", -"Delete permanently" => "Entgültig löschen", +"Share" => "Teilen", +"Delete permanently" => "Endgültig löschen", "Delete" => "Löschen", "Rename" => "Umbenennen", "Pending" => "Ausstehend", "{new_name} already exists" => "{new_name} existiert bereits", "replace" => "ersetzen", -"suggest name" => "Einen Namen vorschlagen", +"suggest name" => "Namen vorschlagen", "cancel" => "abbrechen", "replaced {new_name} with {old_name}" => "{old_name} wurde ersetzt durch {new_name}", "undo" => "rückgängig machen", -"perform delete operation" => "führe das Löschen aus", +"perform delete operation" => "Löschvorgang ausführen", "1 file uploading" => "1 Datei wird hochgeladen", "files uploading" => "Dateien werden hoch geladen", "'.' is an invalid file name." => "'.' ist kein gültiger Dateiname.", "File name cannot be empty." => "Der Dateiname darf nicht leer sein.", -"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ungültiger Name! Die Zeichen '\\', '/', '<', '>', ':', '\"', '|', '?' und '*' sind nicht zulässig.", -"Your storage is full, files can not be updated or synced anymore!" => "Ihr Speicher ist voll. Daher können keine Dateien mehr aktualisiert oder synchronisiert werden!", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ungültiger Name, '\\', '/', '<', '>', ':', '\"', '|', '?' und '*' sind nicht zulässig.", +"Your storage is full, files can not be updated or synced anymore!" => "Ihr Speicher ist voll, daher können keine Dateien mehr aktualisiert oder synchronisiert werden!", "Your storage is almost full ({usedSpacePercent}%)" => "Ihr Speicher ist fast voll ({usedSpacePercent}%)", -"Your download is being prepared. This might take some time if the files are big." => "Ihr Download wird vorbereitet. Dies kann bei größeren Dateien einen Moment dauern.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Ihre Datei kann nicht hochgeladen werden, da sie entweder ein Verzeichnis oder 0 Bytes groß ist.", +"Your download is being prepared. This might take some time if the files are big." => "Ihr Download wird vorbereitet. Dies kann bei größeren Dateien etwas dauern.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Ihre Datei kann nicht hochgeladen werden, weil es sich um einen Ordner handelt oder 0 Bytes groß ist.", "Not enough space available" => "Nicht genügend Speicherplatz verfügbar", "Upload cancelled." => "Upload abgebrochen.", -"File upload is in progress. Leaving the page now will cancel the upload." => "Der Dateiupload läuft. Wenn Sie die Seite jetzt verlassen, wird der Upload abgebrochen.", +"File upload is in progress. Leaving the page now will cancel the upload." => "Dateiupload läuft. Wenn Sie die Seite jetzt verlassen, wird der Upload abgebrochen.", "URL cannot be empty." => "Die URL darf nicht leer sein.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ungültiger Verzeichnisname. Die Nutzung von \"Shared\" ist ownCloud vorbehalten", "Error" => "Fehler", "Name" => "Name", "Size" => "Größe", -"Modified" => "Bearbeitet", +"Modified" => "Geändert", "1 folder" => "1 Ordner", "{count} folders" => "{count} Ordner", "1 file" => "1 Datei", "{count} files" => "{count} Dateien", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Ungültiger Ordnername. Die Verwendung von \"Shared\" ist ownCloud vorbehalten.", +"Unable to rename file" => "Konnte Datei nicht umbenennen", "Upload" => "Hochladen", "File handling" => "Dateibehandlung", "Maximum upload size" => "Maximale Upload-Größe", @@ -62,12 +64,12 @@ "Deleted files" => "Gelöschte Dateien", "Cancel upload" => "Upload abbrechen", "You don’t have write permissions here." => "Sie haben hier keine Schreib-Berechtigungen.", -"Nothing in here. Upload something!" => "Alles leer. Bitte laden Sie etwas hoch!", +"Nothing in here. Upload something!" => "Alles leer. Laden Sie etwas hoch!", "Download" => "Herunterladen", "Unshare" => "Freigabe aufheben", "Upload too large" => "Der Upload ist zu groß", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.", "Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.", "Current scanning" => "Scanne", -"Upgrading filesystem cache..." => "Aktualisiere den Dateisystem-Cache..." +"Upgrading filesystem cache..." => "Dateisystem-Cache wird aktualisiert ..." ); diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php index 60d63c4142..b273f6b522 100644 --- a/apps/files/l10n/el.php +++ b/apps/files/l10n/el.php @@ -1,11 +1,10 @@ "Αδυναμία μετακίνησης του %s - υπάρχει ήδη αρχείο με αυτό το όνομα", "Could not move %s" => "Αδυναμία μετακίνησης του %s", -"Unable to rename file" => "Αδυναμία μετονομασίας αρχείου", "No file was uploaded. Unknown error" => "Δεν ανέβηκε κάποιο αρχείο. Άγνωστο σφάλμα", "There is no error, the file uploaded with success" => "Δεν υπάρχει σφάλμα, το αρχείο εστάλει επιτυχώς", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Το αρχείο που εστάλει υπερβαίνει την οδηγία μέγιστου επιτρεπτού μεγέθους \"upload_max_filesize\" του php.ini", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Το αρχείο υπερβαίνει την οδηγία μέγιστου επιτρεπτού μεγέθους \"MAX_FILE_SIZE\" που έχει οριστεί στην HTML φόρμα", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Το ανεβασμένο αρχείο υπερβαίνει το MAX_FILE_SIZE που ορίζεται στην HTML φόρμα", "The uploaded file was only partially uploaded" => "Το αρχείο εστάλει μόνο εν μέρει", "No file was uploaded" => "Κανένα αρχείο δεν στάλθηκε", "Missing a temporary folder" => "Λείπει ο προσωρινός φάκελος", @@ -13,6 +12,7 @@ "Not enough storage available" => "Μη επαρκής διαθέσιμος αποθηκευτικός χώρος", "Invalid directory." => "Μη έγκυρος φάκελος.", "Files" => "Αρχεία", +"Share" => "Διαμοιρασμός", "Delete permanently" => "Μόνιμη διαγραφή", "Delete" => "Διαγραφή", "Rename" => "Μετονομασία", @@ -46,7 +46,9 @@ "{count} folders" => "{count} φάκελοι", "1 file" => "1 αρχείο", "{count} files" => "{count} αρχεία", -"Upload" => "Αποστολή", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Μη έγκυρο όνομα φακέλου. Η χρήση του 'Κοινόχρηστος' χρησιμοποιείται από το ownCloud", +"Unable to rename file" => "Αδυναμία μετονομασίας αρχείου", +"Upload" => "Μεταφόρτωση", "File handling" => "Διαχείριση αρχείων", "Maximum upload size" => "Μέγιστο μέγεθος αποστολής", "max. possible: " => "μέγιστο δυνατό:", @@ -64,7 +66,7 @@ "You don’t have write permissions here." => "Δεν έχετε δικαιώματα εγγραφής εδώ.", "Nothing in here. Upload something!" => "Δεν υπάρχει τίποτα εδώ. Ανεβάστε κάτι!", "Download" => "Λήψη", -"Unshare" => "Διακοπή κοινής χρήσης", +"Unshare" => "Σταμάτημα διαμοιρασμού", "Upload too large" => "Πολύ μεγάλο αρχείο προς αποστολή", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Τα αρχεία που προσπαθείτε να ανεβάσετε υπερβαίνουν το μέγιστο μέγεθος αποστολής αρχείων σε αυτόν τον διακομιστή.", "Files are being scanned, please wait." => "Τα αρχεία σαρώνονται, παρακαλώ περιμένετε.", diff --git a/apps/files/l10n/en@pirate.php b/apps/files/l10n/en@pirate.php new file mode 100644 index 0000000000..fdd1850da9 --- /dev/null +++ b/apps/files/l10n/en@pirate.php @@ -0,0 +1,3 @@ + "Download" +); diff --git a/apps/files/l10n/eo.php b/apps/files/l10n/eo.php index 3435f43059..3eeb88754c 100644 --- a/apps/files/l10n/eo.php +++ b/apps/files/l10n/eo.php @@ -1,17 +1,17 @@ "Ne eblis movi %s: dosiero kun ĉi tiu nomo jam ekzistas", "Could not move %s" => "Ne eblis movi %s", -"Unable to rename file" => "Ne eblis alinomigi dosieron", "No file was uploaded. Unknown error" => "Neniu dosiero alŝutiĝis. Nekonata eraro.", -"There is no error, the file uploaded with success" => "Ne estas eraro, la dosiero alŝutiĝis sukcese", +"There is no error, the file uploaded with success" => "Ne estas eraro, la dosiero alŝutiĝis sukcese.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "La dosiero alŝutita superas la regulon upload_max_filesize el php.ini: ", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "La dosiero alŝutita superas la regulon MAX_FILE_SIZE, kiu estas difinita en la HTML-formularo", -"The uploaded file was only partially uploaded" => "La alŝutita dosiero nur parte alŝutiĝis", -"No file was uploaded" => "Neniu dosiero estas alŝutita", -"Missing a temporary folder" => "Mankas tempa dosierujo", +"The uploaded file was only partially uploaded" => "la alŝutita dosiero nur parte alŝutiĝis", +"No file was uploaded" => "Neniu dosiero alŝutiĝis.", +"Missing a temporary folder" => "Mankas provizora dosierujo.", "Failed to write to disk" => "Malsukcesis skribo al disko", "Invalid directory." => "Nevalida dosierujo.", "Files" => "Dosieroj", +"Share" => "Kunhavigi", "Delete" => "Forigi", "Rename" => "Alinomigi", "Pending" => "Traktotaj", @@ -22,6 +22,7 @@ "replaced {new_name} with {old_name}" => "anstataŭiĝis {new_name} per {old_name}", "undo" => "malfari", "1 file uploading" => "1 dosiero estas alŝutata", +"files uploading" => "dosieroj estas alŝutataj", "'.' is an invalid file name." => "'.' ne estas valida dosiernomo.", "File name cannot be empty." => "Dosiernomo devas ne malpleni.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nevalida nomo: “\\”, “/”, “<”, “>”, “:”, “\"”, “|”, “?” kaj “*” ne permesatas.", @@ -40,6 +41,7 @@ "{count} folders" => "{count} dosierujoj", "1 file" => "1 dosiero", "{count} files" => "{count} dosierujoj", +"Unable to rename file" => "Ne eblis alinomigi dosieron", "Upload" => "Alŝuti", "File handling" => "Dosieradministro", "Maximum upload size" => "Maksimuma alŝutogrando", @@ -57,7 +59,7 @@ "Nothing in here. Upload something!" => "Nenio estas ĉi tie. Alŝutu ion!", "Download" => "Elŝuti", "Unshare" => "Malkunhavigi", -"Upload too large" => "Elŝuto tro larĝa", +"Upload too large" => "Alŝuto tro larĝa", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "La dosieroj, kiujn vi provas alŝuti, transpasas la maksimuman grandon por dosieralŝutoj en ĉi tiu servilo.", "Files are being scanned, please wait." => "Dosieroj estas skanataj, bonvolu atendi.", "Current scanning" => "Nuna skano" diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php index e231abe429..b11adfabeb 100644 --- a/apps/files/l10n/es.php +++ b/apps/files/l10n/es.php @@ -1,22 +1,22 @@ "No se puede mover %s - Ya existe un archivo con ese nombre", "Could not move %s" => "No se puede mover %s", -"Unable to rename file" => "No se puede renombrar el archivo", -"No file was uploaded. Unknown error" => "Fallo no se subió el fichero", -"There is no error, the file uploaded with success" => "No se ha producido ningún error, el archivo se ha subido con éxito", +"No file was uploaded. Unknown error" => "No se subió ningún archivo. Error desconocido", +"There is no error, the file uploaded with success" => "No hay ningún error, el archivo se ha subido con éxito", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo que intentas subir sobrepasa el tamaño definido por la variable upload_max_filesize en php.ini", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El archivo que intentas subir sobrepasa el tamaño definido por la variable MAX_FILE_SIZE especificada en el formulario HTML", -"The uploaded file was only partially uploaded" => "El archivo que intentas subir solo se subió parcialmente", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El archivo subido sobrepasa la directiva MAX_FILE_SIZE especificada en el formulario HTML", +"The uploaded file was only partially uploaded" => "El archivo se ha subido parcialmente", "No file was uploaded" => "No se ha subido ningún archivo", -"Missing a temporary folder" => "Falta un directorio temporal", +"Missing a temporary folder" => "Falta la carpeta temporal", "Failed to write to disk" => "La escritura en disco ha fallado", "Not enough storage available" => "No hay suficiente espacio disponible", "Invalid directory." => "Directorio invalido.", "Files" => "Archivos", +"Share" => "Compartir", "Delete permanently" => "Eliminar permanentemente", "Delete" => "Eliminar", "Rename" => "Renombrar", -"Pending" => "Pendiente", +"Pending" => "Pendientes", "{new_name} already exists" => "{new_name} ya existe", "replace" => "reemplazar", "suggest name" => "sugerir nombre", @@ -26,18 +26,18 @@ "perform delete operation" => "Eliminar", "1 file uploading" => "subiendo 1 archivo", "files uploading" => "subiendo archivos", -"'.' is an invalid file name." => "'.' es un nombre de archivo inválido.", +"'.' is an invalid file name." => "'.' no es un nombre de archivo válido.", "File name cannot be empty." => "El nombre de archivo no puede estar vacío.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nombre Invalido, \"\\\", \"/\", \"<\", \">\", \":\", \"\", \"|\" \"?\" y \"*\" no están permitidos ", -"Your storage is full, files can not be updated or synced anymore!" => "Su almacenamiento esta lleno, los archivos no pueden ser mas actualizados o sincronizados!", -"Your storage is almost full ({usedSpacePercent}%)" => "Su almacenamiento esta lleno en un ({usedSpacePercent}%)", -"Your download is being prepared. This might take some time if the files are big." => "Tu descarga esta siendo preparada. Esto puede tardar algun tiempo si los archivos son muy grandes.", -"Unable to upload your file as it is a directory or has 0 bytes" => "No ha sido posible subir tu archivo porque es un directorio o tiene 0 bytes", +"Your storage is full, files can not be updated or synced anymore!" => "Su almacenamiento está lleno, ¡no se pueden actualizar ni sincronizar archivos!", +"Your storage is almost full ({usedSpacePercent}%)" => "Su almacenamiento está casi lleno ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Su descarga está siendo preparada. Esto puede tardar algún tiempo si los archivos son muy grandes.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Imposible subir su archivo, es un directorio o tiene 0 bytes", "Not enough space available" => "No hay suficiente espacio disponible", "Upload cancelled." => "Subida cancelada.", -"File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Salir de la página ahora cancelará la subida.", +"File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Si sale de la página ahora, se cancelará la subida.", "URL cannot be empty." => "La URL no puede estar vacía.", -"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nombre de carpeta invalido. El uso de \"Shared\" esta reservado para Owncloud", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "El nombre de carpeta no es válido. El uso de \"Shared\" está reservado para Owncloud", "Error" => "Error", "Name" => "Nombre", "Size" => "Tamaño", @@ -46,6 +46,8 @@ "{count} folders" => "{count} carpetas", "1 file" => "1 archivo", "{count} files" => "{count} archivos", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nombre de carpeta invalido. El uso de \"Shared\" esta reservado para ownCloud", +"Unable to rename file" => "No se puede renombrar el archivo", "Upload" => "Subir", "File handling" => "Tratamiento de archivos", "Maximum upload size" => "Tamaño máximo de subida", @@ -65,9 +67,9 @@ "Nothing in here. Upload something!" => "Aquí no hay nada. ¡Sube algo!", "Download" => "Descargar", "Unshare" => "Dejar de compartir", -"Upload too large" => "El archivo es demasiado grande", +"Upload too large" => "Subida demasido grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido por este servidor.", "Files are being scanned, please wait." => "Se están escaneando los archivos, por favor espere.", -"Current scanning" => "Ahora escaneando", -"Upgrading filesystem cache..." => "Actualizando cache de archivos de sistema" +"Current scanning" => "Escaneo actual", +"Upgrading filesystem cache..." => "Actualizando caché del sistema de archivos" ); diff --git a/apps/files/l10n/es_AR.php b/apps/files/l10n/es_AR.php index bd33fdfa4a..0ae47302ed 100644 --- a/apps/files/l10n/es_AR.php +++ b/apps/files/l10n/es_AR.php @@ -1,22 +1,22 @@ "No se pudo mover %s - Un archivo con este nombre ya existe", "Could not move %s" => "No se pudo mover %s ", -"Unable to rename file" => "No fue posible cambiar el nombre al archivo", "No file was uploaded. Unknown error" => "El archivo no fue subido. Error desconocido", -"There is no error, the file uploaded with success" => "No se han producido errores, el archivo se ha subido con éxito", +"There is no error, the file uploaded with success" => "No hay errores, el archivo fue subido con éxito", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "El archivo que intentás subir excede el tamaño definido por upload_max_filesize en el php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El archivo que intentás subir sobrepasa el tamaño definido por la variable MAX_FILE_SIZE especificada en el formulario HTML", -"The uploaded file was only partially uploaded" => "El archivo que intentás subir solo se subió parcialmente", -"No file was uploaded" => "El archivo no fue subido", -"Missing a temporary folder" => "Falta un directorio temporal", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "El archivo subido sobrepasa el valor MAX_FILE_SIZE especificada en el formulario HTML", +"The uploaded file was only partially uploaded" => "El archivo fue subido parcialmente", +"No file was uploaded" => "No se subió ningún archivo ", +"Missing a temporary folder" => "Error en la carpera temporal", "Failed to write to disk" => "Error al escribir en el disco", "Not enough storage available" => "No hay suficiente capacidad de almacenamiento", "Invalid directory." => "Directorio invalido.", "Files" => "Archivos", +"Share" => "Compartir", "Delete permanently" => "Borrar de manera permanente", "Delete" => "Borrar", "Rename" => "Cambiar nombre", -"Pending" => "Pendiente", +"Pending" => "Pendientes", "{new_name} already exists" => "{new_name} ya existe", "replace" => "reemplazar", "suggest name" => "sugerir nombre", @@ -25,6 +25,7 @@ "undo" => "deshacer", "perform delete operation" => "Eliminar", "1 file uploading" => "Subiendo 1 archivo", +"files uploading" => "Subiendo archivos", "'.' is an invalid file name." => "'.' es un nombre de archivo inválido.", "File name cannot be empty." => "El nombre del archivo no puede quedar vacío.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nombre invalido, '\\', '/', '<', '>', ':', '\"', '|', '?' y '*' no están permitidos.", @@ -45,6 +46,8 @@ "{count} folders" => "{count} directorios", "1 file" => "1 archivo", "{count} files" => "{count} archivos", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nombre de carpeta inválido. El uso de \"Shared\" está reservado por ownCloud", +"Unable to rename file" => "No fue posible cambiar el nombre al archivo", "Upload" => "Subir", "File handling" => "Tratamiento de archivos", "Maximum upload size" => "Tamaño máximo de subida", @@ -64,7 +67,7 @@ "Nothing in here. Upload something!" => "No hay nada. ¡Subí contenido!", "Download" => "Descargar", "Unshare" => "Dejar de compartir", -"Upload too large" => "El archivo es demasiado grande", +"Upload too large" => "El tamaño del archivo que querés subir es demasiado grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que intentás subir sobrepasan el tamaño máximo ", "Files are being scanned, please wait." => "Se están escaneando los archivos, por favor esperá.", "Current scanning" => "Escaneo actual", diff --git a/apps/files/l10n/et_EE.php b/apps/files/l10n/et_EE.php index 64a2f71b27..d3fab4b0bd 100644 --- a/apps/files/l10n/et_EE.php +++ b/apps/files/l10n/et_EE.php @@ -1,11 +1,10 @@ "Ei saa liigutada faili %s - samanimeline fail on juba olemas", "Could not move %s" => "%s liigutamine ebaõnnestus", -"Unable to rename file" => "Faili ümbernimetamine ebaõnnestus", "No file was uploaded. Unknown error" => "Ühtegi faili ei laetud üles. Tundmatu viga", -"There is no error, the file uploaded with success" => "Ühtegi viga pole, fail on üles laetud", -"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Üleslaetava faili suurus ületab php.ini poolt määratud upload_max_filesize suuruse", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Üles laetud faili suurus ületab HTML vormis määratud upload_max_filesize suuruse", +"There is no error, the file uploaded with success" => "Ühtegi tõrget polnud, fail on üles laetud", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Üleslaetava faili suurus ületab php.ini poolt määratud upload_max_filesize suuruse:", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Üleslaetud fail ületab MAX_FILE_SIZE suuruse, mis on HTML vormi jaoks määratud", "The uploaded file was only partially uploaded" => "Fail laeti üles ainult osaliselt", "No file was uploaded" => "Ühtegi faili ei laetud üles", "Missing a temporary folder" => "Ajutiste failide kaust puudub", @@ -13,9 +12,10 @@ "Not enough storage available" => "Saadaval pole piisavalt ruumi", "Invalid directory." => "Vigane kaust.", "Files" => "Failid", +"Share" => "Jaga", "Delete permanently" => "Kustuta jäädavalt", "Delete" => "Kustuta", -"Rename" => "ümber", +"Rename" => "Nimeta ümber", "Pending" => "Ootel", "{new_name} already exists" => "{new_name} on juba olemas", "replace" => "asenda", @@ -24,18 +24,18 @@ "replaced {new_name} with {old_name}" => "asendas nime {old_name} nimega {new_name}", "undo" => "tagasi", "perform delete operation" => "teosta kustutamine", -"1 file uploading" => "1 faili üleslaadimisel", -"files uploading" => "failide üleslaadimine", +"1 file uploading" => "1 fail üleslaadimisel", +"files uploading" => "faili üleslaadimisel", "'.' is an invalid file name." => "'.' on vigane failinimi.", "File name cannot be empty." => "Faili nimi ei saa olla tühi.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Vigane nimi, '\\', '/', '<', '>', ':', '\"', '|', '?' ja '*' pole lubatud.", -"Your storage is full, files can not be updated or synced anymore!" => "Sinu andmemaht on täis! Faile ei uuendata ja sünkroniseerimist ei toimu!", +"Your storage is full, files can not be updated or synced anymore!" => "Sinu andmemaht on täis! Faile ei uuendata ega sünkroniseerita!", "Your storage is almost full ({usedSpacePercent}%)" => "Su andmemaht on peaaegu täis ({usedSpacePercent}%)", -"Your download is being prepared. This might take some time if the files are big." => "Valmistatakse allalaadimist. See võib võtta veidi aega kui on tegu suurte failidega. ", -"Unable to upload your file as it is a directory or has 0 bytes" => "Sinu faili üleslaadimine ebaõnnestus, kuna see on kaust või selle suurus on 0 baiti", +"Your download is being prepared. This might take some time if the files are big." => "Valmistatakse allalaadimist. See võib võtta veidi aega, kui on tegu suurte failidega. ", +"Unable to upload your file as it is a directory or has 0 bytes" => "Faili ei saa üles laadida, kuna see on kaust või selle suurus on 0 baiti", "Not enough space available" => "Pole piisavalt ruumi", "Upload cancelled." => "Üleslaadimine tühistati.", -"File upload is in progress. Leaving the page now will cancel the upload." => "Faili üleslaadimine on töös. Lehelt lahkumine katkestab selle üleslaadimise.", +"File upload is in progress. Leaving the page now will cancel the upload." => "Faili üleslaadimine on töös. Lehelt lahkumine katkestab selle üleslaadimise.", "URL cannot be empty." => "URL ei saa olla tühi.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Vigane kataloogi nimi. 'Shared' kasutamine on reserveeritud ownCloud poolt.", "Error" => "Viga", @@ -46,6 +46,8 @@ "{count} folders" => "{count} kausta", "1 file" => "1 fail", "{count} files" => "{count} faili", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Vigane kausta nimi. 'Shared' kasutamine on reserveeritud ownCloud poolt.", +"Unable to rename file" => "Faili ümbernimetamine ebaõnnestus", "Upload" => "Lae üles", "File handling" => "Failide käsitlemine", "Maximum upload size" => "Maksimaalne üleslaadimise suurus", @@ -61,13 +63,13 @@ "From link" => "Allikast", "Deleted files" => "Kustutatud failid", "Cancel upload" => "Tühista üleslaadimine", -"You don’t have write permissions here." => "Siin puudvad Sul kirjutamisõigused.", +"You don’t have write permissions here." => "Siin puudvad sul kirjutamisõigused.", "Nothing in here. Upload something!" => "Siin pole midagi. Lae midagi üles!", "Download" => "Lae alla", "Unshare" => "Lõpeta jagamine", "Upload too large" => "Üleslaadimine on liiga suur", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Failid, mida sa proovid üles laadida, ületab serveri poolt üleslaetavatele failidele määratud maksimaalse suuruse.", -"Files are being scanned, please wait." => "Faile skannitakse, palun oota", +"Files are being scanned, please wait." => "Faile skannitakse, palun oota.", "Current scanning" => "Praegune skannimine", -"Upgrading filesystem cache..." => "Uuendan failisüsteemi puhvrit..." +"Upgrading filesystem cache..." => "Failisüsteemi puhvri uuendamine..." ); diff --git a/apps/files/l10n/eu.php b/apps/files/l10n/eu.php index 8c244babf0..a4afc2e8ca 100644 --- a/apps/files/l10n/eu.php +++ b/apps/files/l10n/eu.php @@ -1,18 +1,18 @@ "Ezin da %s mugitu - Izen hau duen fitxategia dagoeneko existitzen da", "Could not move %s" => "Ezin dira fitxategiak mugitu %s", -"Unable to rename file" => "Ezin izan da fitxategia berrizendatu", "No file was uploaded. Unknown error" => "Ez da fitxategirik igo. Errore ezezaguna", -"There is no error, the file uploaded with success" => "Ez da arazorik izan, fitxategia ongi igo da", +"There is no error, the file uploaded with success" => "Ez da errorerik egon, fitxategia ongi igo da", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Igotako fitxategiak php.ini fitxategian ezarritako upload_max_filesize muga gainditu du:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Igotako fitxategiaren tamaina HTML inprimakiko MAX_FILESIZE direktiban adierazitakoa baino handiagoa da", -"The uploaded file was only partially uploaded" => "Igotako fitxategiaren zati bat baino gehiago ez da igo", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Igotako fitxategia HTML formularioan zehaztutako MAX_FILE_SIZE direktiba baino handidagoa da.", +"The uploaded file was only partially uploaded" => "Igotako fitxategiaren zati bat bakarrik igo da", "No file was uploaded" => "Ez da fitxategirik igo", -"Missing a temporary folder" => "Aldi baterako karpeta falta da", +"Missing a temporary folder" => "Aldi bateko karpeta falta da", "Failed to write to disk" => "Errore bat izan da diskoan idazterakoan", "Not enough storage available" => "Ez dago behar aina leku erabilgarri,", "Invalid directory." => "Baliogabeko karpeta.", "Files" => "Fitxategiak", +"Share" => "Elkarbanatu", "Delete permanently" => "Ezabatu betirako", "Delete" => "Ezabatu", "Rename" => "Berrizendatu", @@ -25,13 +25,14 @@ "undo" => "desegin", "perform delete operation" => "Ezabatu", "1 file uploading" => "fitxategi 1 igotzen", +"files uploading" => "fitxategiak igotzen", "'.' is an invalid file name." => "'.' ez da fitxategi izen baliogarria.", "File name cannot be empty." => "Fitxategi izena ezin da hutsa izan.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "IZen aliogabea, '\\', '/', '<', '>', ':', '\"', '|', '?' eta '*' ez daude baimenduta.", "Your storage is full, files can not be updated or synced anymore!" => "Zure biltegiratzea beterik dago, ezingo duzu aurrerantzean fitxategirik igo edo sinkronizatu!", "Your storage is almost full ({usedSpacePercent}%)" => "Zure biltegiratzea nahiko beterik dago (%{usedSpacePercent})", "Your download is being prepared. This might take some time if the files are big." => "Zure deskarga prestatu egin behar da. Denbora bat har lezake fitxategiak handiak badira. ", -"Unable to upload your file as it is a directory or has 0 bytes" => "Ezin da zure fitxategia igo, karpeta bat da edo 0 byt ditu", +"Unable to upload your file as it is a directory or has 0 bytes" => "Ezin izan da zure fitxategia igo karpeta bat delako edo 0 byte dituelako", "Not enough space available" => "Ez dago leku nahikorik.", "Upload cancelled." => "Igoera ezeztatuta", "File upload is in progress. Leaving the page now will cancel the upload." => "Fitxategien igoera martxan da. Orria orain uzteak igoera ezeztatutko du.", @@ -45,6 +46,7 @@ "{count} folders" => "{count} karpeta", "1 file" => "fitxategi bat", "{count} files" => "{count} fitxategi", +"Unable to rename file" => "Ezin izan da fitxategia berrizendatu", "Upload" => "Igo", "File handling" => "Fitxategien kudeaketa", "Maximum upload size" => "Igo daitekeen gehienezko tamaina", @@ -64,7 +66,7 @@ "Nothing in here. Upload something!" => "Ez dago ezer. Igo zerbait!", "Download" => "Deskargatu", "Unshare" => "Ez elkarbanatu", -"Upload too large" => "Igotakoa handiegia da", +"Upload too large" => "Igoera handiegia da", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Igotzen saiatzen ari zaren fitxategiak zerbitzari honek igotzeko onartzen duena baino handiagoak dira.", "Files are being scanned, please wait." => "Fitxategiak eskaneatzen ari da, itxoin mezedez.", "Current scanning" => "Orain eskaneatzen ari da", diff --git a/apps/files/l10n/fa.php b/apps/files/l10n/fa.php index 13ef465199..b97067ac19 100644 --- a/apps/files/l10n/fa.php +++ b/apps/files/l10n/fa.php @@ -1,20 +1,20 @@ "%s نمی تواند حرکت کند - در حال حاضر پرونده با این نام وجود دارد. ", "Could not move %s" => "%s نمی تواند حرکت کند ", -"Unable to rename file" => "قادر به تغییر نام پرونده نیست.", "No file was uploaded. Unknown error" => "هیچ فایلی آپلود نشد.خطای ناشناس", -"There is no error, the file uploaded with success" => "هیچ خطایی وجود ندارد فایل با موفقیت بار گذاری شد", +"There is no error, the file uploaded with success" => "هیچ خطایی نیست بارگذاری پرونده موفقیت آمیز بود", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "پرونده آپلود شده بیش ازدستور ماکزیمم_حجم فایل_برای آپلود در php.ini استفاده کرده است.", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "حداکثر حجم مجاز برای بارگذاری از طریق HTML \nMAX_FILE_SIZE", -"The uploaded file was only partially uploaded" => "مقدار کمی از فایل بارگذاری شده", -"No file was uploaded" => "هیچ فایلی بارگذاری نشده", -"Missing a temporary folder" => "یک پوشه موقت گم شده است", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "حداکثر حجم قابل بار گذاری از طریق HTML MAX_FILE_SIZE است", +"The uploaded file was only partially uploaded" => "پرونده بارگذاری شده فقط تاحدودی بارگذاری شده", +"No file was uploaded" => "هیچ پروندهای بارگذاری نشده", +"Missing a temporary folder" => "یک پوشه موقت گم شده", "Failed to write to disk" => "نوشتن بر روی دیسک سخت ناموفق بود", "Not enough storage available" => "فضای کافی در دسترس نیست", "Invalid directory." => "فهرست راهنما نامعتبر می باشد.", -"Files" => "فایل ها", +"Files" => "پرونده‌ها", +"Share" => "اشتراک‌گذاری", "Delete permanently" => "حذف قطعی", -"Delete" => "پاک کردن", +"Delete" => "حذف", "Rename" => "تغییرنام", "Pending" => "در انتظار", "{new_name} already exists" => "{نام _جدید} در حال حاضر وجود دارد.", @@ -41,12 +41,13 @@ "Error" => "خطا", "Name" => "نام", "Size" => "اندازه", -"Modified" => "تغییر یافته", +"Modified" => "تاریخ", "1 folder" => "1 پوشه", "{count} folders" => "{ شمار} پوشه ها", "1 file" => "1 پرونده", "{count} files" => "{ شمار } فایل ها", -"Upload" => "بارگذاری", +"Unable to rename file" => "قادر به تغییر نام پرونده نیست.", +"Upload" => "بارگزاری", "File handling" => "اداره پرونده ها", "Maximum upload size" => "حداکثر اندازه بارگزاری", "max. possible: " => "حداکثرمقدارممکن:", @@ -63,9 +64,9 @@ "Cancel upload" => "متوقف کردن بار گذاری", "You don’t have write permissions here." => "شما اجازه ی نوشتن در اینجا را ندارید", "Nothing in here. Upload something!" => "اینجا هیچ چیز نیست.", -"Download" => "بارگیری", +"Download" => "دانلود", "Unshare" => "لغو اشتراک", -"Upload too large" => "حجم بارگذاری بسیار زیاد است", +"Upload too large" => "سایز فایل برای آپلود زیاد است(م.تنظیمات در php.ini)", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "فایلها بیش از حد تعیین شده در این سرور هستند\nمترجم:با تغییر فایل php,ini میتوان این محدودیت را برطرف کرد", "Files are being scanned, please wait." => "پرونده ها در حال بازرسی هستند لطفا صبر کنید", "Current scanning" => "بازرسی کنونی", diff --git a/apps/files/l10n/fi_FI.php b/apps/files/l10n/fi_FI.php index b797273d51..3d0d724578 100644 --- a/apps/files/l10n/fi_FI.php +++ b/apps/files/l10n/fi_FI.php @@ -1,17 +1,18 @@ "Kohteen %s siirto ei onnistunut - Tiedosto samalla nimellä on jo olemassa", "Could not move %s" => "Kohteen %s siirto ei onnistunut", -"Unable to rename file" => "Tiedoston nimeäminen uudelleen ei onnistunut", "No file was uploaded. Unknown error" => "Tiedostoa ei lähetetty. Tuntematon virhe", "There is no error, the file uploaded with success" => "Ei virheitä, tiedosto lähetettiin onnistuneesti", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Lähetetty tiedosto ylittää HTML-lomakkeessa määritetyn MAX_FILE_SIZE-arvon ylärajan", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Lähetetyn tiedoston koko ylittää php.ini-tiedoston upload_max_filesize-säännön:", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Ladattavan tiedoston maksimikoko ylittää MAX_FILE_SIZE dirketiivin, joka on määritelty HTML-lomakkeessa", "The uploaded file was only partially uploaded" => "Tiedoston lähetys onnistui vain osittain", "No file was uploaded" => "Yhtäkään tiedostoa ei lähetetty", -"Missing a temporary folder" => "Väliaikaiskansiota ei ole olemassa", +"Missing a temporary folder" => "Tilapäiskansio puuttuu", "Failed to write to disk" => "Levylle kirjoitus epäonnistui", "Not enough storage available" => "Tallennustilaa ei ole riittävästi käytettävissä", "Invalid directory." => "Virheellinen kansio.", "Files" => "Tiedostot", +"Share" => "Jaa", "Delete permanently" => "Poista pysyvästi", "Delete" => "Poista", "Rename" => "Nimeä uudelleen", @@ -28,7 +29,7 @@ "Your storage is full, files can not be updated or synced anymore!" => "Tallennustila on loppu, tiedostoja ei voi enää päivittää tai synkronoida!", "Your storage is almost full ({usedSpacePercent}%)" => "Tallennustila on melkein loppu ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Lataustasi valmistellaan. Tämä saattaa kestää hetken, jos tiedostot ovat suuria kooltaan.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Tiedoston lähetys epäonnistui, koska sen koko on 0 tavua tai kyseessä on kansio", +"Unable to upload your file as it is a directory or has 0 bytes" => "Tiedoston lähetys epäonnistui, koska sen koko on 0 tavua tai kyseessä on kansio.", "Not enough space available" => "Tilaa ei ole riittävästi", "Upload cancelled." => "Lähetys peruttu.", "File upload is in progress. Leaving the page now will cancel the upload." => "Tiedoston lähetys on meneillään. Sivulta poistuminen nyt peruu tiedoston lähetyksen.", @@ -36,11 +37,12 @@ "Error" => "Virhe", "Name" => "Nimi", "Size" => "Koko", -"Modified" => "Muutettu", +"Modified" => "Muokattu", "1 folder" => "1 kansio", "{count} folders" => "{count} kansiota", "1 file" => "1 tiedosto", "{count} files" => "{count} tiedostoa", +"Unable to rename file" => "Tiedoston nimeäminen uudelleen ei onnistunut", "Upload" => "Lähetä", "File handling" => "Tiedostonhallinta", "Maximum upload size" => "Lähetettävän tiedoston suurin sallittu koko", diff --git a/apps/files/l10n/fr.php b/apps/files/l10n/fr.php index 4a602c21ec..39c697396c 100644 --- a/apps/files/l10n/fr.php +++ b/apps/files/l10n/fr.php @@ -1,22 +1,22 @@ "Impossible de déplacer %s - Un fichier possédant ce nom existe déjà", "Could not move %s" => "Impossible de déplacer %s", -"Unable to rename file" => "Impossible de renommer le fichier", -"No file was uploaded. Unknown error" => "Aucun fichier n'a été chargé. Erreur inconnue", -"There is no error, the file uploaded with success" => "Aucune erreur, le fichier a été téléversé avec succès", +"No file was uploaded. Unknown error" => "Aucun fichier n'a été envoyé. Erreur inconnue", +"There is no error, the file uploaded with success" => "Aucune erreur, le fichier a été envoyé avec succès.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Le fichier envoyé dépasse la valeur upload_max_filesize située dans le fichier php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Le fichier téléversé excède la valeur de MAX_FILE_SIZE spécifiée dans le formulaire HTML", -"The uploaded file was only partially uploaded" => "Le fichier n'a été que partiellement téléversé", -"No file was uploaded" => "Aucun fichier n'a été téléversé", -"Missing a temporary folder" => "Il manque un répertoire temporaire", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Le fichier envoyé dépasse la directive MAX_FILE_SIZE qui est spécifiée dans le formulaire HTML.", +"The uploaded file was only partially uploaded" => "Le fichier n'a été que partiellement envoyé.", +"No file was uploaded" => "Pas de fichier envoyé.", +"Missing a temporary folder" => "Absence de dossier temporaire.", "Failed to write to disk" => "Erreur d'écriture sur le disque", "Not enough storage available" => "Plus assez d'espace de stockage disponible", "Invalid directory." => "Dossier invalide.", "Files" => "Fichiers", +"Share" => "Partager", "Delete permanently" => "Supprimer de façon définitive", "Delete" => "Supprimer", "Rename" => "Renommer", -"Pending" => "En cours", +"Pending" => "En attente", "{new_name} already exists" => "{new_name} existe déjà", "replace" => "remplacer", "suggest name" => "Suggérer un nom", @@ -24,16 +24,17 @@ "replaced {new_name} with {old_name}" => "{new_name} a été remplacé par {old_name}", "undo" => "annuler", "perform delete operation" => "effectuer l'opération de suppression", -"1 file uploading" => "1 fichier en cours de téléchargement", +"1 file uploading" => "1 fichier en cours d'envoi", +"files uploading" => "fichiers en cours d'envoi", "'.' is an invalid file name." => "'.' n'est pas un nom de fichier valide.", "File name cannot be empty." => "Le nom de fichier ne peut être vide.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nom invalide, les caractères '\\', '/', '<', '>', ':', '\"', '|', '?' et '*' ne sont pas autorisés.", "Your storage is full, files can not be updated or synced anymore!" => "Votre espage de stockage est plein, les fichiers ne peuvent plus être téléversés ou synchronisés !", "Your storage is almost full ({usedSpacePercent}%)" => "Votre espace de stockage est presque plein ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Votre téléchargement est cours de préparation. Ceci peut nécessiter un certain temps si les fichiers sont volumineux.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Impossible de charger vos fichiers car il s'agit d'un dossier ou le fichier fait 0 octet.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Impossible d'envoyer votre fichier dans la mesure où il s'agit d'un répertoire ou d'un fichier de taille nulle", "Not enough space available" => "Espace disponible insuffisant", -"Upload cancelled." => "Chargement annulé.", +"Upload cancelled." => "Envoi annulé.", "File upload is in progress. Leaving the page now will cancel the upload." => "L'envoi du fichier est en cours. Quitter cette page maintenant annulera l'envoi du fichier.", "URL cannot be empty." => "L'URL ne peut-être vide", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Nom de dossier invalide. L'utilisation du mot 'Shared' est réservée à Owncloud", @@ -45,6 +46,8 @@ "{count} folders" => "{count} dossiers", "1 file" => "1 fichier", "{count} files" => "{count} fichiers", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nom de dossier invalide. L'utilisation du mot 'Shared' est réservée à Owncloud", +"Unable to rename file" => "Impossible de renommer le fichier", "Upload" => "Envoyer", "File handling" => "Gestion des fichiers", "Maximum upload size" => "Taille max. d'envoi", @@ -64,7 +67,7 @@ "Nothing in here. Upload something!" => "Il n'y a rien ici ! Envoyez donc quelque chose :)", "Download" => "Télécharger", "Unshare" => "Ne plus partager", -"Upload too large" => "Fichier trop volumineux", +"Upload too large" => "Téléversement trop volumineux", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise par ce serveur.", "Files are being scanned, please wait." => "Les fichiers sont en cours d'analyse, veuillez patienter.", "Current scanning" => "Analyse en cours", diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php index 14992f5838..d22ed4b872 100644 --- a/apps/files/l10n/gl.php +++ b/apps/files/l10n/gl.php @@ -1,18 +1,18 @@ "Non se moveu %s - Xa existe un ficheiro con ese nome.", "Could not move %s" => "Non foi posíbel mover %s", -"Unable to rename file" => "Non é posíbel renomear o ficheiro", -"No file was uploaded. Unknown error" => "Non foi enviado ningún ficheiro. Produciuse un erro descoñecido.", -"There is no error, the file uploaded with success" => "Non se produciu ningún erro. O ficheiro enviouse correctamente", +"No file was uploaded. Unknown error" => "Non se enviou ningún ficheiro. Produciuse un erro descoñecido.", +"There is no error, the file uploaded with success" => "Non houbo erros, o ficheiro enviouse correctamente", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro enviado excede a directiva indicada por upload_max_filesize de php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro enviado excede a directiva MAX_FILE_SIZE que foi indicada no formulario HTML", -"The uploaded file was only partially uploaded" => "O ficheiro enviado foi só parcialmente enviado", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro enviado excede da directiva MAX_FILE_SIZE especificada no formulario HTML", +"The uploaded file was only partially uploaded" => "O ficheiro so foi parcialmente enviado", "No file was uploaded" => "Non se enviou ningún ficheiro", -"Missing a temporary folder" => "Falta un cartafol temporal", +"Missing a temporary folder" => "Falta o cartafol temporal", "Failed to write to disk" => "Produciuse un erro ao escribir no disco", "Not enough storage available" => "Non hai espazo de almacenamento abondo", "Invalid directory." => "O directorio é incorrecto.", "Files" => "Ficheiros", +"Share" => "Compartir", "Delete permanently" => "Eliminar permanentemente", "Delete" => "Eliminar", "Rename" => "Renomear", @@ -46,12 +46,14 @@ "{count} folders" => "{count} cartafoles", "1 file" => "1 ficheiro", "{count} files" => "{count} ficheiros", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nome de cartafol incorrecto. O uso de «Compartido» e «Shared» está reservado para o ownClod", +"Unable to rename file" => "Non é posíbel renomear o ficheiro", "Upload" => "Enviar", "File handling" => "Manexo de ficheiro", "Maximum upload size" => "Tamaño máximo do envío", "max. possible: " => "máx. posíbel: ", "Needed for multi-file and folder downloads." => "Precísase para a descarga de varios ficheiros e cartafoles.", -"Enable ZIP-download" => "Habilitar a descarga-ZIP", +"Enable ZIP-download" => "Activar a descarga ZIP", "0 is unlimited" => "0 significa ilimitado", "Maximum input size for ZIP files" => "Tamaño máximo de descarga para os ficheiros ZIP", "Save" => "Gardar", diff --git a/apps/files/l10n/he.php b/apps/files/l10n/he.php index 36ba7cc5de..963f25ebed 100644 --- a/apps/files/l10n/he.php +++ b/apps/files/l10n/he.php @@ -1,13 +1,14 @@ "לא הועלה קובץ. טעות בלתי מזוהה.", -"There is no error, the file uploaded with success" => "לא אירעה תקלה, הקבצים הועלו בהצלחה", +"There is no error, the file uploaded with success" => "לא התרחשה שגיאה, הקובץ הועלה בהצלחה", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "הקבצים שנשלחו חורגים מהגודל שצוין בהגדרה upload_max_filesize שבקובץ php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "הקובץ שהועלה חרג מההנחיה MAX_FILE_SIZE שצוינה בטופס ה־HTML", -"The uploaded file was only partially uploaded" => "הקובץ שהועלה הועלה בצורה חלקית", -"No file was uploaded" => "לא הועלו קבצים", -"Missing a temporary folder" => "תיקייה זמנית חסרה", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "הקובץ שהועלה גדוך מהערך MAX_FILE_SIZE שהוגדר בתופס HTML", +"The uploaded file was only partially uploaded" => "הקובץ הועלה באופן חלקי בלבד", +"No file was uploaded" => "שום קובץ לא הועלה", +"Missing a temporary folder" => "תקיה זמנית חסרה", "Failed to write to disk" => "הכתיבה לכונן נכשלה", "Files" => "קבצים", +"Share" => "שתף", "Delete permanently" => "מחק לצמיתות", "Delete" => "מחיקה", "Rename" => "שינוי שם", diff --git a/apps/files/l10n/hr.php b/apps/files/l10n/hr.php index a6b83b3d67..d634faee75 100644 --- a/apps/files/l10n/hr.php +++ b/apps/files/l10n/hr.php @@ -1,12 +1,13 @@ "Datoteka je poslana uspješno i bez pogrešaka", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Poslana datoteka izlazi iz okvira MAX_FILE_SIZE direktive postavljene u HTML obrascu", -"The uploaded file was only partially uploaded" => "Datoteka je poslana samo djelomično", -"No file was uploaded" => "Ni jedna datoteka nije poslana", -"Missing a temporary folder" => "Nedostaje privremena mapa", +"There is no error, the file uploaded with success" => "Nema pogreške, datoteka je poslana uspješno.", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Poslana datoteka prelazi veličinu prikazanu u MAX_FILE_SIZE direktivi u HTML formi", +"The uploaded file was only partially uploaded" => "Poslana datoteka je parcijalno poslana", +"No file was uploaded" => "Datoteka nije poslana", +"Missing a temporary folder" => "Nedostaje privremeni direktorij", "Failed to write to disk" => "Neuspjelo pisanje na disk", "Files" => "Datoteke", -"Delete" => "Briši", +"Share" => "Podijeli", +"Delete" => "Obriši", "Rename" => "Promjeni ime", "Pending" => "U tijeku", "replace" => "zamjeni", @@ -14,14 +15,15 @@ "cancel" => "odustani", "undo" => "vrati", "1 file uploading" => "1 datoteka se učitava", +"files uploading" => "datoteke se učitavaju", "Unable to upload your file as it is a directory or has 0 bytes" => "Nemoguće poslati datoteku jer je prazna ili je direktorij", "Upload cancelled." => "Slanje poništeno.", "File upload is in progress. Leaving the page now will cancel the upload." => "Učitavanje datoteke. Napuštanjem stranice će prekinuti učitavanje.", "Error" => "Greška", -"Name" => "Naziv", +"Name" => "Ime", "Size" => "Veličina", "Modified" => "Zadnja promjena", -"Upload" => "Pošalji", +"Upload" => "Učitaj", "File handling" => "datoteka za rukovanje", "Maximum upload size" => "Maksimalna veličina prijenosa", "max. possible: " => "maksimalna moguća: ", @@ -35,8 +37,8 @@ "Folder" => "mapa", "Cancel upload" => "Prekini upload", "Nothing in here. Upload something!" => "Nema ničega u ovoj mapi. Pošalji nešto!", -"Download" => "Preuzmi", -"Unshare" => "Prekini djeljenje", +"Download" => "Preuzimanje", +"Unshare" => "Makni djeljenje", "Upload too large" => "Prijenos je preobiman", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke koje pokušavate prenijeti prelaze maksimalnu veličinu za prijenos datoteka na ovom poslužitelju.", "Files are being scanned, please wait." => "Datoteke se skeniraju, molimo pričekajte.", diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php index 103523b65f..76b8bd420d 100644 --- a/apps/files/l10n/hu_HU.php +++ b/apps/files/l10n/hu_HU.php @@ -1,18 +1,18 @@ "%s áthelyezése nem sikerült - már létezik másik fájl ezzel a névvel", "Could not move %s" => "Nem sikerült %s áthelyezése", -"Unable to rename file" => "Nem lehet átnevezni a fájlt", "No file was uploaded. Unknown error" => "Nem történt feltöltés. Ismeretlen hiba", "There is no error, the file uploaded with success" => "A fájlt sikerült feltölteni", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "A feltöltött fájl mérete meghaladja a php.ini állományban megadott upload_max_filesize paraméter értékét.", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "A feltöltött fájl mérete meghaladja a MAX_FILE_SIZE paramétert, ami a HTML formban került megadásra.", "The uploaded file was only partially uploaded" => "Az eredeti fájlt csak részben sikerült feltölteni.", -"No file was uploaded" => "Nem töltődött fel semmi", +"No file was uploaded" => "Nem töltődött fel állomány", "Missing a temporary folder" => "Hiányzik egy ideiglenes mappa", "Failed to write to disk" => "Nem sikerült a lemezre történő írás", "Not enough storage available" => "Nincs elég szabad hely.", "Invalid directory." => "Érvénytelen mappa.", "Files" => "Fájlok", +"Share" => "Megosztás", "Delete permanently" => "Végleges törlés", "Delete" => "Törlés", "Rename" => "Átnevezés", @@ -46,6 +46,8 @@ "{count} folders" => "{count} mappa", "1 file" => "1 fájl", "{count} files" => "{count} fájl", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Érvénytelen mappanév. A 'Shared' az ownCloud számára fenntartott elnevezés", +"Unable to rename file" => "Nem lehet átnevezni a fájlt", "Upload" => "Feltöltés", "File handling" => "Fájlkezelés", "Maximum upload size" => "Maximális feltölthető fájlméret", @@ -64,7 +66,7 @@ "You don’t have write permissions here." => "Itt nincs írásjoga.", "Nothing in here. Upload something!" => "Itt nincs semmi. Töltsön fel valamit!", "Download" => "Letöltés", -"Unshare" => "Megosztás visszavonása", +"Unshare" => "A megosztás visszavonása", "Upload too large" => "A feltöltés túl nagy", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "A feltöltendő állományok mérete meghaladja a kiszolgálón megengedett maximális méretet.", "Files are being scanned, please wait." => "A fájllista ellenőrzése zajlik, kis türelmet!", diff --git a/apps/files/l10n/ia.php b/apps/files/l10n/ia.php index b3233cc37d..886922d954 100644 --- a/apps/files/l10n/ia.php +++ b/apps/files/l10n/ia.php @@ -1,9 +1,11 @@ "Le file incargate solmente esseva incargate partialmente", -"No file was uploaded" => "Nulle file esseva incargate", +"No file was uploaded" => "Nulle file esseva incargate.", "Missing a temporary folder" => "Manca un dossier temporari", "Files" => "Files", +"Share" => "Compartir", "Delete" => "Deler", +"Error" => "Error", "Name" => "Nomine", "Size" => "Dimension", "Modified" => "Modificate", diff --git a/apps/files/l10n/id.php b/apps/files/l10n/id.php index 3894ce0de9..58cc0ea7fd 100644 --- a/apps/files/l10n/id.php +++ b/apps/files/l10n/id.php @@ -1,8 +1,7 @@ "Tidak dapat memindahkan %s - Berkas dengan nama ini sudah ada", "Could not move %s" => "Tidak dapat memindahkan %s", -"Unable to rename file" => "Tidak dapat mengubah nama berkas", -"No file was uploaded. Unknown error" => "Tidak ada berkas yang diunggah. Galat tidak dikenal", +"No file was uploaded. Unknown error" => "Tidak ada berkas yang diunggah. Galat tidak dikenal.", "There is no error, the file uploaded with success" => "Tidak ada galat, berkas sukses diunggah", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Berkas yang diunggah melampaui direktif upload_max_filesize pada php.ini", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Berkas yang diunggah melampaui direktif MAX_FILE_SIZE yang ditentukan dalam formulir HTML.", @@ -13,6 +12,7 @@ "Not enough storage available" => "Ruang penyimpanan tidak mencukupi", "Invalid directory." => "Direktori tidak valid.", "Files" => "Berkas", +"Share" => "Bagikan", "Delete permanently" => "Hapus secara permanen", "Delete" => "Hapus", "Rename" => "Ubah nama", @@ -32,7 +32,7 @@ "Your storage is full, files can not be updated or synced anymore!" => "Ruang penyimpanan Anda penuh, berkas tidak dapat diperbarui atau disinkronkan lagi!", "Your storage is almost full ({usedSpacePercent}%)" => "Ruang penyimpanan hampir penuh ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Unduhan Anda sedang disiapkan. Prosesnya dapat berlangsung agak lama jika ukuran berkasnya besar.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Gagal mengunggah berkas Anda karena berupa direktori atau ukurannya 0 byte", +"Unable to upload your file as it is a directory or has 0 bytes" => "Gagal mengunggah berkas Anda karena berupa direktori atau mempunyai ukuran 0 byte", "Not enough space available" => "Ruang penyimpanan tidak mencukupi", "Upload cancelled." => "Pengunggahan dibatalkan.", "File upload is in progress. Leaving the page now will cancel the upload." => "Berkas sedang diunggah. Meninggalkan halaman ini akan membatalkan proses.", @@ -46,6 +46,7 @@ "{count} folders" => "{count} folder", "1 file" => "1 berkas", "{count} files" => "{count} berkas", +"Unable to rename file" => "Tidak dapat mengubah nama berkas", "Upload" => "Unggah", "File handling" => "Penanganan berkas", "Maximum upload size" => "Ukuran pengunggahan maksimum", @@ -65,7 +66,7 @@ "Nothing in here. Upload something!" => "Tidak ada apa-apa di sini. Unggah sesuatu!", "Download" => "Unduh", "Unshare" => "Batalkan berbagi", -"Upload too large" => "Unggahan terlalu besar", +"Upload too large" => "Yang diunggah terlalu besar", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Berkas yang dicoba untuk diunggah melebihi ukuran maksimum pengunggahan berkas di server ini.", "Files are being scanned, please wait." => "Berkas sedang dipindai, silakan tunggu.", "Current scanning" => "Yang sedang dipindai", diff --git a/apps/files/l10n/is.php b/apps/files/l10n/is.php index c7c8e9ccdb..aa10c838c1 100644 --- a/apps/files/l10n/is.php +++ b/apps/files/l10n/is.php @@ -1,7 +1,6 @@ "Gat ekki fært %s - Skrá með þessu nafni er þegar til", "Could not move %s" => "Gat ekki fært %s", -"Unable to rename file" => "Gat ekki endurskýrt skrá", "No file was uploaded. Unknown error" => "Engin skrá var send inn. Óþekkt villa.", "There is no error, the file uploaded with success" => "Engin villa, innsending heppnaðist", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Innsend skrá er stærri en upload_max stillingin í php.ini:", @@ -12,6 +11,7 @@ "Failed to write to disk" => "Tókst ekki að skrifa á disk", "Invalid directory." => "Ógild mappa.", "Files" => "Skrár", +"Share" => "Deila", "Delete" => "Eyða", "Rename" => "Endurskýra", "Pending" => "Bíður", @@ -39,6 +39,7 @@ "{count} folders" => "{count} möppur", "1 file" => "1 skrá", "{count} files" => "{count} skrár", +"Unable to rename file" => "Gat ekki endurskýrt skrá", "Upload" => "Senda inn", "File handling" => "Meðhöndlun skrár", "Maximum upload size" => "Hámarks stærð innsendingar", diff --git a/apps/files/l10n/it.php b/apps/files/l10n/it.php index 20819e2564..c588285aac 100644 --- a/apps/files/l10n/it.php +++ b/apps/files/l10n/it.php @@ -1,18 +1,18 @@ "Impossibile spostare %s - un file con questo nome esiste già", "Could not move %s" => "Impossibile spostare %s", -"Unable to rename file" => "Impossibile rinominare il file", "No file was uploaded. Unknown error" => "Nessun file è stato inviato. Errore sconosciuto", -"There is no error, the file uploaded with success" => "Non ci sono errori, file caricato con successo", +"There is no error, the file uploaded with success" => "Non ci sono errori, il file è stato caricato correttamente", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Il file caricato supera la direttiva upload_max_filesize in php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Il file caricato supera il valore MAX_FILE_SIZE definito nel form HTML", -"The uploaded file was only partially uploaded" => "Il file è stato parzialmente caricato", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Il file inviato supera la direttiva MAX_FILE_SIZE specificata nel modulo HTML", +"The uploaded file was only partially uploaded" => "Il file è stato caricato solo parzialmente", "No file was uploaded" => "Nessun file è stato caricato", -"Missing a temporary folder" => "Cartella temporanea mancante", +"Missing a temporary folder" => "Manca una cartella temporanea", "Failed to write to disk" => "Scrittura su disco non riuscita", "Not enough storage available" => "Spazio di archiviazione insufficiente", "Invalid directory." => "Cartella non valida.", "Files" => "File", +"Share" => "Condividi", "Delete permanently" => "Elimina definitivamente", "Delete" => "Elimina", "Rename" => "Rinomina", @@ -32,7 +32,7 @@ "Your storage is full, files can not be updated or synced anymore!" => "Lo spazio di archiviazione è pieno, i file non possono essere più aggiornati o sincronizzati!", "Your storage is almost full ({usedSpacePercent}%)" => "Lo spazio di archiviazione è quasi pieno ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Il tuo scaricamento è in fase di preparazione. Ciò potrebbe richiedere del tempo se i file sono grandi.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Impossibile inviare il file poiché è una cartella o ha dimensione 0 byte", +"Unable to upload your file as it is a directory or has 0 bytes" => "Impossibile caricare il file poiché è una cartella o ha una dimensione di 0 byte", "Not enough space available" => "Spazio disponibile insufficiente", "Upload cancelled." => "Invio annullato", "File upload is in progress. Leaving the page now will cancel the upload." => "Caricamento del file in corso. La chiusura della pagina annullerà il caricamento.", @@ -46,6 +46,8 @@ "{count} folders" => "{count} cartelle", "1 file" => "1 file", "{count} files" => "{count} file", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nome della cartella non valido. L'uso di 'Shared' è riservato a ownCloud", +"Unable to rename file" => "Impossibile rinominare il file", "Upload" => "Carica", "File handling" => "Gestione file", "Maximum upload size" => "Dimensione massima upload", @@ -65,7 +67,7 @@ "Nothing in here. Upload something!" => "Non c'è niente qui. Carica qualcosa!", "Download" => "Scarica", "Unshare" => "Rimuovi condivisione", -"Upload too large" => "Il file caricato è troppo grande", +"Upload too large" => "Caricamento troppo grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "I file che stai provando a caricare superano la dimensione massima consentita su questo server.", "Files are being scanned, please wait." => "Scansione dei file in corso, attendi", "Current scanning" => "Scansione corrente", diff --git a/apps/files/l10n/ja_JP.php b/apps/files/l10n/ja_JP.php index 28453618d9..55dcf3640e 100644 --- a/apps/files/l10n/ja_JP.php +++ b/apps/files/l10n/ja_JP.php @@ -1,22 +1,22 @@ "%s を移動できませんでした ― この名前のファイルはすでに存在します", "Could not move %s" => "%s を移動できませんでした", -"Unable to rename file" => "ファイル名の変更ができません", "No file was uploaded. Unknown error" => "ファイルは何もアップロードされていません。不明なエラー", "There is no error, the file uploaded with success" => "エラーはありません。ファイルのアップロードは成功しました", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "アップロードされたファイルはphp.ini の upload_max_filesize に設定されたサイズを超えています:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "アップロードされたファイルはHTMLのフォームに設定されたMAX_FILE_SIZEに設定されたサイズを超えています", -"The uploaded file was only partially uploaded" => "ファイルは一部分しかアップロードされませんでした", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "アップロードファイルはHTMLフォームで指定された MAX_FILE_SIZE の制限を超えています", +"The uploaded file was only partially uploaded" => "アップロードファイルは一部分だけアップロードされました", "No file was uploaded" => "ファイルはアップロードされませんでした", -"Missing a temporary folder" => "テンポラリフォルダが見つかりません", +"Missing a temporary folder" => "一時保存フォルダが見つかりません", "Failed to write to disk" => "ディスクへの書き込みに失敗しました", "Not enough storage available" => "ストレージに十分な空き容量がありません", "Invalid directory." => "無効なディレクトリです。", "Files" => "ファイル", +"Share" => "共有", "Delete permanently" => "完全に削除する", "Delete" => "削除", "Rename" => "名前の変更", -"Pending" => "保留", +"Pending" => "中断", "{new_name} already exists" => "{new_name} はすでに存在しています", "replace" => "置き換え", "suggest name" => "推奨名称", @@ -41,11 +41,13 @@ "Error" => "エラー", "Name" => "名前", "Size" => "サイズ", -"Modified" => "更新日時", +"Modified" => "変更", "1 folder" => "1 フォルダ", "{count} folders" => "{count} フォルダ", "1 file" => "1 ファイル", "{count} files" => "{count} ファイル", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "無効なフォルダ名です。'Shared' の利用はownCloudで予約済みです", +"Unable to rename file" => "ファイル名の変更ができません", "Upload" => "アップロード", "File handling" => "ファイル操作", "Maximum upload size" => "最大アップロードサイズ", @@ -55,7 +57,7 @@ "0 is unlimited" => "0を指定した場合は無制限", "Maximum input size for ZIP files" => "ZIPファイルへの最大入力サイズ", "Save" => "保存", -"New" => "新規", +"New" => "新規作成", "Text file" => "テキストファイル", "Folder" => "フォルダ", "From link" => "リンク", @@ -64,8 +66,8 @@ "You don’t have write permissions here." => "あなたには書き込み権限がありません。", "Nothing in here. Upload something!" => "ここには何もありません。何かアップロードしてください。", "Download" => "ダウンロード", -"Unshare" => "共有しない", -"Upload too large" => "ファイルサイズが大きすぎます", +"Unshare" => "共有解除", +"Upload too large" => "アップロードには大きすぎます。", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "アップロードしようとしているファイルは、サーバで規定された最大サイズを超えています。", "Files are being scanned, please wait." => "ファイルをスキャンしています、しばらくお待ちください。", "Current scanning" => "スキャン中", diff --git a/apps/files/l10n/ka_GE.php b/apps/files/l10n/ka_GE.php index 6ea75a2ea9..c50ca2594b 100644 --- a/apps/files/l10n/ka_GE.php +++ b/apps/files/l10n/ka_GE.php @@ -1,7 +1,6 @@ "%s –ის გადატანა ვერ მოხერხდა – ფაილი ამ სახელით უკვე არსებობს", "Could not move %s" => "%s –ის გადატანა ვერ მოხერხდა", -"Unable to rename file" => "ფაილის სახელის გადარქმევა ვერ მოხერხდა", "No file was uploaded. Unknown error" => "ფაილი არ აიტვირთა. უცნობი შეცდომა", "There is no error, the file uploaded with success" => "ჭოცდომა არ დაფიქსირდა, ფაილი წარმატებით აიტვირთა", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "ატვირთული ფაილი აჭარბებს upload_max_filesize დირექტივას php.ini ფაილში", @@ -13,6 +12,7 @@ "Not enough storage available" => "საცავში საკმარისი ადგილი არ არის", "Invalid directory." => "დაუშვებელი დირექტორია.", "Files" => "ფაილები", +"Share" => "გაზიარება", "Delete permanently" => "სრულად წაშლა", "Delete" => "წაშლა", "Rename" => "გადარქმევა", @@ -46,6 +46,7 @@ "{count} folders" => "{count} საქაღალდე", "1 file" => "1 ფაილი", "{count} files" => "{count} ფაილი", +"Unable to rename file" => "ფაილის სახელის გადარქმევა ვერ მოხერხდა", "Upload" => "ატვირთვა", "File handling" => "ფაილის დამუშავება", "Maximum upload size" => "მაქსიმუმ ატვირთის ზომა", @@ -64,7 +65,7 @@ "You don’t have write permissions here." => "თქვენ არ გაქვთ ჩაწერის უფლება აქ.", "Nothing in here. Upload something!" => "აქ არაფერი არ არის. ატვირთე რამე!", "Download" => "ჩამოტვირთვა", -"Unshare" => "გაზიარების მოხსნა", +"Unshare" => "გაუზიარებადი", "Upload too large" => "ასატვირთი ფაილი ძალიან დიდია", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ფაილის ზომა რომლის ატვირთვასაც თქვენ აპირებთ, აჭარბებს სერვერზე დაშვებულ მაქსიმუმს.", "Files are being scanned, please wait." => "მიმდინარეობს ფაილების სკანირება, გთხოვთ დაელოდოთ.", diff --git a/apps/files/l10n/ko.php b/apps/files/l10n/ko.php index 88378bb486..c78f58542e 100644 --- a/apps/files/l10n/ko.php +++ b/apps/files/l10n/ko.php @@ -1,34 +1,38 @@ "%s 항목을 이동시키지 못하였음 - 파일 이름이 이미 존재함", "Could not move %s" => "%s 항목을 이딩시키지 못하였음", -"Unable to rename file" => "파일 이름바꾸기 할 수 없음", "No file was uploaded. Unknown error" => "파일이 업로드되지 않았습니다. 알 수 없는 오류입니다", -"There is no error, the file uploaded with success" => "업로드에 성공하였습니다.", +"There is no error, the file uploaded with success" => "파일 업로드에 성공하였습니다.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "업로드한 파일이 php.ini의 upload_max_filesize보다 큽니다:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "업로드한 파일이 HTML 문서에 지정한 MAX_FILE_SIZE보다 더 큼", -"The uploaded file was only partially uploaded" => "파일이 부분적으로 업로드됨", -"No file was uploaded" => "업로드된 파일 없음", -"Missing a temporary folder" => "임시 폴더가 사라짐", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "업로드한 파일 크기가 HTML 폼의 MAX_FILE_SIZE보다 큼", +"The uploaded file was only partially uploaded" => "파일의 일부분만 업로드됨", +"No file was uploaded" => "파일이 업로드되지 않았음", +"Missing a temporary folder" => "임시 폴더가 없음", "Failed to write to disk" => "디스크에 쓰지 못했습니다", +"Not enough storage available" => "저장소가 용량이 충분하지 않습니다.", "Invalid directory." => "올바르지 않은 디렉터리입니다.", "Files" => "파일", +"Share" => "공유", +"Delete permanently" => "영원히 삭제", "Delete" => "삭제", "Rename" => "이름 바꾸기", -"Pending" => "보류 중", +"Pending" => "대기 중", "{new_name} already exists" => "{new_name}이(가) 이미 존재함", "replace" => "바꾸기", "suggest name" => "이름 제안", "cancel" => "취소", "replaced {new_name} with {old_name}" => "{old_name}이(가) {new_name}(으)로 대체됨", -"undo" => "실행 취소", +"undo" => "되돌리기", +"perform delete operation" => "삭제 작업중", "1 file uploading" => "파일 1개 업로드 중", +"files uploading" => "파일 업로드중", "'.' is an invalid file name." => "'.' 는 올바르지 않은 파일 이름 입니다.", "File name cannot be empty." => "파일 이름이 비어 있을 수 없습니다.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "폴더 이름이 올바르지 않습니다. 이름에 문자 '\\', '/', '<', '>', ':', '\"', '|', '? ', '*'는 사용할 수 없습니다.", "Your storage is full, files can not be updated or synced anymore!" => "저장 공간이 가득 찼습니다. 파일을 업데이트하거나 동기화할 수 없습니다!", "Your storage is almost full ({usedSpacePercent}%)" => "저장 공간이 거의 가득 찼습니다 ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "다운로드가 준비 중입니다. 파일 크기가 크다면 시간이 오래 걸릴 수도 있습니다.", -"Unable to upload your file as it is a directory or has 0 bytes" => "이 파일은 디렉터리이거나 비어 있기 때문에 업로드할 수 없습니다", +"Unable to upload your file as it is a directory or has 0 bytes" => "디렉터리 및 빈 파일은 업로드할 수 없습니다", "Not enough space available" => "여유 공간이 부족합니다", "Upload cancelled." => "업로드가 취소되었습니다.", "File upload is in progress. Leaving the page now will cancel the upload." => "파일 업로드가 진행 중입니다. 이 페이지를 벗어나면 업로드가 취소됩니다.", @@ -42,6 +46,7 @@ "{count} folders" => "폴더 {count}개", "1 file" => "파일 1개", "{count} files" => "파일 {count}개", +"Unable to rename file" => "파일 이름바꾸기 할 수 없음", "Upload" => "업로드", "File handling" => "파일 처리", "Maximum upload size" => "최대 업로드 크기", @@ -55,11 +60,13 @@ "Text file" => "텍스트 파일", "Folder" => "폴더", "From link" => "링크에서", +"Deleted files" => "파일 삭제됨", "Cancel upload" => "업로드 취소", +"You don’t have write permissions here." => "당신은 여기에 쓰기를 할 수 있는 권한이 없습니다.", "Nothing in here. Upload something!" => "내용이 없습니다. 업로드할 수 있습니다!", "Download" => "다운로드", "Unshare" => "공유 해제", -"Upload too large" => "업로드 용량 초과", +"Upload too large" => "업로드한 파일이 너무 큼", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "이 파일이 서버에서 허용하는 최대 업로드 가능 용량보다 큽니다.", "Files are being scanned, please wait." => "파일을 검색하고 있습니다. 기다려 주십시오.", "Current scanning" => "현재 검색", diff --git a/apps/files/l10n/lb.php b/apps/files/l10n/lb.php index 6533a12308..4a60295c5c 100644 --- a/apps/files/l10n/lb.php +++ b/apps/files/l10n/lb.php @@ -2,10 +2,11 @@ "There is no error, the file uploaded with success" => "Keen Feeler, Datei ass komplett ropgelueden ginn", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Déi ropgelueden Datei ass méi grouss wei d'MAX_FILE_SIZE Eegenschaft déi an der HTML form uginn ass", "The uploaded file was only partially uploaded" => "Déi ropgelueden Datei ass nëmmen hallef ropgelueden ginn", -"No file was uploaded" => "Et ass keng Datei ropgelueden ginn", +"No file was uploaded" => "Et ass kee Fichier ropgeluede ginn", "Missing a temporary folder" => "Et feelt en temporären Dossier", "Failed to write to disk" => "Konnt net op den Disk schreiwen", "Files" => "Dateien", +"Share" => "Deelen", "Delete" => "Läschen", "replace" => "ersetzen", "cancel" => "ofbriechen", @@ -31,7 +32,7 @@ "Folder" => "Dossier", "Cancel upload" => "Upload ofbriechen", "Nothing in here. Upload something!" => "Hei ass näischt. Lued eppes rop!", -"Download" => "Eroflueden", +"Download" => "Download", "Unshare" => "Net méi deelen", "Upload too large" => "Upload ze grouss", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Déi Dateien déi Dir probéiert erop ze lueden sinn méi grouss wei déi Maximal Gréisst déi op dësem Server erlaabt ass.", diff --git a/apps/files/l10n/lt_LT.php b/apps/files/l10n/lt_LT.php index 750500a3d5..3e2ea80c94 100644 --- a/apps/files/l10n/lt_LT.php +++ b/apps/files/l10n/lt_LT.php @@ -1,11 +1,12 @@ "Klaidų nėra, failas įkeltas sėkmingai", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Įkeliamo failo dydis viršija MAX_FILE_SIZE parametrą, kuris yra nustatytas HTML formoje", +"There is no error, the file uploaded with success" => "Failas įkeltas sėkmingai, be klaidų", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Įkeliamo failo dydis viršija MAX_FILE_SIZE nustatymą, kuris naudojamas HTML formoje.", "The uploaded file was only partially uploaded" => "Failas buvo įkeltas tik dalinai", -"No file was uploaded" => "Nebuvo įkeltas nė vienas failas", +"No file was uploaded" => "Nebuvo įkeltas joks failas", "Missing a temporary folder" => "Nėra laikinojo katalogo", "Failed to write to disk" => "Nepavyko įrašyti į diską", "Files" => "Failai", +"Share" => "Dalintis", "Delete" => "Ištrinti", "Rename" => "Pervadinti", "Pending" => "Laukiantis", @@ -44,7 +45,7 @@ "Download" => "Atsisiųsti", "Unshare" => "Nebesidalinti", "Upload too large" => "Įkėlimui failas per didelis", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Bandomų įkelti failų dydis viršija maksimalų leidžiamą šiame serveryje", +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Bandomų įkelti failų dydis viršija maksimalų, kuris leidžiamas šiame serveryje", "Files are being scanned, please wait." => "Skenuojami failai, prašome palaukti.", "Current scanning" => "Šiuo metu skenuojama" ); diff --git a/apps/files/l10n/lv.php b/apps/files/l10n/lv.php index 1292514547..f62bdd2d49 100644 --- a/apps/files/l10n/lv.php +++ b/apps/files/l10n/lv.php @@ -1,9 +1,8 @@ "Nevarēja pārvietot %s — jau eksistē datne ar tādu nosaukumu", "Could not move %s" => "Nevarēja pārvietot %s", -"Unable to rename file" => "Nevarēja pārsaukt datni", "No file was uploaded. Unknown error" => "Netika augšupielādēta neviena datne. Nezināma kļūda", -"There is no error, the file uploaded with success" => "Augšupielāde pabeigta bez kļūdām", +"There is no error, the file uploaded with success" => "Viss kārtībā, datne augšupielādēta veiksmīga", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Augšupielādētā datne pārsniedz upload_max_filesize norādījumu php.ini datnē:", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Augšupielādētā datne pārsniedz MAX_FILE_SIZE norādi, kas ir norādīta HTML formā", "The uploaded file was only partially uploaded" => "Augšupielādētā datne ir tikai daļēji augšupielādēta", @@ -13,6 +12,7 @@ "Not enough storage available" => "Nav pietiekami daudz vietas", "Invalid directory." => "Nederīga direktorija.", "Files" => "Datnes", +"Share" => "Dalīties", "Delete permanently" => "Dzēst pavisam", "Delete" => "Dzēst", "Rename" => "Pārsaukt", @@ -31,7 +31,7 @@ "Your storage is full, files can not be updated or synced anymore!" => "Jūsu krātuve ir pilna, datnes vairs nevar augšupielādēt vai sinhronizēt!", "Your storage is almost full ({usedSpacePercent}%)" => "Jūsu krātuve ir gandrīz pilna ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Tiek sagatavota lejupielāde. Tas var aizņemt kādu laiciņu, ja datnes ir lielas.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Nevar augšupielādēt jūsu datni, jo tā ir direktorija vai arī tās izmērs ir 0 baiti", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nevar augšupielādēt jūsu datni, jo tā ir direktorija vai arī tā ir 0 baitu liela", "Not enough space available" => "Nepietiek brīvas vietas", "Upload cancelled." => "Augšupielāde ir atcelta.", "File upload is in progress. Leaving the page now will cancel the upload." => "Notiek augšupielāde. Pametot lapu tagad, tiks atcelta augšupielāde.", @@ -45,6 +45,7 @@ "{count} folders" => "{count} mapes", "1 file" => "1 datne", "{count} files" => "{count} datnes", +"Unable to rename file" => "Nevarēja pārsaukt datni", "Upload" => "Augšupielādēt", "File handling" => "Datņu pārvaldība", "Maximum upload size" => "Maksimālais datņu augšupielādes apjoms", diff --git a/apps/files/l10n/mk.php b/apps/files/l10n/mk.php index 78fed25cf9..992618f06b 100644 --- a/apps/files/l10n/mk.php +++ b/apps/files/l10n/mk.php @@ -1,13 +1,14 @@ "Ниту еден фајл не се вчита. Непозната грешка", -"There is no error, the file uploaded with success" => "Нема грешка, датотеката беше подигната успешно", +"There is no error, the file uploaded with success" => "Датотеката беше успешно подигната.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Подигнатата датотека ја надминува upload_max_filesize директивата во php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Подигнатата датотеката ја надминува MAX_FILE_SIZE директивата која беше поставена во HTML формата", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Големината на датотеката ја надминува MAX_FILE_SIZE директивата која беше специфицирана во HTML формата", "The uploaded file was only partially uploaded" => "Датотеката беше само делумно подигната.", -"No file was uploaded" => "Не беше подигната датотека", -"Missing a temporary folder" => "Не постои привремена папка", +"No file was uploaded" => "Не беше подигната датотека.", +"Missing a temporary folder" => "Недостасува привремена папка", "Failed to write to disk" => "Неуспеав да запишам на диск", "Files" => "Датотеки", +"Share" => "Сподели", "Delete" => "Избриши", "Rename" => "Преименувај", "Pending" => "Чека", @@ -48,7 +49,7 @@ "Nothing in here. Upload something!" => "Тука нема ништо. Снимете нешто!", "Download" => "Преземи", "Unshare" => "Не споделувај", -"Upload too large" => "Датотеката е премногу голема", +"Upload too large" => "Фајлот кој се вчитува е преголем", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеките кои се обидувате да ги подигнете ја надминуваат максималната големина за подигнување датотеки на овој сервер.", "Files are being scanned, please wait." => "Се скенираат датотеки, ве молам почекајте.", "Current scanning" => "Моментално скенирам" diff --git a/apps/files/l10n/ms_MY.php b/apps/files/l10n/ms_MY.php index a390288b36..2ce4f16332 100644 --- a/apps/files/l10n/ms_MY.php +++ b/apps/files/l10n/ms_MY.php @@ -1,12 +1,13 @@ "Tiada fail dimuatnaik. Ralat tidak diketahui.", -"There is no error, the file uploaded with success" => "Tiada ralat, fail berjaya dimuat naik.", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Fail yang dimuat naik melebihi MAX_FILE_SIZE yang dinyatakan dalam form HTML ", -"The uploaded file was only partially uploaded" => "Sebahagian daripada fail telah dimuat naik. ", -"No file was uploaded" => "Tiada fail yang dimuat naik", -"Missing a temporary folder" => "Folder sementara hilang", +"There is no error, the file uploaded with success" => "Tiada ralat berlaku, fail berjaya dimuatnaik", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Saiz fail yang dimuatnaik melebihi MAX_FILE_SIZE yang ditetapkan dalam borang HTML", +"The uploaded file was only partially uploaded" => "Fail yang dimuatnaik tidak lengkap", +"No file was uploaded" => "Tiada fail dimuatnaik", +"Missing a temporary folder" => "Direktori sementara hilang", "Failed to write to disk" => "Gagal untuk disimpan", -"Files" => "fail", +"Files" => "Fail-fail", +"Share" => "Kongsi", "Delete" => "Padam", "Pending" => "Dalam proses", "replace" => "ganti", @@ -14,7 +15,7 @@ "Unable to upload your file as it is a directory or has 0 bytes" => "Tidak boleh memuatnaik fail anda kerana mungkin ianya direktori atau saiz fail 0 bytes", "Upload cancelled." => "Muatnaik dibatalkan.", "Error" => "Ralat", -"Name" => "Nama ", +"Name" => "Nama", "Size" => "Saiz", "Modified" => "Dimodifikasi", "Upload" => "Muat naik", @@ -32,7 +33,7 @@ "Cancel upload" => "Batal muat naik", "Nothing in here. Upload something!" => "Tiada apa-apa di sini. Muat naik sesuatu!", "Download" => "Muat turun", -"Upload too large" => "Muat naik terlalu besar", +"Upload too large" => "Muatnaik terlalu besar", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fail yang cuba dimuat naik melebihi saiz maksimum fail upload server", "Files are being scanned, please wait." => "Fail sedang diimbas, harap bersabar.", "Current scanning" => "Imbasan semasa" diff --git a/apps/files/l10n/nb_NO.php b/apps/files/l10n/nb_NO.php index 54042c9124..d5710a4927 100644 --- a/apps/files/l10n/nb_NO.php +++ b/apps/files/l10n/nb_NO.php @@ -1,12 +1,18 @@ "Kan ikke flytte %s - En fil med samme navn finnes allerede", +"Could not move %s" => "Kunne ikke flytte %s", "No file was uploaded. Unknown error" => "Ingen filer ble lastet opp. Ukjent feil.", -"There is no error, the file uploaded with success" => "Det er ingen feil. Filen ble lastet opp.", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Filstørrelsen overskrider maksgrensen på MAX_FILE_SIZE som ble oppgitt i HTML-skjemaet", -"The uploaded file was only partially uploaded" => "Filopplastningen ble bare delvis gjennomført", -"No file was uploaded" => "Ingen fil ble lastet opp", -"Missing a temporary folder" => "Mangler en midlertidig mappe", +"There is no error, the file uploaded with success" => "Pust ut, ingen feil. Filen ble lastet opp problemfritt", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Filstørrelsen overskrider maksgrensedirektivet upload_max_filesize i php.ini-konfigurasjonen.", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Filen du prøvde å laste opp var større enn grensen satt i MAX_FILE_SIZE i HTML-skjemaet.", +"The uploaded file was only partially uploaded" => "Filen du prøvde å laste opp ble kun delvis lastet opp", +"No file was uploaded" => "Ingen filer ble lastet opp", +"Missing a temporary folder" => "Mangler midlertidig mappe", "Failed to write to disk" => "Klarte ikke å skrive til disk", +"Not enough storage available" => "Ikke nok lagringsplass", +"Invalid directory." => "Ugyldig katalog.", "Files" => "Filer", +"Share" => "Del", "Delete permanently" => "Slett permanent", "Delete" => "Slett", "Rename" => "Omdøp", @@ -17,12 +23,21 @@ "cancel" => "avbryt", "replaced {new_name} with {old_name}" => "erstatt {new_name} med {old_name}", "undo" => "angre", +"perform delete operation" => "utfør sletting", "1 file uploading" => "1 fil lastes opp", +"files uploading" => "filer lastes opp", +"'.' is an invalid file name." => "'.' er et ugyldig filnavn.", +"File name cannot be empty." => "Filnavn kan ikke være tomt.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldig navn, '\\', '/', '<', '>', ':', '\"', '|', '?' og '*' er ikke tillatt.", +"Your storage is full, files can not be updated or synced anymore!" => "Lagringsplass er oppbrukt, filer kan ikke lenger oppdateres eller synkroniseres!", +"Your storage is almost full ({usedSpacePercent}%)" => "Lagringsplass er nesten oppbruker ([usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Nedlastingen din klargjøres. Hvis filene er store kan dette ta litt tid.", "Unable to upload your file as it is a directory or has 0 bytes" => "Kan ikke laste opp filen din siden det er en mappe eller den har 0 bytes", +"Not enough space available" => "Ikke nok lagringsplass", "Upload cancelled." => "Opplasting avbrutt.", "File upload is in progress. Leaving the page now will cancel the upload." => "Filopplasting pågår. Forlater du siden nå avbrytes opplastingen.", "URL cannot be empty." => "URL-en kan ikke være tom.", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ugyldig mappenavn. Bruk av \"Shared\" er reservert av ownCloud.", "Error" => "Feil", "Name" => "Navn", "Size" => "Størrelse", @@ -31,6 +46,8 @@ "{count} folders" => "{count} mapper", "1 file" => "1 fil", "{count} files" => "{count} filer", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Ugyldig mappenavn. Bruk av \"Shared\" er reservert av ownCloud.", +"Unable to rename file" => "Kan ikke gi nytt navn", "Upload" => "Last opp", "File handling" => "Filhåndtering", "Maximum upload size" => "Maksimum opplastingsstørrelse", @@ -44,12 +61,15 @@ "Text file" => "Tekstfil", "Folder" => "Mappe", "From link" => "Fra link", +"Deleted files" => "Slettet filer", "Cancel upload" => "Avbryt opplasting", +"You don’t have write permissions here." => "Du har ikke skrivetilgang her.", "Nothing in here. Upload something!" => "Ingenting her. Last opp noe!", "Download" => "Last ned", "Unshare" => "Avslutt deling", -"Upload too large" => "Opplasting for stor", +"Upload too large" => "Filen er for stor", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver å laste opp er for store for å laste opp til denne serveren.", "Files are being scanned, please wait." => "Skanner etter filer, vennligst vent.", -"Current scanning" => "Pågående skanning" +"Current scanning" => "Pågående skanning", +"Upgrading filesystem cache..." => "Oppgraderer filsystemets mellomlager..." ); diff --git a/apps/files/l10n/nl.php b/apps/files/l10n/nl.php index 38b55d34d9..bc4158df3b 100644 --- a/apps/files/l10n/nl.php +++ b/apps/files/l10n/nl.php @@ -1,22 +1,22 @@ "Kon %s niet verplaatsen - Er bestaat al een bestand met deze naam", "Could not move %s" => "Kon %s niet verplaatsen", -"Unable to rename file" => "Kan bestand niet hernoemen", "No file was uploaded. Unknown error" => "Er was geen bestand geladen. Onbekende fout", -"There is no error, the file uploaded with success" => "Geen fout opgetreden, bestand successvol geupload.", +"There is no error, the file uploaded with success" => "De upload van het bestand is goedgegaan.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Het geüploade bestand overscheidt de upload_max_filesize optie in php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Het geüploade bestand is groter dan de MAX_FILE_SIZE richtlijn die is opgegeven in de HTML-formulier", -"The uploaded file was only partially uploaded" => "Het bestand is slechts gedeeltelijk geupload", -"No file was uploaded" => "Geen bestand geüpload", -"Missing a temporary folder" => "Een tijdelijke map mist", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Het bestand overschrijdt de MAX_FILE_SIZE instelling dat is opgegeven in het HTML formulier", +"The uploaded file was only partially uploaded" => "Het bestand is gedeeltelijk geüpload", +"No file was uploaded" => "Er is geen bestand geüpload", +"Missing a temporary folder" => "Er ontbreekt een tijdelijke map", "Failed to write to disk" => "Schrijven naar schijf mislukt", "Not enough storage available" => "Niet genoeg opslagruimte beschikbaar", "Invalid directory." => "Ongeldige directory.", "Files" => "Bestanden", +"Share" => "Delen", "Delete permanently" => "Verwijder definitief", "Delete" => "Verwijder", "Rename" => "Hernoem", -"Pending" => "Wachten", +"Pending" => "In behandeling", "{new_name} already exists" => "{new_name} bestaat al", "replace" => "vervang", "suggest name" => "Stel een naam voor", @@ -32,7 +32,7 @@ "Your storage is full, files can not be updated or synced anymore!" => "Uw opslagruimte zit vol, Bestanden kunnen niet meer worden ge-upload of gesynchroniseerd!", "Your storage is almost full ({usedSpacePercent}%)" => "Uw opslagruimte zit bijna vol ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Uw download wordt voorbereid. Dit kan enige tijd duren bij grote bestanden.", -"Unable to upload your file as it is a directory or has 0 bytes" => "uploaden van de file mislukt, het is of een directory of de bestandsgrootte is 0 bytes", +"Unable to upload your file as it is a directory or has 0 bytes" => "Het lukt niet om uw bestand te uploaded, omdat het een folder of 0 bytes is", "Not enough space available" => "Niet genoeg ruimte beschikbaar", "Upload cancelled." => "Uploaden geannuleerd.", "File upload is in progress. Leaving the page now will cancel the upload." => "Bestandsupload is bezig. Wanneer de pagina nu verlaten wordt, stopt de upload.", @@ -40,13 +40,15 @@ "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ongeldige mapnaam. Gebruik van'Gedeeld' is voorbehouden aan Owncloud", "Error" => "Fout", "Name" => "Naam", -"Size" => "Bestandsgrootte", -"Modified" => "Laatst aangepast", +"Size" => "Grootte", +"Modified" => "Aangepast", "1 folder" => "1 map", "{count} folders" => "{count} mappen", "1 file" => "1 bestand", "{count} files" => "{count} bestanden", -"Upload" => "Upload", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Ongeldige mapnaam. Gebruik van 'Gedeeld' is voorbehouden aan Owncloud zelf", +"Unable to rename file" => "Kan bestand niet hernoemen", +"Upload" => "Uploaden", "File handling" => "Bestand", "Maximum upload size" => "Maximale bestandsgrootte voor uploads", "max. possible: " => "max. mogelijk: ", @@ -54,7 +56,7 @@ "Enable ZIP-download" => "Zet ZIP-download aan", "0 is unlimited" => "0 is ongelimiteerd", "Maximum input size for ZIP files" => "Maximale grootte voor ZIP bestanden", -"Save" => "Opslaan", +"Save" => "Bewaren", "New" => "Nieuw", "Text file" => "Tekstbestand", "Folder" => "Map", @@ -63,9 +65,9 @@ "Cancel upload" => "Upload afbreken", "You don’t have write permissions here." => "U hebt hier geen schrijfpermissies.", "Nothing in here. Upload something!" => "Er bevindt zich hier niets. Upload een bestand!", -"Download" => "Download", -"Unshare" => "Stop delen", -"Upload too large" => "Bestanden te groot", +"Download" => "Downloaden", +"Unshare" => "Stop met delen", +"Upload too large" => "Upload is te groot", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "De bestanden die u probeert te uploaden zijn groter dan de maximaal toegestane bestandsgrootte voor deze server.", "Files are being scanned, please wait." => "Bestanden worden gescand, even wachten.", "Current scanning" => "Er wordt gescand", diff --git a/apps/files/l10n/nn_NO.php b/apps/files/l10n/nn_NO.php index 8f32dc012e..29593b6f2d 100644 --- a/apps/files/l10n/nn_NO.php +++ b/apps/files/l10n/nn_NO.php @@ -1,23 +1,75 @@ "Klarte ikkje flytta %s – det finst allereie ei fil med dette namnet", +"Could not move %s" => "Klarte ikkje flytta %s", +"No file was uploaded. Unknown error" => "Ingen filer lasta opp. Ukjend feil", "There is no error, the file uploaded with success" => "Ingen feil, fila vart lasta opp", +"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Fila du lasta opp er større enn det «upload_max_filesize» i php.ini tillater: ", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den opplasta fila er større enn variabelen MAX_FILE_SIZE i HTML-skjemaet", "The uploaded file was only partially uploaded" => "Fila vart berre delvis lasta opp", "No file was uploaded" => "Ingen filer vart lasta opp", "Missing a temporary folder" => "Manglar ei mellombels mappe", +"Failed to write to disk" => "Klarte ikkje skriva til disk", +"Not enough storage available" => "Ikkje nok lagringsplass tilgjengeleg", +"Invalid directory." => "Ugyldig mappe.", "Files" => "Filer", +"Share" => "Del", +"Delete permanently" => "Slett for godt", "Delete" => "Slett", +"Rename" => "Endra namn", +"Pending" => "Under vegs", +"{new_name} already exists" => "{new_name} finst allereie", +"replace" => "byt ut", +"suggest name" => "føreslå namn", +"cancel" => "avbryt", +"replaced {new_name} with {old_name}" => "bytte ut {new_name} med {old_name}", +"undo" => "angre", +"perform delete operation" => "utfør sletting", +"1 file uploading" => "1 fil lastar opp", +"files uploading" => "filer lastar opp", +"'.' is an invalid file name." => "«.» er eit ugyldig filnamn.", +"File name cannot be empty." => "Filnamnet kan ikkje vera tomt.", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ugyldig namn, «\\», «/», «<», «>», «:», «\"», «|», «?» og «*» er ikkje tillate.", +"Your storage is full, files can not be updated or synced anymore!" => "Lagringa di er full, kan ikkje lenger oppdatera eller synkronisera!", +"Your storage is almost full ({usedSpacePercent}%)" => "Lagringa di er nesten full ({usedSpacePercent} %)", +"Your download is being prepared. This might take some time if the files are big." => "Gjer klar nedlastinga di. Dette kan ta ei stund viss filene er store.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Klarte ikkje lasta opp fila sidan ho er ei mappe eller er på 0 byte", +"Not enough space available" => "Ikkje nok lagringsplass tilgjengeleg", +"Upload cancelled." => "Opplasting avbroten.", +"File upload is in progress. Leaving the page now will cancel the upload." => "Fila lastar no opp. Viss du forlèt sida no vil opplastinga verta avbroten.", +"URL cannot be empty." => "Nettadressa kan ikkje vera tom.", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Ugyldig mappenamn. Mappa «Shared» er reservert av ownCloud", "Error" => "Feil", "Name" => "Namn", "Size" => "Storleik", "Modified" => "Endra", +"1 folder" => "1 mappe", +"{count} folders" => "{count} mapper", +"1 file" => "1 fil", +"{count} files" => "{count} filer", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Ugyldig mappenamn. Mappa «Shared» er reservert av ownCloud", +"Unable to rename file" => "Klarte ikkje endra filnamnet", "Upload" => "Last opp", +"File handling" => "Filhandtering", "Maximum upload size" => "Maksimal opplastingsstorleik", +"max. possible: " => "maks. moglege:", +"Needed for multi-file and folder downloads." => "Nødvendig for fleirfils- og mappenedlastingar.", +"Enable ZIP-download" => "Slå på ZIP-nedlasting", +"0 is unlimited" => "0 er ubegrensa", +"Maximum input size for ZIP files" => "Maksimal storleik for ZIP-filer", "Save" => "Lagre", "New" => "Ny", "Text file" => "Tekst fil", "Folder" => "Mappe", +"From link" => "Frå lenkje", +"Deleted files" => "Sletta filer", +"Cancel upload" => "Avbryt opplasting", +"You don’t have write permissions here." => "Du har ikkje skriverettar her.", "Nothing in here. Upload something!" => "Ingenting her. Last noko opp!", "Download" => "Last ned", +"Unshare" => "Udel", "Upload too large" => "For stor opplasting", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver å laste opp er større enn maksgrensa til denne tenaren." +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver å lasta opp er større enn maksgrensa til denne tenaren.", +"Files are being scanned, please wait." => "Skannar filer, ver venleg og vent.", +"Current scanning" => "Køyrande skanning", +"Upgrading filesystem cache..." => "Oppgraderer mellomlageret av filsystemet …" ); diff --git a/apps/files/l10n/oc.php b/apps/files/l10n/oc.php index b1ef621658..fa31ddf9f4 100644 --- a/apps/files/l10n/oc.php +++ b/apps/files/l10n/oc.php @@ -6,6 +6,7 @@ "Missing a temporary folder" => "Un dorsièr temporari manca", "Failed to write to disk" => "L'escriptura sul disc a fracassat", "Files" => "Fichièrs", +"Share" => "Parteja", "Delete" => "Escafa", "Rename" => "Torna nomenar", "Pending" => "Al esperar", @@ -14,6 +15,7 @@ "cancel" => "anulla", "undo" => "defar", "1 file uploading" => "1 fichièr al amontcargar", +"files uploading" => "fichièrs al amontcargar", "Unable to upload your file as it is a directory or has 0 bytes" => "Impossible d'amontcargar lo teu fichièr qu'es un repertòri o que ten pas que 0 octet.", "Upload cancelled." => "Amontcargar anullat.", "File upload is in progress. Leaving the page now will cancel the upload." => "Un amontcargar es a se far. Daissar aquesta pagina ara tamparà lo cargament. ", @@ -36,7 +38,7 @@ "Cancel upload" => " Anulla l'amontcargar", "Nothing in here. Upload something!" => "Pas res dedins. Amontcarga qualquaren", "Download" => "Avalcarga", -"Unshare" => "Non parteja", +"Unshare" => "Pas partejador", "Upload too large" => "Amontcargament tròp gròs", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los fichièrs que sias a amontcargar son tròp pesucs per la talha maxi pel servidor.", "Files are being scanned, please wait." => "Los fiichièrs son a èsser explorats, ", diff --git a/apps/files/l10n/pl.php b/apps/files/l10n/pl.php index e9a78e2f44..4bdac05578 100644 --- a/apps/files/l10n/pl.php +++ b/apps/files/l10n/pl.php @@ -1,18 +1,18 @@ "Nie można było przenieść %s - Plik o takiej nazwie już istnieje", "Could not move %s" => "Nie można było przenieść %s", -"Unable to rename file" => "Nie można zmienić nazwy pliku", "No file was uploaded. Unknown error" => "Żaden plik nie został załadowany. Nieznany błąd", -"There is no error, the file uploaded with success" => "Przesłano plik", +"There is no error, the file uploaded with success" => "Nie było błędów, plik wysłano poprawnie.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Wgrany plik przekracza wartość upload_max_filesize zdefiniowaną w php.ini: ", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Wysłany plik przekracza wielkość dyrektywy MAX_FILE_SIZE określonej w formularzu HTML", "The uploaded file was only partially uploaded" => "Załadowany plik został wysłany tylko częściowo.", -"No file was uploaded" => "Nie przesłano żadnego pliku", -"Missing a temporary folder" => "Brak katalogu tymczasowego", +"No file was uploaded" => "Nie wysłano żadnego pliku", +"Missing a temporary folder" => "Brak folderu tymczasowego", "Failed to write to disk" => "Błąd zapisu na dysk", "Not enough storage available" => "Za mało dostępnego miejsca", "Invalid directory." => "Zła ścieżka.", "Files" => "Pliki", +"Share" => "Udostępnij", "Delete permanently" => "Trwale usuń", "Delete" => "Usuń", "Rename" => "Zmień nazwę", @@ -46,7 +46,9 @@ "{count} folders" => "Ilość folderów: {count}", "1 file" => "1 plik", "{count} files" => "Ilość plików: {count}", -"Upload" => "Prześlij", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nieprawidłowa nazwa folderu. Wykorzystanie 'Shared' jest zarezerwowane przez ownCloud", +"Unable to rename file" => "Nie można zmienić nazwy pliku", +"Upload" => "Wyślij", "File handling" => "Zarządzanie plikami", "Maximum upload size" => "Maksymalny rozmiar wysyłanego pliku", "max. possible: " => "maks. możliwy:", @@ -57,15 +59,15 @@ "Save" => "Zapisz", "New" => "Nowy", "Text file" => "Plik tekstowy", -"Folder" => "Katalog", +"Folder" => "Folder", "From link" => "Z odnośnika", "Deleted files" => "Pliki usunięte", "Cancel upload" => "Anuluj wysyłanie", "You don’t have write permissions here." => "Nie masz uprawnień do zapisu w tym miejscu.", "Nothing in here. Upload something!" => "Pusto. Wyślij coś!", "Download" => "Pobierz", -"Unshare" => "Nie udostępniaj", -"Upload too large" => "Wysyłany plik ma za duży rozmiar", +"Unshare" => "Zatrzymaj współdzielenie", +"Upload too large" => "Ładowany plik jest za duży", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Pliki, które próbujesz przesłać, przekraczają maksymalną dopuszczalną wielkość.", "Files are being scanned, please wait." => "Skanowanie plików, proszę czekać.", "Current scanning" => "Aktualnie skanowane", diff --git a/apps/files/l10n/pt_BR.php b/apps/files/l10n/pt_BR.php index ad8f37c24f..0f349b6948 100644 --- a/apps/files/l10n/pt_BR.php +++ b/apps/files/l10n/pt_BR.php @@ -1,18 +1,18 @@ "Impossível mover %s - Um arquivo com este nome já existe", "Could not move %s" => "Impossível mover %s", -"Unable to rename file" => "Impossível renomear arquivo", "No file was uploaded. Unknown error" => "Nenhum arquivo foi enviado. Erro desconhecido", -"There is no error, the file uploaded with success" => "Não houve nenhum erro, o arquivo foi transferido com sucesso", +"There is no error, the file uploaded with success" => "Sem erros, o arquivo foi enviado com sucesso", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O arquivo enviado excede a diretiva upload_max_filesize no php.ini: ", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O arquivo carregado excede o MAX_FILE_SIZE que foi especificado no formulário HTML", -"The uploaded file was only partially uploaded" => "O arquivo foi transferido parcialmente", -"No file was uploaded" => "Nenhum arquivo foi transferido", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O arquivo carregado excede o argumento MAX_FILE_SIZE especificado no formulário HTML", +"The uploaded file was only partially uploaded" => "O arquivo foi parcialmente enviado", +"No file was uploaded" => "Nenhum arquivo enviado", "Missing a temporary folder" => "Pasta temporária não encontrada", "Failed to write to disk" => "Falha ao escrever no disco", "Not enough storage available" => "Espaço de armazenamento insuficiente", "Invalid directory." => "Diretório inválido.", "Files" => "Arquivos", +"Share" => "Compartilhar", "Delete permanently" => "Excluir permanentemente", "Delete" => "Excluir", "Rename" => "Renomear", @@ -46,7 +46,9 @@ "{count} folders" => "{count} pastas", "1 file" => "1 arquivo", "{count} files" => "{count} arquivos", -"Upload" => "Carregar", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nome de pasta inválido. O uso do nome 'Compartilhado' é reservado ao ownCloud", +"Unable to rename file" => "Impossível renomear arquivo", +"Upload" => "Upload", "File handling" => "Tratamento de Arquivo", "Maximum upload size" => "Tamanho máximo para carregar", "max. possible: " => "max. possível:", @@ -54,7 +56,7 @@ "Enable ZIP-download" => "Habilitar ZIP-download", "0 is unlimited" => "0 para ilimitado", "Maximum input size for ZIP files" => "Tamanho máximo para arquivo ZIP", -"Save" => "Salvar", +"Save" => "Guardar", "New" => "Novo", "Text file" => "Arquivo texto", "Folder" => "Pasta", @@ -65,7 +67,7 @@ "Nothing in here. Upload something!" => "Nada aqui.Carrege alguma coisa!", "Download" => "Baixar", "Unshare" => "Descompartilhar", -"Upload too large" => "Arquivo muito grande", +"Upload too large" => "Upload muito grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os arquivos que você está tentando carregar excedeu o tamanho máximo para arquivos no servidor.", "Files are being scanned, please wait." => "Arquivos sendo escaneados, por favor aguarde.", "Current scanning" => "Scanning atual", diff --git a/apps/files/l10n/pt_PT.php b/apps/files/l10n/pt_PT.php index 2ad4099083..d90e299970 100644 --- a/apps/files/l10n/pt_PT.php +++ b/apps/files/l10n/pt_PT.php @@ -1,20 +1,20 @@ "Não foi possível mover o ficheiro %s - Já existe um ficheiro com esse nome", "Could not move %s" => "Não foi possível move o ficheiro %s", -"Unable to rename file" => "Não foi possível renomear o ficheiro", "No file was uploaded. Unknown error" => "Nenhum ficheiro foi carregado. Erro desconhecido", -"There is no error, the file uploaded with success" => "Sem erro, ficheiro enviado com sucesso", +"There is no error, the file uploaded with success" => "Não ocorreram erros, o ficheiro foi submetido com sucesso", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "O ficheiro enviado excede o limite permitido na directiva do php.ini upload_max_filesize", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro enviado excede o diretivo MAX_FILE_SIZE especificado no formulário HTML", -"The uploaded file was only partially uploaded" => "O ficheiro enviado só foi enviado parcialmente", -"No file was uploaded" => "Não foi enviado nenhum ficheiro", -"Missing a temporary folder" => "Falta uma pasta temporária", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O tamanho do ficheiro carregado ultrapassa o valor MAX_FILE_SIZE definido no formulário HTML", +"The uploaded file was only partially uploaded" => "O ficheiro seleccionado foi apenas carregado parcialmente", +"No file was uploaded" => "Nenhum ficheiro foi submetido", +"Missing a temporary folder" => "Está a faltar a pasta temporária", "Failed to write to disk" => "Falhou a escrita no disco", "Not enough storage available" => "Não há espaço suficiente em disco", "Invalid directory." => "Directório Inválido", "Files" => "Ficheiros", +"Share" => "Partilhar", "Delete permanently" => "Eliminar permanentemente", -"Delete" => "Apagar", +"Delete" => "Eliminar", "Rename" => "Renomear", "Pending" => "Pendente", "{new_name} already exists" => "O nome {new_name} já existe", @@ -25,6 +25,7 @@ "undo" => "desfazer", "perform delete operation" => "Executar a tarefa de apagar", "1 file uploading" => "A enviar 1 ficheiro", +"files uploading" => "A enviar os ficheiros", "'.' is an invalid file name." => "'.' não é um nome de ficheiro válido!", "File name cannot be empty." => "O nome do ficheiro não pode estar vazio.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nome Inválido, os caracteres '\\', '/', '<', '>', ':', '\"', '|', '?' e '*' não são permitidos.", @@ -45,11 +46,13 @@ "{count} folders" => "{count} pastas", "1 file" => "1 ficheiro", "{count} files" => "{count} ficheiros", -"Upload" => "Enviar", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nome da pasta inválido. Palavra 'Shared' é reservado pela ownCloud", +"Unable to rename file" => "Não foi possível renomear o ficheiro", +"Upload" => "Carregar", "File handling" => "Manuseamento de ficheiros", "Maximum upload size" => "Tamanho máximo de envio", "max. possible: " => "max. possivel: ", -"Needed for multi-file and folder downloads." => "Necessário para descarregamento múltiplo de ficheiros e pastas", +"Needed for multi-file and folder downloads." => "Necessário para multi download de ficheiros e pastas", "Enable ZIP-download" => "Permitir descarregar em ficheiro ZIP", "0 is unlimited" => "0 é ilimitado", "Maximum input size for ZIP files" => "Tamanho máximo para ficheiros ZIP", @@ -64,8 +67,8 @@ "Nothing in here. Upload something!" => "Vazio. Envie alguma coisa!", "Download" => "Transferir", "Unshare" => "Deixar de partilhar", -"Upload too large" => "Envio muito grande", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que está a tentar enviar excedem o tamanho máximo de envio permitido neste servidor.", +"Upload too large" => "Upload muito grande", +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiro que está a tentar enviar excedem o tamanho máximo de envio neste servidor.", "Files are being scanned, please wait." => "Os ficheiros estão a ser analisados, por favor aguarde.", "Current scanning" => "Análise actual", "Upgrading filesystem cache..." => "Atualizar cache do sistema de ficheiros..." diff --git a/apps/files/l10n/ro.php b/apps/files/l10n/ro.php index e3cab80fbc..8fdf62aeb3 100644 --- a/apps/files/l10n/ro.php +++ b/apps/files/l10n/ro.php @@ -1,17 +1,19 @@ "Nu se poate de mutat %s - Fișier cu acest nume deja există", "Could not move %s" => "Nu s-a putut muta %s", -"Unable to rename file" => "Nu s-a putut redenumi fișierul", "No file was uploaded. Unknown error" => "Nici un fișier nu a fost încărcat. Eroare necunoscută", -"There is no error, the file uploaded with success" => "Nicio eroare, fișierul a fost încărcat cu succes", +"There is no error, the file uploaded with success" => "Nu a apărut nici o eroare, fișierul a fost încărcat cu succes", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Fisierul incarcat depaseste upload_max_filesize permisi in php.ini: ", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Fișierul are o dimensiune mai mare decât variabile MAX_FILE_SIZE specificată în formularul HTML", "The uploaded file was only partially uploaded" => "Fișierul a fost încărcat doar parțial", -"No file was uploaded" => "Niciun fișier încărcat", -"Missing a temporary folder" => "Lipsește un dosar temporar", +"No file was uploaded" => "Nu a fost încărcat nici un fișier", +"Missing a temporary folder" => "Lipsește un director temporar", "Failed to write to disk" => "Eroare la scriere pe disc", +"Not enough storage available" => "Nu este suficient spațiu disponibil", "Invalid directory." => "Director invalid.", "Files" => "Fișiere", +"Share" => "Partajează", +"Delete permanently" => "Stergere permanenta", "Delete" => "Șterge", "Rename" => "Redenumire", "Pending" => "În așteptare", @@ -21,10 +23,14 @@ "cancel" => "anulare", "replaced {new_name} with {old_name}" => "{new_name} inlocuit cu {old_name}", "undo" => "Anulează ultima acțiune", +"perform delete operation" => "efectueaza operatiunea de stergere", "1 file uploading" => "un fișier se încarcă", +"files uploading" => "fișiere se încarcă", "'.' is an invalid file name." => "'.' este un nume invalid de fișier.", "File name cannot be empty." => "Numele fișierului nu poate rămâne gol.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nume invalid, '\\', '/', '<', '>', ':', '\"', '|', '?' si '*' nu sunt permise.", +"Your storage is full, files can not be updated or synced anymore!" => "Spatiul de stocare este plin, nu mai puteti incarca s-au sincroniza alte fisiere.", +"Your storage is almost full ({usedSpacePercent}%)" => "Spatiul de stocare este aproape plin ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Se pregătește descărcarea. Aceasta poate să dureze ceva timp dacă fișierele sunt mari.", "Unable to upload your file as it is a directory or has 0 bytes" => "Nu s-a putut încărca fișierul tău deoarece pare să fie un director sau are 0 bytes.", "Not enough space available" => "Nu este suficient spațiu disponibil", @@ -40,7 +46,8 @@ "{count} folders" => "{count} foldare", "1 file" => "1 fisier", "{count} files" => "{count} fisiere", -"Upload" => "Încarcă", +"Unable to rename file" => "Nu s-a putut redenumi fișierul", +"Upload" => "Încărcare", "File handling" => "Manipulare fișiere", "Maximum upload size" => "Dimensiune maximă admisă la încărcare", "max. possible: " => "max. posibil:", @@ -48,17 +55,20 @@ "Enable ZIP-download" => "Activează descărcare fișiere compresate", "0 is unlimited" => "0 e nelimitat", "Maximum input size for ZIP files" => "Dimensiunea maximă de intrare pentru fișiere compresate", -"Save" => "Salvare", +"Save" => "Salvează", "New" => "Nou", "Text file" => "Fișier text", "Folder" => "Dosar", "From link" => "de la adresa", +"Deleted files" => "Sterge fisierele", "Cancel upload" => "Anulează încărcarea", +"You don’t have write permissions here." => "Nu ai permisiunea de a sterge fisiere aici.", "Nothing in here. Upload something!" => "Nimic aici. Încarcă ceva!", "Download" => "Descarcă", -"Unshare" => "Anulează partajarea", +"Unshare" => "Anulare partajare", "Upload too large" => "Fișierul încărcat este prea mare", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fișierul care l-ai încărcat a depășită limita maximă admisă la încărcare pe acest server.", "Files are being scanned, please wait." => "Fișierele sunt scanate, te rog așteptă.", -"Current scanning" => "În curs de scanare" +"Current scanning" => "În curs de scanare", +"Upgrading filesystem cache..." => "Modernizare fisiere de sistem cache.." ); diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php index 37f2e083c4..83412bf2be 100644 --- a/apps/files/l10n/ru.php +++ b/apps/files/l10n/ru.php @@ -1,18 +1,18 @@ "Невозможно переместить %s - файл с таким именем уже существует", "Could not move %s" => "Невозможно переместить %s", -"Unable to rename file" => "Невозможно переименовать файл", "No file was uploaded. Unknown error" => "Файл не был загружен. Неизвестная ошибка", -"There is no error, the file uploaded with success" => "Файл успешно загружен", +"There is no error, the file uploaded with success" => "Файл загружен успешно.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Файл превышает размер установленный upload_max_filesize в php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Файл превышает размер MAX_FILE_SIZE, указаный в HTML-форме", -"The uploaded file was only partially uploaded" => "Файл был загружен не полностью", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Загружаемый файл превосходит значение переменной MAX_FILE_SIZE, указанной в форме HTML", +"The uploaded file was only partially uploaded" => "Файл загружен частично", "No file was uploaded" => "Файл не был загружен", -"Missing a temporary folder" => "Невозможно найти временную папку", +"Missing a temporary folder" => "Отсутствует временная папка", "Failed to write to disk" => "Ошибка записи на диск", "Not enough storage available" => "Недостаточно доступного места в хранилище", "Invalid directory." => "Неправильный каталог.", "Files" => "Файлы", +"Share" => "Открыть доступ", "Delete permanently" => "Удалено навсегда", "Delete" => "Удалить", "Rename" => "Переименовать", @@ -25,27 +25,29 @@ "undo" => "отмена", "perform delete operation" => "выполняется операция удаления", "1 file uploading" => "загружается 1 файл", +"files uploading" => "файлы загружаются", "'.' is an invalid file name." => "'.' - неправильное имя файла.", "File name cannot be empty." => "Имя файла не может быть пустым.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неправильное имя, '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' недопустимы.", "Your storage is full, files can not be updated or synced anymore!" => "Ваше дисковое пространство полностью заполнено, произведите очистку перед загрузкой новых файлов.", "Your storage is almost full ({usedSpacePercent}%)" => "Ваше хранилище почти заполнено ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Загрузка началась. Это может потребовать много времени, если файл большого размера.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Не удается загрузить файл размером 0 байт в каталог", +"Unable to upload your file as it is a directory or has 0 bytes" => "Файл не был загружен: его размер 0 байт либо это не файл, а директория.", "Not enough space available" => "Недостаточно свободного места", "Upload cancelled." => "Загрузка отменена.", "File upload is in progress. Leaving the page now will cancel the upload." => "Файл в процессе загрузки. Покинув страницу вы прервёте загрузку.", "URL cannot be empty." => "Ссылка не может быть пустой.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Неправильное имя каталога. Имя 'Shared' зарезервировано.", "Error" => "Ошибка", -"Name" => "Название", +"Name" => "Имя", "Size" => "Размер", "Modified" => "Изменён", "1 folder" => "1 папка", "{count} folders" => "{count} папок", "1 file" => "1 файл", "{count} files" => "{count} файлов", -"Upload" => "Загрузить", +"Unable to rename file" => "Невозможно переименовать файл", +"Upload" => "Загрузка", "File handling" => "Управление файлами", "Maximum upload size" => "Максимальный размер загружаемого файла", "max. possible: " => "макс. возможно: ", @@ -63,8 +65,8 @@ "You don’t have write permissions here." => "У вас нет разрешений на запись здесь.", "Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!", "Download" => "Скачать", -"Unshare" => "Отменить публикацию", -"Upload too large" => "Файл слишком большой", +"Unshare" => "Закрыть общий доступ", +"Upload too large" => "Файл слишком велик", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файлы, которые Вы пытаетесь загрузить, превышают лимит для файлов на этом сервере.", "Files are being scanned, please wait." => "Подождите, файлы сканируются.", "Current scanning" => "Текущее сканирование", diff --git a/apps/files/l10n/ru_RU.php b/apps/files/l10n/ru_RU.php index a7c6c83fde..e0bfab3321 100644 --- a/apps/files/l10n/ru_RU.php +++ b/apps/files/l10n/ru_RU.php @@ -1,70 +1,16 @@ "Неполучается перенести %s - Файл с таким именем уже существует", -"Could not move %s" => "Неполучается перенести %s ", -"Unable to rename file" => "Невозможно переименовать файл", "No file was uploaded. Unknown error" => "Файл не был загружен. Неизвестная ошибка", -"There is no error, the file uploaded with success" => "Ошибка отсутствует, файл загружен успешно.", -"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Размер загружаемого файла превышает upload_max_filesize директиву в php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Размер загруженного", -"The uploaded file was only partially uploaded" => "Загружаемый файл был загружен частично", +"There is no error, the file uploaded with success" => "Ошибки нет, файл успешно загружен", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Размер загружаемого файла превысил максимально допустимый в директиве MAX_FILE_SIZE, специфицированной в HTML-форме", +"The uploaded file was only partially uploaded" => "Загружаемый файл был загружен лишь частично", "No file was uploaded" => "Файл не был загружен", -"Missing a temporary folder" => "Отсутствует временная папка", +"Missing a temporary folder" => "Отсутствие временной папки", "Failed to write to disk" => "Не удалось записать на диск", "Not enough storage available" => "Недостаточно места в хранилище", -"Invalid directory." => "Неверный каталог.", -"Files" => "Файлы", -"Delete permanently" => "Удалить навсегда", +"Share" => "Сделать общим", "Delete" => "Удалить", -"Rename" => "Переименовать", -"Pending" => "Ожидающий решения", -"{new_name} already exists" => "{новое_имя} уже существует", -"replace" => "отмена", -"suggest name" => "подобрать название", -"cancel" => "отменить", -"replaced {new_name} with {old_name}" => "заменено {новое_имя} с {старое_имя}", -"undo" => "отменить действие", -"perform delete operation" => "выполняется процесс удаления", -"1 file uploading" => "загрузка 1 файла", -"'.' is an invalid file name." => "'.' является неверным именем файла.", -"File name cannot be empty." => "Имя файла не может быть пустым.", -"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Некорректное имя, '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' не допустимы.", -"Your storage is full, files can not be updated or synced anymore!" => "Ваше хранилище переполнено, фалы больше не могут быть обновлены или синхронизированы!", -"Your storage is almost full ({usedSpacePercent}%)" => "Ваше хранилище почти полно ({usedSpacePercent}%)", -"Your download is being prepared. This might take some time if the files are big." => "Идёт подготовка к скачке Вашего файла. Это может занять некоторое время, если фалы большие.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Невозможно загрузить файл,\n так как он имеет нулевой размер или является директорией", -"Not enough space available" => "Не достаточно свободного места", -"Upload cancelled." => "Загрузка отменена", -"File upload is in progress. Leaving the page now will cancel the upload." => "Процесс загрузки файла. Если покинуть страницу сейчас, загрузка будет отменена.", -"URL cannot be empty." => "URL не должен быть пустым.", -"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Неверное имя папки. Использование наименования 'Опубликовано' зарезервировано Owncloud", "Error" => "Ошибка", "Name" => "Имя", -"Size" => "Размер", -"Modified" => "Изменен", -"1 folder" => "1 папка", -"{count} folders" => "{количество} папок", -"1 file" => "1 файл", -"{count} files" => "{количество} файлов", -"Upload" => "Загрузить ", -"File handling" => "Работа с файлами", -"Maximum upload size" => "Максимальный размер загружаемого файла", -"max. possible: " => "Максимально возможный", -"Needed for multi-file and folder downloads." => "Необходимо для множественной загрузки.", -"Enable ZIP-download" => "Включение ZIP-загрузки", -"0 is unlimited" => "0 без ограничений", -"Maximum input size for ZIP files" => "Максимальный размер входящих ZIP-файлов ", "Save" => "Сохранить", -"New" => "Новый", -"Text file" => "Текстовый файл", -"Folder" => "Папка", -"From link" => "По ссылке", -"Cancel upload" => "Отмена загрузки", -"Nothing in here. Upload something!" => "Здесь ничего нет. Загрузите что-нибудь!", -"Download" => "Загрузить", -"Unshare" => "Скрыть", -"Upload too large" => "Загрузка слишком велика", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Размер файлов, которые Вы пытаетесь загрузить, превышает максимально допустимый размер для загрузки на данный сервер.", -"Files are being scanned, please wait." => "Файлы сканируются, пожалуйста, подождите.", -"Current scanning" => "Текущее сканирование", -"Upgrading filesystem cache..." => "Обновление кэша файловой системы... " +"Download" => "Загрузка" ); diff --git a/apps/files/l10n/si_LK.php b/apps/files/l10n/si_LK.php index dfcca6f689..351021a9f8 100644 --- a/apps/files/l10n/si_LK.php +++ b/apps/files/l10n/si_LK.php @@ -1,13 +1,14 @@ "ගොනුවක් උඩුගත නොවුනි. නොහැඳිනු දෝෂයක්", -"There is no error, the file uploaded with success" => "නිවැරදි ව ගොනුව උඩුගත කෙරිනි", +"There is no error, the file uploaded with success" => "දෝෂයක් නොමැත. සාර්ථකව ගොනුව උඩුගත කෙරුණි", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "උඩුගත කළ ගොනුවේ විශාලත්වය HTML පෝරමයේ නියම කළ ඇති MAX_FILE_SIZE විශාලත්වයට වඩා වැඩිය", "The uploaded file was only partially uploaded" => "උඩුගත කළ ගොනුවේ කොටසක් පමණක් උඩුගත විය", -"No file was uploaded" => "කිසිදු ගොනවක් උඩුගත නොවිනි", -"Missing a temporary folder" => "තාවකාලික ෆොල්ඩරයක් සොයාගත නොහැක", +"No file was uploaded" => "ගොනුවක් උඩුගත නොවුණි", +"Missing a temporary folder" => "තාවකාලික ෆොල්ඩරයක් අතුරුදහන්", "Failed to write to disk" => "තැටිගත කිරීම අසාර්ථකයි", "Files" => "ගොනු", -"Delete" => "මකන්න", +"Share" => "බෙදා හදා ගන්න", +"Delete" => "මකා දමන්න", "Rename" => "නැවත නම් කරන්න", "replace" => "ප්‍රතිස්ථාපනය කරන්න", "suggest name" => "නමක් යෝජනා කරන්න", @@ -23,7 +24,7 @@ "Modified" => "වෙනස් කළ", "1 folder" => "1 ෆොල්ඩරයක්", "1 file" => "1 ගොනුවක්", -"Upload" => "උඩුගත කිරීම", +"Upload" => "උඩුගත කරන්න", "File handling" => "ගොනු පරිහරණය", "Maximum upload size" => "උඩුගත කිරීමක උපරිම ප්‍රමාණය", "max. possible: " => "හැකි උපරිමය:", @@ -38,7 +39,7 @@ "From link" => "යොමුවෙන්", "Cancel upload" => "උඩුගත කිරීම අත් හරින්න", "Nothing in here. Upload something!" => "මෙහි කිසිවක් නොමැත. යමක් උඩුගත කරන්න", -"Download" => "බාගත කිරීම", +"Download" => "බාන්න", "Unshare" => "නොබෙදු", "Upload too large" => "උඩුගත කිරීම විශාල වැඩිය", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ඔබ උඩුගත කිරීමට තැත් කරන ගොනු මෙම සේවාදායකයා උඩුගත කිරීමට ඉඩදී ඇති උපරිම ගොනු විශාලත්වයට වඩා වැඩිය", diff --git a/apps/files/l10n/sk_SK.php b/apps/files/l10n/sk_SK.php index ee89a4c7d6..ad33c9b4ee 100644 --- a/apps/files/l10n/sk_SK.php +++ b/apps/files/l10n/sk_SK.php @@ -1,22 +1,22 @@ "Nie je možné presunúť %s - súbor s týmto menom už existuje", "Could not move %s" => "Nie je možné presunúť %s", -"Unable to rename file" => "Nemožno premenovať súbor", "No file was uploaded. Unknown error" => "Žiaden súbor nebol odoslaný. Neznáma chyba", "There is no error, the file uploaded with success" => "Nenastala žiadna chyba, súbor bol úspešne nahraný", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Nahraný súbor predčil konfiguračnú direktívu upload_max_filesize v súbore php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Nahrávaný súbor presiahol MAX_FILE_SIZE direktívu, ktorá bola špecifikovaná v HTML formulári", -"The uploaded file was only partially uploaded" => "Nahrávaný súbor bol iba čiastočne nahraný", -"No file was uploaded" => "Žiaden súbor nebol nahraný", -"Missing a temporary folder" => "Chýbajúci dočasný priečinok", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Ukladaný súbor prekračuje nastavenie MAX_FILE_SIZE z volieb HTML formulára.", +"The uploaded file was only partially uploaded" => "Ukladaný súbor sa nahral len čiastočne", +"No file was uploaded" => "Žiadny súbor nebol uložený", +"Missing a temporary folder" => "Chýba dočasný priečinok", "Failed to write to disk" => "Zápis na disk sa nepodaril", "Not enough storage available" => "Nedostatok dostupného úložného priestoru", "Invalid directory." => "Neplatný priečinok", "Files" => "Súbory", +"Share" => "Zdieľať", "Delete permanently" => "Zmazať trvalo", -"Delete" => "Odstrániť", +"Delete" => "Zmazať", "Rename" => "Premenovať", -"Pending" => "Čaká sa", +"Pending" => "Prebieha", "{new_name} already exists" => "{new_name} už existuje", "replace" => "nahradiť", "suggest name" => "pomôcť s menom", @@ -32,20 +32,22 @@ "Your storage is full, files can not be updated or synced anymore!" => "Vaše úložisko je plné. Súbory nemožno aktualizovať ani synchronizovať!", "Your storage is almost full ({usedSpacePercent}%)" => "Vaše úložisko je takmer plné ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Vaše sťahovanie sa pripravuje. Ak sú sťahované súbory veľké, môže to chvíľu trvať.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Nemôžem nahrať súbor lebo je to priečinok alebo má 0 bajtov.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Nedá sa odoslať Váš súbor, pretože je to priečinok, alebo je jeho veľkosť 0 bajtov", "Not enough space available" => "Nie je k dispozícii dostatok miesta", "Upload cancelled." => "Odosielanie zrušené", "File upload is in progress. Leaving the page now will cancel the upload." => "Opustenie stránky zruší práve prebiehajúce odosielanie súboru.", "URL cannot be empty." => "URL nemôže byť prázdne", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Neplatné meno priečinka. Používanie mena 'Shared' je vyhradené len pre Owncloud", "Error" => "Chyba", -"Name" => "Meno", +"Name" => "Názov", "Size" => "Veľkosť", "Modified" => "Upravené", "1 folder" => "1 priečinok", "{count} folders" => "{count} priečinkov", "1 file" => "1 súbor", "{count} files" => "{count} súborov", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Neplatný názov priečinka. Názov \"Shared\" je rezervovaný pre ownCloud", +"Unable to rename file" => "Nemožno premenovať súbor", "Upload" => "Odoslať", "File handling" => "Nastavenie správania sa k súborom", "Maximum upload size" => "Maximálna veľkosť odosielaného súboru", @@ -55,7 +57,7 @@ "0 is unlimited" => "0 znamená neobmedzené", "Maximum input size for ZIP files" => "Najväčšia veľkosť ZIP súborov", "Save" => "Uložiť", -"New" => "Nový", +"New" => "Nová", "Text file" => "Textový súbor", "Folder" => "Priečinok", "From link" => "Z odkazu", @@ -63,9 +65,9 @@ "Cancel upload" => "Zrušiť odosielanie", "You don’t have write permissions here." => "Nemáte oprávnenie na zápis.", "Nothing in here. Upload something!" => "Žiadny súbor. Nahrajte niečo!", -"Download" => "Stiahnuť", -"Unshare" => "Nezdielať", -"Upload too large" => "Odosielaný súbor je príliš veľký", +"Download" => "Sťahovanie", +"Unshare" => "Zrušiť zdieľanie", +"Upload too large" => "Nahrávanie je príliš veľké", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Súbory, ktoré sa snažíte nahrať, presahujú maximálnu veľkosť pre nahratie súborov na tento server.", "Files are being scanned, please wait." => "Čakajte, súbory sú prehľadávané.", "Current scanning" => "Práve prezerané", diff --git a/apps/files/l10n/sl.php b/apps/files/l10n/sl.php index 65d463e13d..6902d311ab 100644 --- a/apps/files/l10n/sl.php +++ b/apps/files/l10n/sl.php @@ -1,19 +1,19 @@ "Ni mogoče premakniti %s - datoteka s tem imenom že obstaja", "Could not move %s" => "Ni mogoče premakniti %s", -"Unable to rename file" => "Ni mogoče preimenovati datoteke", -"No file was uploaded. Unknown error" => "Ni poslane nobene datoteke. Neznana napaka.", -"There is no error, the file uploaded with success" => "Datoteka je uspešno poslana.", +"No file was uploaded. Unknown error" => "Ni poslane datoteke. Neznana napaka.", +"There is no error, the file uploaded with success" => "Datoteka je uspešno naložena.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Poslana datoteka presega dovoljeno velikost, ki je določena z možnostjo upload_max_filesize v datoteki php.ini:", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Poslana datoteka presega velikost, ki jo določa parameter največje dovoljene velikosti v obrazcu HTML.", -"The uploaded file was only partially uploaded" => "Datoteka je le delno naložena", -"No file was uploaded" => "Nobena datoteka ni bila naložena", +"The uploaded file was only partially uploaded" => "Poslan je le del datoteke.", +"No file was uploaded" => "Ni poslane datoteke", "Missing a temporary folder" => "Manjka začasna mapa", "Failed to write to disk" => "Pisanje na disk je spodletelo", "Not enough storage available" => "Na voljo ni dovolj prostora", "Invalid directory." => "Neveljavna mapa.", "Files" => "Datoteke", -"Delete permanently" => "Izbriši trajno", +"Share" => "Souporaba", +"Delete permanently" => "Izbriši dokončno", "Delete" => "Izbriši", "Rename" => "Preimenuj", "Pending" => "V čakanju ...", @@ -32,7 +32,7 @@ "Your storage is full, files can not be updated or synced anymore!" => "Shramba je povsem napolnjena. Datotek ni več mogoče posodabljati in usklajevati!", "Your storage is almost full ({usedSpacePercent}%)" => "Mesto za shranjevanje je skoraj polno ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Postopek priprave datoteke za prejem je lahko dolgotrajen, če je datoteka zelo velika.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Pošiljanje ni mogoče, saj gre za mapo, ali pa je datoteka velikosti 0 bajtov.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Pošiljanja ni mogoče izvesti, saj gre za mapo oziroma datoteko velikosti 0 bajtov.", "Not enough space available" => "Na voljo ni dovolj prostora.", "Upload cancelled." => "Pošiljanje je preklicano.", "File upload is in progress. Leaving the page now will cancel the upload." => "V teku je pošiljanje datoteke. Če zapustite to stran zdaj, bo pošiljanje preklicano.", @@ -46,6 +46,7 @@ "{count} folders" => "{count} map", "1 file" => "1 datoteka", "{count} files" => "{count} datotek", +"Unable to rename file" => "Ni mogoče preimenovati datoteke", "Upload" => "Pošlji", "File handling" => "Upravljanje z datotekami", "Maximum upload size" => "Največja velikost za pošiljanja", @@ -55,7 +56,7 @@ "0 is unlimited" => "0 predstavlja neomejeno vrednost", "Maximum input size for ZIP files" => "Največja vhodna velikost za datoteke ZIP", "Save" => "Shrani", -"New" => "Nova", +"New" => "Novo", "Text file" => "Besedilna datoteka", "Folder" => "Mapa", "From link" => "Iz povezave", @@ -64,7 +65,7 @@ "You don’t have write permissions here." => "Za to mesto ni ustreznih dovoljenj za pisanje.", "Nothing in here. Upload something!" => "Tukaj še ni ničesar. Najprej je treba kakšno datoteko poslati v oblak!", "Download" => "Prejmi", -"Unshare" => "Odstrani iz souporabe", +"Unshare" => "Prekliči souporabo", "Upload too large" => "Prekoračenje omejitve velikosti", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke, ki jih želite poslati, presegajo največjo dovoljeno velikost na strežniku.", "Files are being scanned, please wait." => "Poteka preučevanje datotek, počakajte ...", diff --git a/apps/files/l10n/sq.php b/apps/files/l10n/sq.php index 57c21ba002..63c95f692e 100644 --- a/apps/files/l10n/sq.php +++ b/apps/files/l10n/sq.php @@ -1,7 +1,6 @@ "%s nuk u spostua - Aty ekziston një skedar me të njëjtin emër", "Could not move %s" => "%s nuk u spostua", -"Unable to rename file" => "Nuk është i mundur riemërtimi i skedarit", "No file was uploaded. Unknown error" => "Nuk u ngarkua asnjë skedar. Veprim i gabuar i panjohur", "There is no error, the file uploaded with success" => "Nuk pati veprime të gabuara, skedari u ngarkua me sukses", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Skedari i ngarkuar tejkalon udhëzimin upload_max_filesize tek php.ini:", @@ -13,6 +12,7 @@ "Not enough storage available" => "Nuk ka mbetur hapësirë memorizimi e mjaftueshme", "Invalid directory." => "Dosje e pavlefshme.", "Files" => "Skedarët", +"Share" => "Nda", "Delete permanently" => "Elimino përfundimisht", "Delete" => "Elimino", "Rename" => "Riemërto", @@ -46,6 +46,7 @@ "{count} folders" => "{count} dosje", "1 file" => "1 skedar", "{count} files" => "{count} skedarë", +"Unable to rename file" => "Nuk është i mundur riemërtimi i skedarit", "Upload" => "Ngarko", "File handling" => "Trajtimi i skedarit", "Maximum upload size" => "Dimensioni maksimal i ngarkimit", diff --git a/apps/files/l10n/sr.php b/apps/files/l10n/sr.php index ff0733e211..3be6dde91a 100644 --- a/apps/files/l10n/sr.php +++ b/apps/files/l10n/sr.php @@ -1,4 +1,7 @@ "Не могу да преместим %s – датотека с овим именом већ постоји", +"Could not move %s" => "Не могу да преместим %s", +"No file was uploaded. Unknown error" => "Ниједна датотека није отпремљена услед непознате грешке", "There is no error, the file uploaded with success" => "Није дошло до грешке. Датотека је успешно отпремљена.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Отпремљена датотека прелази смерницу upload_max_filesize у датотеци php.ini:", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Отпремљена датотека прелази смерницу MAX_FILE_SIZE која је наведена у HTML обрасцу", @@ -6,7 +9,11 @@ "No file was uploaded" => "Датотека није отпремљена", "Missing a temporary folder" => "Недостаје привремена фасцикла", "Failed to write to disk" => "Не могу да пишем на диск", +"Not enough storage available" => "Нема довољно простора", +"Invalid directory." => "неисправна фасцикла.", "Files" => "Датотеке", +"Share" => "Дели", +"Delete permanently" => "Обриши за стално", "Delete" => "Обриши", "Rename" => "Преименуј", "Pending" => "На чекању", @@ -16,19 +23,30 @@ "cancel" => "откажи", "replaced {new_name} with {old_name}" => "замењено {new_name} са {old_name}", "undo" => "опозови", +"perform delete operation" => "обриши", "1 file uploading" => "Отпремам 1 датотеку", +"files uploading" => "датотеке се отпремају", +"'.' is an invalid file name." => "Датотека „.“ је неисправног имена.", +"File name cannot be empty." => "Име датотеке не може бити празно.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Неисправан назив. Следећи знакови нису дозвољени: \\, /, <, >, :, \", |, ? и *.", +"Your storage is full, files can not be updated or synced anymore!" => "Ваше складиште је пуно. Датотеке више не могу бити ажуриране ни синхронизоване.", +"Your storage is almost full ({usedSpacePercent}%)" => "Ваше складиште је скоро па пуно ({usedSpacePercent}%)", +"Your download is being prepared. This might take some time if the files are big." => "Припремам преузимање. Ово може да потраје ако су датотеке велике.", "Unable to upload your file as it is a directory or has 0 bytes" => "Не могу да отпремим датотеку као фасциклу или она има 0 бајтова", +"Not enough space available" => "Нема довољно простора", "Upload cancelled." => "Отпремање је прекинуто.", "File upload is in progress. Leaving the page now will cancel the upload." => "Отпремање датотеке је у току. Ако сада напустите страницу, прекинућете отпремање.", +"URL cannot be empty." => "Адреса не може бити празна.", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Неисправно име фасцикле. Фасцикла „Shared“ је резервисана за ownCloud.", "Error" => "Грешка", -"Name" => "Назив", +"Name" => "Име", "Size" => "Величина", "Modified" => "Измењено", "1 folder" => "1 фасцикла", "{count} folders" => "{count} фасцикле/и", "1 file" => "1 датотека", "{count} files" => "{count} датотеке/а", +"Unable to rename file" => "Не могу да преименујем датотеку", "Upload" => "Отпреми", "File handling" => "Управљање датотекама", "Maximum upload size" => "Највећа величина датотеке", @@ -42,12 +60,15 @@ "Text file" => "текстуална датотека", "Folder" => "фасцикла", "From link" => "Са везе", +"Deleted files" => "Обрисане датотеке", "Cancel upload" => "Прекини отпремање", +"You don’t have write permissions here." => "Овде немате дозволу за писање.", "Nothing in here. Upload something!" => "Овде нема ничег. Отпремите нешто!", "Download" => "Преузми", "Unshare" => "Укини дељење", "Upload too large" => "Датотека је превелика", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеке које желите да отпремите прелазе ограничење у величини.", "Files are being scanned, please wait." => "Скенирам датотеке…", -"Current scanning" => "Тренутно скенирање" +"Current scanning" => "Тренутно скенирање", +"Upgrading filesystem cache..." => "Дограђујем кеш система датотека…" ); diff --git a/apps/files/l10n/sv.php b/apps/files/l10n/sv.php index 125788ad13..82d169d569 100644 --- a/apps/files/l10n/sv.php +++ b/apps/files/l10n/sv.php @@ -1,18 +1,18 @@ "Kunde inte flytta %s - Det finns redan en fil med detta namn", "Could not move %s" => "Kan inte flytta %s", -"Unable to rename file" => "Kan inte byta namn på filen", "No file was uploaded. Unknown error" => "Ingen fil uppladdad. Okänt fel", -"There is no error, the file uploaded with success" => "Inga fel uppstod. Filen laddades upp utan problem", +"There is no error, the file uploaded with success" => "Inga fel uppstod. Filen laddades upp utan problem.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Den uppladdade filen överskrider upload_max_filesize direktivet php.ini:", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den uppladdade filen överstiger MAX_FILE_SIZE direktivet som anges i HTML-formulär", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den uppladdade filen överskrider MAX_FILE_SIZE direktivet som har angetts i HTML formuläret", "The uploaded file was only partially uploaded" => "Den uppladdade filen var endast delvis uppladdad", -"No file was uploaded" => "Ingen fil blev uppladdad", -"Missing a temporary folder" => "Saknar en tillfällig mapp", +"No file was uploaded" => "Ingen fil laddades upp", +"Missing a temporary folder" => "En temporär mapp saknas", "Failed to write to disk" => "Misslyckades spara till disk", "Not enough storage available" => "Inte tillräckligt med lagringsutrymme tillgängligt", "Invalid directory." => "Felaktig mapp.", "Files" => "Filer", +"Share" => "Dela", "Delete permanently" => "Radera permanent", "Delete" => "Radera", "Rename" => "Byt namn", @@ -25,13 +25,14 @@ "undo" => "ångra", "perform delete operation" => "utför raderingen", "1 file uploading" => "1 filuppladdning", +"files uploading" => "filer laddas upp", "'.' is an invalid file name." => "'.' är ett ogiltigt filnamn.", "File name cannot be empty." => "Filnamn kan inte vara tomt.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ogiltigt namn, '\\', '/', '<', '>', ':', '\"', '|', '?' och '*' är inte tillåtet.", "Your storage is full, files can not be updated or synced anymore!" => "Ditt lagringsutrymme är fullt, filer kan ej längre laddas upp eller synkas!", "Your storage is almost full ({usedSpacePercent}%)" => "Ditt lagringsutrymme är nästan fullt ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Din nedladdning förbereds. Det kan ta tid om det är stora filer.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Kunde inte ladda upp dina filer eftersom det antingen är en mapp eller har 0 bytes.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Kan inte ladda upp din fil eftersom det är en katalog eller har 0 bytes", "Not enough space available" => "Inte tillräckligt med utrymme tillgängligt", "Upload cancelled." => "Uppladdning avbruten.", "File upload is in progress. Leaving the page now will cancel the upload." => "Filuppladdning pågår. Lämnar du sidan så avbryts uppladdningen.", @@ -45,6 +46,7 @@ "{count} folders" => "{count} mappar", "1 file" => "1 fil", "{count} files" => "{count} filer", +"Unable to rename file" => "Kan inte byta namn på filen", "Upload" => "Ladda upp", "File handling" => "Filhantering", "Maximum upload size" => "Maximal storlek att ladda upp", diff --git a/apps/files/l10n/ta_LK.php b/apps/files/l10n/ta_LK.php index b88379043d..e5f7bbdf9b 100644 --- a/apps/files/l10n/ta_LK.php +++ b/apps/files/l10n/ta_LK.php @@ -7,7 +7,8 @@ "Missing a temporary folder" => "ஒரு தற்காலிகமான கோப்புறையை காணவில்லை", "Failed to write to disk" => "வட்டில் எழுத முடியவில்லை", "Files" => "கோப்புகள்", -"Delete" => "அழிக்க", +"Share" => "பகிர்வு", +"Delete" => "நீக்குக", "Rename" => "பெயர்மாற்றம்", "Pending" => "நிலுவையிலுள்ள", "{new_name} already exists" => "{new_name} ஏற்கனவே உள்ளது", @@ -38,7 +39,7 @@ "Enable ZIP-download" => "ZIP பதிவிறக்கலை இயலுமைப்படுத்துக", "0 is unlimited" => "0 ஆனது எல்லையற்றது", "Maximum input size for ZIP files" => "ZIP கோப்புகளுக்கான ஆகக்கூடிய உள்ளீட்டு அளவு", -"Save" => "சேமிக்க", +"Save" => "சேமிக்க ", "New" => "புதிய", "Text file" => "கோப்பு உரை", "Folder" => "கோப்புறை", diff --git a/apps/files/l10n/th_TH.php b/apps/files/l10n/th_TH.php index 0e7d32bf12..06d26edfec 100644 --- a/apps/files/l10n/th_TH.php +++ b/apps/files/l10n/th_TH.php @@ -1,18 +1,18 @@ "ไม่สามารถย้าย %s ได้ - ไฟล์ที่ใช้ชื่อนี้มีอยู่แล้ว", "Could not move %s" => "ไม่สามารถย้าย %s ได้", -"Unable to rename file" => "ไม่สามารถเปลี่ยนชื่อไฟล์ได้", "No file was uploaded. Unknown error" => "ยังไม่มีไฟล์ใดที่ถูกอัพโหลด เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ", -"There is no error, the file uploaded with success" => "ไม่มีข้อผิดพลาดใดๆ ไฟล์ถูกอัพโหลดเรียบร้อยแล้ว", +"There is no error, the file uploaded with success" => "ไม่พบข้อผิดพลาดใดๆ, ไฟล์ถูกอัพโหลดเรียบร้อยแล้ว", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "ขนาดไฟล์ที่อัพโหลดมีขนาดเกิน upload_max_filesize ที่ระบุไว้ใน php.ini", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "ไฟล์ที่อัพโหลดมีขนาดเกินคำสั่ง MAX_FILE_SIZE ที่ระบุเอาไว้ในรูปแบบคำสั่งในภาษา HTML", -"The uploaded file was only partially uploaded" => "ไฟล์ที่อัพโหลดยังไม่ได้ถูกอัพโหลดอย่างสมบูรณ์", -"No file was uploaded" => "ยังไม่มีไฟล์ที่ถูกอัพโหลด", -"Missing a temporary folder" => "แฟ้มเอกสารชั่วคราวเกิดการสูญหาย", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "ไฟล์ที่อัพโหลดมีขนาดไฟล์ใหญ่เกินจำนวนที่กำหนดไว้ในคำสั่ง MAX_FILE_SIZE ที่ถูกระบุไว้ในรูปแบบของ HTML", +"The uploaded file was only partially uploaded" => "ไฟล์ถูกอัพโหลดได้เพียงบางส่วนเท่านั้น", +"No file was uploaded" => "ไม่มีไฟล์ที่ถูกอัพโหลด", +"Missing a temporary folder" => "โฟลเดอร์ชั่วคราวเกิดการสูญหาย", "Failed to write to disk" => "เขียนข้อมูลลงแผ่นดิสก์ล้มเหลว", "Not enough storage available" => "เหลือพื้นที่ไม่เพียงสำหรับใช้งาน", "Invalid directory." => "ไดเร็กทอรี่ไม่ถูกต้อง", "Files" => "ไฟล์", +"Share" => "แชร์", "Delete" => "ลบ", "Rename" => "เปลี่ยนชื่อ", "Pending" => "อยู่ระหว่างดำเนินการ", @@ -24,13 +24,14 @@ "undo" => "เลิกทำ", "perform delete operation" => "ดำเนินการตามคำสั่งลบ", "1 file uploading" => "กำลังอัพโหลดไฟล์ 1 ไฟล์", +"files uploading" => "การอัพโหลดไฟล์", "'.' is an invalid file name." => "'.' เป็นชื่อไฟล์ที่ไม่ถูกต้อง", "File name cannot be empty." => "ชื่อไฟล์ไม่สามารถเว้นว่างได้", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "ชื่อที่ใช้ไม่ถูกต้อง, '\\', '/', '<', '>', ':', '\"', '|', '?' และ '*' ไม่ได้รับอนุญาตให้ใช้งานได้", "Your storage is full, files can not be updated or synced anymore!" => "พื้นที่จัดเก็บข้อมูลของคุณเต็มแล้ว ไม่สามารถอัพเดทหรือผสานไฟล์ต่างๆได้อีกต่อไป", "Your storage is almost full ({usedSpacePercent}%)" => "พื้นที่จัดเก็บข้อมูลของคุณใกล้เต็มแล้ว ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "กำลังเตรียมดาวน์โหลดข้อมูล หากไฟล์มีขนาดใหญ่ อาจใช้เวลาสักครู่", -"Unable to upload your file as it is a directory or has 0 bytes" => "ไม่สามารถอัพโหลดไฟล์ของคุณได้ เนื่องจากไฟล์ดังกล่าวเป็นไดเร็กทอรี่หรือมีขนาด 0 ไบต์", +"Unable to upload your file as it is a directory or has 0 bytes" => "ไม่สามารถอัพโหลดไฟล์ของคุณได้ เนื่องจากไฟล์ดังกล่าวเป็นไดเร็กทอรี่ หรือ มีขนาดไฟล์ 0 ไบต์", "Not enough space available" => "มีพื้นที่เหลือไม่เพียงพอ", "Upload cancelled." => "การอัพโหลดถูกยกเลิก", "File upload is in progress. Leaving the page now will cancel the upload." => "การอัพโหลดไฟล์กำลังอยู่ในระหว่างดำเนินการ การออกจากหน้าเว็บนี้จะทำให้การอัพโหลดถูกยกเลิก", @@ -39,11 +40,12 @@ "Error" => "ข้อผิดพลาด", "Name" => "ชื่อ", "Size" => "ขนาด", -"Modified" => "ปรับปรุงล่าสุด", +"Modified" => "แก้ไขแล้ว", "1 folder" => "1 โฟลเดอร์", "{count} folders" => "{count} โฟลเดอร์", "1 file" => "1 ไฟล์", "{count} files" => "{count} ไฟล์", +"Unable to rename file" => "ไม่สามารถเปลี่ยนชื่อไฟล์ได้", "Upload" => "อัพโหลด", "File handling" => "การจัดกาไฟล์", "Maximum upload size" => "ขนาดไฟล์สูงสุดที่อัพโหลดได้", @@ -60,7 +62,7 @@ "Cancel upload" => "ยกเลิกการอัพโหลด", "Nothing in here. Upload something!" => "ยังไม่มีไฟล์ใดๆอยู่ที่นี่ กรุณาอัพโหลดไฟล์!", "Download" => "ดาวน์โหลด", -"Unshare" => "ยกเลิกการแชร์ข้อมูล", +"Unshare" => "ยกเลิกการแชร์", "Upload too large" => "ไฟล์ที่อัพโหลดมีขนาดใหญ่เกินไป", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "ไฟล์ที่คุณพยายามที่จะอัพโหลดมีขนาดเกินกว่าขนาดสูงสุดที่กำหนดไว้ให้อัพโหลดได้สำหรับเซิร์ฟเวอร์นี้", "Files are being scanned, please wait." => "ไฟล์กำลังอยู่ระหว่างการสแกน, กรุณารอสักครู่.", diff --git a/apps/files/l10n/tr.php b/apps/files/l10n/tr.php index 84da59cee0..6a096d2703 100644 --- a/apps/files/l10n/tr.php +++ b/apps/files/l10n/tr.php @@ -1,18 +1,18 @@ "%s taşınamadı. Bu isimde dosya zaten var.", "Could not move %s" => "%s taşınamadı", -"Unable to rename file" => "Dosya adı değiştirilemedi", "No file was uploaded. Unknown error" => "Dosya yüklenmedi. Bilinmeyen hata", -"There is no error, the file uploaded with success" => "Bir hata yok, dosya başarıyla yüklendi", +"There is no error, the file uploaded with success" => "Dosya başarıyla yüklendi, hata oluşmadı", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "php.ini dosyasında upload_max_filesize ile belirtilen dosya yükleme sınırı aşıldı.", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Yüklenen dosya HTML formundaki MAX_FILE_SIZE sınırını aşıyor", -"The uploaded file was only partially uploaded" => "Yüklenen dosyanın sadece bir kısmı yüklendi", -"No file was uploaded" => "Hiç dosya yüklenmedi", -"Missing a temporary folder" => "Geçici bir klasör eksik", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Yüklenecek dosyanın boyutu HTML formunda belirtilen MAX_FILE_SIZE limitini aşıyor", +"The uploaded file was only partially uploaded" => "Dosya kısmen karşıya yüklenebildi", +"No file was uploaded" => "Hiç dosya gönderilmedi", +"Missing a temporary folder" => "Geçici dizin eksik", "Failed to write to disk" => "Diske yazılamadı", "Not enough storage available" => "Yeterli disk alanı yok", "Invalid directory." => "Geçersiz dizin.", "Files" => "Dosyalar", +"Share" => "Paylaş", "Delete permanently" => "Kalıcı olarak sil", "Delete" => "Sil", "Rename" => "İsim değiştir.", @@ -39,13 +39,15 @@ "URL cannot be empty." => "URL boş olamaz.", "Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "Geçersiz dizin adı. Shared isminin kullanımı Owncloud tarafından rezerver edilmiştir.", "Error" => "Hata", -"Name" => "Ad", +"Name" => "İsim", "Size" => "Boyut", "Modified" => "Değiştirilme", "1 folder" => "1 dizin", "{count} folders" => "{count} dizin", "1 file" => "1 dosya", "{count} files" => "{count} dosya", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Geçersiz dizin adı. 'Shared' dizin ismi kullanımı ownCloud tarafından rezerve edilmiştir.", +"Unable to rename file" => "Dosya adı değiştirilemedi", "Upload" => "Yükle", "File handling" => "Dosya taşıma", "Maximum upload size" => "Maksimum yükleme boyutu", @@ -65,7 +67,7 @@ "Nothing in here. Upload something!" => "Burada hiçbir şey yok. Birşeyler yükleyin!", "Download" => "İndir", "Unshare" => "Paylaşılmayan", -"Upload too large" => "Yüklemeniz çok büyük", +"Upload too large" => "Yükleme çok büyük", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Yüklemeye çalıştığınız dosyalar bu sunucudaki maksimum yükleme boyutunu aşıyor.", "Files are being scanned, please wait." => "Dosyalar taranıyor, lütfen bekleyin.", "Current scanning" => "Güncel tarama", diff --git a/apps/files/l10n/ug.php b/apps/files/l10n/ug.php new file mode 100644 index 0000000000..fb8f187ade --- /dev/null +++ b/apps/files/l10n/ug.php @@ -0,0 +1,44 @@ + "%s يۆتكىيەلمەيدۇ", +"No file was uploaded. Unknown error" => "ھېچقانداق ھۆججەت يۈكلەنمىدى. يوچۇن خاتالىق", +"No file was uploaded" => "ھېچقانداق ھۆججەت يۈكلەنمىدى", +"Missing a temporary folder" => "ۋاقىتلىق قىسقۇچ كەم.", +"Failed to write to disk" => "دىسكىغا يازالمىدى", +"Not enough storage available" => "يېتەرلىك ساقلاش بوشلۇقى يوق", +"Files" => "ھۆججەتلەر", +"Share" => "ھەمبەھىر", +"Delete permanently" => "مەڭگۈلۈك ئۆچۈر", +"Delete" => "ئۆچۈر", +"Rename" => "ئات ئۆزگەرت", +"Pending" => "كۈتۈۋاتىدۇ", +"{new_name} already exists" => "{new_name} مەۋجۇت", +"replace" => "ئالماشتۇر", +"suggest name" => "تەۋسىيە ئات", +"cancel" => "ۋاز كەچ", +"undo" => "يېنىۋال", +"1 file uploading" => "1 ھۆججەت يۈكلىنىۋاتىدۇ", +"files uploading" => "ھۆججەت يۈكلىنىۋاتىدۇ", +"Not enough space available" => "يېتەرلىك بوشلۇق يوق", +"Upload cancelled." => "يۈكلەشتىن ۋاز كەچتى.", +"File upload is in progress. Leaving the page now will cancel the upload." => "ھۆججەت يۈكلەش مەشغۇلاتى ئېلىپ بېرىلىۋاتىدۇ. Leaving the page now will cancel the upload.", +"Error" => "خاتالىق", +"Name" => "ئاتى", +"Size" => "چوڭلۇقى", +"Modified" => "ئۆزگەرتكەن", +"1 folder" => "1 قىسقۇچ", +"1 file" => "1 ھۆججەت", +"{count} files" => "{count} ھۆججەت", +"Unable to rename file" => "ھۆججەت ئاتىنى ئۆزگەرتكىلى بولمايدۇ", +"Upload" => "يۈكلە", +"Save" => "ساقلا", +"New" => "يېڭى", +"Text file" => "تېكىست ھۆججەت", +"Folder" => "قىسقۇچ", +"Deleted files" => "ئۆچۈرۈلگەن ھۆججەتلەر", +"Cancel upload" => "يۈكلەشتىن ۋاز كەچ", +"Nothing in here. Upload something!" => "بۇ جايدا ھېچنېمە يوق. Upload something!", +"Download" => "چۈشۈر", +"Unshare" => "ھەمبەھىرلىمە", +"Upload too large" => "يۈكلەندىغىنى بەك چوڭ", +"Upgrading filesystem cache..." => "ھۆججەت سىستېما غەملىكىنى يۈكسەلدۈرۈۋاتىدۇ…" +); diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php index 3c8eef9f36..324b28936e 100644 --- a/apps/files/l10n/uk.php +++ b/apps/files/l10n/uk.php @@ -1,7 +1,6 @@ "Не вдалося перемістити %s - Файл з таким ім'ям вже існує", "Could not move %s" => "Не вдалося перемістити %s", -"Unable to rename file" => "Не вдалося перейменувати файл", "No file was uploaded. Unknown error" => "Не завантажено жодного файлу. Невідома помилка", "There is no error, the file uploaded with success" => "Файл успішно вивантажено без помилок.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Розмір звантаження перевищує upload_max_filesize параметра в php.ini: ", @@ -13,6 +12,7 @@ "Not enough storage available" => "Місця більше немає", "Invalid directory." => "Невірний каталог.", "Files" => "Файли", +"Share" => "Поділитися", "Delete permanently" => "Видалити назавжди", "Delete" => "Видалити", "Rename" => "Перейменувати", @@ -25,6 +25,7 @@ "undo" => "відмінити", "perform delete operation" => "виконати операцію видалення", "1 file uploading" => "1 файл завантажується", +"files uploading" => "файли завантажуються", "'.' is an invalid file name." => "'.' це невірне ім'я файлу.", "File name cannot be empty." => " Ім'я файлу не може бути порожнім.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Невірне ім'я, '\\', '/', '<', '>', ':', '\"', '|', '?' та '*' не дозволені.", @@ -45,7 +46,8 @@ "{count} folders" => "{count} папок", "1 file" => "1 файл", "{count} files" => "{count} файлів", -"Upload" => "Відвантажити", +"Unable to rename file" => "Не вдалося перейменувати файл", +"Upload" => "Вивантажити", "File handling" => "Робота з файлами", "Maximum upload size" => "Максимальний розмір відвантажень", "max. possible: " => "макс.можливе:", @@ -63,7 +65,7 @@ "You don’t have write permissions here." => "У вас тут немає прав на запис.", "Nothing in here. Upload something!" => "Тут нічого немає. Відвантажте що-небудь!", "Download" => "Завантажити", -"Unshare" => "Заборонити доступ", +"Unshare" => "Закрити доступ", "Upload too large" => "Файл занадто великий", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файли,що ви намагаєтесь відвантажити перевищують максимальний дозволений розмір файлів на цьому сервері.", "Files are being scanned, please wait." => "Файли скануються, зачекайте, будь-ласка.", diff --git a/apps/files/l10n/ur_PK.php b/apps/files/l10n/ur_PK.php index e13a623fec..aa87eeda38 100644 --- a/apps/files/l10n/ur_PK.php +++ b/apps/files/l10n/ur_PK.php @@ -1,3 +1,4 @@ "ایرر" +"Error" => "ایرر", +"Unshare" => "شئیرنگ ختم کریں" ); diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php index 73cf154492..c8aa11295c 100644 --- a/apps/files/l10n/vi.php +++ b/apps/files/l10n/vi.php @@ -1,22 +1,22 @@ "Không thể di chuyển %s - Đã có tên file này trên hệ thống", +"Could not move %s - File with this name already exists" => "Không thể di chuyển %s - Đã có tên tập tin này trên hệ thống", "Could not move %s" => "Không thể di chuyển %s", -"Unable to rename file" => "Không thể đổi tên file", "No file was uploaded. Unknown error" => "Không có tập tin nào được tải lên. Lỗi không xác định", "There is no error, the file uploaded with success" => "Không có lỗi, các tập tin đã được tải lên thành công", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "The uploaded file exceeds the upload_max_filesize directive in php.ini: ", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Kích thước những tập tin tải lên vượt quá MAX_FILE_SIZE đã được quy định", -"The uploaded file was only partially uploaded" => "Tập tin tải lên mới chỉ tải lên được một phần", -"No file was uploaded" => "Không có tập tin nào được tải lên", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Tập tin được tải lên vượt quá MAX_FILE_SIZE được quy định trong mẫu HTML", +"The uploaded file was only partially uploaded" => "Các tập tin được tải lên chỉ tải lên được một phần", +"No file was uploaded" => "Chưa có file nào được tải lên", "Missing a temporary folder" => "Không tìm thấy thư mục tạm", "Failed to write to disk" => "Không thể ghi ", "Not enough storage available" => "Không đủ không gian lưu trữ", "Invalid directory." => "Thư mục không hợp lệ", "Files" => "Tập tin", +"Share" => "Chia sẻ", "Delete permanently" => "Xóa vĩnh vễn", "Delete" => "Xóa", "Rename" => "Sửa tên", -"Pending" => "Chờ", +"Pending" => "Đang chờ", "{new_name} already exists" => "{new_name} đã tồn tại", "replace" => "thay thế", "suggest name" => "tên gợi ý", @@ -25,13 +25,15 @@ "undo" => "lùi lại", "perform delete operation" => "thực hiện việc xóa", "1 file uploading" => "1 tệp tin đang được tải lên", +"files uploading" => "tệp tin đang được tải lên", "'.' is an invalid file name." => "'.' là một tên file không hợp lệ", "File name cannot be empty." => "Tên file không được rỗng", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Tên không hợp lệ, '\\', '/', '<', '>', ':', '\"', '|', '?' và '*' thì không được phép dùng.", "Your storage is full, files can not be updated or synced anymore!" => "Your storage is full, files can not be updated or synced anymore!", "Your storage is almost full ({usedSpacePercent}%)" => "Your storage is almost full ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "Your download is being prepared. This might take some time if the files are big.", -"Unable to upload your file as it is a directory or has 0 bytes" => "Không thể tải lên tập tin này do nó là một thư mục hoặc kích thước tập tin bằng 0 byte", +"Unable to upload your file as it is a directory or has 0 bytes" => "Không thể tải lên tập tin của bạn ,nó như là một thư mục hoặc có 0 byte", +"Not enough space available" => "Không đủ chỗ trống cần thiết", "Upload cancelled." => "Hủy tải lên", "File upload is in progress. Leaving the page now will cancel the upload." => "Tập tin tải lên đang được xử lý. Nếu bạn rời khỏi trang bây giờ sẽ hủy quá trình này.", "URL cannot be empty." => "URL không được để trống.", @@ -44,6 +46,7 @@ "{count} folders" => "{count} thư mục", "1 file" => "1 tập tin", "{count} files" => "{count} tập tin", +"Unable to rename file" => "Không thể đổi tên file", "Upload" => "Tải lên", "File handling" => "Xử lý tập tin", "Maximum upload size" => "Kích thước tối đa ", @@ -59,12 +62,13 @@ "From link" => "Từ liên kết", "Deleted files" => "File đã bị xóa", "Cancel upload" => "Hủy upload", +"You don’t have write permissions here." => "Bạn không có quyền ghi vào đây.", "Nothing in here. Upload something!" => "Không có gì ở đây .Hãy tải lên một cái gì đó !", -"Download" => "Tải xuống", -"Unshare" => "Không chia sẽ", +"Download" => "Tải về", +"Unshare" => "Bỏ chia sẻ", "Upload too large" => "Tập tin tải lên quá lớn", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Các tập tin bạn đang tải lên vượt quá kích thước tối đa cho phép trên máy chủ .", "Files are being scanned, please wait." => "Tập tin đang được quét ,vui lòng chờ.", "Current scanning" => "Hiện tại đang quét", -"Upgrading filesystem cache..." => "Upgrading filesystem cache..." +"Upgrading filesystem cache..." => "Đang nâng cấp bộ nhớ đệm cho tập tin hệ thống..." ); diff --git a/apps/files/l10n/zh_CN.GB2312.php b/apps/files/l10n/zh_CN.GB2312.php index 33e21e544c..0d87975918 100644 --- a/apps/files/l10n/zh_CN.GB2312.php +++ b/apps/files/l10n/zh_CN.GB2312.php @@ -1,15 +1,16 @@ "没有上传文件。未知错误", -"There is no error, the file uploaded with success" => "没有任何错误,文件上传成功了", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上传的文件超过了HTML表单指定的MAX_FILE_SIZE", -"The uploaded file was only partially uploaded" => "文件只有部分被上传", -"No file was uploaded" => "没有上传完成的文件", -"Missing a temporary folder" => "丢失了一个临时文件夹", +"There is no error, the file uploaded with success" => "文件上传成功", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上传的文件超过了 HTML 表格中指定的 MAX_FILE_SIZE 选项", +"The uploaded file was only partially uploaded" => "文件部分上传", +"No file was uploaded" => "没有上传文件", +"Missing a temporary folder" => "缺失临时文件夹", "Failed to write to disk" => "写磁盘失败", "Files" => "文件", +"Share" => "分享", "Delete" => "删除", "Rename" => "重命名", -"Pending" => "Pending", +"Pending" => "等待中", "{new_name} already exists" => "{new_name} 已存在", "replace" => "替换", "suggest name" => "推荐名称", @@ -17,12 +18,13 @@ "replaced {new_name} with {old_name}" => "已用 {old_name} 替换 {new_name}", "undo" => "撤销", "1 file uploading" => "1 个文件正在上传", -"Unable to upload your file as it is a directory or has 0 bytes" => "不能上传你指定的文件,可能因为它是个文件夹或者大小为0", +"files uploading" => "个文件正在上传", +"Unable to upload your file as it is a directory or has 0 bytes" => "不能上传您的文件,由于它是文件夹或者为空文件", "Upload cancelled." => "上传取消了", "File upload is in progress. Leaving the page now will cancel the upload." => "文件正在上传。关闭页面会取消上传。", "URL cannot be empty." => "网址不能为空。", "Error" => "出错", -"Name" => "名字", +"Name" => "名称", "Size" => "大小", "Modified" => "修改日期", "1 folder" => "1 个文件夹", @@ -45,8 +47,8 @@ "Cancel upload" => "取消上传", "Nothing in here. Upload something!" => "这里没有东西.上传点什么!", "Download" => "下载", -"Unshare" => "取消共享", -"Upload too large" => "上传的文件太大了", +"Unshare" => "取消分享", +"Upload too large" => "上传过大", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "你正在试图上传的文件超过了此服务器支持的最大的文件大小.", "Files are being scanned, please wait." => "正在扫描文件,请稍候.", "Current scanning" => "正在扫描" diff --git a/apps/files/l10n/zh_CN.php b/apps/files/l10n/zh_CN.php index 8740298c62..c883670e84 100644 --- a/apps/files/l10n/zh_CN.php +++ b/apps/files/l10n/zh_CN.php @@ -1,22 +1,22 @@ "无法移动 %s - 同名文件已存在", "Could not move %s" => "无法移动 %s", -"Unable to rename file" => "无法重命名文件", "No file was uploaded. Unknown error" => "没有文件被上传。未知错误", -"There is no error, the file uploaded with success" => "没有发生错误,文件上传成功。", +"There is no error, the file uploaded with success" => "文件上传成功,没有错误发生", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "上传文件大小已超过php.ini中upload_max_filesize所规定的值", -"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上传的文件超过了在HTML 表单中指定的MAX_FILE_SIZE", -"The uploaded file was only partially uploaded" => "只上传了文件的一部分", -"No file was uploaded" => "文件没有上传", +"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上传的文件长度超出了 HTML 表单中 MAX_FILE_SIZE 的限制", +"The uploaded file was only partially uploaded" => "已上传文件只上传了部分(不完整)", +"No file was uploaded" => "没有文件被上传", "Missing a temporary folder" => "缺少临时目录", "Failed to write to disk" => "写入磁盘失败", "Not enough storage available" => "没有足够的存储空间", "Invalid directory." => "无效文件夹。", "Files" => "文件", +"Share" => "分享", "Delete permanently" => "永久删除", "Delete" => "删除", "Rename" => "重命名", -"Pending" => "操作等待中", +"Pending" => "等待", "{new_name} already exists" => "{new_name} 已存在", "replace" => "替换", "suggest name" => "建议名称", @@ -25,13 +25,14 @@ "undo" => "撤销", "perform delete operation" => "进行删除操作", "1 file uploading" => "1个文件上传中", +"files uploading" => "文件上传中", "'.' is an invalid file name." => "'.' 是一个无效的文件名。", "File name cannot be empty." => "文件名不能为空。", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "无效名称,'\\', '/', '<', '>', ':', '\"', '|', '?' 和 '*' 不被允许使用。", "Your storage is full, files can not be updated or synced anymore!" => "您的存储空间已满,文件将无法更新或同步!", "Your storage is almost full ({usedSpacePercent}%)" => "您的存储空间即将用完 ({usedSpacePercent}%)", "Your download is being prepared. This might take some time if the files are big." => "下载正在准备中。如果文件较大可能会花费一些时间。", -"Unable to upload your file as it is a directory or has 0 bytes" => "无法上传文件,因为它是一个目录或者大小为 0 字节", +"Unable to upload your file as it is a directory or has 0 bytes" => "无法上传您的文件,文件夹或者空文件", "Not enough space available" => "没有足够可用空间", "Upload cancelled." => "上传已取消", "File upload is in progress. Leaving the page now will cancel the upload." => "文件正在上传中。现在离开此页会导致上传动作被取消。", @@ -45,6 +46,8 @@ "{count} folders" => "{count} 个文件夹", "1 file" => "1 个文件", "{count} files" => "{count} 个文件", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "无效的文件夹名。”Shared“ 是 Owncloud 预留的文件夹", +"Unable to rename file" => "无法重命名文件", "Upload" => "上传", "File handling" => "文件处理", "Maximum upload size" => "最大上传大小", @@ -63,7 +66,7 @@ "You don’t have write permissions here." => "您没有写权限", "Nothing in here. Upload something!" => "这里还什么都没有。上传些东西吧!", "Download" => "下载", -"Unshare" => "取消分享", +"Unshare" => "取消共享", "Upload too large" => "上传文件过大", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "您正尝试上传的文件超过了此服务器可以上传的最大容量限制", "Files are being scanned, please wait." => "文件正在被扫描,请稍候。", diff --git a/apps/files/l10n/zh_HK.php b/apps/files/l10n/zh_HK.php index 063acef5f0..caafc74b85 100644 --- a/apps/files/l10n/zh_HK.php +++ b/apps/files/l10n/zh_HK.php @@ -1,5 +1,6 @@ "文件", +"Share" => "分享", "Delete" => "刪除", "Error" => "錯誤", "Name" => "名稱", diff --git a/apps/files/l10n/zh_TW.php b/apps/files/l10n/zh_TW.php index 978ff3fa0a..0bd207888d 100644 --- a/apps/files/l10n/zh_TW.php +++ b/apps/files/l10n/zh_TW.php @@ -1,18 +1,18 @@ "無法移動 %s - 同名的檔案已經存在", "Could not move %s" => "無法移動 %s", -"Unable to rename file" => "無法重新命名檔案", "No file was uploaded. Unknown error" => "沒有檔案被上傳。未知的錯誤。", "There is no error, the file uploaded with success" => "無錯誤,檔案上傳成功", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "上傳的檔案大小超過 php.ini 當中 upload_max_filesize 參數的設定:", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "上傳的檔案大小超過 HTML 表單中 MAX_FILE_SIZE 的限制", "The uploaded file was only partially uploaded" => "只有檔案的一部分被上傳", -"No file was uploaded" => "無已上傳檔案", -"Missing a temporary folder" => "遺失暫存資料夾", +"No file was uploaded" => "沒有檔案被上傳", +"Missing a temporary folder" => "找不到暫存資料夾", "Failed to write to disk" => "寫入硬碟失敗", "Not enough storage available" => "儲存空間不足", "Invalid directory." => "無效的資料夾。", "Files" => "檔案", +"Share" => "分享", "Delete permanently" => "永久刪除", "Delete" => "刪除", "Rename" => "重新命名", @@ -34,10 +34,10 @@ "Your download is being prepared. This might take some time if the files are big." => "正在準備您的下載,若您的檔案較大,將會需要更多時間。", "Unable to upload your file as it is a directory or has 0 bytes" => "無法上傳您的檔案因為它可能是一個目錄或檔案大小為0", "Not enough space available" => "沒有足夠的可用空間", -"Upload cancelled." => "上傳取消", +"Upload cancelled." => "上傳已取消", "File upload is in progress. Leaving the page now will cancel the upload." => "檔案上傳中。離開此頁面將會取消上傳。", -"URL cannot be empty." => "URL 不能為空白.", -"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "無效的資料夾名稱,'Shared' 的使用被 Owncloud 保留", +"URL cannot be empty." => "URL 不能為空白。", +"Invalid folder name. Usage of 'Shared' is reserved by Owncloud" => "無效的資料夾名稱,'Shared' 的使用被 ownCloud 保留", "Error" => "錯誤", "Name" => "名稱", "Size" => "大小", @@ -46,6 +46,8 @@ "{count} folders" => "{count} 個資料夾", "1 file" => "1 個檔案", "{count} files" => "{count} 個檔案", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "無效的資料夾名稱,'Shared' 的使用被 ownCloud 保留", +"Unable to rename file" => "無法重新命名檔案", "Upload" => "上傳", "File handling" => "檔案處理", "Maximum upload size" => "最大上傳檔案大小", @@ -61,13 +63,13 @@ "From link" => "從連結", "Deleted files" => "已刪除的檔案", "Cancel upload" => "取消上傳", -"You don’t have write permissions here." => "您在這裏沒有編輯權。", -"Nothing in here. Upload something!" => "沒有任何東西。請上傳內容!", +"You don’t have write permissions here." => "您在這裡沒有編輯權。", +"Nothing in here. Upload something!" => "這裡什麼也沒有,上傳一些東西吧!", "Download" => "下載", "Unshare" => "取消共享", "Upload too large" => "上傳過大", -"The files you are trying to upload exceed the maximum size for file uploads on this server." => "您試圖上傳的檔案已超過伺服器的最大檔案大小限制。 ", +"The files you are trying to upload exceed the maximum size for file uploads on this server." => "您試圖上傳的檔案已超過伺服器的最大檔案大小限制。", "Files are being scanned, please wait." => "正在掃描檔案,請稍等。", "Current scanning" => "目前掃描", -"Upgrading filesystem cache..." => "正在更新檔案系統快取..." +"Upgrading filesystem cache..." => "正在升級檔案系統快取..." ); diff --git a/apps/files/lib/app.php b/apps/files/lib/app.php new file mode 100644 index 0000000000..c2a4b9c267 --- /dev/null +++ b/apps/files/lib/app.php @@ -0,0 +1,79 @@ +. + * + */ + + +namespace OCA\Files; + +class App { + private $l10n; + private $view; + + public function __construct($view, $l10n) { + $this->view = $view; + $this->l10n = $l10n; + } + + /** + * rename a file + * + * @param string $dir + * @param string $oldname + * @param string $newname + * @return array + */ + public function rename($dir, $oldname, $newname) { + $result = array( + 'success' => false, + 'data' => NULL + ); + + // rename to "/Shared" is denied + if( $dir === '/' and $newname === 'Shared' ) { + $result['data'] = array( + 'message' => $this->l10n->t("Invalid folder name. Usage of 'Shared' is reserved by ownCloud") + ); + } elseif( + // rename to "." is denied + $newname !== '.' and + // rename of "/Shared" is denied + !($dir === '/' and $oldname === 'Shared') and + // THEN try to rename + $this->view->rename($dir . '/' . $oldname, $dir . '/' . $newname) + ) { + // successful rename + $result['success'] = true; + $result['data'] = array( + 'dir' => $dir, + 'file' => $oldname, + 'newname' => $newname + ); + } else { + // rename failed + $result['data'] = array( + 'message' => $this->l10n->t('Unable to rename file') + ); + } + return $result; + } + +} \ No newline at end of file diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 69fcb94e68..b576253f4f 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -34,7 +34,7 @@ value="(max )"> - + @@ -46,7 +46,6 @@
diff --git a/apps/files/templates/part.breadcrumb.php b/apps/files/templates/part.breadcrumb.php index 7ea1755d1d..9886b42e42 100644 --- a/apps/files/templates/part.breadcrumb.php +++ b/apps/files/templates/part.breadcrumb.php @@ -1,5 +1,5 @@ -
+
diff --git a/apps/files/tests/ajax_rename.php b/apps/files/tests/ajax_rename.php new file mode 100644 index 0000000000..23e5761ddd --- /dev/null +++ b/apps/files/tests/ajax_rename.php @@ -0,0 +1,117 @@ +. + * + */ + +class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { + + function setUp() { + // mock OC_L10n + $l10nMock = $this->getMock('\OC_L10N', array('t'), array(), '', false); + $l10nMock->expects($this->any()) + ->method('t') + ->will($this->returnArgument(0)); + $viewMock = $this->getMock('\OC\Files\View', array('rename', 'normalizePath'), array(), '', false); + $viewMock->expects($this->any()) + ->method('normalizePath') + ->will($this->returnArgument(0)); + $viewMock->expects($this->any()) + ->method('rename') + ->will($this->returnValue(true)); + $this->files = new \OCA\Files\App($viewMock, $l10nMock); + } + + /** + * @brief test rename of file/folder named "Shared" + */ + function testRenameSharedFolder() { + $dir = '/'; + $oldname = 'Shared'; + $newname = 'new_name'; + + $result = $this->files->rename($dir, $oldname, $newname); + $expected = array( + 'success' => false, + 'data' => array('message' => 'Unable to rename file') + ); + + $this->assertEquals($expected, $result); + } + + /** + * @brief test rename of file/folder named "Shared" + */ + function testRenameSharedFolderInSubdirectory() { + $dir = '/test'; + $oldname = 'Shared'; + $newname = 'new_name'; + + $result = $this->files->rename($dir, $oldname, $newname); + $expected = array( + 'success' => true, + 'data' => array( + 'dir' => $dir, + 'file' => $oldname, + 'newname' => $newname + ) + ); + + $this->assertEquals($expected, $result); + } + + /** + * @brief test rename of file/folder to "Shared" + */ + function testRenameFolderToShared() { + $dir = '/'; + $oldname = 'oldname'; + $newname = 'Shared'; + + $result = $this->files->rename($dir, $oldname, $newname); + $expected = array( + 'success' => false, + 'data' => array('message' => "Invalid folder name. Usage of 'Shared' is reserved by ownCloud") + ); + + $this->assertEquals($expected, $result); + } + + /** + * @brief test rename of file/folder + */ + function testRenameFolder() { + $dir = '/'; + $oldname = 'oldname'; + $newname = 'newname'; + + $result = $this->files->rename($dir, $oldname, $newname); + $expected = array( + 'success' => true, + 'data' => array( + 'dir' => $dir, + 'file' => $oldname, + 'newname' => $newname + ) + ); + + $this->assertEquals($expected, $result); + } +} \ No newline at end of file diff --git a/apps/files_encryption/3rdparty/Crypt_Blowfish/Blowfish.php b/apps/files_encryption/3rdparty/Crypt_Blowfish/Blowfish.php new file mode 100644 index 0000000000..4ccacb963e --- /dev/null +++ b/apps/files_encryption/3rdparty/Crypt_Blowfish/Blowfish.php @@ -0,0 +1,317 @@ + + * @copyright 2005 Matthew Fonda + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Blowfish.php,v 1.81 2005/05/30 18:40:36 mfonda Exp $ + * @link http://pear.php.net/package/Crypt_Blowfish + */ + + +require_once 'PEAR.php'; + + +/** + * + * Example usage: + * $bf = new Crypt_Blowfish('some secret key!'); + * $encrypted = $bf->encrypt('this is some example plain text'); + * $plaintext = $bf->decrypt($encrypted); + * echo "plain text: $plaintext"; + * + * + * @category Encryption + * @package Crypt_Blowfish + * @author Matthew Fonda + * @copyright 2005 Matthew Fonda + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @link http://pear.php.net/package/Crypt_Blowfish + * @version @package_version@ + * @access public + */ +class Crypt_Blowfish +{ + /** + * P-Array contains 18 32-bit subkeys + * + * @var array + * @access private + */ + var $_P = array(); + + + /** + * Array of four S-Blocks each containing 256 32-bit entries + * + * @var array + * @access private + */ + var $_S = array(); + + /** + * Mcrypt td resource + * + * @var resource + * @access private + */ + var $_td = null; + + /** + * Initialization vector + * + * @var string + * @access private + */ + var $_iv = null; + + + /** + * Crypt_Blowfish Constructor + * Initializes the Crypt_Blowfish object, and gives a sets + * the secret key + * + * @param string $key + * @access public + */ + function Crypt_Blowfish($key) + { + if (extension_loaded('mcrypt')) { + $this->_td = mcrypt_module_open(MCRYPT_BLOWFISH, '', 'ecb', ''); + $this->_iv = mcrypt_create_iv(8, MCRYPT_RAND); + } + $this->setKey($key); + } + + /** + * Deprecated isReady method + * + * @return bool + * @access public + * @deprecated + */ + function isReady() + { + return true; + } + + /** + * Deprecated init method - init is now a private + * method and has been replaced with _init + * + * @return bool + * @access public + * @deprecated + * @see Crypt_Blowfish::_init() + */ + function init() + { + $this->_init(); + } + + /** + * Initializes the Crypt_Blowfish object + * + * @access private + */ + function _init() + { + $defaults = new Crypt_Blowfish_DefaultKey(); + $this->_P = $defaults->P; + $this->_S = $defaults->S; + } + + /** + * Enciphers a single 64 bit block + * + * @param int &$Xl + * @param int &$Xr + * @access private + */ + function _encipher(&$Xl, &$Xr) + { + for ($i = 0; $i < 16; $i++) { + $temp = $Xl ^ $this->_P[$i]; + $Xl = ((($this->_S[0][($temp>>24) & 255] + + $this->_S[1][($temp>>16) & 255]) ^ + $this->_S[2][($temp>>8) & 255]) + + $this->_S[3][$temp & 255]) ^ $Xr; + $Xr = $temp; + } + $Xr = $Xl ^ $this->_P[16]; + $Xl = $temp ^ $this->_P[17]; + } + + + /** + * Deciphers a single 64 bit block + * + * @param int &$Xl + * @param int &$Xr + * @access private + */ + function _decipher(&$Xl, &$Xr) + { + for ($i = 17; $i > 1; $i--) { + $temp = $Xl ^ $this->_P[$i]; + $Xl = ((($this->_S[0][($temp>>24) & 255] + + $this->_S[1][($temp>>16) & 255]) ^ + $this->_S[2][($temp>>8) & 255]) + + $this->_S[3][$temp & 255]) ^ $Xr; + $Xr = $temp; + } + $Xr = $Xl ^ $this->_P[1]; + $Xl = $temp ^ $this->_P[0]; + } + + + /** + * Encrypts a string + * + * @param string $plainText + * @return string Returns cipher text on success, PEAR_Error on failure + * @access public + */ + function encrypt($plainText) + { + if (!is_string($plainText)) { + PEAR::raiseError('Plain text must be a string', 0, PEAR_ERROR_DIE); + } + + if (extension_loaded('mcrypt')) { + return mcrypt_generic($this->_td, $plainText); + } + + $cipherText = ''; + $len = strlen($plainText); + $plainText .= str_repeat(chr(0),(8 - ($len%8))%8); + for ($i = 0; $i < $len; $i += 8) { + list(,$Xl,$Xr) = unpack("N2",substr($plainText,$i,8)); + $this->_encipher($Xl, $Xr); + $cipherText .= pack("N2", $Xl, $Xr); + } + return $cipherText; + } + + + /** + * Decrypts an encrypted string + * + * @param string $cipherText + * @return string Returns plain text on success, PEAR_Error on failure + * @access public + */ + function decrypt($cipherText) + { + if (!is_string($cipherText)) { + PEAR::raiseError('Cipher text must be a string', 1, PEAR_ERROR_DIE); + } + + if (extension_loaded('mcrypt')) { + return mdecrypt_generic($this->_td, $cipherText); + } + + $plainText = ''; + $len = strlen($cipherText); + $cipherText .= str_repeat(chr(0),(8 - ($len%8))%8); + for ($i = 0; $i < $len; $i += 8) { + list(,$Xl,$Xr) = unpack("N2",substr($cipherText,$i,8)); + $this->_decipher($Xl, $Xr); + $plainText .= pack("N2", $Xl, $Xr); + } + return $plainText; + } + + + /** + * Sets the secret key + * The key must be non-zero, and less than or equal to + * 56 characters in length. + * + * @param string $key + * @return bool Returns true on success, PEAR_Error on failure + * @access public + */ + function setKey($key) + { + if (!is_string($key)) { + PEAR::raiseError('Key must be a string', 2, PEAR_ERROR_DIE); + } + + $len = strlen($key); + + if ($len > 56 || $len == 0) { + PEAR::raiseError('Key must be less than 56 characters and non-zero. Supplied key length: ' . $len, 3, PEAR_ERROR_DIE); + } + + if (extension_loaded('mcrypt')) { + mcrypt_generic_init($this->_td, $key, $this->_iv); + return true; + } + + require_once 'Blowfish/DefaultKey.php'; + $this->_init(); + + $k = 0; + $data = 0; + $datal = 0; + $datar = 0; + + for ($i = 0; $i < 18; $i++) { + $data = 0; + for ($j = 4; $j > 0; $j--) { + $data = $data << 8 | ord($key{$k}); + $k = ($k+1) % $len; + } + $this->_P[$i] ^= $data; + } + + for ($i = 0; $i <= 16; $i += 2) { + $this->_encipher($datal, $datar); + $this->_P[$i] = $datal; + $this->_P[$i+1] = $datar; + } + for ($i = 0; $i < 256; $i += 2) { + $this->_encipher($datal, $datar); + $this->_S[0][$i] = $datal; + $this->_S[0][$i+1] = $datar; + } + for ($i = 0; $i < 256; $i += 2) { + $this->_encipher($datal, $datar); + $this->_S[1][$i] = $datal; + $this->_S[1][$i+1] = $datar; + } + for ($i = 0; $i < 256; $i += 2) { + $this->_encipher($datal, $datar); + $this->_S[2][$i] = $datal; + $this->_S[2][$i+1] = $datar; + } + for ($i = 0; $i < 256; $i += 2) { + $this->_encipher($datal, $datar); + $this->_S[3][$i] = $datal; + $this->_S[3][$i+1] = $datar; + } + + return true; + } + +} + +?> diff --git a/apps/files_encryption/3rdparty/Crypt_Blowfish/Blowfish/DefaultKey.php b/apps/files_encryption/3rdparty/Crypt_Blowfish/Blowfish/DefaultKey.php new file mode 100644 index 0000000000..2ff8ac788a --- /dev/null +++ b/apps/files_encryption/3rdparty/Crypt_Blowfish/Blowfish/DefaultKey.php @@ -0,0 +1,327 @@ + + * @copyright 2005 Matthew Fonda + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: DefaultKey.php,v 1.81 2005/05/30 18:40:37 mfonda Exp $ + * @link http://pear.php.net/package/Crypt_Blowfish + */ + + +/** + * Class containing default key + * + * @category Encryption + * @package Crypt_Blowfish + * @author Matthew Fonda + * @copyright 2005 Matthew Fonda + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @link http://pear.php.net/package/Crypt_Blowfish + * @version @package_version@ + * @access public + */ +class Crypt_Blowfish_DefaultKey +{ + var $P = array(); + + var $S = array(); + + function Crypt_Blowfish_DefaultKey() + { + $this->P = array( + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, + 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, + 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, + 0x9216D5D9, 0x8979FB1B + ); + + $this->S = array( + array( + 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, + 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, + 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, + 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, + 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, + 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, + 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, + 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, + 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, + 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, + 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, + 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, + 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, + 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, + 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, + 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, + 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, + 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, + 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, + 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, + 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, + 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, + 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, + 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, + 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, + 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, + 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, + 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, + 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, + 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, + 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, + 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, + 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, + 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, + 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, + 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, + 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, + 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, + 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, + 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, + 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, + 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, + 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, + 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, + 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, + 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, + 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, + 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, + 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, + 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, + 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, + 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, + 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, + 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, + 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, + 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, + 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, + 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, + 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, + 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, + 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, + 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, + 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, + 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A + ), + array( + 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, + 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, + 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, + 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, + 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, + 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, + 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, + 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, + 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, + 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, + 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, + 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, + 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, + 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, + 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, + 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, + 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, + 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, + 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, + 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, + 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, + 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, + 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, + 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, + 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, + 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, + 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, + 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, + 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, + 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, + 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, + 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, + 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, + 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, + 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, + 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, + 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, + 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, + 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, + 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, + 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, + 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, + 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, + 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, + 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, + 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, + 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, + 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, + 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, + 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, + 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, + 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, + 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, + 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, + 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, + 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, + 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, + 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, + 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, + 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, + 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, + 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, + 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, + 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7 + ), + array( + 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, + 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, + 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, + 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, + 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, + 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, + 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, + 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, + 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, + 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, + 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, + 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, + 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, + 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, + 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, + 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, + 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, + 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, + 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, + 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, + 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, + 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, + 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, + 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, + 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, + 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, + 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, + 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, + 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, + 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, + 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, + 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, + 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, + 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, + 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, + 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, + 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, + 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, + 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, + 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, + 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, + 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, + 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, + 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, + 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, + 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, + 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, + 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, + 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, + 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, + 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, + 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, + 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, + 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, + 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, + 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, + 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, + 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, + 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, + 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, + 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, + 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, + 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, + 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0 + ), + array( + 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, + 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, + 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, + 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, + 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, + 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, + 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, + 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, + 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, + 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, + 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, + 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, + 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, + 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, + 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, + 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, + 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, + 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, + 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, + 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, + 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, + 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, + 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, + 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, + 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, + 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, + 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, + 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, + 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, + 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, + 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, + 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, + 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, + 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, + 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, + 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, + 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, + 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, + 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, + 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, + 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, + 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, + 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, + 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, + 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, + 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, + 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, + 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, + 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, + 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, + 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, + 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, + 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, + 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, + 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, + 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, + 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, + 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, + 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, + 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, + 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, + 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, + 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, + 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 + ) + ); + } + +} + +?> diff --git a/apps/files_encryption/ajax/adminrecovery.php b/apps/files_encryption/ajax/adminrecovery.php new file mode 100644 index 0000000000..6a0186d5a9 --- /dev/null +++ b/apps/files_encryption/ajax/adminrecovery.php @@ -0,0 +1,59 @@ + + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + * + * @brief Script to handle admin settings for encrypted key recovery + */ +use OCA\Encryption; + +\OCP\JSON::checkAdminUser(); +\OCP\JSON::checkAppEnabled('files_encryption'); +\OCP\JSON::callCheck(); + +$l = OC_L10N::get('files_encryption'); + +$return = false; +// Enable recoveryAdmin + +$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId'); + +if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] === '1') { + + $return = \OCA\Encryption\Helper::adminEnableRecovery($recoveryKeyId, $_POST['recoveryPassword']); + + // Return success or failure + if ($return) { + \OCP\JSON::success(array('data' => array('message' => $l->t('Recovery key successfully enabled')))); + } else { + \OCP\JSON::error(array( + 'data' => array( + 'message' => $l->t( + 'Could not enable recovery key. Please check your recovery key password!') + ) + )); + } + +// Disable recoveryAdmin +} elseif ( + isset($_POST['adminEnableRecovery']) + && '0' === $_POST['adminEnableRecovery'] +) { + $return = \OCA\Encryption\Helper::adminDisableRecovery($_POST['recoveryPassword']); + + // Return success or failure + if ($return) { + \OCP\JSON::success(array('data' => array('message' => $l->t('Recovery key successfully disabled')))); + } else { + \OCP\JSON::error(array( + 'data' => array( + 'message' => $l->t( + 'Could not disable recovery key. Please check your recovery key password!') + ) + )); + } +} + + diff --git a/apps/files_encryption/ajax/changeRecoveryPassword.php b/apps/files_encryption/ajax/changeRecoveryPassword.php new file mode 100644 index 0000000000..b0594f967b --- /dev/null +++ b/apps/files_encryption/ajax/changeRecoveryPassword.php @@ -0,0 +1,52 @@ + + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + * + * @brief Script to change recovery key password + * + */ + +use OCA\Encryption; + +\OCP\JSON::checkAdminUser(); +\OCP\JSON::checkAppEnabled('files_encryption'); +\OCP\JSON::callCheck(); + +$l = OC_L10N::get('core'); + +$return = false; + +$oldPassword = $_POST['oldPassword']; +$newPassword = $_POST['newPassword']; + +$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser()); + +$result = $util->checkRecoveryPassword($oldPassword); + +if ($result) { + $keyId = $util->getRecoveryKeyId(); + $keyPath = '/owncloud_private_key/' . $keyId . '.private.key'; + $view = new \OC\Files\View('/'); + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $encryptedRecoveryKey = $view->file_get_contents($keyPath); + $decryptedRecoveryKey = \OCA\Encryption\Crypt::symmetricDecryptFileContent($encryptedRecoveryKey, $oldPassword); + $encryptedRecoveryKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword); + $view->file_put_contents($keyPath, $encryptedRecoveryKey); + + \OC_FileProxy::$enabled = $proxyStatus; + + $return = true; +} + +// success or failure +if ($return) { + \OCP\JSON::success(array('data' => array('message' => $l->t('Password successfully changed.')))); +} else { + \OCP\JSON::error(array('data' => array('message' => $l->t('Could not change the password. Maybe the old password was not correct.')))); +} \ No newline at end of file diff --git a/apps/files_encryption/ajax/userrecovery.php b/apps/files_encryption/ajax/userrecovery.php new file mode 100644 index 0000000000..1d0f1ac2d1 --- /dev/null +++ b/apps/files_encryption/ajax/userrecovery.php @@ -0,0 +1,41 @@ + + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + * + * @brief Script to handle admin settings for encrypted key recovery + */ + +use OCA\Encryption; + +\OCP\JSON::checkLoggedIn(); +\OCP\JSON::checkAppEnabled('files_encryption'); +\OCP\JSON::callCheck(); + +if ( + isset($_POST['userEnableRecovery']) + && (0 == $_POST['userEnableRecovery'] || '1' === $_POST['userEnableRecovery']) +) { + + $userId = \OCP\USER::getUser(); + $view = new \OC_FilesystemView('/'); + $util = new \OCA\Encryption\Util($view, $userId); + + // Save recovery preference to DB + $return = $util->setRecoveryForUser($_POST['userEnableRecovery']); + + if ($_POST['userEnableRecovery'] === '1') { + $util->addRecoveryKeys(); + } else { + $util->removeRecoveryKeys(); + } + +} else { + + $return = false; + +} + +// Return success or failure +($return) ? \OCP\JSON::success() : \OCP\JSON::error(); \ No newline at end of file diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php index bf16fec3ae..99bdc2c247 100644 --- a/apps/files_encryption/appinfo/app.php +++ b/apps/files_encryption/appinfo/app.php @@ -8,42 +8,50 @@ OC::$CLASSPATH['OCA\Encryption\Stream'] = 'files_encryption/lib/stream.php'; OC::$CLASSPATH['OCA\Encryption\Proxy'] = 'files_encryption/lib/proxy.php'; OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php'; OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php'; +OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php'; -OC_FileProxy::register( new OCA\Encryption\Proxy() ); +OC_FileProxy::register(new OCA\Encryption\Proxy()); -// User-related hooks -OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' ); -OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' ); +// User related hooks +OCA\Encryption\Helper::registerUserHooks(); -// Sharing-related hooks -OCP\Util::connectHook( 'OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared' ); -OCP\Util::connectHook( 'OCP\Share', 'pre_unshare', 'OCA\Encryption\Hooks', 'preUnshare' ); -OCP\Util::connectHook( 'OCP\Share', 'pre_unshareAll', 'OCA\Encryption\Hooks', 'preUnshareAll' ); +// Sharing related hooks +OCA\Encryption\Helper::registerShareHooks(); -// Webdav-related hooks -OCP\Util::connectHook( 'OC_Webdav_Properties', 'update', 'OCA\Encryption\Hooks', 'updateKeyfile' ); +// Filesystem related hooks +OCA\Encryption\Helper::registerFilesystemHooks(); -stream_wrapper_register( 'crypt', 'OCA\Encryption\Stream' ); +stream_wrapper_register('crypt', 'OCA\Encryption\Stream'); -$session = new OCA\Encryption\Session(); +// check if we are logged in +if (OCP\User::isLoggedIn()) { -if ( - ! $session->getPrivateKey( \OCP\USER::getUser() ) - && OCP\User::isLoggedIn() - && OCA\Encryption\Crypt::mode() == 'server' -) { + // ensure filesystem is loaded + if(!\OC\Files\Filesystem::$loaded) { + \OC_Util::setupFS(); + } - // Force the user to log-in again if the encryption key isn't unlocked - // (happens when a user is logged in before the encryption app is - // enabled) - OCP\User::logout(); - - header( "Location: " . OC::$WEBROOT.'/' ); - - exit(); + $view = new OC_FilesystemView('/'); + $session = new \OCA\Encryption\Session($view); + // check if user has a private key + if ( + !$session->getPrivateKey(\OCP\USER::getUser()) + && OCA\Encryption\Crypt::mode() === 'server' + ) { + + // Force the user to log-in again if the encryption key isn't unlocked + // (happens when a user is logged in before the encryption app is + // enabled) + OCP\User::logout(); + + header("Location: " . OC::$WEBROOT . '/'); + + exit(); + } } // Register settings scripts -OCP\App::registerAdmin( 'files_encryption', 'settings' ); -OCP\App::registerPersonal( 'files_encryption', 'settings-personal' ); +OCP\App::registerAdmin('files_encryption', 'settings-admin'); +OCP\App::registerPersonal('files_encryption', 'settings-personal'); + diff --git a/apps/files_encryption/appinfo/database.xml b/apps/files_encryption/appinfo/database.xml index d294c35d63..4587930da0 100644 --- a/apps/files_encryption/appinfo/database.xml +++ b/apps/files_encryption/appinfo/database.xml @@ -18,6 +18,21 @@ text true 64 + What client-side / server-side configuration is used + + + recovery_enabled + integer + true + 0 + Whether encryption key recovery is enabled + + + migration_status + integer + true + 0 + Whether encryption migration has been performed diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml index 39ea155488..ea8f6cf6f3 100644 --- a/apps/files_encryption/appinfo/info.xml +++ b/apps/files_encryption/appinfo/info.xml @@ -2,9 +2,9 @@ files_encryption Encryption - Server side encryption of files. Warning: You will lose your data if you enable this App and forget your password. Encryption is not yet compatible with LDAP. + WARNING: This is a preview release of the new ownCloud 5 encryption system. Testing and feedback is very welcome but don't use this in production yet. Encryption is not yet compatible with LDAP. AGPL - Sam Tuke + Sam Tuke, Bjoern Schiessle, Florin Peter 4 true diff --git a/apps/files_encryption/appinfo/spec.txt b/apps/files_encryption/appinfo/spec.txt index 2d22dffe08..ddd3983a9e 100644 --- a/apps/files_encryption/appinfo/spec.txt +++ b/apps/files_encryption/appinfo/spec.txt @@ -9,6 +9,57 @@ Encrypted files [encrypted data string][delimiter][IV][padding] [anhAAjAmcGXqj1X9g==][00iv00][MSHU5N5gECP7aAg7][xx] (square braces added) + +- Directory structure: + - Encrypted user data (catfiles) are stored in the usual /data/user/files dir + - Keyfiles are stored in /data/user/files_encryption/keyfiles + - Sharekey are stored in /data/user/files_encryption/share-files + +- File extensions: + - Catfiles have to keep the file extension of the original file, pre-encryption + - Keyfiles use .keyfile + - Sharekeys have .shareKey + +Shared files +------------ + +Shared files have a centrally stored catfile and keyfile, and one sharekey for +each user that shares it. + +When sharing is used, a different encryption method is used to encrypt the +keyfile (openssl_seal). Although shared files have a keyfile, its contents +use a different format therefore. + +Each time a shared file is edited or deleted, all sharekeys for users sharing +that file must have their sharekeys changed also. The keyfile and catfile +however need only changing in the owners files, as there is only one copy of +these. + +Publicly shared files (public links) +------------------------------------ + +Files shared via public links use a separate system user account called 'ownCloud'. All public files are shared to that user's public key, and the private key is used to access the files when the public link is used in browser. + +This means that files shared via public links are accessible only to users who know the shared URL, or to admins who know the 'ownCloud' user password. + +Lost password recovery +---------------------- + +In order to enable users to read their encrypted files in the event of a password loss/reset scenario, administrators can choose to enable a 'recoveryAdmin' account. This is a user that all user files will automatically be shared to of the option is enabled. This allows the recoveryAdmin user to generate new keyfiles for the user. By default the UID of the recoveryAdmin is 'recoveryAdmin'. + +OC_FilesystemView +----------------- + +files_encryption deals extensively with paths and the filesystem. In order to minimise bugs, it makes calls to filesystem methods in a consistent way: OC_FilesystemView{} objects always use '/' as their root, and specify paths each time particular methods are called. e.g. do this: + +$view->file_exists( 'path/to/file' ); + +Not: + +$view->chroot( 'path/to' ); +$view->file_exists( 'file' ); + +Using this convention means that $view objects are more predictable and less likely to break. Problems with paths are the #1 cause of bugs in this app, and consistent $view handling is an important way to prevent them. Notes ----- @@ -16,4 +67,11 @@ Notes - The user passphrase is required in order to set up or upgrade the app. New keypair generation, and the re-encryption of legacy encrypted files requires it. Therefore an appinfo/update.php script cannot be used, and upgrade logic - is handled in the login hook listener. \ No newline at end of file + is handled in the login hook listener. Therefore each time the user logs in + their files are scanned to detect unencrypted and legacy encrypted files, and + they are (re)encrypted as necessary. This may present a performance issue; we + need to monitor this. +- When files are saved to ownCloud via WebDAV, a .part file extension is used so + that the file isn't cached before the upload has been completed. .part files + are not compatible with files_encrytion's key management system however, so + we have to always sanitise such paths manually before using them. \ No newline at end of file diff --git a/apps/files_encryption/appinfo/version b/apps/files_encryption/appinfo/version index 1d71ef9744..bd73f47072 100644 --- a/apps/files_encryption/appinfo/version +++ b/apps/files_encryption/appinfo/version @@ -1 +1 @@ -0.3 \ No newline at end of file +0.4 diff --git a/apps/files_encryption/css/settings-personal.css b/apps/files_encryption/css/settings-personal.css new file mode 100644 index 0000000000..4ee0acc976 --- /dev/null +++ b/apps/files_encryption/css/settings-personal.css @@ -0,0 +1,10 @@ +/* Copyright (c) 2013, Sam Tuke, + This file is licensed under the Affero General Public License version 3 or later. + See the COPYING-README file. */ + +#encryptAllError +, #encryptAllSuccess +, #recoveryEnabledError +, #recoveryEnabledSuccess { + display: none; +} \ No newline at end of file diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 2731d5a92f..d1b08a0b97 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -23,10 +23,11 @@ namespace OCA\Encryption; +use OC\Files\Filesystem; + /** * Class for hook specific logic */ - class Hooks { // TODO: use passphrase for encrypting private key that is separate to @@ -36,156 +37,489 @@ class Hooks { * @brief Startup encryption backend upon user login * @note This method should never be called for users using client side encryption */ - public static function login( $params ) { - + public static function login($params) { + // Manually initialise Filesystem{} singleton with correct // fake root path, in order to avoid fatal webdav errors - \OC\Files\Filesystem::init( $params['uid'], $params['uid'] . '/' . 'files' . '/' ); - - $view = new \OC_FilesystemView( '/' ); + // NOTE: disabled because this give errors on webdav! + //\OC\Files\Filesystem::init( $params['uid'], '/' . 'files' . '/' ); - $util = new Util( $view, $params['uid'] ); - - // Check files_encryption infrastructure is ready for action - if ( ! $util->ready() ) { - - \OC_Log::write( 'Encryption library', 'User account "' . $params['uid'] . '" is not ready for encryption; configuration started', \OC_Log::DEBUG ); - - return $util->setupServerSide( $params['password'] ); + $view = new \OC_FilesystemView('/'); + // ensure filesystem is loaded + if(!\OC\Files\Filesystem::$loaded) { + \OC_Util::setupFS($params['uid']); } - - \OC_FileProxy::$enabled = false; - - $encryptedKey = Keymanager::getPrivateKey( $view, $params['uid'] ); - - \OC_FileProxy::$enabled = true; - - $privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] ); - - $session = new Session(); - - $session->setPrivateKey( $privateKey, $params['uid'] ); - - $view1 = new \OC_FilesystemView( '/' . $params['uid'] ); - - // Set legacy encryption key if it exists, to support - // depreciated encryption system - if ( - $view1->file_exists( 'encryption.key' ) - && $encLegacyKey = $view1->file_get_contents( 'encryption.key' ) - ) { - - $plainLegacyKey = Crypt::legacyDecrypt( $encLegacyKey, $params['password'] ); - - $session->setLegacyKey( $plainLegacyKey ); - + + $util = new Util($view, $params['uid']); + + // setup user, if user not ready force relogin + if (Helper::setupUser($util, $params['password']) === false) { + return false; } - - $publicKey = Keymanager::getPublicKey( $view, $params['uid'] ); - - // Encrypt existing user files: - // This serves to upgrade old versions of the encryption - // app (see appinfo/spec.txt) - if ( - $util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] ) - ) { - - \OC_Log::write( - 'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" started at login' - , \OC_Log::INFO - ); - + + $encryptedKey = Keymanager::getPrivateKey($view, $params['uid']); + + $privateKey = Crypt::symmetricDecryptFileContent($encryptedKey, $params['password']); + + $session = new \OCA\Encryption\Session($view); + + $session->setPrivateKey($privateKey, $params['uid']); + + // Check if first-run file migration has already been performed + $migrationCompleted = $util->getMigrationStatus(); + + // If migration not yet done + if (!$migrationCompleted) { + + $userView = new \OC_FilesystemView('/' . $params['uid']); + + // Set legacy encryption key if it exists, to support + // depreciated encryption system + if ( + $userView->file_exists('encryption.key') + && $encLegacyKey = $userView->file_get_contents('encryption.key') + ) { + + $plainLegacyKey = Crypt::legacyBlockDecrypt($encLegacyKey, $params['password']); + + $session->setLegacyKey($plainLegacyKey); + + } + + // Encrypt existing user files: + // This serves to upgrade old versions of the encryption + // app (see appinfo/spec.txt) + if ( + $util->encryptAll('/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password']) + ) { + + \OC_Log::write( + 'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed' + , \OC_Log::INFO + ); + + } + + // Register successful migration in DB + $util->setMigrationStatus(1); + } return true; } - + + /** + * @brief setup encryption backend upon user created + * @note This method should never be called for users using client side encryption + */ + public static function postCreateUser($params) { + $view = new \OC_FilesystemView('/'); + + $util = new Util($view, $params['uid']); + + Helper::setupUser($util, $params['password']); + } + + /** + * @brief cleanup encryption backend upon user deleted + * @note This method should never be called for users using client side encryption + */ + public static function postDeleteUser($params) { + $view = new \OC_FilesystemView('/'); + + // cleanup public key + $publicKey = '/public-keys/' . $params['uid'] . '.public.key'; + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $view->unlink($publicKey); + + \OC_FileProxy::$enabled = $proxyStatus; + } + /** * @brief Change a user's encryption passphrase * @param array $params keys: uid, password */ - public static function setPassphrase( $params ) { - + public static function setPassphrase($params) { + // Only attempt to change passphrase if server-side encryption // is in use (client-side encryption does not have access to // the necessary keys) - if ( Crypt::mode() == 'server' ) { - - $session = new Session(); - - // Get existing decrypted private key - $privateKey = $session->getPrivateKey(); - - // Encrypt private key with new user pwd as passphrase - $encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $privateKey, $params['password'] ); - - // Save private key - Keymanager::setPrivateKey( $encryptedPrivateKey ); - - // NOTE: Session does not need to be updated as the - // private key has not changed, only the passphrase - // used to decrypt it has changed - - } - - } - - /** - * @brief update the encryption key of the file uploaded by the client - */ - public static function updateKeyfile( $params ) { - - if ( Crypt::mode() == 'client' ) { - - if ( isset( $params['properties']['key'] ) ) { - - $view = new \OC_FilesystemView( '/' ); - $userId = \OCP\User::getUser(); - - Keymanager::setFileKey( $view, $params['path'], $userId, $params['properties']['key'] ); - - } else { - - \OC_Log::write( - 'Encryption library', "Client side encryption is enabled but the client doesn't provide a encryption key for the file!" - , \OC_Log::ERROR - ); - - error_log( "Client side encryption is enabled but the client doesn't provide an encryption key for the file!" ); - + if (Crypt::mode() === 'server') { + + if ($params['uid'] === \OCP\User::getUser()) { + + $view = new \OC_FilesystemView('/'); + + $session = new \OCA\Encryption\Session($view); + + // Get existing decrypted private key + $privateKey = $session->getPrivateKey(); + + // Encrypt private key with new user pwd as passphrase + $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password']); + + // Save private key + Keymanager::setPrivateKey($encryptedPrivateKey); + + // NOTE: Session does not need to be updated as the + // private key has not changed, only the passphrase + // used to decrypt it has changed + + + } else { // admin changed the password for a different user, create new keys and reencrypt file keys + + $user = $params['uid']; + $recoveryPassword = $params['recoveryPassword']; + $newUserPassword = $params['password']; + + $view = new \OC_FilesystemView('/'); + + // make sure that the users home is mounted + \OC\Files\Filesystem::initMountPoints($user); + + $keypair = Crypt::createKeypair(); + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // Save public key + $view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']); + + // Encrypt private key empty passphrase + $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword); + + // Save private key + $view->file_put_contents( + '/' . $user . '/files_encryption/' . $user . '.private.key', $encryptedPrivateKey); + + if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files + $util = new Util($view, $user); + $util->recoverUsersFiles($recoveryPassword); + } + + \OC_FileProxy::$enabled = $proxyStatus; } - } - } - - /** - * @brief + + /* + * @brief check if files can be encrypted to every user. */ - public static function postShared( $params ) { - } - /** - * @brief + * @param $params */ - public static function preUnshare( $params ) { - - // Delete existing catfile - - // Generate new catfile and env keys - - // Save env keys to user folders + public static function preShared($params) { + + $users = array(); + $view = new \OC\Files\View('/public-keys/'); + + switch ($params['shareType']) { + case \OCP\Share::SHARE_TYPE_USER: + $users[] = $params['shareWith']; + break; + case \OCP\Share::SHARE_TYPE_GROUP: + $users = \OC_Group::usersInGroup($params['shareWith']); + break; + } + + $error = false; + foreach ($users as $user) { + if (!$view->file_exists($user . '.public.key')) { + $error = true; + break; + } + } + + if ($error) // Set flag var 'run' to notify emitting + // script that hook execution failed + { + $params['run']->run = false; + } + // TODO: Make sure files_sharing provides user + // feedback on failed share } - + /** - * @brief + * @brief */ - public static function preUnshareAll( $params ) { - - trigger_error( "preUnshareAll" ); - + public static function postShared($params) { + + // NOTE: $params has keys: + // [itemType] => file + // itemSource -> int, filecache file ID + // [parent] => + // [itemTarget] => /13 + // shareWith -> string, uid of user being shared to + // fileTarget -> path of file being shared + // uidOwner -> owner of the original file being shared + // [shareType] => 0 + // [shareWith] => test1 + // [uidOwner] => admin + // [permissions] => 17 + // [fileSource] => 13 + // [fileTarget] => /test8 + // [id] => 10 + // [token] => + // [run] => whether emitting script should continue to run + // TODO: Should other kinds of item be encrypted too? + + if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { + + $view = new \OC_FilesystemView('/'); + $session = new \OCA\Encryption\Session($view); + $userId = \OCP\User::getUser(); + $util = new Util($view, $userId); + $path = $util->fileIdToPath($params['itemSource']); + + $share = $util->getParentFromShare($params['id']); + //if parent is set, then this is a re-share action + if ($share['parent'] !== null) { + + // get the parent from current share + $parent = $util->getShareParent($params['parent']); + + // if parent is file the it is an 1:1 share + if ($parent['item_type'] === 'file') { + + // prefix path with Shared + $path = '/Shared' . $parent['file_target']; + } else { + + // NOTE: parent is folder but shared was a file! + // we try to rebuild the missing path + // some examples we face here + // user1 share folder1 with user2 folder1 has + // the following structure + // /folder1/subfolder1/subsubfolder1/somefile.txt + // user2 re-share subfolder2 with user3 + // user3 re-share somefile.txt user4 + // so our path should be + // /Shared/subfolder1/subsubfolder1/somefile.txt + // while user3 is sharing + + if ($params['itemType'] === 'file') { + // get target path + $targetPath = $util->fileIdToPath($params['fileSource']); + $targetPathSplit = array_reverse(explode('/', $targetPath)); + + // init values + $path = ''; + $sharedPart = ltrim($parent['file_target'], '/'); + + // rebuild path + foreach ($targetPathSplit as $pathPart) { + if ($pathPart !== $sharedPart) { + $path = '/' . $pathPart . $path; + } else { + break; + } + } + // prefix path with Shared + $path = '/Shared' . $parent['file_target'] . $path; + } else { + // prefix path with Shared + $path = '/Shared' . $parent['file_target'] . $params['fileTarget']; + } + } + } + + $sharingEnabled = \OCP\Share::isEnabled(); + + // get the path including mount point only if not a shared folder + if(strncmp($path, '/Shared' , strlen('/Shared') !== 0)) { + // get path including the the storage mount point + $path = $util->getPathWithMountPoint($params['itemSource']); + } + + // if a folder was shared, get a list of all (sub-)folders + if ($params['itemType'] === 'folder') { + $allFiles = $util->getAllFiles($path); + } else { + $allFiles = array($path); + } + + foreach ($allFiles as $path) { + $usersSharing = $util->getSharingUsersArray($sharingEnabled, $path); + $util->setSharedFileKeyfiles($session, $usersSharing, $path); + } + } + } + + /** + * @brief + */ + public static function postUnshare($params) { + + // NOTE: $params has keys: + // [itemType] => file + // [itemSource] => 13 + // [shareType] => 0 + // [shareWith] => test1 + // [itemParent] => + + if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { + + $view = new \OC_FilesystemView('/'); + $userId = \OCP\User::getUser(); + $util = new Util($view, $userId); + $path = $util->fileIdToPath($params['itemSource']); + + // check if this is a re-share + if ($params['itemParent']) { + + // get the parent from current share + $parent = $util->getShareParent($params['itemParent']); + + // get target path + $targetPath = $util->fileIdToPath($params['itemSource']); + $targetPathSplit = array_reverse(explode('/', $targetPath)); + + // init values + $path = ''; + $sharedPart = ltrim($parent['file_target'], '/'); + + // rebuild path + foreach ($targetPathSplit as $pathPart) { + if ($pathPart !== $sharedPart) { + $path = '/' . $pathPart . $path; + } else { + break; + } + } + + // prefix path with Shared + $path = '/Shared' . $parent['file_target'] . $path; + } + + // for group shares get a list of the group members + if ($params['shareType'] === \OCP\Share::SHARE_TYPE_GROUP) { + $userIds = \OC_Group::usersInGroup($params['shareWith']); + } else { + if ($params['shareType'] === \OCP\Share::SHARE_TYPE_LINK) { + $userIds = array($util->getPublicShareKeyId()); + } else { + $userIds = array($params['shareWith']); + } + } + + // get the path including mount point only if not a shared folder + if(strncmp($path, '/Shared' , strlen('/Shared') !== 0)) { + // get path including the the storage mount point + $path = $util->getPathWithMountPoint($params['itemSource']); + } + + // if we unshare a folder we need a list of all (sub-)files + if ($params['itemType'] === 'folder') { + $allFiles = $util->getAllFiles( $path ); + } else { + $allFiles = array($path); + } + + foreach ($allFiles as $path) { + + // check if the user still has access to the file, otherwise delete share key + $sharingUsers = $util->getSharingUsersArray(true, $path); + + // Unshare every user who no longer has access to the file + $delUsers = array_diff($userIds, $sharingUsers); + + // delete share key + Keymanager::delShareKey($view, $delUsers, $path); + } + + } + } + + /** + * @brief after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing + * @param array with oldpath and newpath + * + * This function is connected to the rename signal of OC_Filesystem and adjust the name and location + * of the stored versions along the actual file + */ + public static function postRename($params) { + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $view = new \OC_FilesystemView('/'); + $session = new \OCA\Encryption\Session($view); + $userId = \OCP\User::getUser(); + $util = new Util($view, $userId); + + // Format paths to be relative to user files dir + $oldKeyfilePath = \OC\Files\Filesystem::normalizePath( + $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['oldpath']); + $newKeyfilePath = \OC\Files\Filesystem::normalizePath( + $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $params['newpath']); + + // add key ext if this is not an folder + if (!$view->is_dir($oldKeyfilePath)) { + $oldKeyfilePath .= '.key'; + $newKeyfilePath .= '.key'; + + // handle share-keys + $localKeyPath = $view->getLocalFile($userId . '/files_encryption/share-keys/' . $params['oldpath']); + $matches = glob(preg_quote($localKeyPath) . '*.shareKey'); + foreach ($matches as $src) { + $dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src)); + + // create destination folder if not exists + if (!file_exists(dirname($dst))) { + mkdir(dirname($dst), 0750, true); + } + + rename($src, $dst); + } + + } else { + // handle share-keys folders + $oldShareKeyfilePath = \OC\Files\Filesystem::normalizePath( + $userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['oldpath']); + $newShareKeyfilePath = \OC\Files\Filesystem::normalizePath( + $userId . '/' . 'files_encryption' . '/' . 'share-keys' . '/' . $params['newpath']); + + // create destination folder if not exists + if (!$view->file_exists(dirname($newShareKeyfilePath))) { + $view->mkdir(dirname($newShareKeyfilePath), 0750, true); + } + + $view->rename($oldShareKeyfilePath, $newShareKeyfilePath); + } + + // Rename keyfile so it isn't orphaned + if ($view->file_exists($oldKeyfilePath)) { + + // create destination folder if not exists + if (!$view->file_exists(dirname($newKeyfilePath))) { + $view->mkdir(dirname($newKeyfilePath), 0750, true); + } + + $view->rename($oldKeyfilePath, $newKeyfilePath); + } + + // build the path to the file + $newPath = '/' . $userId . '/files' . $params['newpath']; + $newPathRelative = $params['newpath']; + + if ($util->fixFileSize($newPath)) { + // get sharing app state + $sharingEnabled = \OCP\Share::isEnabled(); + + // get users + $usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative); + + // update sharing-keys + $util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative); + } + + \OC_FileProxy::$enabled = $proxyStatus; } - } diff --git a/apps/files_encryption/js/settings-admin.js b/apps/files_encryption/js/settings-admin.js new file mode 100644 index 0000000000..7c1866445e --- /dev/null +++ b/apps/files_encryption/js/settings-admin.js @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2013, Sam Tuke , Robin Appelman + * + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +OC.msg={ + startSaving:function(selector){ + $(selector) + .html( t('settings', 'Saving...') ) + .removeClass('success') + .removeClass('error') + .stop(true, true) + .show(); + }, + finishedSaving:function(selector, data){ + if( data.status === "success" ){ + $(selector).html( data.data.message ) + .addClass('success') + .stop(true, true) + .delay(3000) + .fadeOut(900); + }else{ + $(selector).html( data.data.message ).addClass('error'); + } + } +}; + +$(document).ready(function(){ + // Trigger ajax on recoveryAdmin status change + var enabledStatus = $('#adminEnableRecovery').val(); + + $('input:password[name="recoveryPassword"]').keyup(function(event) { + var recoveryPassword = $( '#recoveryPassword' ).val(); + var checkedButton = $('input:radio[name="adminEnableRecovery"]:checked').val(); + var uncheckedValue = (1+parseInt(checkedButton)) % 2; + if (recoveryPassword != '' ) { + $('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').removeAttr("disabled"); + } else { + $('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').attr("disabled", "true"); + } + }); + + $( 'input:radio[name="adminEnableRecovery"]' ).change( + function() { + var recoveryStatus = $( this ).val(); + var oldStatus = (1+parseInt(recoveryStatus)) % 2; + var recoveryPassword = $( '#recoveryPassword' ).val(); + $.post( + OC.filePath( 'files_encryption', 'ajax', 'adminrecovery.php' ) + , { adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword } + , function( result ) { + if (result.status === "error") { + OC.Notification.show(t('admin', result.data.message)); + $('input:radio[name="adminEnableRecovery"][value="'+oldStatus.toString()+'"]').attr("checked", "true"); + } else { + OC.Notification.hide(); + if (recoveryStatus === "0") { + $('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true"); + $('input:password[name="changeRecoveryPassword"]').attr("disabled", "true"); + $('input:password[name="changeRecoveryPassword"]').val(""); + } else { + $('input:password[name="changeRecoveryPassword"]').removeAttr("disabled"); + } + } + } + ); + } + ); + + // change recovery password + + $('input:password[name="changeRecoveryPassword"]').keyup(function(event) { + var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val(); + var newRecoveryPassword = $('input:password[id="newRecoveryPassword"]').val(); + if (newRecoveryPassword != '' && oldRecoveryPassword != '' ) { + $('button:button[name="submitChangeRecoveryKey"]').removeAttr("disabled"); + } else { + $('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true"); + } + }); + + + $('button:button[name="submitChangeRecoveryKey"]').click(function() { + var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val(); + var newRecoveryPassword = $('input:password[id="newRecoveryPassword"]').val(); + OC.msg.startSaving('#encryption .msg'); + $.post( + OC.filePath( 'files_encryption', 'ajax', 'changeRecoveryPassword.php' ) + , { oldPassword: oldRecoveryPassword, newPassword: newRecoveryPassword } + , function( data ) { + if (data.status == "error") { + OC.msg.finishedSaving('#encryption .msg', data); + } else { + OC.msg.finishedSaving('#encryption .msg', data); + } + } + ); + }); + +}); \ No newline at end of file diff --git a/apps/files_encryption/js/settings-personal.js b/apps/files_encryption/js/settings-personal.js new file mode 100644 index 0000000000..312b672ad4 --- /dev/null +++ b/apps/files_encryption/js/settings-personal.js @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2013, Sam Tuke + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +$(document).ready(function(){ + // Trigger ajax on recoveryAdmin status change + $( 'input:radio[name="userEnableRecovery"]' ).change( + function() { + + // Hide feedback messages in case they're already visible + $('#recoveryEnabledSuccess').hide(); + $('#recoveryEnabledError').hide(); + + var recoveryStatus = $( this ).val(); + + $.post( + OC.filePath( 'files_encryption', 'ajax', 'userrecovery.php' ) + , { userEnableRecovery: recoveryStatus } + , function( data ) { + if ( data.status == "success" ) { + $('#recoveryEnabledSuccess').show(); + } else { + $('#recoveryEnabledError').show(); + } + } + ); + // Ensure page is not reloaded on form submit + return false; + } + ); + + $("#encryptAll").click( + function(){ + + // Hide feedback messages in case they're already visible + $('#encryptAllSuccess').hide(); + $('#encryptAllError').hide(); + + var userPassword = $( '#userPassword' ).val(); + var encryptAll = $( '#encryptAll' ).val(); + + $.post( + OC.filePath( 'files_encryption', 'ajax', 'encryptall.php' ) + , { encryptAll: encryptAll, userPassword: userPassword } + , function( data ) { + if ( data.status == "success" ) { + $('#encryptAllSuccess').show(); + } else { + $('#encryptAllError').show(); + } + } + ); + // Ensure page is not reloaded on form submit + return false; + } + + ); +}); \ No newline at end of file diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js deleted file mode 100644 index 0be857bb73..0000000000 --- a/apps/files_encryption/js/settings.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2011, Robin Appelman - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */ - - -$(document).ready(function(){ - $('#encryption_blacklist').multiSelect({ - oncheck:blackListChange, - onuncheck:blackListChange, - createText:'...' - }); - - function blackListChange(){ - var blackList=$('#encryption_blacklist').val().join(','); - OC.AppConfig.setValue('files_encryption','type_blacklist',blackList); - } -}) \ No newline at end of file diff --git a/apps/files_encryption/l10n/ar.php b/apps/files_encryption/l10n/ar.php index c8a475afd6..1adc158c6b 100644 --- a/apps/files_encryption/l10n/ar.php +++ b/apps/files_encryption/l10n/ar.php @@ -1,7 +1,4 @@ "التشفير", -"File encryption is enabled." => "تشفير الملفات فعال.", -"The following file types will not be encrypted:" => "الملفات الاتية لن يتم تشفيرها:", -"Exclude the following file types from encryption:" => "إستثناء أنواع الملفات الاتية من التشفير: ", -"None" => "لا شيء" +"Saving..." => "جاري الحفظ...", +"Encryption" => "التشفير" ); diff --git a/apps/files_encryption/l10n/bg_BG.php b/apps/files_encryption/l10n/bg_BG.php index 07a97f5f8a..f21f7641c1 100644 --- a/apps/files_encryption/l10n/bg_BG.php +++ b/apps/files_encryption/l10n/bg_BG.php @@ -1,4 +1,4 @@ "Криптиране", -"None" => "Няма" +"Saving..." => "Записване...", +"Encryption" => "Криптиране" ); diff --git a/apps/files_encryption/l10n/bn_BD.php b/apps/files_encryption/l10n/bn_BD.php index 43767d5651..068de46e7a 100644 --- a/apps/files_encryption/l10n/bn_BD.php +++ b/apps/files_encryption/l10n/bn_BD.php @@ -1,4 +1,4 @@ "সংকেতায়ন", -"None" => "কোনটিই নয়" +"Saving..." => "সংরক্ষণ করা হচ্ছে..", +"Encryption" => "সংকেতায়ন" ); diff --git a/apps/files_encryption/l10n/ca.php b/apps/files_encryption/l10n/ca.php index 0c661353a7..46e91d1f07 100644 --- a/apps/files_encryption/l10n/ca.php +++ b/apps/files_encryption/l10n/ca.php @@ -1,7 +1,4 @@ "Encriptatge", -"File encryption is enabled." => "L'encriptació de fitxers està activada.", -"The following file types will not be encrypted:" => "Els tipus de fitxers següents no s'encriptaran:", -"Exclude the following file types from encryption:" => "Exclou els tipus de fitxers següents de l'encriptatge:", -"None" => "Cap" +"Saving..." => "Desant...", +"Encryption" => "Xifrat" ); diff --git a/apps/files_encryption/l10n/cs_CZ.php b/apps/files_encryption/l10n/cs_CZ.php index d225688a07..f9b2dd06b6 100644 --- a/apps/files_encryption/l10n/cs_CZ.php +++ b/apps/files_encryption/l10n/cs_CZ.php @@ -1,7 +1,4 @@ "Šifrování", -"File encryption is enabled." => "Šifrování je povoleno.", -"The following file types will not be encrypted:" => "Následující typy souborů nebudou šifrovány:", -"Exclude the following file types from encryption:" => "Vyjmout následující typy souborů ze šifrování:", -"None" => "Žádné" +"Saving..." => "Ukládám...", +"Encryption" => "Šifrování" ); diff --git a/apps/files_encryption/l10n/cy_GB.php b/apps/files_encryption/l10n/cy_GB.php new file mode 100644 index 0000000000..6e18a7913c --- /dev/null +++ b/apps/files_encryption/l10n/cy_GB.php @@ -0,0 +1,4 @@ + "Yn cadw...", +"Encryption" => "Amgryptiad" +); diff --git a/apps/files_encryption/l10n/da.php b/apps/files_encryption/l10n/da.php index b085381ea7..1cd43390aa 100644 --- a/apps/files_encryption/l10n/da.php +++ b/apps/files_encryption/l10n/da.php @@ -1,7 +1,4 @@ "Kryptering", -"File encryption is enabled." => "Fil kryptering aktiveret.", -"The following file types will not be encrypted:" => "De følgende filtyper vil ikke blive krypteret:", -"Exclude the following file types from encryption:" => "Ekskluder de følgende fil typer fra kryptering:", -"None" => "Ingen" +"Saving..." => "Gemmer...", +"Encryption" => "Kryptering" ); diff --git a/apps/files_encryption/l10n/de.php b/apps/files_encryption/l10n/de.php index cdcd8a40b2..2ab77f480c 100644 --- a/apps/files_encryption/l10n/de.php +++ b/apps/files_encryption/l10n/de.php @@ -1,7 +1,4 @@ "Verschlüsselung", -"File encryption is enabled." => "Dateiverschlüsselung ist aktiviert", -"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:", -"Exclude the following file types from encryption:" => "Schließe die folgenden Dateitypen von der Verschlüsselung aus:", -"None" => "Keine" +"Saving..." => "Speichern...", +"Encryption" => "Verschlüsselung" ); diff --git a/apps/files_encryption/l10n/de_DE.php b/apps/files_encryption/l10n/de_DE.php index 4f08b98eb2..884ed3c50b 100644 --- a/apps/files_encryption/l10n/de_DE.php +++ b/apps/files_encryption/l10n/de_DE.php @@ -1,7 +1,19 @@ "Der Wiederherstellungsschlüssel wurde erfolgreich aktiviert.", +"Could not enable recovery key. Please check your recovery key password!" => "Der Wiederherstellungsschlüssel konnte nicht aktiviert werden. Bitte überprüfen Sie das Passwort für den Wiederherstellungsschlüssel!", +"Recovery key successfully disabled" => "Der Wiederherstellungsschlüssel wurde erfolgreich deaktiviert.", +"Could not disable recovery key. Please check your recovery key password!" => "Der Wiederherstellungsschlüssel konnte nicht deaktiviert werden. Bitte überprüfen Sie das Passwort für den Wiederherstellungsschlüssel!", +"Password successfully changed." => "Das Passwort wurde erfolgreich geändert.", +"Could not change the password. Maybe the old password was not correct." => "Das Passwort konnte nicht geändert werden. Vielleicht war das alte Passwort nicht richtig.", +"Saving..." => "Speichern...", "Encryption" => "Verschlüsselung", -"File encryption is enabled." => "Datei-Verschlüsselung ist aktiviert", -"The following file types will not be encrypted:" => "Die folgenden Dateitypen werden nicht verschlüsselt:", -"Exclude the following file types from encryption:" => "Die folgenden Dateitypen von der Verschlüsselung ausnehmen:", -"None" => "Keine" +"Recovery account password" => "Account-Passwort wiederherstellen", +"Enabled" => "Aktiviert", +"Disabled" => "Deaktiviert", +"Old Recovery account password" => "Altes Passwort für die Account-Wiederherstellung", +"New Recovery account password" => "Neues Passwort für die Account-Wiederherstellung", +"Change Password" => "Passwort ändern", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "Durch die Aktivierung dieser Option haben Sie die Möglichkeit, wieder auf Ihre verschlüsselten Dateien zugreifen zu können, wenn Sie Ihr Passwort verloren haben.", +"File recovery settings updated" => "Die Einstellungen für die Dateiwiederherstellung wurden aktualisiert.", +"Could not update file recovery" => "Die Dateiwiederherstellung konnte nicht aktualisiert werden." ); diff --git a/apps/files_encryption/l10n/el.php b/apps/files_encryption/l10n/el.php index 0031a73194..7067799cd2 100644 --- a/apps/files_encryption/l10n/el.php +++ b/apps/files_encryption/l10n/el.php @@ -1,7 +1,4 @@ "Κρυπτογράφηση", -"File encryption is enabled." => "Η κρυπτογράφηση αρχείων είναι ενεργή.", -"The following file types will not be encrypted:" => "Οι παρακάτω τύποι αρχείων δεν θα κρυπτογραφηθούν:", -"Exclude the following file types from encryption:" => "Εξαίρεση των παρακάτω τύπων αρχείων από την κρυπτογράφηση:", -"None" => "Καμία" +"Saving..." => "Γίνεται αποθήκευση...", +"Encryption" => "Κρυπτογράφηση" ); diff --git a/apps/files_encryption/l10n/eo.php b/apps/files_encryption/l10n/eo.php index 50847062c3..ea405fda1a 100644 --- a/apps/files_encryption/l10n/eo.php +++ b/apps/files_encryption/l10n/eo.php @@ -1,4 +1,4 @@ "Ĉifrado", -"None" => "Nenio" +"Saving..." => "Konservante...", +"Encryption" => "Ĉifrado" ); diff --git a/apps/files_encryption/l10n/es.php b/apps/files_encryption/l10n/es.php index 4ea87b92e7..58adbd716a 100644 --- a/apps/files_encryption/l10n/es.php +++ b/apps/files_encryption/l10n/es.php @@ -1,7 +1,22 @@ "Se ha habilitado la recuperación de archivos", +"Could not enable recovery key. Please check your recovery key password!" => "No se pudo habilitar la clave de recuperación. Por favor compruebe su contraseña.", +"Recovery key successfully disabled" => "Clave de recuperación deshabilitada", +"Could not disable recovery key. Please check your recovery key password!" => "No se pudo deshabilitar la clave de recuperación. Por favor compruebe su contraseña!", +"Password successfully changed." => "Su contraseña ha sido cambiada", +"Could not change the password. Maybe the old password was not correct." => "No se pudo cambiar la contraseña. Compruebe que la contraseña actual sea correcta.", +"Saving..." => "Guardando...", "Encryption" => "Cifrado", -"File encryption is enabled." => "La encriptacion de archivo esta activada.", -"The following file types will not be encrypted:" => "Los siguientes tipos de archivo no seran encriptados:", -"Exclude the following file types from encryption:" => "Excluir los siguientes tipos de archivo de la encriptacion:", -"None" => "Ninguno" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "Habilitar clave de recuperación de contraseñas ():", +"Recovery account password" => "Recuperar contraseña", +"Enabled" => "Habilitar", +"Disabled" => "Deshabilitado", +"Change encryption passwords recovery key:" => "Cambiar clave de cifrado de contraseñas:", +"Old Recovery account password" => "Contraseña de recuperación actual", +"New Recovery account password" => "Contraseña de recuperación nueva", +"Change Password" => "Cambiar contraseña", +"Enable password recovery by sharing all files with your administrator:" => "Habilitar recuperación de contraseña compartiendo todos los archivos con su administrador", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "Habilitar esta opción para poder acceder a sus archivos cifrados si pierde su contraseña", +"File recovery settings updated" => "Opciones de recuperación de archivos actualizada", +"Could not update file recovery" => "No se pudo actualizar la recuperación de archivos" ); diff --git a/apps/files_encryption/l10n/es_AR.php b/apps/files_encryption/l10n/es_AR.php index af522879e1..857186a55f 100644 --- a/apps/files_encryption/l10n/es_AR.php +++ b/apps/files_encryption/l10n/es_AR.php @@ -1,7 +1,4 @@ "Encriptación", -"File encryption is enabled." => "La encriptación de archivos no está habilitada", -"The following file types will not be encrypted:" => "Los siguientes tipos de archivos no serán encriptados", -"Exclude the following file types from encryption:" => "Excluir los siguientes tipos de archivos de encriptación:", -"None" => "Ninguno" +"Saving..." => "Guardando...", +"Encryption" => "Encriptación" ); diff --git a/apps/files_encryption/l10n/et_EE.php b/apps/files_encryption/l10n/et_EE.php index 0d189ac062..e762647f78 100644 --- a/apps/files_encryption/l10n/et_EE.php +++ b/apps/files_encryption/l10n/et_EE.php @@ -1,7 +1,22 @@ "Taastevõtme lubamine õnnestus", +"Could not enable recovery key. Please check your recovery key password!" => "Ei suutnud lubada taastevõtit. Palun kontrolli oma taastevõtme parooli!", +"Recovery key successfully disabled" => "Taastevõtme keelamine õnnestus", +"Could not disable recovery key. Please check your recovery key password!" => "Ei suuda keelata taastevõtit. Palun kontrolli oma taastevõtme parooli!", +"Password successfully changed." => "Parool edukalt vahetatud.", +"Could not change the password. Maybe the old password was not correct." => "Ei suutnud vahetada parooli. Võib-olla on vana parool valesti sisestatud.", +"Saving..." => "Salvestamine...", "Encryption" => "Krüpteerimine", -"File encryption is enabled." => "Faili krüpteerimine on sisse lülitatud.", -"The following file types will not be encrypted:" => "Järgnevaid failitüüpe ei krüpteerita:", -"Exclude the following file types from encryption:" => "Järgnevaid failitüüpe ei krüpteerita:", -"None" => "Pole" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "Luba krüpteerimise paroolide taastevõti (võimalda parooli jagamine taastevõtmesse):", +"Recovery account password" => "Konto taasteparool", +"Enabled" => "Sisse lülitatud", +"Disabled" => "Väljalülitatud", +"Change encryption passwords recovery key:" => "Muuda taaste võtme krüpteerimise paroole:", +"Old Recovery account password" => "Konto vana taaste parool", +"New Recovery account password" => "Konto uus taasteparool", +"Change Password" => "Muuda parooli", +"Enable password recovery by sharing all files with your administrator:" => "Luba parooli taaste jagades kõik failid administraatoriga:", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "Valiku lubamine võimaldab taastada ligipääsu krüpteeritud failidele kui parool on kadunud", +"File recovery settings updated" => "Faili taaste seaded uuendatud", +"Could not update file recovery" => "Ei suuda uuendada taastefaili" ); diff --git a/apps/files_encryption/l10n/eu.php b/apps/files_encryption/l10n/eu.php index 5a22b65728..253953e5c5 100644 --- a/apps/files_encryption/l10n/eu.php +++ b/apps/files_encryption/l10n/eu.php @@ -1,7 +1,4 @@ "Enkriptazioa", -"File encryption is enabled." => "Fitxategien enkriptazioa gaituta dago.", -"The following file types will not be encrypted:" => "Hurrengo fitxategi motak ez dira enkriptatuko:", -"Exclude the following file types from encryption:" => "Baztertu hurrengo fitxategi motak enkriptatzetik:", -"None" => "Bat ere ez" +"Saving..." => "Gordetzen...", +"Encryption" => "Enkriptazioa" ); diff --git a/apps/files_encryption/l10n/fa.php b/apps/files_encryption/l10n/fa.php index 7acf196b79..af2e36b2a8 100644 --- a/apps/files_encryption/l10n/fa.php +++ b/apps/files_encryption/l10n/fa.php @@ -1,7 +1,4 @@ "رمزگذاری", -"File encryption is enabled." => "رمزنگاری فایلها فعال شد.", -"The following file types will not be encrypted:" => "فایلهای زیر رمزنگاری نخواهند شد:", -"Exclude the following file types from encryption:" => "فایلهای زیر از رمزنگاری نادیده گرفته می شوند:", -"None" => "هیچ‌کدام" +"Saving..." => "در حال ذخیره سازی...", +"Encryption" => "رمزگذاری" ); diff --git a/apps/files_encryption/l10n/fi_FI.php b/apps/files_encryption/l10n/fi_FI.php index 6352d396b3..a00cc8ab96 100644 --- a/apps/files_encryption/l10n/fi_FI.php +++ b/apps/files_encryption/l10n/fi_FI.php @@ -1,7 +1,9 @@ "Salasana vaihdettiin onnistuneesti.", +"Could not change the password. Maybe the old password was not correct." => "Salasanan vaihto epäonnistui. Kenties vanha salasana oli väärin.", +"Saving..." => "Tallennetaan...", "Encryption" => "Salaus", -"File encryption is enabled." => "Tiedostojen salaus on käytössä.", -"The following file types will not be encrypted:" => "Seuraavia tiedostotyyppejä ei salata:", -"Exclude the following file types from encryption:" => "Älä salaa seuravia tiedostotyyppejä:", -"None" => "Ei mitään" +"Enabled" => "Käytössä", +"Disabled" => "Ei käytössä", +"Change Password" => "Vaihda salasana" ); diff --git a/apps/files_encryption/l10n/fr.php b/apps/files_encryption/l10n/fr.php index 88f1e4a393..98fb70691d 100644 --- a/apps/files_encryption/l10n/fr.php +++ b/apps/files_encryption/l10n/fr.php @@ -1,7 +1,4 @@ "Chiffrement", -"File encryption is enabled." => "Le chiffrement des fichiers est activé", -"The following file types will not be encrypted:" => "Les fichiers de types suivants ne seront pas chiffrés :", -"Exclude the following file types from encryption:" => "Ne pas chiffrer les fichiers dont les types sont les suivants :", -"None" => "Aucun" +"Saving..." => "Enregistrement...", +"Encryption" => "Chiffrement" ); diff --git a/apps/files_encryption/l10n/gl.php b/apps/files_encryption/l10n/gl.php index 3210f71545..ca93efba9a 100644 --- a/apps/files_encryption/l10n/gl.php +++ b/apps/files_encryption/l10n/gl.php @@ -1,7 +1,22 @@ "Activada satisfactoriamente a chave de recuperación", +"Could not enable recovery key. Please check your recovery key password!" => "Non foi posíbel activar a chave de recuperación. Comprobe o contrasinal da chave de recuperación!", +"Recovery key successfully disabled" => "Desactivada satisfactoriamente a chave de recuperación", +"Could not disable recovery key. Please check your recovery key password!" => "Non foi posíbel desactivar a chave de recuperación. Comprobe o contrasinal da chave de recuperación!", +"Password successfully changed." => "O contrasinal foi cambiado satisfactoriamente", +"Could not change the password. Maybe the old password was not correct." => "Non foi posíbel cambiar o contrasinal. Probabelmente o contrasinal antigo non é o correcto.", +"Saving..." => "Gardando...", "Encryption" => "Cifrado", -"File encryption is enabled." => "O cifrado de ficheiros está activado", -"The following file types will not be encrypted:" => "Os seguintes tipos de ficheiros non van seren cifrados:", -"Exclude the following file types from encryption:" => "Excluír os seguintes tipos de ficheiros do cifrado:", -"None" => "Ningún" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "Activar a chave de recuperación do cifrado de contrasinais (permite compartir a chave de recuperación):", +"Recovery account password" => "Recuperación do contrasinal da conta", +"Enabled" => "Activado", +"Disabled" => "Desactivado", +"Change encryption passwords recovery key:" => "Cambiar a chave de la recuperación do cifrado de contrasinais:", +"Old Recovery account password" => "Antigo contrasinal de recuperación da conta", +"New Recovery account password" => "Novo contrasinal de recuperación da conta", +"Change Password" => "Cambiar o contrasinal", +"Enable password recovery by sharing all files with your administrator:" => "Activar a recuperación de contrasinais compartindo todos os ficheiros co administrador:", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "Ao activar esta opción permitiráselle volver a obter acceso aos ficheiros cifrados se perde o contrasinal", +"File recovery settings updated" => "Actualizouse o ficheiro de axustes de recuperación", +"Could not update file recovery" => "Non foi posíbel actualizar o ficheiro de recuperación" ); diff --git a/apps/files_encryption/l10n/he.php b/apps/files_encryption/l10n/he.php index cbb74bfee9..7a80cfa2f9 100644 --- a/apps/files_encryption/l10n/he.php +++ b/apps/files_encryption/l10n/he.php @@ -1,4 +1,4 @@ "הצפנה", -"None" => "כלום" +"Saving..." => "שמירה…", +"Encryption" => "הצפנה" ); diff --git a/apps/files_encryption/l10n/hr.php b/apps/files_encryption/l10n/hr.php new file mode 100644 index 0000000000..9b9284ddc5 --- /dev/null +++ b/apps/files_encryption/l10n/hr.php @@ -0,0 +1,3 @@ + "Spremanje..." +); diff --git a/apps/files_encryption/l10n/hu_HU.php b/apps/files_encryption/l10n/hu_HU.php index 4043da108c..bf95c31f2c 100644 --- a/apps/files_encryption/l10n/hu_HU.php +++ b/apps/files_encryption/l10n/hu_HU.php @@ -1,7 +1,4 @@ "Titkosítás", -"File encryption is enabled." => "Az állományok titkosítása be van kapcsolva.", -"The following file types will not be encrypted:" => "A következő fájltípusok nem kerülnek titkosításra:", -"Exclude the following file types from encryption:" => "Zárjuk ki a titkosításból a következő fájltípusokat:", -"None" => "Egyik sem" +"Saving..." => "Mentés...", +"Encryption" => "Titkosítás" ); diff --git a/apps/files_encryption/l10n/id.php b/apps/files_encryption/l10n/id.php index 6044348e72..ad827b5379 100644 --- a/apps/files_encryption/l10n/id.php +++ b/apps/files_encryption/l10n/id.php @@ -1,7 +1,4 @@ "Enkripsi", -"File encryption is enabled." => "Enkripsi berkas aktif.", -"The following file types will not be encrypted:" => "Tipe berkas berikut tidak akan dienkripsi:", -"Exclude the following file types from encryption:" => "Kecualikan tipe berkas berikut dari enkripsi:", -"None" => "Tidak ada" +"Saving..." => "Menyimpan...", +"Encryption" => "Enkripsi" ); diff --git a/apps/files_encryption/l10n/is.php b/apps/files_encryption/l10n/is.php index bd964185c4..0f98c6bd3b 100644 --- a/apps/files_encryption/l10n/is.php +++ b/apps/files_encryption/l10n/is.php @@ -1,4 +1,4 @@ "Dulkóðun", -"None" => "Ekkert" +"Saving..." => "Er að vista ...", +"Encryption" => "Dulkóðun" ); diff --git a/apps/files_encryption/l10n/it.php b/apps/files_encryption/l10n/it.php index 9ab9bc492a..63ae4b70b4 100644 --- a/apps/files_encryption/l10n/it.php +++ b/apps/files_encryption/l10n/it.php @@ -1,7 +1,22 @@ "Chiave di ripristino abilitata correttamente", +"Could not enable recovery key. Please check your recovery key password!" => "Impossibile abilitare la chiave di ripristino. Verifica la password della chiave di ripristino.", +"Recovery key successfully disabled" => "Chiave di ripristinata disabilitata correttamente", +"Could not disable recovery key. Please check your recovery key password!" => "Impossibile disabilitare la chiave di ripristino. Verifica la password della chiave di ripristino.", +"Password successfully changed." => "Password modificata correttamente.", +"Could not change the password. Maybe the old password was not correct." => "Impossibile cambiare la password. Forse la vecchia password non era corretta.", +"Saving..." => "Salvataggio in corso...", "Encryption" => "Cifratura", -"File encryption is enabled." => "La cifratura dei file è abilitata.", -"The following file types will not be encrypted:" => "I seguenti tipi di file non saranno cifrati:", -"Exclude the following file types from encryption:" => "Escludi i seguenti tipi di file dalla cifratura:", -"None" => "Nessuna" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "Abilita la chiave di ripristino delle password di cifratura (consente di condividere la chiave di ripristino):", +"Recovery account password" => "Password di ripristino dell'account", +"Enabled" => "Abilitata", +"Disabled" => "Disabilitata", +"Change encryption passwords recovery key:" => "Cambia la chiave di ripristino delle password di cifratura:", +"Old Recovery account password" => "Vecchia password di ripristino dell'account", +"New Recovery account password" => "Nuova password di ripristino dell'account", +"Change Password" => "Modifica password", +"Enable password recovery by sharing all files with your administrator:" => "Abilita il ripristino della password condividendo tutti i file con l'amministratore:", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "L'abilitazione di questa opzione ti consentirà di ottenere nuovamente accesso ai tuoi file cifrati in caso di smarrimento della password", +"File recovery settings updated" => "Impostazioni di ripristino dei file aggiornate", +"Could not update file recovery" => "Impossibile aggiornare il ripristino dei file" ); diff --git a/apps/files_encryption/l10n/ja_JP.php b/apps/files_encryption/l10n/ja_JP.php index 35fba615ae..6d2d3e249c 100644 --- a/apps/files_encryption/l10n/ja_JP.php +++ b/apps/files_encryption/l10n/ja_JP.php @@ -1,7 +1,22 @@ "リカバリ用のキーは正常に有効化されました", +"Could not enable recovery key. Please check your recovery key password!" => "リカバリ用のキーを有効にできませんでした。リカバリ用のキーのパスワードを確認して下さい!", +"Recovery key successfully disabled" => "リカバリ用のキーを正常に無効化しました", +"Could not disable recovery key. Please check your recovery key password!" => "リカバリ用のキーを無効化できませんでした。リカバリ用のキーのパスワードを確認して下さい!", +"Password successfully changed." => "パスワードを変更できました。", +"Could not change the password. Maybe the old password was not correct." => "パスワードを変更できませんでした。古いパスワードが間違っているかもしれません。", +"Saving..." => "保存中...", "Encryption" => "暗号化", -"File encryption is enabled." => "ファイルの暗号化は有効です。", -"The following file types will not be encrypted:" => "次のファイルタイプは暗号化されません:", -"Exclude the following file types from encryption:" => "次のファイルタイプを暗号化から除外:", -"None" => "なし" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "暗号化パスワードの復旧キーを有効にする(復旧キーを共有することを許可):", +"Recovery account password" => "復旧アカウントのパスワード", +"Enabled" => "有効", +"Disabled" => "無効", +"Change encryption passwords recovery key:" => "復旧キーの暗号化パスワードを変更:", +"Old Recovery account password" => "古い復旧アカウントのパスワード", +"New Recovery account password" => "新しい復旧アカウントのパスワード", +"Change Password" => "パスワードを変更", +"Enable password recovery by sharing all files with your administrator:" => "管理者が全ての共有ファイルに対してパスワードによる復旧を有効にする:", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "このオプションを有効にすると、もしパスワードが分からなくなったとしても、暗号化されたファイルに再度アクセスすることが出来るようになります。", +"File recovery settings updated" => "ファイル復旧設定が更新されました", +"Could not update file recovery" => "ファイル復旧を更新できませんでした" ); diff --git a/apps/files_encryption/l10n/ka_GE.php b/apps/files_encryption/l10n/ka_GE.php index 0362c676f0..55a59f4434 100644 --- a/apps/files_encryption/l10n/ka_GE.php +++ b/apps/files_encryption/l10n/ka_GE.php @@ -1,7 +1,4 @@ "ენკრიპცია", -"File encryption is enabled." => "ფაილის ენკრიპცია ჩართულია.", -"The following file types will not be encrypted:" => "შემდეგი ფაილური ტიპების ენკრიპცია არ მოხდება:", -"Exclude the following file types from encryption:" => "ამოიღე შემდეგი ფაილის ტიპები ენკრიპციიდან:", -"None" => "არა" +"Saving..." => "შენახვა...", +"Encryption" => "ენკრიპცია" ); diff --git a/apps/files_encryption/l10n/ko.php b/apps/files_encryption/l10n/ko.php index bd1580578c..cf8149da3a 100644 --- a/apps/files_encryption/l10n/ko.php +++ b/apps/files_encryption/l10n/ko.php @@ -1,4 +1,4 @@ "암호화", -"None" => "없음" +"Saving..." => "저장 중...", +"Encryption" => "암호화" ); diff --git a/apps/files_encryption/l10n/ku_IQ.php b/apps/files_encryption/l10n/ku_IQ.php index 02c030014f..61b720372e 100644 --- a/apps/files_encryption/l10n/ku_IQ.php +++ b/apps/files_encryption/l10n/ku_IQ.php @@ -1,4 +1,4 @@ "نهێنیکردن", -"None" => "هیچ" +"Saving..." => "پاشکه‌وتده‌کات...", +"Encryption" => "نهێنیکردن" ); diff --git a/apps/files_encryption/l10n/lb.php b/apps/files_encryption/l10n/lb.php new file mode 100644 index 0000000000..77bad68173 --- /dev/null +++ b/apps/files_encryption/l10n/lb.php @@ -0,0 +1,3 @@ + "Speicheren..." +); diff --git a/apps/files_encryption/l10n/lt_LT.php b/apps/files_encryption/l10n/lt_LT.php index 67769c8f36..6bc80ff44a 100644 --- a/apps/files_encryption/l10n/lt_LT.php +++ b/apps/files_encryption/l10n/lt_LT.php @@ -1,4 +1,4 @@ "Šifravimas", -"None" => "Nieko" +"Saving..." => "Saugoma...", +"Encryption" => "Šifravimas" ); diff --git a/apps/files_encryption/l10n/lv.php b/apps/files_encryption/l10n/lv.php index fc31ccdb92..04922854ce 100644 --- a/apps/files_encryption/l10n/lv.php +++ b/apps/files_encryption/l10n/lv.php @@ -1,7 +1,4 @@ "Šifrēšana", -"File encryption is enabled." => "Datņu šifrēšana ir aktivēta.", -"The following file types will not be encrypted:" => "Sekojošās datnes netiks šifrētas:", -"Exclude the following file types from encryption:" => "Sekojošos datņu tipus izslēgt no šifrēšanas:", -"None" => "Nav" +"Saving..." => "Saglabā...", +"Encryption" => "Šifrēšana" ); diff --git a/apps/files_encryption/l10n/mk.php b/apps/files_encryption/l10n/mk.php index 513606fadc..a7216f205a 100644 --- a/apps/files_encryption/l10n/mk.php +++ b/apps/files_encryption/l10n/mk.php @@ -1,4 +1,4 @@ "Енкрипција", -"None" => "Ништо" +"Saving..." => "Снимам...", +"Encryption" => "Енкрипција" ); diff --git a/apps/files_encryption/l10n/ms_MY.php b/apps/files_encryption/l10n/ms_MY.php new file mode 100644 index 0000000000..bb963cb72d --- /dev/null +++ b/apps/files_encryption/l10n/ms_MY.php @@ -0,0 +1,3 @@ + "Simpan..." +); diff --git a/apps/files_encryption/l10n/nb_NO.php b/apps/files_encryption/l10n/nb_NO.php index a5e16a0342..d4e2b1ffb5 100644 --- a/apps/files_encryption/l10n/nb_NO.php +++ b/apps/files_encryption/l10n/nb_NO.php @@ -1,7 +1,4 @@ "Kryptering", -"File encryption is enabled." => "Fil-kryptering er aktivert.", -"The following file types will not be encrypted:" => "Følgende filtyper vil ikke bli kryptert:", -"Exclude the following file types from encryption:" => "Ekskluder følgende filtyper fra kryptering:", -"None" => "Ingen" +"Saving..." => "Lagrer...", +"Encryption" => "Kryptering" ); diff --git a/apps/files_encryption/l10n/nl.php b/apps/files_encryption/l10n/nl.php index b1cba96aad..9c462917cf 100644 --- a/apps/files_encryption/l10n/nl.php +++ b/apps/files_encryption/l10n/nl.php @@ -1,7 +1,22 @@ "Herstelsleutel succesvol geactiveerd", +"Could not enable recovery key. Please check your recovery key password!" => "Kon herstelsleutel niet activeren. Controleer het wachtwoord van uw herstelsleutel!", +"Recovery key successfully disabled" => "Herstelsleutel succesvol gedeactiveerd", +"Could not disable recovery key. Please check your recovery key password!" => "Kon herstelsleutel niet deactiveren. Controleer het wachtwoord van uw herstelsleutel!", +"Password successfully changed." => "Wachtwoord succesvol gewijzigd.", +"Could not change the password. Maybe the old password was not correct." => "Kon wachtwoord niet wijzigen. Wellicht oude wachtwoord niet juist ingevoerd.", +"Saving..." => "Opslaan", "Encryption" => "Versleuteling", -"File encryption is enabled." => "Bestandsversleuteling geactiveerd.", -"The following file types will not be encrypted:" => "De volgende bestandstypen zullen niet worden versleuteld:", -"Exclude the following file types from encryption:" => "Sluit de volgende bestandstypen uit van versleuteling:", -"None" => "Geen" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "Activeer versleuteling van wachtwoorden herstelsleutel (maak delen met herstel sleutel mogelijk):", +"Recovery account password" => "Herstel account wachtwoord", +"Enabled" => "Geactiveerd", +"Disabled" => "Gedeactiveerd", +"Change encryption passwords recovery key:" => "Wijzig versleuteling wachtwoord herstelsleutel", +"Old Recovery account password" => "Oude herstel account wachtwoord", +"New Recovery account password" => "Nieuwe herstel account wachtwoord", +"Change Password" => "Wijzigen wachtwoord", +"Enable password recovery by sharing all files with your administrator:" => "Activeer wachtwoordherstel door alle bestanden met uw beheerder te delen:", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "Door deze optie te activeren kunt u toegang tot uw versleutelde bestanden krijgen als u uw wachtwoord kwijt bent", +"File recovery settings updated" => "Bestandsherstel instellingen bijgewerkt", +"Could not update file recovery" => "Kon bestandsherstel niet bijwerken" ); diff --git a/apps/files_encryption/l10n/nn_NO.php b/apps/files_encryption/l10n/nn_NO.php new file mode 100644 index 0000000000..97b3a27a9d --- /dev/null +++ b/apps/files_encryption/l10n/nn_NO.php @@ -0,0 +1,3 @@ + "Lagrar …" +); diff --git a/apps/files_encryption/l10n/oc.php b/apps/files_encryption/l10n/oc.php new file mode 100644 index 0000000000..0a34c4cda1 --- /dev/null +++ b/apps/files_encryption/l10n/oc.php @@ -0,0 +1,3 @@ + "Enregistra..." +); diff --git a/apps/files_encryption/l10n/pl.php b/apps/files_encryption/l10n/pl.php index 2fa86f454f..a6f90db4cd 100644 --- a/apps/files_encryption/l10n/pl.php +++ b/apps/files_encryption/l10n/pl.php @@ -1,7 +1,18 @@ "Zmiana hasła udana.", +"Could not change the password. Maybe the old password was not correct." => "Nie można zmienić hasła. Może stare hasło nie było poprawne.", +"Saving..." => "Zapisywanie...", "Encryption" => "Szyfrowanie", -"File encryption is enabled." => "Szyfrowanie plików jest włączone", -"The following file types will not be encrypted:" => "Poniższe typy plików nie będą szyfrowane:", -"Exclude the following file types from encryption:" => "Wyłącz poniższe typy plików z szyfrowania:", -"None" => "Brak" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "Włącz szyfrowanie odzyskiwanych haseł klucza (zezwalaj na odzyskiwanie klucza):", +"Recovery account password" => "Odzyskiwanie hasła konta", +"Enabled" => "Włączone", +"Disabled" => "Wyłączone", +"Change encryption passwords recovery key:" => "Zmiana klucza szyfrowania haseł odzyskiwania:", +"Old Recovery account password" => "Stare hasło odzyskiwania", +"New Recovery account password" => "Nowe hasło odzyskiwania", +"Change Password" => "Zmień hasło", +"Enable password recovery by sharing all files with your administrator:" => "Włączyć hasło odzyskiwania przez udostępnianie wszystkich plików z administratorem:", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "Włączenie tej opcji umożliwia otrzymać dostęp do zaszyfrowanych plików w przypadku utraty hasła", +"File recovery settings updated" => "Ustawienia odzyskiwania plików zmienione", +"Could not update file recovery" => "Nie można zmienić pliku odzyskiwania" ); diff --git a/apps/files_encryption/l10n/pt_BR.php b/apps/files_encryption/l10n/pt_BR.php index 28807db72c..e5fa0b55ca 100644 --- a/apps/files_encryption/l10n/pt_BR.php +++ b/apps/files_encryption/l10n/pt_BR.php @@ -1,7 +1,22 @@ "Recuperação de chave habilitada com sucesso", +"Could not enable recovery key. Please check your recovery key password!" => "Impossível habilitar recuperação de chave. Por favor verifique sua senha para recuperação de chave!", +"Recovery key successfully disabled" => "Recuperação de chave desabilitada com sucesso", +"Could not disable recovery key. Please check your recovery key password!" => "Impossível desabilitar recuperação de chave. Por favor verifique sua senha para recuperação de chave!", +"Password successfully changed." => "Senha alterada com sucesso.", +"Could not change the password. Maybe the old password was not correct." => "Não foi possível alterar a senha. Talvez a senha antiga não estava correta.", +"Saving..." => "Salvando...", "Encryption" => "Criptografia", -"File encryption is enabled." => "A criptografia de arquivos está ativada.", -"The following file types will not be encrypted:" => "Os seguintes tipos de arquivo não serão criptografados:", -"Exclude the following file types from encryption:" => "Excluir os seguintes tipos de arquivo da criptografia:", -"None" => "Nenhuma" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "Ativar a criptografia de chave de recuperação de senhas (permitir compartilhar a chave de recuperação):", +"Recovery account password" => "Recuperar a senha da conta", +"Enabled" => "Habilidado", +"Disabled" => "Desabilitado", +"Change encryption passwords recovery key:" => "Mudar a criptografia de chave de recuperação de senhas:", +"Old Recovery account password" => "Recuperação de senha de conta antiga", +"New Recovery account password" => "Senha Nova da conta de Recuperação", +"Change Password" => "Trocar Senha", +"Enable password recovery by sharing all files with your administrator:" => "Habilitar recuperação de senha através da partilha de todos os arquivos com o administrador:", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "Ativando esta opção irá permitir que você reobtainha acesso aos seus arquivos criptografados se sua senha for perdida", +"File recovery settings updated" => "Configurações de recuperação de arquivo atualizado", +"Could not update file recovery" => "Não foi possível atualizar a recuperação de arquivos" ); diff --git a/apps/files_encryption/l10n/pt_PT.php b/apps/files_encryption/l10n/pt_PT.php index 1c46011fc1..e1bb17ecaa 100644 --- a/apps/files_encryption/l10n/pt_PT.php +++ b/apps/files_encryption/l10n/pt_PT.php @@ -1,7 +1,9 @@ "Password alterada com sucesso.", +"Could not change the password. Maybe the old password was not correct." => "Não foi possivel alterar a password. Possivelmente a password antiga não está correcta.", +"Saving..." => "A guardar...", "Encryption" => "Encriptação", -"File encryption is enabled." => "A encriptação de ficheiros está ligada", -"The following file types will not be encrypted:" => "Os seguintes ficheiros não serão encriptados:", -"Exclude the following file types from encryption:" => "Excluir da encriptação os seguintes tipos de ficheiro:", -"None" => "Nenhum" +"Enabled" => "Activado", +"Disabled" => "Desactivado", +"Change Password" => "Mudar a Password" ); diff --git a/apps/files_encryption/l10n/ro.php b/apps/files_encryption/l10n/ro.php index a5a6fb3cb7..9e04b627c4 100644 --- a/apps/files_encryption/l10n/ro.php +++ b/apps/files_encryption/l10n/ro.php @@ -1,4 +1,4 @@ "Încriptare", -"None" => "Niciuna" +"Saving..." => "Se salvează...", +"Encryption" => "Încriptare" ); diff --git a/apps/files_encryption/l10n/ru.php b/apps/files_encryption/l10n/ru.php index 22c1e3da37..aaf7f0997c 100644 --- a/apps/files_encryption/l10n/ru.php +++ b/apps/files_encryption/l10n/ru.php @@ -1,7 +1,18 @@ "Пароль изменен удачно.", +"Could not change the password. Maybe the old password was not correct." => "Невозможно изменить пароль. Возможно старый пароль не был верен.", +"Saving..." => "Сохранение...", "Encryption" => "Шифрование", -"File encryption is enabled." => "Шифрование файла включено.", -"The following file types will not be encrypted:" => "Следующие типы файлов не будут зашифрованы:", -"Exclude the following file types from encryption:" => "Исключить следующие типы файлов из шифрованных:", -"None" => "Ничего" +"Enable encryption passwords recovery key (allow sharing to recovery key):" => "Включить шифрование пароля ключа восстановления (понадобится разрешение для восстановления ключа)", +"Recovery account password" => "Восстановление пароля учетной записи", +"Enabled" => "Включено", +"Disabled" => "Отключено", +"Change encryption passwords recovery key:" => "Изменить шифрование пароля ключа восстановления:", +"Old Recovery account password" => "Старое Восстановление пароля учетной записи", +"New Recovery account password" => "Новое Восстановление пароля учетной записи", +"Change Password" => "Изменить пароль", +"Enable password recovery by sharing all files with your administrator:" => "Включить восстановление пароля путем доступа Вашего администратора ко всем файлам", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "Включение этой опции позволит вам получить доступ к зашифрованным файлам, в случае утери пароля", +"File recovery settings updated" => "Настройки файла восстановления обновлены", +"Could not update file recovery" => "Невозможно обновить файл восстановления" ); diff --git a/apps/files_encryption/l10n/ru_RU.php b/apps/files_encryption/l10n/ru_RU.php index 7222235485..1351f63f89 100644 --- a/apps/files_encryption/l10n/ru_RU.php +++ b/apps/files_encryption/l10n/ru_RU.php @@ -1,4 +1,3 @@ "Шифрование", -"None" => "Ни один" +"Saving..." => "Сохранение" ); diff --git a/apps/files_encryption/l10n/si_LK.php b/apps/files_encryption/l10n/si_LK.php index d9cec4b722..6c678bb3a4 100644 --- a/apps/files_encryption/l10n/si_LK.php +++ b/apps/files_encryption/l10n/si_LK.php @@ -1,4 +1,4 @@ "ගුප්ත කේතනය", -"None" => "කිසිවක් නැත" +"Saving..." => "සුරැකෙමින් පවතී...", +"Encryption" => "ගුප්ත කේතනය" ); diff --git a/apps/files_encryption/l10n/sk_SK.php b/apps/files_encryption/l10n/sk_SK.php index bebb623471..279481fbd4 100644 --- a/apps/files_encryption/l10n/sk_SK.php +++ b/apps/files_encryption/l10n/sk_SK.php @@ -1,7 +1,11 @@ "Heslo úspešne zmenené.", +"Saving..." => "Ukladám...", "Encryption" => "Šifrovanie", -"File encryption is enabled." => "Šifrovanie súborov nastavené.", -"The following file types will not be encrypted:" => "Uvedené typy súborov nebudú šifrované:", -"Exclude the following file types from encryption:" => "Nešifrovať uvedené typy súborov", -"None" => "Žiadne" +"Enabled" => "Povolené", +"Disabled" => "Zakázané", +"Change encryption passwords recovery key:" => "Zmeniť šifrovacie heslo obnovovacieho kľúča:", +"Change Password" => "Zmeniť heslo", +"File recovery settings updated" => "Nastavenie obnovy súborov aktualizované", +"Could not update file recovery" => "Nemožno aktualizovať obnovenie súborov" ); diff --git a/apps/files_encryption/l10n/sl.php b/apps/files_encryption/l10n/sl.php index 4754e21214..a420fe161d 100644 --- a/apps/files_encryption/l10n/sl.php +++ b/apps/files_encryption/l10n/sl.php @@ -1,7 +1,4 @@ "Šifriranje", -"File encryption is enabled." => "Šifriranje datotek je omogočeno.", -"The following file types will not be encrypted:" => "Navedene vrste datotek ne bodo šifrirane:", -"Exclude the following file types from encryption:" => "Ne šifriraj navedenih vrst datotek:", -"None" => "Brez" +"Saving..." => "Poteka shranjevanje ...", +"Encryption" => "Šifriranje" ); diff --git a/apps/files_encryption/l10n/sr.php b/apps/files_encryption/l10n/sr.php index 91f7fc62a9..a36e37c179 100644 --- a/apps/files_encryption/l10n/sr.php +++ b/apps/files_encryption/l10n/sr.php @@ -1,4 +1,4 @@ "Шифровање", -"None" => "Ништа" +"Saving..." => "Чување у току...", +"Encryption" => "Шифровање" ); diff --git a/apps/files_encryption/l10n/sv.php b/apps/files_encryption/l10n/sv.php index e214a937a1..966963b554 100644 --- a/apps/files_encryption/l10n/sv.php +++ b/apps/files_encryption/l10n/sv.php @@ -1,7 +1,4 @@ "Kryptering", -"File encryption is enabled." => "Filkryptering är aktiverat.", -"The following file types will not be encrypted:" => "Följande filtyper kommer inte att krypteras:", -"Exclude the following file types from encryption:" => "Exkludera följande filtyper från kryptering:", -"None" => "Ingen" +"Saving..." => "Sparar...", +"Encryption" => "Kryptering" ); diff --git a/apps/files_encryption/l10n/ta_LK.php b/apps/files_encryption/l10n/ta_LK.php index 152e631d0f..63fe9ecde8 100644 --- a/apps/files_encryption/l10n/ta_LK.php +++ b/apps/files_encryption/l10n/ta_LK.php @@ -1,4 +1,4 @@ "மறைக்குறியீடு", -"None" => "ஒன்றுமில்லை" +"Saving..." => "சேமிக்கப்படுகிறது...", +"Encryption" => "மறைக்குறியீடு" ); diff --git a/apps/files_encryption/l10n/th_TH.php b/apps/files_encryption/l10n/th_TH.php index e46d249118..6cab4370cc 100644 --- a/apps/files_encryption/l10n/th_TH.php +++ b/apps/files_encryption/l10n/th_TH.php @@ -1,4 +1,4 @@ "การเข้ารหัส", -"None" => "ไม่ต้อง" +"Saving..." => "กำลังบันทึกข้อมูล...", +"Encryption" => "การเข้ารหัส" ); diff --git a/apps/files_encryption/l10n/tr.php b/apps/files_encryption/l10n/tr.php index 6b42c757e6..917ff0a0ea 100644 --- a/apps/files_encryption/l10n/tr.php +++ b/apps/files_encryption/l10n/tr.php @@ -1,7 +1,4 @@ "Şifreleme", -"File encryption is enabled." => "Dosya şifreleme aktif.", -"The following file types will not be encrypted:" => "Belirtilen dosya tipleri şifrelenmeyecek:", -"Exclude the following file types from encryption:" => "Seçilen dosya tiplerini şifreleme:", -"None" => "Hiçbiri" +"Saving..." => "Kaydediliyor...", +"Encryption" => "Şifreleme" ); diff --git a/apps/files_encryption/l10n/ug.php b/apps/files_encryption/l10n/ug.php new file mode 100644 index 0000000000..954d95b413 --- /dev/null +++ b/apps/files_encryption/l10n/ug.php @@ -0,0 +1,4 @@ + "ساقلاۋاتىدۇ…", +"Encryption" => "شىفىرلاش" +); diff --git a/apps/files_encryption/l10n/uk.php b/apps/files_encryption/l10n/uk.php index d495714119..1c176a3914 100644 --- a/apps/files_encryption/l10n/uk.php +++ b/apps/files_encryption/l10n/uk.php @@ -1,7 +1,4 @@ "Шифрування", -"File encryption is enabled." => "Увімкнуто шифрування файлів.", -"The following file types will not be encrypted:" => "Такі типи файлів шифруватись не будуть:", -"Exclude the following file types from encryption:" => "Виключити наступні типи файлів з ​​шифрування:", -"None" => "Жоден" +"Saving..." => "Зберігаю...", +"Encryption" => "Шифрування" ); diff --git a/apps/files_encryption/l10n/vi.php b/apps/files_encryption/l10n/vi.php index 0a88d1b2db..0af5bdc9a6 100644 --- a/apps/files_encryption/l10n/vi.php +++ b/apps/files_encryption/l10n/vi.php @@ -1,7 +1,4 @@ "Mã hóa", -"File encryption is enabled." => "Mã hóa file đã mở", -"The following file types will not be encrypted:" => "Loại file sau sẽ không được mã hóa", -"Exclude the following file types from encryption:" => "Việc mã hóa không bao gồm loại file sau", -"None" => "Không có gì hết" +"Saving..." => "Đang lưu...", +"Encryption" => "Mã hóa" ); diff --git a/apps/files_encryption/l10n/zh_CN.GB2312.php b/apps/files_encryption/l10n/zh_CN.GB2312.php index 12d903e656..3c405a81ac 100644 --- a/apps/files_encryption/l10n/zh_CN.GB2312.php +++ b/apps/files_encryption/l10n/zh_CN.GB2312.php @@ -1,4 +1,4 @@ "加密", -"None" => "无" +"Saving..." => "保存中...", +"Encryption" => "加密" ); diff --git a/apps/files_encryption/l10n/zh_CN.php b/apps/files_encryption/l10n/zh_CN.php index 13fa95203e..e565fce801 100644 --- a/apps/files_encryption/l10n/zh_CN.php +++ b/apps/files_encryption/l10n/zh_CN.php @@ -1,7 +1,4 @@ "加密", -"File encryption is enabled." => "文件加密已启用.", -"The following file types will not be encrypted:" => "如下的文件类型将不会被加密:", -"Exclude the following file types from encryption:" => "从加密中排除如下的文件类型:", -"None" => "无" +"Saving..." => "保存中", +"Encryption" => "加密" ); diff --git a/apps/files_encryption/l10n/zh_HK.php b/apps/files_encryption/l10n/zh_HK.php index 0c0b709fdc..0a38a2ddf8 100644 --- a/apps/files_encryption/l10n/zh_HK.php +++ b/apps/files_encryption/l10n/zh_HK.php @@ -1,6 +1,3 @@ "加密", -"File encryption is enabled." => "檔案加密已開啟", -"The following file types will not be encrypted:" => "以下文件類別將不會被加密", -"None" => "空" +"Encryption" => "加密" ); diff --git a/apps/files_encryption/l10n/zh_TW.php b/apps/files_encryption/l10n/zh_TW.php index 95e61b45dc..faea3f54a1 100644 --- a/apps/files_encryption/l10n/zh_TW.php +++ b/apps/files_encryption/l10n/zh_TW.php @@ -1,7 +1,14 @@ "成功變更密碼。", +"Could not change the password. Maybe the old password was not correct." => "無法變更密碼,或許是輸入的舊密碼不正確。", +"Saving..." => "儲存中...", "Encryption" => "加密", -"File encryption is enabled." => "檔案加密已被啟用", -"The following file types will not be encrypted:" => "以下的文件類型不會被加密:", -"Exclude the following file types from encryption:" => "從加密中排除的檔案類型:", -"None" => "無" +"Enabled" => "已啓用", +"Disabled" => "已停用", +"Change encryption passwords recovery key:" => "變更加密密碼還原金鑰:", +"Change Password" => "變更密碼", +"Enable password recovery by sharing all files with your administrator:" => "與管理員分享所有檔案以啓用密碼還原功能:", +"Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" => "啓用此選項允許您未來遺失密碼時重新取得已加密的檔案", +"File recovery settings updated" => "檔案還原設定已更新", +"Could not update file recovery" => "無法更新檔案還原設定" ); diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index 437a18669e..ddeb3590f6 100755 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -25,13 +25,8 @@ namespace OCA\Encryption; -require_once 'Crypt_Blowfish/Blowfish.php'; - -// Todo: -// - Add a setting "Don´t encrypt files larger than xx because of performance" -// - Don't use a password directly as encryption key. but a key which is -// stored on the server and encrypted with the user password. -> change pass -// faster +//require_once '../3rdparty/Crypt_Blowfish/Blowfish.php'; +require_once realpath(dirname(__FILE__) . '/../3rdparty/Crypt_Blowfish/Blowfish.php'); /** * Class for common cryptography functionality @@ -41,10 +36,10 @@ class Crypt { /** * @brief return encryption mode client or server side encryption - * @param string user name (use system wide setting if name=null) + * @param string $user name (use system wide setting if name=null) * @return string 'client' or 'server' */ - public static function mode( $user = null ) { + public static function mode($user = null) { return 'server'; @@ -56,30 +51,33 @@ class Crypt { */ public static function createKeypair() { - $res = openssl_pkey_new(); + $res = openssl_pkey_new(array('private_key_bits' => 4096)); // Get private key - openssl_pkey_export( $res, $privateKey ); + openssl_pkey_export($res, $privateKey); // Get public key - $publicKey = openssl_pkey_get_details( $res ); + $publicKey = openssl_pkey_get_details($res); $publicKey = $publicKey['key']; - return( array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ) ); + return (array( + 'publicKey' => $publicKey, + 'privateKey' => $privateKey + )); } /** * @brief Add arbitrary padding to encrypted data * @param string $data data to be padded - * @return padded data + * @return string padded data * @note In order to end up with data exactly 8192 bytes long we must * add two letters. It is impossible to achieve exactly 8192 length * blocks with encryption alone, hence padding is added to achieve the * required length. */ - public static function addPadding( $data ) { + public static function addPadding($data) { $padded = $data . 'xx'; @@ -90,13 +88,13 @@ class Crypt { /** * @brief Remove arbitrary padding to encrypted data * @param string $padded padded data to remove padding from - * @return unpadded data on success, false on error + * @return string unpadded data on success, false on error */ - public static function removePadding( $padded ) { + public static function removePadding($padded) { - if ( substr( $padded, -2 ) == 'xx' ) { + if (substr($padded, -2) === 'xx') { - $data = substr( $padded, 0, -2 ); + $data = substr($padded, 0, -2); return $data; @@ -111,29 +109,30 @@ class Crypt { /** * @brief Check if a file's contents contains an IV and is symmetrically encrypted - * @return true / false + * @param $content + * @return boolean * @note see also OCA\Encryption\Util->isEncryptedPath() */ - public static function isCatfile( $content ) { + public static function isCatfileContent($content) { - if ( !$content ) { + if (!$content) { return false; } - $noPadding = self::removePadding( $content ); + $noPadding = self::removePadding($content); // Fetch encryption metadata from end of file - $meta = substr( $noPadding, -22 ); + $meta = substr($noPadding, -22); // Fetch IV from end of file - $iv = substr( $meta, -16 ); + $iv = substr($meta, -16); // Fetch identifier from start of metadata - $identifier = substr( $meta, 0, 6 ); + $identifier = substr($meta, 0, 6); - if ( $identifier == '00iv00') { + if ($identifier === '00iv00') { return true; @@ -150,36 +149,36 @@ class Crypt { * @param string $path * @return bool */ - public static function isEncryptedMeta( $path ) { + public static function isEncryptedMeta($path) { // TODO: Use DI to get \OC\Files\Filesystem out of here // Fetch all file metadata from DB - $metadata = \OC\Files\Filesystem::getFileInfo( $path, '' ); + $metadata = \OC\Files\Filesystem::getFileInfo($path); // Return encryption status - return isset( $metadata['encrypted'] ) and ( bool )$metadata['encrypted']; + return isset($metadata['encrypted']) && ( bool )$metadata['encrypted']; } /** * @brief Check if a file is encrypted via legacy system + * @param $data * @param string $relPath The path of the file, relative to user/data; * e.g. filename or /Docs/filename, NOT admin/files/filename - * @return true / false + * @return boolean */ - public static function isLegacyEncryptedContent( $data, $relPath ) { + public static function isLegacyEncryptedContent($data, $relPath) { // Fetch all file metadata from DB - $metadata = \OC\Files\Filesystem::getFileInfo( $relPath, '' ); + $metadata = \OC\Files\Filesystem::getFileInfo($relPath, ''); // If a file is flagged with encryption in DB, but isn't a // valid content + IV combination, it's probably using the // legacy encryption system - if ( - isset( $metadata['encrypted'] ) - and $metadata['encrypted'] === true - and ! self::isCatfile( $data ) + if (isset($metadata['encrypted']) + && $metadata['encrypted'] === true + && !self::isCatfileContent($data) ) { return true; @@ -194,17 +193,20 @@ class Crypt { /** * @brief Symmetrically encrypt a string - * @returns encrypted file + * @param $plainContent + * @param $iv + * @param string $passphrase + * @return string encrypted file content */ - public static function encrypt( $plainContent, $iv, $passphrase = '' ) { + public static function encrypt($plainContent, $iv, $passphrase = '') { - if ( $encryptedContent = openssl_encrypt( $plainContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { + if ($encryptedContent = openssl_encrypt($plainContent, 'AES-128-CFB', $passphrase, false, $iv)) { return $encryptedContent; } else { - \OC_Log::write( 'Encryption library', 'Encryption (symmetric) of content failed', \OC_Log::ERROR ); + \OCP\Util::writeLog('Encryption library', 'Encryption (symmetric) of content failed', \OCP\Util::ERROR); return false; @@ -214,18 +216,21 @@ class Crypt { /** * @brief Symmetrically decrypt a string - * @returns decrypted file + * @param $encryptedContent + * @param $iv + * @param $passphrase + * @throws \Exception + * @return string decrypted file content */ - public static function decrypt( $encryptedContent, $iv, $passphrase ) { + public static function decrypt($encryptedContent, $iv, $passphrase) { - if ( $plainContent = openssl_decrypt( $encryptedContent, 'AES-128-CFB', $passphrase, false, $iv ) ) { + if ($plainContent = openssl_decrypt($encryptedContent, 'AES-128-CFB', $passphrase, false, $iv)) { return $plainContent; - } else { - throw new \Exception( 'Encryption library: Decryption (symmetric) of content failed' ); + throw new \Exception('Encryption library: Decryption (symmetric) of content failed'); } @@ -237,7 +242,7 @@ class Crypt { * @param string $iv IV to be concatenated * @returns string concatenated content */ - public static function concatIv ( $content, $iv ) { + public static function concatIv($content, $iv) { $combined = $content . '00iv00' . $iv; @@ -250,20 +255,20 @@ class Crypt { * @param string $catFile concatenated data to be split * @returns array keys: encrypted, iv */ - public static function splitIv ( $catFile ) { + public static function splitIv($catFile) { // Fetch encryption metadata from end of file - $meta = substr( $catFile, -22 ); + $meta = substr($catFile, -22); // Fetch IV from end of file - $iv = substr( $meta, -16 ); + $iv = substr($meta, -16); // Remove IV and IV identifier text to expose encrypted content - $encrypted = substr( $catFile, 0, -22 ); + $encrypted = substr($catFile, 0, -22); $split = array( - 'encrypted' => $encrypted - , 'iv' => $iv + 'encrypted' => $encrypted, + 'iv' => $iv ); return $split; @@ -272,14 +277,16 @@ class Crypt { /** * @brief Symmetrically encrypts a string and returns keyfile content - * @param $plainContent content to be encrypted in keyfile - * @returns encrypted content combined with IV + * @param string $plainContent content to be encrypted in keyfile + * @param string $passphrase + * @return bool|string + * @return string encrypted content combined with IV * @note IV need not be specified, as it will be stored in the returned keyfile * and remain accessible therein. */ - public static function symmetricEncryptFileContent( $plainContent, $passphrase = '' ) { + public static function symmetricEncryptFileContent($plainContent, $passphrase = '') { - if ( !$plainContent ) { + if (!$plainContent) { return false; @@ -287,18 +294,18 @@ class Crypt { $iv = self::generateIv(); - if ( $encryptedContent = self::encrypt( $plainContent, $iv, $passphrase ) ) { + if ($encryptedContent = self::encrypt($plainContent, $iv, $passphrase)) { // Combine content to encrypt with IV identifier and actual IV - $catfile = self::concatIv( $encryptedContent, $iv ); + $catfile = self::concatIv($encryptedContent, $iv); - $padded = self::addPadding( $catfile ); + $padded = self::addPadding($catfile); return $padded; } else { - \OC_Log::write( 'Encryption library', 'Encryption (symmetric) of keyfile content failed', \OC_Log::ERROR ); + \OCP\Util::writeLog('Encryption library', 'Encryption (symmetric) of keyfile content failed', \OCP\Util::ERROR); return false; @@ -309,31 +316,37 @@ class Crypt { /** * @brief Symmetrically decrypts keyfile content - * @param string $source - * @param string $target - * @param string $key the decryption key - * @returns decrypted content + * @param $keyfileContent + * @param string $passphrase + * @throws \Exception + * @return bool|string + * @internal param string $source + * @internal param string $target + * @internal param string $key the decryption key + * @returns string decrypted content * * This function decrypts a file */ - public static function symmetricDecryptFileContent( $keyfileContent, $passphrase = '' ) { + public static function symmetricDecryptFileContent($keyfileContent, $passphrase = '') { - if ( !$keyfileContent ) { + if (!$keyfileContent) { - throw new \Exception( 'Encryption library: no data provided for decryption' ); + throw new \Exception('Encryption library: no data provided for decryption'); } // Remove padding - $noPadding = self::removePadding( $keyfileContent ); + $noPadding = self::removePadding($keyfileContent); // Split into enc data and catfile - $catfile = self::splitIv( $noPadding ); + $catfile = self::splitIv($noPadding); - if ( $plainContent = self::decrypt( $catfile['encrypted'], $catfile['iv'], $passphrase ) ) { + if ($plainContent = self::decrypt($catfile['encrypted'], $catfile['iv'], $passphrase)) { return $plainContent; + } else { + return false; } } @@ -346,15 +359,15 @@ class Crypt { * * This function decrypts a file */ - public static function symmetricEncryptFileContentKeyfile( $plainContent ) { + public static function symmetricEncryptFileContentKeyfile($plainContent) { $key = self::generateKey(); - if( $encryptedContent = self::symmetricEncryptFileContent( $plainContent, $key ) ) { + if ($encryptedContent = self::symmetricEncryptFileContent($plainContent, $key)) { return array( - 'key' => $key - , 'encrypted' => $encryptedContent + 'key' => $key, + 'encrypted' => $encryptedContent ); } else { @@ -368,22 +381,41 @@ class Crypt { /** * @brief Create asymmetrically encrypted keyfile content using a generated key * @param string $plainContent content to be encrypted - * @returns array keys: key, encrypted - * @note symmetricDecryptFileContent() can be used to decrypt files created using this method - * - * This function decrypts a file + * @param array $publicKeys array keys must be the userId of corresponding user + * @returns array keys: keys (array, key = userId), data + * @note symmetricDecryptFileContent() can decrypt files created using this method */ - public static function multiKeyEncrypt( $plainContent, array $publicKeys ) { + public static function multiKeyEncrypt($plainContent, array $publicKeys) { + + // openssl_seal returns false without errors if $plainContent + // is empty, so trigger our own error + if (empty($plainContent)) { + + throw new \Exception('Cannot mutliKeyEncrypt empty plain content'); + + } // Set empty vars to be set by openssl by reference $sealed = ''; - $envKeys = array(); + $shareKeys = array(); + $mappedShareKeys = array(); - if( openssl_seal( $plainContent, $sealed, $envKeys, $publicKeys ) ) { + if (openssl_seal($plainContent, $sealed, $shareKeys, $publicKeys)) { + + $i = 0; + + // Ensure each shareKey is labelled with its + // corresponding userId + foreach ($publicKeys as $userId => $publicKey) { + + $mappedShareKeys[$userId] = $shareKeys[$i]; + $i++; + + } return array( - 'keys' => $envKeys - , 'encrypted' => $sealed + 'keys' => $mappedShareKeys, + 'data' => $sealed ); } else { @@ -396,27 +428,31 @@ class Crypt { /** * @brief Asymmetrically encrypt a file using multiple public keys - * @param string $plainContent content to be encrypted + * @param $encryptedContent + * @param $shareKey + * @param $privateKey + * @return bool + * @internal param string $plainContent content to be encrypted * @returns string $plainContent decrypted string * @note symmetricDecryptFileContent() can be used to decrypt files created using this method * * This function decrypts a file */ - public static function multiKeyDecrypt( $encryptedContent, $envKey, $privateKey ) { + public static function multiKeyDecrypt($encryptedContent, $shareKey, $privateKey) { - if ( !$encryptedContent ) { + if (!$encryptedContent) { return false; } - if ( openssl_open( $encryptedContent, $plainContent, $envKey, $privateKey ) ) { + if (openssl_open($encryptedContent, $plainContent, $shareKey, $privateKey)) { return $plainContent; } else { - \OC_Log::write( 'Encryption library', 'Decryption (asymmetric) of sealed content failed', \OC_Log::ERROR ); + \OCP\Util::writeLog('Encryption library', 'Decryption (asymmetric) of sealed content failed', \OCP\Util::ERROR); return false; @@ -425,12 +461,14 @@ class Crypt { } /** - * @brief Asymmetrically encrypt a string using a public key - * @returns encrypted file + * @brief Asymetrically encrypt a string using a public key + * @param $plainContent + * @param $publicKey + * @return string encrypted file */ - public static function keyEncrypt( $plainContent, $publicKey ) { + public static function keyEncrypt($plainContent, $publicKey) { - openssl_public_encrypt( $plainContent, $encryptedContent, $publicKey ); + openssl_public_encrypt($plainContent, $encryptedContent, $publicKey); return $encryptedContent; @@ -438,110 +476,19 @@ class Crypt { /** * @brief Asymetrically decrypt a file using a private key - * @returns decrypted file + * @param $encryptedContent + * @param $privatekey + * @return string decrypted file */ - public static function keyDecrypt( $encryptedContent, $privatekey ) { + public static function keyDecrypt($encryptedContent, $privatekey) { - openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey ); - - return $plainContent; - - } - - /** - * @brief Encrypts content symmetrically and generates keyfile asymmetrically - * @returns array containing catfile and new keyfile. - * keys: data, key - * @note this method is a wrapper for combining other crypt class methods - */ - public static function keyEncryptKeyfile( $plainContent, $publicKey ) { - - // Encrypt plain data, generate keyfile & encrypted file - $cryptedData = self::symmetricEncryptFileContentKeyfile( $plainContent ); - - // Encrypt keyfile - $cryptedKey = self::keyEncrypt( $cryptedData['key'], $publicKey ); - - return array( 'data' => $cryptedData['encrypted'], 'key' => $cryptedKey ); - - } - - /** - * @brief Takes catfile, keyfile, and private key, and - * performs decryption - * @returns decrypted content - * @note this method is a wrapper for combining other crypt class methods - */ - public static function keyDecryptKeyfile( $catfile, $keyfile, $privateKey ) { - - // Decrypt the keyfile with the user's private key - $decryptedKeyfile = self::keyDecrypt( $keyfile, $privateKey ); - - // Decrypt the catfile symmetrically using the decrypted keyfile - $decryptedData = self::symmetricDecryptFileContent( $catfile, $decryptedKeyfile ); - - return $decryptedData; - - } - - /** - * @brief Symmetrically encrypt a file by combining encrypted component data blocks - */ - public static function symmetricBlockEncryptFileContent( $plainContent, $key ) { - - $crypted = ''; - - $remaining = $plainContent; - - $testarray = array(); - - while( strlen( $remaining ) ) { - - //echo "\n\n\$block = ".substr( $remaining, 0, 6126 ); - - // Encrypt a chunk of unencrypted data and add it to the rest - $block = self::symmetricEncryptFileContent( substr( $remaining, 0, 6126 ), $key ); - - $padded = self::addPadding( $block ); - - $crypted .= $block; - - $testarray[] = $block; - - // Remove the data already encrypted from remaining unencrypted data - $remaining = substr( $remaining, 6126 ); + $result = @openssl_private_decrypt($encryptedContent, $plainContent, $privatekey); + if ($result) { + return $plainContent; } - return $crypted; - - } - - - /** - * @brief Symmetrically decrypt a file by combining encrypted component data blocks - */ - public static function symmetricBlockDecryptFileContent( $crypted, $key ) { - - $decrypted = ''; - - $remaining = $crypted; - - $testarray = array(); - - while( strlen( $remaining ) ) { - - $testarray[] = substr( $remaining, 0, 8192 ); - - // Decrypt a chunk of unencrypted data and add it to the rest - $decrypted .= self::symmetricDecryptFileContent( $remaining, $key ); - - // Remove the data already encrypted from remaining unencrypted data - $remaining = substr( $remaining, 8192 ); - - } - - return $decrypted; + return $result; } @@ -551,24 +498,24 @@ class Crypt { */ public static function generateIv() { - if ( $random = openssl_random_pseudo_bytes( 12, $strong ) ) { + if ($random = openssl_random_pseudo_bytes(12, $strong)) { - if ( !$strong ) { + if (!$strong) { // If OpenSSL indicates randomness is insecure, log error - \OC_Log::write( 'Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()', \OC_Log::WARN ); + \OCP\Util::writeLog('Encryption library', 'Insecure symmetric key was generated using openssl_random_pseudo_bytes()', \OCP\Util::WARN); } // We encode the iv purely for string manipulation // purposes - it gets decoded before use - $iv = base64_encode( $random ); + $iv = base64_encode($random); return $iv; } else { - throw new \Exception( 'Generating IV failed' ); + throw new \Exception('Generating IV failed'); } @@ -581,12 +528,12 @@ class Crypt { public static function generateKey() { // Generate key - if ( $key = base64_encode( openssl_random_pseudo_bytes( 183, $strong ) ) ) { + if ($key = base64_encode(openssl_random_pseudo_bytes(183, $strong))) { - if ( !$strong ) { + if (!$strong) { // If OpenSSL indicates randomness is insecure, log error - throw new \Exception ( 'Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()' ); + throw new \Exception('Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()'); } @@ -603,15 +550,15 @@ class Crypt { /** * @brief Get the blowfish encryption handeler for a key * @param $key string (optional) - * @return Crypt_Blowfish blowfish object + * @return \Crypt_Blowfish blowfish object * * if the key is left out, the default handeler will be used */ - public static function getBlowfish( $key = '' ) { + public static function getBlowfish($key = '') { - if ( $key ) { + if ($key) { - return new \Crypt_Blowfish( $key ); + return new \Crypt_Blowfish($key); } else { @@ -621,13 +568,17 @@ class Crypt { } - public static function legacyCreateKey( $passphrase ) { + /** + * @param $passphrase + * @return mixed + */ + public static function legacyCreateKey($passphrase) { // Generate a random integer - $key = mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ) . mt_rand( 10000, 99999 ); + $key = mt_rand(10000, 99999) . mt_rand(10000, 99999) . mt_rand(10000, 99999) . mt_rand(10000, 99999); // Encrypt the key with the passphrase - $legacyEncKey = self::legacyEncrypt( $key, $passphrase ); + $legacyEncKey = self::legacyEncrypt($key, $passphrase); return $legacyEncKey; @@ -635,61 +586,79 @@ class Crypt { /** * @brief encrypts content using legacy blowfish system - * @param $content the cleartext message you want to encrypt - * @param $key the encryption key (optional) - * @returns encrypted content + * @param string $content the cleartext message you want to encrypt + * @param string $passphrase + * @returns string encrypted content * * This function encrypts an content */ - public static function legacyEncrypt( $content, $passphrase = '' ) { + public static function legacyEncrypt($content, $passphrase = '') { - $bf = self::getBlowfish( $passphrase ); + $bf = self::getBlowfish($passphrase); - return $bf->encrypt( $content ); + return $bf->encrypt($content); } /** * @brief decrypts content using legacy blowfish system - * @param $content the cleartext message you want to decrypt - * @param $key the encryption key (optional) - * @returns cleartext content + * @param string $content the cleartext message you want to decrypt + * @param string $passphrase + * @return string cleartext content * * This function decrypts an content */ - public static function legacyDecrypt( $content, $passphrase = '' ) { + private static function legacyDecrypt($content, $passphrase = '') { - $bf = self::getBlowfish( $passphrase ); + $bf = self::getBlowfish($passphrase); - $decrypted = $bf->decrypt( $content ); - - $trimmed = rtrim( $decrypted, "\0" ); - - return $trimmed; - - } - - public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKey, $newPassphrase ) { - - $decrypted = self::legacyDecrypt( $legacyEncryptedContent, $legacyPassphrase ); - - $recrypted = self::keyEncryptKeyfile( $decrypted, $publicKey ); - - return $recrypted; + $decrypted = $bf->decrypt($content); + return $decrypted; } /** - * @brief Re-encryptes a legacy blowfish encrypted file using AES with integrated IV - * @param $legacyContent the legacy encrypted content to re-encrypt - * @returns cleartext content - * - * This function decrypts an content + * @param $data + * @param string $key + * @param int $maxLength + * @return string */ - public static function legacyRecrypt( $legacyContent, $legacyPassphrase, $newPassphrase ) { + public static function legacyBlockDecrypt($data, $key = '', $maxLength = 0) { - // TODO: write me + $result = ''; + while (strlen($data)) { + $result .= self::legacyDecrypt(substr($data, 0, 8192), $key); + $data = substr($data, 8192); + } + if ($maxLength > 0) { + return substr($result, 0, $maxLength); + } else { + return rtrim($result, "\0"); + } + } + + /** + * @param $legacyEncryptedContent + * @param $legacyPassphrase + * @param $publicKeys + * @return array + */ + public static function legacyKeyRecryptKeyfile($legacyEncryptedContent, $legacyPassphrase, $publicKeys) { + + $decrypted = self::legacyBlockDecrypt($legacyEncryptedContent, $legacyPassphrase); + + // Encrypt plain data, generate keyfile & encrypted file + $cryptedData = self::symmetricEncryptFileContentKeyfile($decrypted); + + // Encrypt plain keyfile to multiple sharefiles + $multiEncrypted = Crypt::multiKeyEncrypt($cryptedData['key'], $publicKeys); + + return array( + 'data' => $cryptedData['encrypted'], + 'filekey' => $multiEncrypted['data'], + 'sharekeys' => $multiEncrypted['keys'] + ); } -} +} \ No newline at end of file diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php new file mode 100755 index 0000000000..e078ab3554 --- /dev/null +++ b/apps/files_encryption/lib/helper.php @@ -0,0 +1,203 @@ + + * + * 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 . + * + */ + +namespace OCA\Encryption; + +/** + * @brief Class to manage registration of hooks an various helper methods + * @package OCA\Encryption + */ +class Helper { + + /** + * @brief register share related hooks + * + */ + public static function registerShareHooks() { + + \OCP\Util::connectHook('OCP\Share', 'pre_shared', 'OCA\Encryption\Hooks', 'preShared'); + \OCP\Util::connectHook('OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared'); + \OCP\Util::connectHook('OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare'); + } + + /** + * @brief register user related hooks + * + */ + public static function registerUserHooks() { + + \OCP\Util::connectHook('OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login'); + \OCP\Util::connectHook('OC_User', 'post_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase'); + \OCP\Util::connectHook('OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser'); + \OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser'); + } + + /** + * @brief register filesystem related hooks + * + */ + public static function registerFilesystemHooks() { + + \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename'); + } + + /** + * @brief setup user for files_encryption + * + * @param Util $util + * @param string $password + * @return bool + */ + public static function setupUser($util, $password) { + // Check files_encryption infrastructure is ready for action + if (!$util->ready()) { + + \OCP\Util::writeLog('Encryption library', 'User account "' . $util->getUserId() + . '" is not ready for encryption; configuration started', \OCP\Util::DEBUG); + + if (!$util->setupServerSide($password)) { + return false; + } + } + + return true; + } + + /** + * @brief enable recovery + * + * @param $recoveryKeyId + * @param $recoveryPassword + * @internal param \OCA\Encryption\Util $util + * @internal param string $password + * @return bool + */ + public static function adminEnableRecovery($recoveryKeyId, $recoveryPassword) { + $view = new \OC\Files\View('/'); + + if ($recoveryKeyId === null) { + $recoveryKeyId = 'recovery_' . substr(md5(time()), 0, 8); + \OC_Appconfig::setValue('files_encryption', 'recoveryKeyId', $recoveryKeyId); + } + + if (!$view->is_dir('/owncloud_private_key')) { + $view->mkdir('/owncloud_private_key'); + } + + if ( + (!$view->file_exists("/public-keys/" . $recoveryKeyId . ".public.key") + || !$view->file_exists("/owncloud_private_key/" . $recoveryKeyId . ".private.key")) + ) { + + $keypair = \OCA\Encryption\Crypt::createKeypair(); + + \OC_FileProxy::$enabled = false; + + // Save public key + + if (!$view->is_dir('/public-keys')) { + $view->mkdir('/public-keys'); + } + + $view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']); + + // Encrypt private key empthy passphrase + $encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $recoveryPassword); + + // Save private key + $view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey); + + // create control file which let us check later on if the entered password was correct. + $encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt("ownCloud", $keypair['publicKey']); + if (!$view->is_dir('/control-file')) { + $view->mkdir('/control-file'); + } + $view->file_put_contents('/control-file/controlfile.enc', $encryptedControlData); + + \OC_FileProxy::$enabled = true; + + // Set recoveryAdmin as enabled + \OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1); + + $return = true; + + } else { // get recovery key and check the password + $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser()); + $return = $util->checkRecoveryPassword($recoveryPassword); + if ($return) { + \OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 1); + } + } + + return $return; + } + + + /** + * @brief disable recovery + * + * @param $recoveryPassword + * @return bool + */ + public static function adminDisableRecovery($recoveryPassword) { + $util = new Util(new \OC_FilesystemView('/'), \OCP\User::getUser()); + $return = $util->checkRecoveryPassword($recoveryPassword); + + if ($return) { + // Set recoveryAdmin as disabled + \OC_Appconfig::setValue('files_encryption', 'recoveryAdminEnabled', 0); + } + + return $return; + } + + + /** + * @brief checks if access is public/anonymous user + * @return bool + */ + public static function isPublicAccess() { + if (\OCP\USER::getUser() === false + || (isset($_GET['service']) && $_GET['service'] == 'files' + && isset($_GET['t'])) + ) { + return true; + } else { + return false; + } + } + + /** + * @brief Format a path to be relative to the /user/files/ directory + * @param string $path the absolute path + * @return string e.g. turns '/admin/files/test.txt' into 'test.txt' + */ + public static function stripUserFilesPath($path) { + $trimmed = ltrim($path, '/'); + $split = explode('/', $trimmed); + $sliced = array_slice($split, 2); + $relPath = implode('/', $sliced); + + return $relPath; + } +} \ No newline at end of file diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 9558779715..e911c1785d 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -28,19 +28,26 @@ namespace OCA\Encryption; * @note Where a method requires a view object, it's root must be '/' */ class Keymanager { - + /** * @brief retrieve the ENCRYPTED private key from a user - * - * @return string private key or false + * + * @param \OC_FilesystemView $view + * @param string $user + * @return string private key or false (hopefully) * @note the key returned by this method must be decrypted before use */ - public static function getPrivateKey( \OC_FilesystemView $view, $user ) { - - $path = '/' . $user . '/' . 'files_encryption' . '/' . $user.'.private.key'; - - $key = $view->file_get_contents( $path ); - + public static function getPrivateKey(\OC_FilesystemView $view, $user) { + + $path = '/' . $user . '/' . 'files_encryption' . '/' . $user . '.private.key'; + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $key = $view->file_get_contents($path); + + \OC_FileProxy::$enabled = $proxyStatus; + return $key; } @@ -50,102 +57,152 @@ class Keymanager { * @param $userId * @return string public key or false */ - public static function getPublicKey( \OC_FilesystemView $view, $userId ) { - - return $view->file_get_contents( '/public-keys/' . '/' . $userId . '.public.key' ); - + public static function getPublicKey(\OC_FilesystemView $view, $userId) { + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $result = $view->file_get_contents('/public-keys/' . $userId . '.public.key'); + + \OC_FileProxy::$enabled = $proxyStatus; + + return $result; + } - + /** - * @brief retrieve both keys from a user (private and public) + * @brief Retrieve a user's public and private key * @param \OC_FilesystemView $view * @param $userId * @return array keys: privateKey, publicKey */ - public static function getUserKeys( \OC_FilesystemView $view, $userId ) { - - return array( - 'publicKey' => self::getPublicKey( $view, $userId ) - , 'privateKey' => self::getPrivateKey( $view, $userId ) - ); - - } - - /** - * @brief Retrieve public keys of all users with access to a file - * @param string $path Path to file - * @return array of public keys for the given file - * @note Checks that the sharing app is enabled should be performed - * by client code, that isn't checked here - */ - public static function getPublicKeys( \OC_FilesystemView $view, $userId, $filePath ) { - - $path = ltrim( $path, '/' ); - - $filepath = '/' . $userId . '/files/' . $filePath; - - // Check if sharing is enabled - if ( OC_App::isEnabled( 'files_sharing' ) ) { - + public static function getUserKeys(\OC_FilesystemView $view, $userId) { + + return array( + 'publicKey' => self::getPublicKey($view, $userId), + 'privateKey' => self::getPrivateKey($view, $userId) + ); - - } else { - - // check if it is a file owned by the user and not shared at all - $userview = new \OC_FilesystemView( '/'.$userId.'/files/' ); - - if ( $userview->file_exists( $path ) ) { - - $users[] = $userId; - - } - - } - - $view = new \OC_FilesystemView( '/public-keys/' ); - - $keylist = array(); - - $count = 0; - - foreach ( $users as $user ) { - - $keylist['key'.++$count] = $view->file_get_contents( $user.'.public.key' ); - - } - - return $keylist; - } - + + /** + * @brief Retrieve public keys for given users + * @param \OC_FilesystemView $view + * @param array $userIds + * @return array of public keys for the specified users + */ + public static function getPublicKeys(\OC_FilesystemView $view, array $userIds) { + + $keys = array(); + + foreach ($userIds as $userId) { + + $keys[$userId] = self::getPublicKey($view, $userId); + + } + + return $keys; + + } + /** * @brief store file encryption key * + * @param \OC_FilesystemView $view * @param string $path relative path of the file, including filename - * @param string $key + * @param $userId + * @param $catfile + * @internal param string $key * @return bool true/false - * @note The keyfile is not encrypted here. Client code must + * @note The keyfile is not encrypted here. Client code must * asymmetrically encrypt the keyfile before passing it to this method */ - public static function setFileKey( \OC_FilesystemView $view, $path, $userId, $catfile ) { - - $basePath = '/' . $userId . '/files_encryption/keyfiles'; - - $targetPath = self::keySetPreparation( $view, $path, $basePath, $userId ); - - if ( $view->is_dir( $basePath . '/' . $targetPath ) ) { - - - + public static function setFileKey(\OC_FilesystemView $view, $path, $userId, $catfile) { + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + //here we need the currently logged in user, while userId can be a different user + $util = new Util($view, \OCP\User::getUser()); + list($owner, $filename) = $util->getUidAndFilename($path); + + $basePath = '/' . $owner . '/files_encryption/keyfiles'; + + $targetPath = self::keySetPreparation($view, $filename, $basePath, $owner); + + if (!$view->is_dir($basePath . '/' . $targetPath)) { + + // create all parent folders + $info = pathinfo($basePath . '/' . $targetPath); + $keyfileFolderName = $view->getLocalFolder($info['dirname']); + + if (!file_exists($keyfileFolderName)) { + + mkdir($keyfileFolderName, 0750, true); + + } + } + + // try reusing key file if part file + if (self::isPartialFilePath($targetPath)) { + + $result = $view->file_put_contents( + $basePath . '/' . self::fixPartialFilePath($targetPath) . '.key', $catfile); + } else { - // Save the keyfile in parallel directory - return $view->file_put_contents( $basePath . '/' . $targetPath . '.key', $catfile ); - + $result = $view->file_put_contents($basePath . '/' . $targetPath . '.key', $catfile); + } - + + \OC_FileProxy::$enabled = $proxyStatus; + + return $result; + } - + + /** + * @brief Remove .path extension from a file path + * @param string $path Path that may identify a .part file + * @return string File path without .part extension + * @note this is needed for reusing keys + */ + public static function fixPartialFilePath($path) { + + if (preg_match('/\.part$/', $path) || preg_match('/\.etmp$/', $path)) { + + $newLength = strlen($path) - 5; + $fPath = substr($path, 0, $newLength); + + return $fPath; + + } else { + + return $path; + + } + + } + + /** + * @brief Check if a path is a .part file + * @param string $path Path that may identify a .part file + * @return bool + */ + public static function isPartialFilePath($path) { + + if (preg_match('/\.part$/', $path) || preg_match('/\.etmp$/', $path)) { + + return true; + + } else { + + return false; + + } + + } + /** * @brief retrieve keyfile for an encrypted file * @param \OC_FilesystemView $view @@ -156,168 +213,359 @@ class Keymanager { * @note The keyfile returned is asymmetrically encrypted. Decryption * of the keyfile must be performed by client code */ - public static function getFileKey( \OC_FilesystemView $view, $userId, $filePath ) { - - $filePath_f = ltrim( $filePath, '/' ); - - $catfilePath = '/' . $userId . '/files_encryption/keyfiles/' . $filePath_f . '.key'; - - if ( $view->file_exists( $catfilePath ) ) { + public static function getFileKey(\OC_FilesystemView $view, $userId, $filePath) { + + // try reusing key file if part file + if (self::isPartialFilePath($filePath)) { + + $result = self::getFileKey($view, $userId, self::fixPartialFilePath($filePath)); + + if ($result) { + + return $result; + + } - return $view->file_get_contents( $catfilePath ); - - } else { - - return false; - } - + + $util = new Util($view, \OCP\User::getUser()); + + list($owner, $filename) = $util->getUidAndFilename($filePath); + $filePath_f = ltrim($filename, '/'); + + $keyfilePath = '/' . $owner . '/files_encryption/keyfiles/' . $filePath_f . '.key'; + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + if ($view->file_exists($keyfilePath)) { + + $result = $view->file_get_contents($keyfilePath); + + } else { + + $result = false; + + } + + \OC_FileProxy::$enabled = $proxyStatus; + + return $result; + } - + /** * @brief Delete a keyfile * - * @param OC_FilesystemView $view + * @param \OC_FilesystemView $view * @param string $userId username * @param string $path path of the file the key belongs to * @return bool Outcome of unlink operation * @note $path must be relative to data/user/files. e.g. mydoc.txt NOT * /data/admin/files/mydoc.txt */ - public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) { - - $trimmed = ltrim( $path, '/' ); - $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed . '.key'; - - // Unlink doesn't tell us if file was deleted (not found returns - // true), so we perform our own test - if ( $view->file_exists( $keyPath ) ) { - - return $view->unlink( $keyPath ); - + public static function deleteFileKey(\OC_FilesystemView $view, $userId, $path) { + + $trimmed = ltrim($path, '/'); + $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed; + + $result = false; + + if ($view->is_dir($keyPath)) { + + $result = $view->unlink($keyPath); + } else { - - \OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR ); - - return false; - + if ($view->file_exists($keyPath . '.key')) { + + $result = $view->unlink($keyPath . '.key'); + + } } - + + if (!$result) { + + \OCP\Util::writeLog('Encryption library', + 'Could not delete keyfile; does not exist: "' . $keyPath, \OCP\Util::ERROR); + + } + + return $result; + } - + /** * @brief store private key from the user - * @param string key + * @param string $key * @return bool * @note Encryption of the private key must be performed by client code * as no encryption takes place here */ - public static function setPrivateKey( $key ) { - + public static function setPrivateKey($key) { + $user = \OCP\User::getUser(); - - $view = new \OC_FilesystemView( '/' . $user . '/files_encryption' ); - + + $view = new \OC_FilesystemView('/' . $user . '/files_encryption'); + + $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - - if ( !$view->file_exists( '' ) ) - $view->mkdir( '' ); - - return $view->file_put_contents( $user . '.private.key', $key ); + + if (!$view->file_exists('')) + $view->mkdir(''); + + $result = $view->file_put_contents($user . '.private.key', $key); + + \OC_FileProxy::$enabled = $proxyStatus; + + return $result; } - - /** - * @brief store private keys from the user - * - * @param string privatekey - * @param string publickey - * @return bool true/false - */ - public static function setUserKeys($privatekey, $publickey) { - - return ( self::setPrivateKey( $privatekey ) && self::setPublicKey( $publickey ) ); - - } - - /** - * @brief store public key of the user - * - * @param string key - * @return bool true/false - */ - public static function setPublicKey( $key ) { - - $view = new \OC_FilesystemView( '/public-keys' ); - - \OC_FileProxy::$enabled = false; - - if ( !$view->file_exists( '' ) ) - $view->mkdir( '' ); - - return $view->file_put_contents( \OCP\User::getUser() . '.public.key', $key ); - - } - /** - * @brief store file encryption key + * @brief store share key * + * @param \OC_FilesystemView $view * @param string $path relative path of the file, including filename - * @param string $key - * @param null $view - * @param string $dbClassName + * @param $userId + * @param $shareKey + * @internal param string $key + * @internal param string $dbClassName * @return bool true/false * @note The keyfile is not encrypted here. Client code must * asymmetrically encrypt the keyfile before passing it to this method */ - public static function setShareKey( \OC_FilesystemView $view, $path, $userId, $shareKey ) { - - $basePath = '/' . $userId . '/files_encryption/share-keys'; - - $shareKeyPath = self::keySetPreparation( $view, $path, $basePath, $userId ); - - return $view->file_put_contents( $basePath . '/' . $shareKeyPath . '.shareKey', $shareKey ); - - } - - /** - * @brief Make preparations to vars and filesystem for saving a keyfile - */ - public static function keySetPreparation( \OC_FilesystemView $view, $path, $basePath, $userId ) { - - $targetPath = ltrim( $path, '/' ); - - $path_parts = pathinfo( $targetPath ); - - // If the file resides within a subdirectory, create it - if ( - isset( $path_parts['dirname'] ) - && ! $view->file_exists( $basePath . '/' . $path_parts['dirname'] ) - ) { - - $view->mkdir( $basePath . '/' . $path_parts['dirname'] ); - + public static function setShareKey(\OC_FilesystemView $view, $path, $userId, $shareKey) { + + // Here we need the currently logged in user, while userId can be a different user + $util = new Util($view, \OCP\User::getUser()); + + list($owner, $filename) = $util->getUidAndFilename($path); + + $basePath = '/' . $owner . '/files_encryption/share-keys'; + + $shareKeyPath = self::keySetPreparation($view, $filename, $basePath, $owner); + + // try reusing key file if part file + if (self::isPartialFilePath($shareKeyPath)) { + + $writePath = $basePath . '/' . self::fixPartialFilePath($shareKeyPath) . '.' . $userId . '.shareKey'; + + } else { + + $writePath = $basePath . '/' . $shareKeyPath . '.' . $userId . '.shareKey'; + } - - return $targetPath; - + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $result = $view->file_put_contents($writePath, $shareKey); + + \OC_FileProxy::$enabled = $proxyStatus; + + if ( + is_int($result) + && $result > 0 + ) { + + return true; + + } else { + + return false; + + } + } /** - * @brief Fetch the legacy encryption key from user files - * @param string $login used to locate the legacy key - * @param string $passphrase used to decrypt the legacy key - * @return true / false - * - * if the key is left out, the default handler will be used + * @brief store multiple share keys for a single file + * @param \OC_FilesystemView $view + * @param $path + * @param array $shareKeys + * @return bool */ - public function getLegacyKey() { - - $user = \OCP\User::getUser(); - $view = new \OC_FilesystemView( '/' . $user ); - return $view->file_get_contents( 'encryption.key' ); - + public static function setShareKeys(\OC_FilesystemView $view, $path, array $shareKeys) { + + // $shareKeys must be an array with the following format: + // [userId] => [encrypted key] + + $result = true; + + foreach ($shareKeys as $userId => $shareKey) { + + if (!self::setShareKey($view, $path, $userId, $shareKey)) { + + // If any of the keys are not set, flag false + $result = false; + + } + + } + + // Returns false if any of the keys weren't set + return $result; + + } + + /** + * @brief retrieve shareKey for an encrypted file + * @param \OC_FilesystemView $view + * @param string $userId + * @param string $filePath + * @internal param \OCA\Encryption\file $string name + * @return string file key or false + * @note The sharekey returned is encrypted. Decryption + * of the keyfile must be performed by client code + */ + public static function getShareKey(\OC_FilesystemView $view, $userId, $filePath) { + + // try reusing key file if part file + if (self::isPartialFilePath($filePath)) { + + $result = self::getShareKey($view, $userId, self::fixPartialFilePath($filePath)); + + if ($result) { + + return $result; + + } + + } + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + //here we need the currently logged in user, while userId can be a different user + $util = new Util($view, \OCP\User::getUser()); + + list($owner, $filename) = $util->getUidAndFilename($filePath); + $shareKeyPath = \OC\Files\Filesystem::normalizePath( + '/' . $owner . '/files_encryption/share-keys/' . $filename . '.' . $userId . '.shareKey'); + + if ($view->file_exists($shareKeyPath)) { + + $result = $view->file_get_contents($shareKeyPath); + + } else { + + $result = false; + + } + + \OC_FileProxy::$enabled = $proxyStatus; + + return $result; + + } + + /** + * @brief delete all share keys of a given file + * @param \OC_FilesystemView $view + * @param string $userId owner of the file + * @param string $filePath path to the file, relative to the owners file dir + */ + public static function delAllShareKeys(\OC_FilesystemView $view, $userId, $filePath) { + + if ($view->is_dir($userId . '/files/' . $filePath)) { + $view->unlink($userId . '/files_encryption/share-keys/' . $filePath); + } else { + $localKeyPath = $view->getLocalFile($userId . '/files_encryption/share-keys/' . $filePath); + $matches = glob(preg_quote($localKeyPath) . '*.shareKey'); + foreach ($matches as $ma) { + $result = unlink($ma); + if (!$result) { + \OCP\Util::writeLog('Encryption library', + 'Keyfile or shareKey could not be deleted for file "' . $filePath . '"', \OCP\Util::ERROR); + } + } + } + } + + /** + * @brief Delete a single user's shareKey for a single file + */ + public static function delShareKey(\OC_FilesystemView $view, $userIds, $filePath) { + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + //here we need the currently logged in user, while userId can be a different user + $util = new Util($view, \OCP\User::getUser()); + + list($owner, $filename) = $util->getUidAndFilename($filePath); + + $shareKeyPath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files_encryption/share-keys/' . $filename); + + if ($view->is_dir($shareKeyPath)) { + + $localPath = \OC\Files\Filesystem::normalizePath($view->getLocalFolder($shareKeyPath)); + self::recursiveDelShareKeys($localPath, $userIds); + + } else { + + foreach ($userIds as $userId) { + + if (!$view->unlink($shareKeyPath . '.' . $userId . '.shareKey')) { + \OCP\Util::writeLog('Encryption library', + 'Could not delete shareKey; does not exist: "' . $shareKeyPath . '.' . $userId + . '.shareKey"', \OCP\Util::ERROR); + } + + } + } + + \OC_FileProxy::$enabled = $proxyStatus; + } + + /** + * @brief recursively delete share keys from given users + * + * @param string $dir directory + * @param array $userIds user ids for which the share keys should be deleted + */ + private static function recursiveDelShareKeys($dir, $userIds) { + foreach ($userIds as $userId) { + $matches = glob(preg_quote($dir) . '/*' . preg_quote('.' . $userId . '.shareKey')); + } + /** @var $matches array */ + foreach ($matches as $ma) { + if (!unlink($ma)) { + \OCP\Util::writeLog('Encryption library', + 'Could not delete shareKey; does not exist: "' . $ma . '"', \OCP\Util::ERROR); + } + } + $subdirs = $directories = glob(preg_quote($dir) . '/*', GLOB_ONLYDIR); + foreach ($subdirs as $subdir) { + self::recursiveDelShareKeys($subdir, $userIds); + } + } + + /** + * @brief Make preparations to vars and filesystem for saving a keyfile + */ + public static function keySetPreparation(\OC_FilesystemView $view, $path, $basePath, $userId) { + + $targetPath = ltrim($path, '/'); + + $path_parts = pathinfo($targetPath); + + // If the file resides within a subdirectory, create it + if ( + isset($path_parts['dirname']) + && !$view->file_exists($basePath . '/' . $path_parts['dirname']) + ) { + $sub_dirs = explode(DIRECTORY_SEPARATOR, $basePath . '/' . $path_parts['dirname']); + $dir = ''; + foreach ($sub_dirs as $sub_dir) { + $dir .= '/' . $sub_dir; + if (!$view->is_dir($dir)) { + $view->mkdir($dir); + } + } + } + + return $targetPath; + } - } \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 55cddf2bec..0df34a38bd 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -1,41 +1,45 @@ . -* -*/ + * ownCloud + * + * @author Sam Tuke, Robin Appelman + * @copyright 2012 Sam Tuke samtuke@owncloud.com, Robin Appelman + * icewind1991@gmail.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 . + * + */ /** -* @brief Encryption proxy which handles filesystem operations before and after -* execution and encrypts, and handles keyfiles accordingly. Used for -* webui. -*/ + * @brief Encryption proxy which handles filesystem operations before and after + * execution and encrypts, and handles keyfiles accordingly. Used for + * webui. + */ namespace OCA\Encryption; +/** + * Class Proxy + * @package OCA\Encryption + */ class Proxy extends \OC_FileProxy { private static $blackList = null; //mimetypes blacklisted from encryption - + private static $enableEncryption = null; - + /** * Check if a file requires encryption * @param string $path @@ -43,347 +47,383 @@ class Proxy extends \OC_FileProxy { * * Tests if server side encryption is enabled, and file is allowed by blacklists */ - private static function shouldEncrypt( $path ) { - - if ( is_null( self::$enableEncryption ) ) { - - if ( - \OCP\Config::getAppValue( 'files_encryption', 'enable_encryption', 'true' ) == 'true' - && Crypt::mode() == 'server' + private static function shouldEncrypt($path) { + + if (is_null(self::$enableEncryption)) { + + if ( + \OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true') === 'true' + && Crypt::mode() === 'server' ) { - + self::$enableEncryption = true; - + } else { - + self::$enableEncryption = false; - + } - + } - - if ( !self::$enableEncryption ) { - + + if (!self::$enableEncryption) { + return false; - + } - - if ( is_null(self::$blackList ) ) { - - self::$blackList = explode(',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) ); - + + if (is_null(self::$blackList)) { + + self::$blackList = explode(',', \OCP\Config::getAppValue('files_encryption', 'type_blacklist', '')); + } - - if ( Crypt::isCatfile( $path ) ) { - + + if (Crypt::isCatfileContent($path)) { + return true; - + } - - $extension = substr( $path, strrpos( $path, '.' ) +1 ); - - if ( array_search( $extension, self::$blackList ) === false ) { - + + $extension = substr($path, strrpos($path, '.') + 1); + + if (array_search($extension, self::$blackList) === false) { + return true; - + } - + return false; } - - public function preFile_put_contents( $path, &$data ) { - - if ( self::shouldEncrypt( $path ) ) { - - if ( !is_resource( $data ) ) { //stream put contents should have been converted to fopen - - $userId = \OCP\USER::getUser(); - - $rootView = new \OC_FilesystemView( '/' ); - - // Set the filesize for userland, before encrypting - $size = strlen( $data ); - - // Disable encryption proxy to prevent recursive calls - \OC_FileProxy::$enabled = false; - - // TODO: Check if file is shared, if so, use multiKeyEncrypt - - // Encrypt plain data and fetch key - $encrypted = Crypt::keyEncryptKeyfile( $data, Keymanager::getPublicKey( $rootView, $userId ) ); - - // Replace plain content with encrypted content by reference - $data = $encrypted['data']; - - $filePath = explode( '/', $path ); - - $filePath = array_slice( $filePath, 3 ); - - $filePath = '/' . implode( '/', $filePath ); - - // TODO: make keyfile dir dynamic from app config - - $view = new \OC_FilesystemView( '/' ); - - // Save keyfile for newly encrypted file in parallel directory tree - Keymanager::setFileKey( $view, $filePath, $userId, $encrypted['key'] ); - - // Update the file cache with file info - \OC\Files\Filesystem::putFileInfo( $path, array( 'encrypted'=>true, 'size' => $size ), '' ); - - // Re-enable proxy - our work is done - \OC_FileProxy::$enabled = true; - + + /** + * @param $path + * @param $data + * @return bool + */ + public function preFile_put_contents($path, &$data) { + + if (self::shouldEncrypt($path)) { + + if (!is_resource($data)) { + + // get root view + $view = new \OC_FilesystemView('/'); + + // get relative path + $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); + + if (!isset($relativePath)) { + return true; + } + + $handle = fopen('crypt://' . $relativePath . '.etmp', 'w'); + if (is_resource($handle)) { + + // write data to stream + fwrite($handle, $data); + + // close stream + fclose($handle); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get encrypted content + $data = $view->file_get_contents($path . '.etmp'); + + // remove our temp file + $view->unlink($path . '.etmp'); + + // re-enable proxy - our work is done + \OC_FileProxy::$enabled = $proxyStatus; + } } } - + + return true; + } - + /** * @param string $path Path of file from which has been read * @param string $data Data that has been read from file */ - public function postFile_get_contents( $path, $data ) { - - // TODO: Use dependency injection to add required args for view and user etc. to this method + public function postFile_get_contents($path, $data) { + + $plainData = null; + $view = new \OC_FilesystemView('/'); + + // get relative path + $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); + + // init session + $session = new \OCA\Encryption\Session($view); - // Disable encryption proxy to prevent recursive calls - \OC_FileProxy::$enabled = false; - // If data is a catfile - if ( - Crypt::mode() == 'server' - && Crypt::isCatfile( $data ) + if ( + Crypt::mode() === 'server' + && Crypt::isCatfileContent($data) ) { - - $split = explode( '/', $path ); - - $filePath = array_slice( $split, 3 ); - - $filePath = '/' . implode( '/', $filePath ); - - //$cached = \OC\Files\Filesystem::getFileInfo( $path, '' ); - - $view = new \OC_FilesystemView( '' ); - - $userId = \OCP\USER::getUser(); - - // TODO: Check if file is shared, if so, use multiKeyDecrypt - - $encryptedKeyfile = Keymanager::getFileKey( $view, $userId, $filePath ); - $session = new Session(); - - $decrypted = Crypt::keyDecryptKeyfile( $data, $encryptedKeyfile, $session->getPrivateKey( $split[1] ) ); - + $handle = fopen('crypt://' . $relativePath, 'r'); + + if (is_resource($handle)) { + while (($plainDataChunk = fgets($handle, 8192)) !== false) { + $plainData .= $plainDataChunk; + } + } + } elseif ( - Crypt::mode() == 'server' - && isset( $_SESSION['legacyenckey'] ) - && Crypt::isEncryptedMeta( $path ) + Crypt::mode() == 'server' + && \OC::$session->exists('legacyenckey') + && Crypt::isEncryptedMeta($path) ) { - - $decrypted = Crypt::legacyDecrypt( $data, $_SESSION['legacyenckey'] ); - + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $plainData = Crypt::legacyBlockDecrypt($data, $session->getLegacyKey()); + + \OC_FileProxy::$enabled = $proxyStatus; } - - \OC_FileProxy::$enabled = true; - - if ( ! isset( $decrypted ) ) { - - $decrypted = $data; - + + if (!isset($plainData)) { + + $plainData = $data; + } - - return $decrypted; - + + return $plainData; + } - + /** * @brief When a file is deleted, remove its keyfile also */ - public function preUnlink( $path ) { - - // Disable encryption proxy to prevent recursive calls - \OC_FileProxy::$enabled = false; - - $view = new \OC_FilesystemView( '/' ); - - $userId = \OCP\USER::getUser(); - - // Format path to be relative to user files dir - $trimmed = ltrim( $path, '/' ); - $split = explode( '/', $trimmed ); - $sliced = array_slice( $split, 2 ); - $relPath = implode( '/', $sliced ); - - if ( $view->is_dir( $path ) ) { - - // Dirs must be handled separately as deleteFileKey - // doesn't handle them - $view->unlink( $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/'. $relPath ); - - } else { - - // Delete keyfile so it isn't orphaned - $result = Keymanager::deleteFileKey( $view, $userId, $relPath ); - - \OC_FileProxy::$enabled = true; - - return $result; - + public function preUnlink($path) { + + // let the trashbin handle this + if (\OCP\App::isEnabled('files_trashbin')) { + return true; } - + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $view = new \OC_FilesystemView('/'); + + $userId = \OCP\USER::getUser(); + + $util = new Util($view, $userId); + + // get relative path + $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); + + list($owner, $ownerPath) = $util->getUidAndFilename($relativePath); + + // Delete keyfile & shareKey so it isn't orphaned + if (!Keymanager::deleteFileKey($view, $owner, $ownerPath)) { + \OCP\Util::writeLog('Encryption library', + 'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OCP\Util::ERROR); + } + + Keymanager::delAllShareKeys($view, $owner, $ownerPath); + + \OC_FileProxy::$enabled = $proxyStatus; + + // If we don't return true then file delete will fail; better + // to leave orphaned keyfiles than to disallow file deletion + return true; + } /** - * @brief When a file is renamed, rename its keyfile also - * @return bool Result of rename() - * @note This is pre rather than post because using post didn't work + * @param $path + * @return bool */ - public function preRename( $oldPath, $newPath ) { - - // Disable encryption proxy to prevent recursive calls - \OC_FileProxy::$enabled = false; - - $view = new \OC_FilesystemView( '/' ); - - $userId = \OCP\USER::getUser(); - - // Format paths to be relative to user files dir - $oldTrimmed = ltrim( $oldPath, '/' ); - $oldSplit = explode( '/', $oldTrimmed ); - $oldSliced = array_slice( $oldSplit, 2 ); - $oldRelPath = implode( '/', $oldSliced ); - $oldKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $oldRelPath . '.key'; - - $newTrimmed = ltrim( $newPath, '/' ); - $newSplit = explode( '/', $newTrimmed ); - $newSliced = array_slice( $newSplit, 2 ); - $newRelPath = implode( '/', $newSliced ); - $newKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $newRelPath . '.key'; - - // Rename keyfile so it isn't orphaned - $result = $view->rename( $oldKeyfilePath, $newKeyfilePath ); - - \OC_FileProxy::$enabled = true; - - return $result; - + public function postTouch($path) { + $this->handleFile($path); + + return true; } - - public function postFopen( $path, &$result ){ - - if ( !$result ) { - + + /** + * @param $path + * @param $result + * @return resource + */ + public function postFopen($path, &$result) { + + if (!$result) { + return $result; - + } - - // Reformat path for use with OC_FSV - $path_split = explode( '/', $path ); - $path_f = implode( array_slice( $path_split, 3 ) ); - + + // split the path parts + $pathParts = explode('/', $path); + + // get relative path + $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); + + // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted + if (isset($pathParts[2]) && $pathParts[2] === 'cache') { + return $result; + } + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - - $meta = stream_get_meta_data( $result ); - - $view = new \OC_FilesystemView( '' ); - - $util = new Util( $view, \OCP\USER::getUser()); - + + $meta = stream_get_meta_data($result); + + $view = new \OC_FilesystemView(''); + + $util = new Util($view, \OCP\USER::getUser()); + // If file is already encrypted, decrypt using crypto protocol - if ( - Crypt::mode() == 'server' - && $util->isEncryptedPath( $path ) + if ( + Crypt::mode() === 'server' + && $util->isEncryptedPath($path) ) { - + // Close the original encrypted file - fclose( $result ); - + fclose($result); + // Open the file using the crypto stream wrapper // protocol and let it do the decryption work instead - $result = fopen( 'crypt://' . $path_f, $meta['mode'] ); - - - } elseif ( - self::shouldEncrypt( $path ) - and $meta ['mode'] != 'r' - and $meta['mode'] != 'rb' + $result = fopen('crypt://' . $relativePath, $meta['mode']); + + } elseif ( + self::shouldEncrypt($path) + and $meta ['mode'] !== 'r' + and $meta['mode'] !== 'rb' ) { - // If the file is not yet encrypted, but should be - // encrypted when it's saved (it's not read only) - - // NOTE: this is the case for new files saved via WebDAV - - if ( - $view->file_exists( $path ) - and $view->filesize( $path ) > 0 - ) { - $x = $view->file_get_contents( $path ); - - $tmp = tmpfile(); - -// // Make a temporary copy of the original file -// \OCP\Files::streamCopy( $result, $tmp ); -// -// // Close the original stream, we'll return another one -// fclose( $result ); -// -// $view->file_put_contents( $path_f, $tmp ); -// -// fclose( $tmp ); - - } - - $result = fopen( 'crypt://'.$path_f, $meta['mode'] ); - + $result = fopen('crypt://' . $relativePath, $meta['mode']); } - + // Re-enable the proxy - \OC_FileProxy::$enabled = true; - + \OC_FileProxy::$enabled = $proxyStatus; + return $result; - + } - public function postGetMimeType( $path, $mime ) { - - if ( Crypt::isCatfile( $path ) ) { - - $mime = \OCP\Files::getMimeType( 'crypt://' . $path, 'w' ); - - } - - return $mime; - - } + /** + * @param $path + * @param $data + * @return array + */ + public function postGetFileInfo($path, $data) { - public function postStat( $path, $data ) { - - if ( Crypt::isCatfile( $path ) ) { - - $cached = \OC\Files\Filesystem::getFileInfo( $path, '' ); - - $data['size'] = $cached['size']; - + // if path is a folder do nothing + if (is_array($data) && array_key_exists('size', $data)) { + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get file size + $data['size'] = self::postFileSize($path, $data['size']); + + // Re-enable the proxy + \OC_FileProxy::$enabled = $proxyStatus; } - + return $data; } - public function postFileSize( $path, $size ) { - - if ( Crypt::isCatfile( $path ) ) { - - $cached = \OC\Files\Filesystem::getFileInfo( $path, '' ); - - return $cached['size']; - - } else { - + /** + * @param $path + * @param $size + * @return bool + */ + public function postFileSize($path, $size) { + + $view = new \OC_FilesystemView('/'); + + // if path is a folder do nothing + if ($view->is_dir($path)) { return $size; - } + + // get relative path + $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); + + // if path is empty we cannot resolve anything + if (empty($relativePath)) { + return $size; + } + + $fileInfo = false; + // get file info from database/cache if not .part file + if (!Keymanager::isPartialFilePath($path)) { + $fileInfo = $view->getFileInfo($path); + } + + // if file is encrypted return real file size + if (is_array($fileInfo) && $fileInfo['encrypted'] === true) { + $size = $fileInfo['unencrypted_size']; + } else { + // self healing if file was removed from file cache + if (!is_array($fileInfo)) { + $fileInfo = array(); + } + + $userId = \OCP\User::getUser(); + $util = new Util($view, $userId); + $fixSize = $util->getFileSize($path); + if ($fixSize > 0) { + $size = $fixSize; + + $fileInfo['encrypted'] = true; + $fileInfo['unencrypted_size'] = $size; + + // put file info if not .part file + if (!Keymanager::isPartialFilePath($relativePath)) { + $view->putFileInfo($path, $fileInfo); + } + } + + } + return $size; + } + + /** + * @param $path + */ + public function handleFile($path) { + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $view = new \OC_FilesystemView('/'); + $session = new \OCA\Encryption\Session($view); + $userId = \OCP\User::getUser(); + $util = new Util($view, $userId); + + // split the path parts + $pathParts = explode('/', $path); + + // get relative path + $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); + + // only if file is on 'files' folder fix file size and sharing + if (isset($pathParts[2]) && $pathParts[2] === 'files' && $util->fixFileSize($path)) { + + // get sharing app state + $sharingEnabled = \OCP\Share::isEnabled(); + + // get users + $usersSharing = $util->getSharingUsersArray($sharingEnabled, $relativePath); + + // update sharing-keys + $util->setSharedFileKeyfiles($session, $usersSharing, $relativePath); + } + + \OC_FileProxy::$enabled = $proxyStatus; } } diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php index 769a40b359..bff1737554 100644 --- a/apps/files_encryption/lib/session.php +++ b/apps/files_encryption/lib/session.php @@ -28,76 +28,164 @@ namespace OCA\Encryption; class Session { + private $view; + + /** + * @brief if session is started, check if ownCloud key pair is set up, if not create it + * @param \OC_FilesystemView $view + * + * @note The ownCloud key pair is used to allow public link sharing even if encryption is enabled + */ + public function __construct($view) { + + $this->view = $view; + + if (!$this->view->is_dir('owncloud_private_key')) { + + $this->view->mkdir('owncloud_private_key'); + + } + + $publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId'); + + if ($publicShareKeyId === null) { + $publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8); + \OC_Appconfig::setValue('files_encryption', 'publicShareKeyId', $publicShareKeyId); + } + + if ( + !$this->view->file_exists("/public-keys/" . $publicShareKeyId . ".public.key") + || !$this->view->file_exists("/owncloud_private_key/" . $publicShareKeyId . ".private.key") + ) { + + $keypair = Crypt::createKeypair(); + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // Save public key + + if (!$view->is_dir('/public-keys')) { + $view->mkdir('/public-keys'); + } + + $this->view->file_put_contents('/public-keys/' . $publicShareKeyId . '.public.key', $keypair['publicKey']); + + // Encrypt private key empty passphrase + $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], ''); + + // Save private key + $this->view->file_put_contents( + '/owncloud_private_key/' . $publicShareKeyId . '.private.key', $encryptedPrivateKey); + + \OC_FileProxy::$enabled = $proxyStatus; + + } + + if (\OCA\Encryption\Helper::isPublicAccess()) { + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $encryptedKey = $this->view->file_get_contents( '/owncloud_private_key/' . $publicShareKeyId . '.private.key' ); + $privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, '' ); + $this->setPublicSharePrivateKey( $privateKey ); + + \OC_FileProxy::$enabled = $proxyStatus; + } + } + /** * @brief Sets user private key to session + * @param string $privateKey * @return bool * + * @note this should only be set on login */ - public function setPrivateKey( $privateKey ) { - - $_SESSION['privateKey'] = $privateKey; - + public function setPrivateKey($privateKey) { + + \OC::$session->set('privateKey', $privateKey); + return true; - + } - + /** - * @brief Gets user private key from session + * @brief Gets user or public share private key from session * @returns string $privateKey The user's plaintext private key * */ public function getPrivateKey() { - - if ( - isset( $_SESSION['privateKey'] ) - && !empty( $_SESSION['privateKey'] ) - ) { - - return $_SESSION['privateKey']; - + // return the public share private key if this is a public access + if (\OCA\Encryption\Helper::isPublicAccess()) { + return $this->getPublicSharePrivateKey(); } else { - - return false; - + if (!is_null( \OC::$session->get('privateKey') )) { + return \OC::$session->get('privateKey'); + } else { + return false; + } } - } - + /** - * @brief Sets user legacy key to session + * @brief Sets public user private key to session + * @param string $privateKey * @return bool + */ + public function setPublicSharePrivateKey($privateKey) { + + \OC::$session->set('publicSharePrivateKey', $privateKey); + + return true; + + } + + /** + * @brief Gets public share private key from session + * @returns string $privateKey * */ - public function setLegacyKey( $legacyKey ) { - - if ( $_SESSION['legacyKey'] = $legacyKey ) { - - return true; - + public function getPublicSharePrivateKey() { + + if (!is_null( \OC::$session->get('publicSharePrivateKey') )) { + return \OC::$session->get('publicSharePrivateKey'); + } else { + return false; } - } - + + + /** + * @brief Sets user legacy key to session + * @param $legacyKey + * @return bool + */ + public function setLegacyKey($legacyKey) { + + \OC::$session->set('legacyKey', $legacyKey); + + return true; + } + /** * @brief Gets user legacy key from session * @returns string $legacyKey The user's plaintext legacy key * */ public function getLegacyKey() { - - if ( - isset( $_SESSION['legacyKey'] ) - && !empty( $_SESSION['legacyKey'] ) - ) { - - return $_SESSION['legacyKey']; - + + if ( !is_null( \OC::$session->get('legacyKey') ) ) { + + return \OC::$session->get('legacyKey'); + } else { - + return false; - + } - + } -} \ No newline at end of file +} diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 65d7d57a05..072c528664 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -3,7 +3,7 @@ * ownCloud * * @author Robin Appelman - * @copyright 2012 Sam Tuke , 2011 Robin Appelman + * @copyright 2012 Sam Tuke , 2011 Robin Appelman * * * This library is free software; you can redistribute it and/or @@ -32,27 +32,28 @@ namespace OCA\Encryption; /** * @brief Provides 'crypt://' stream wrapper protocol. - * @note We use a stream wrapper because it is the most secure way to handle + * @note We use a stream wrapper because it is the most secure way to handle * decrypted content transfers. There is no safe way to decrypt the entire file * somewhere on the server, so we have to encrypt and decrypt blocks on the fly. * @note Paths used with this protocol MUST BE RELATIVE. Use URLs like: - * crypt://filename, or crypt://subdirectory/filename, NOT - * crypt:///home/user/owncloud/data. Otherwise keyfiles will be put in - * [owncloud]/data/user/files_encryption/keyfiles/home/user/owncloud/data and + * crypt://filename, or crypt://subdirectory/filename, NOT + * crypt:///home/user/owncloud/data. Otherwise keyfiles will be put in + * [owncloud]/data/user/files_encryption/keyfiles/home/user/owncloud/data and * will not be accessible to other methods. - * @note Data read and written must always be 8192 bytes long, as this is the - * buffer size used internally by PHP. The encryption process makes the input - * data longer, and input is chunked into smaller pieces in order to result in + * @note Data read and written must always be 8192 bytes long, as this is the + * buffer size used internally by PHP. The encryption process makes the input + * data longer, and input is chunked into smaller pieces in order to result in * a 8192 encrypted block size. + * @note When files are deleted via webdav, or when they are updated and the + * previous version deleted, this is handled by OC\Files\View, and thus the + * encryption proxies are used and keyfiles deleted. */ class Stream { + private $plainKey; + private $encKeyfiles; - public static $sourceStreams = array(); - - // TODO: make all below properties private again once unit testing is - // configured correctly - public $rawPath; // The raw path received by stream_open - public $path_f; // The raw path formatted to include username and data dir + private $rawPath; // The raw path relative to the data dir + private $relPath; // rel path to users file dir private $userId; private $handle; // Resource returned by fopen private $path; @@ -60,226 +61,191 @@ class Stream { private $meta = array(); // Header / meta for source stream private $count; private $writeCache; - public $size; + private $size; + private $unencryptedSize; private $publicKey; private $keyfile; private $encKeyfile; private static $view; // a fsview object set to user dir private $rootView; // a fsview object set to '/' - public function stream_open( $path, $mode, $options, &$opened_path ) { - - // Get access to filesystem via filesystemview object - if ( !self::$view ) { - - self::$view = new \OC_FilesystemView( $this->userId . '/' ); + /** + * @param $path + * @param $mode + * @param $options + * @param $opened_path + * @return bool + */ + public function stream_open($path, $mode, $options, &$opened_path) { + if (!isset($this->rootView)) { + $this->rootView = new \OC_FilesystemView('/'); } - - // Set rootview object if necessary - if ( ! $this->rootView ) { - $this->rootView = new \OC_FilesystemView( $this->userId . '/' ); + $util = new Util($this->rootView, \OCP\USER::getUser()); - } - - $this->userId = \OCP\User::getUser(); - - // Get the bare file path - $path = str_replace( 'crypt://', '', $path ); - - $this->rawPath = $path; - - $this->path_f = $this->userId . '/files/' . $path; - - if ( - dirname( $path ) == 'streams' - and isset( self::$sourceStreams[basename( $path )] ) + $this->userId = $util->getUserId(); + + // Strip identifier text from path, this gives us the path relative to data//files + $this->relPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + + // rawPath is relative to the data directory + $this->rawPath = $util->getUserFilesDir() . $this->relPath; + + // Disable fileproxies so we can get the file size and open the source file without recursive encryption + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + if ( + $mode === 'w' + or $mode === 'w+' + or $mode === 'wb' + or $mode === 'wb+' ) { - - // Is this just for unit testing purposes? - $this->handle = self::$sourceStreams[basename( $path )]['stream']; - - $this->path = self::$sourceStreams[basename( $path )]['path']; - - $this->size = self::$sourceStreams[basename( $path )]['size']; + // We're writing a new file so start write counter with 0 bytes + $this->size = 0; + $this->unencryptedSize = 0; } else { - if ( - $mode == 'w' - or $mode == 'w+' - or $mode == 'wb' - or $mode == 'wb+' - ) { + $this->size = $this->rootView->filesize($this->rawPath, $mode); + } - $this->size = 0; + $this->handle = $this->rootView->fopen($this->rawPath, $mode); - } else { - - - - $this->size = self::$view->filesize( $this->path_f, $mode ); - - //$this->size = filesize( $path ); - - } + \OC_FileProxy::$enabled = $proxyStatus; - // Disable fileproxies so we can open the source file without recursive encryption - \OC_FileProxy::$enabled = false; + if (!is_resource($this->handle)) { - //$this->handle = fopen( $path, $mode ); - - $this->handle = self::$view->fopen( $this->path_f, $mode ); - - \OC_FileProxy::$enabled = true; + \OCP\Util::writeLog('files_encryption', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR); - if ( !is_resource( $this->handle ) ) { + } else { - \OCP\Util::writeLog( 'files_encryption', 'failed to open '.$path, \OCP\Util::ERROR ); - - } + $this->meta = stream_get_meta_data($this->handle); } - if ( is_resource( $this->handle ) ) { - $this->meta = stream_get_meta_data( $this->handle ); - - } - - return is_resource( $this->handle ); + return is_resource($this->handle); } - - public function stream_seek( $offset, $whence = SEEK_SET ) { - + + /** + * @param $offset + * @param int $whence + */ + public function stream_seek($offset, $whence = SEEK_SET) { + $this->flush(); - - fseek( $this->handle, $offset, $whence ); - + + fseek($this->handle, $offset, $whence); + } - - public function stream_tell() { - return ftell($this->handle); - } - - public function stream_read( $count ) { - + + /** + * @param $count + * @return bool|string + * @throws \Exception + */ + public function stream_read($count) { + $this->writeCache = ''; - if ( $count != 8192 ) { - + if ($count !== 8192) { + // $count will always be 8192 https://bugs.php.net/bug.php?id=21641 // This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed' - \OCP\Util::writeLog( 'files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL ); + \OCP\Util::writeLog('files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL); die(); } -// $pos = ftell( $this->handle ); -// // Get the data from the file handle - $data = fread( $this->handle, 8192 ); - - if ( strlen( $data ) ) { - - $this->getKey(); - - $result = Crypt::symmetricDecryptFileContent( $data, $this->keyfile ); - - } else { + $data = fread($this->handle, 8192); - $result = ''; + $result = ''; + + if (strlen($data)) { + + if (!$this->getKey()) { + + // Error! We don't have a key to decrypt the file with + throw new \Exception( + 'Encryption key not found for "' . $this->rawPath . '" during attempted read via stream'); + + } + + // Decrypt data + $result = Crypt::symmetricDecryptFileContent($data, $this->plainKey); } -// $length = $this->size - $pos; -// -// if ( $length < 8192 ) { -// -// $result = substr( $result, 0, $length ); -// -// } - return $result; } - + /** * @brief Encrypt and pad data ready for writing to disk * @param string $plainData data to be encrypted * @param string $key key to use for encryption - * @return encrypted data on success, false on failure + * @return string encrypted data on success, false on failure */ - public function preWriteEncrypt( $plainData, $key ) { - + public function preWriteEncrypt($plainData, $key) { + // Encrypt data to 'catfile', which includes IV - if ( $encrypted = Crypt::symmetricEncryptFileContent( $plainData, $key ) ) { - - return $encrypted; - + if ($encrypted = Crypt::symmetricEncryptFileContent($plainData, $key)) { + + return $encrypted; + } else { - + return false; - + } - + } - + /** - * @brief Get the keyfile for the current file, generate one if necessary - * @param bool $generate if true, a new key will be generated if none can be found + * @brief Fetch the plain encryption key for the file and set it as plainKey property + * @internal param bool $generate if true, a new key will be generated if none can be found * @return bool true on key found and set, false on key not found and new key generated and set */ public function getKey() { - - // If a keyfile already exists for a file named identically to - // file to be written - if ( self::$view->file_exists( $this->userId . '/'. 'files_encryption' . '/' . 'keyfiles' . '/' . $this->rawPath . '.key' ) ) { - - // TODO: add error handling for when file exists but no - // keyfile - - // Fetch existing keyfile - $this->encKeyfile = Keymanager::getFileKey( $this->rootView, $this->userId, $this->rawPath ); - - $this->getUser(); - - $session = new Session(); - - $privateKey = $session->getPrivateKey( $this->userId ); - - $this->keyfile = Crypt::keyDecrypt( $this->encKeyfile, $privateKey ); - + + // Check if key is already set + if (isset($this->plainKey) && isset($this->encKeyfile)) { + return true; - + + } + + // Fetch and decrypt keyfile + // Fetch existing keyfile + $this->encKeyfile = Keymanager::getFileKey($this->rootView, $this->userId, $this->relPath); + + // If a keyfile already exists + if ($this->encKeyfile) { + + $session = new \OCA\Encryption\Session( $this->rootView ); + + $privateKey = $session->getPrivateKey($this->userId); + + $shareKey = Keymanager::getShareKey($this->rootView, $this->userId, $this->relPath); + + $this->plainKey = Crypt::multiKeyDecrypt($this->encKeyfile, $shareKey, $privateKey); + + return true; + } else { - + return false; - + } - + } - - public function getuser() { - - // Only get the user again if it isn't already set - if ( empty( $this->userId ) ) { - - // TODO: Move this user call out of here - it belongs - // elsewhere - $this->userId = \OCP\User::getUser(); - - } - - // TODO: Add a method for getting the user in case OCP\User:: - // getUser() doesn't work (can that scenario ever occur?) - - } - + /** * @brief Handle plain data from the stream, and write it in 8192 byte blocks * @param string $data data to be written to disk @@ -289,99 +255,55 @@ class Stream { * @note Padding is added to each encrypted block to ensure that the resulting block is exactly 8192 bytes. This is removed during stream_read * @note PHP automatically updates the file pointer after writing data to reflect it's length. There is generally no need to update the poitner manually using fseek */ - public function stream_write( $data ) { - + public function stream_write($data) { + // Disable the file proxies so that encryption is not // automatically attempted when the file is written to disk - // we are handling that separately here and we don't want to // get into an infinite loop + $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - + // Get the length of the unencrypted data that we are handling - $length = strlen( $data ); - - // So far this round, no data has been written - $written = 0; - - // Find out where we are up to in the writing of data to the + $length = strlen($data); + + // Find out where we are up to in the writing of data to the // file - $pointer = ftell( $this->handle ); - - // Make sure the userId is set - $this->getuser(); - - // TODO: Check if file is shared, if so, use multiKeyEncrypt and - // save shareKeys in necessary user directories - + $pointer = ftell($this->handle); + // Get / generate the keyfile for the file we're handling // If we're writing a new file (not overwriting an existing // one), save the newly generated keyfile - if ( ! $this->getKey() ) { - - $this->keyfile = Crypt::generateKey(); - - $this->publicKey = Keymanager::getPublicKey( $this->rootView, $this->userId ); - - $this->encKeyfile = Crypt::keyEncrypt( $this->keyfile, $this->publicKey ); - - $view = new \OC_FilesystemView( '/' ); - $userId = \OCP\User::getUser(); - - // Save the new encrypted file key - Keymanager::setFileKey( $view, $this->rawPath, $userId, $this->encKeyfile ); - + if (!$this->getKey()) { + + $this->plainKey = Crypt::generateKey(); + } // If extra data is left over from the last round, make sure it // is integrated into the next 6126 / 8192 block - if ( $this->writeCache ) { - + if ($this->writeCache) { + // Concat writeCache to start of $data $data = $this->writeCache . $data; - - // Clear the write cache, ready for resuse - it has been + + // Clear the write cache, ready for reuse - it has been // flushed and its old contents processed $this->writeCache = ''; } -// -// // Make sure we always start on a block start - if ( 0 != ( $pointer % 8192 ) ) { - // if the current position of - // file indicator is not aligned to a 8192 byte block, fix it - // so that it is -// fseek( $this->handle, - ( $pointer % 8192 ), SEEK_CUR ); -// -// $pointer = ftell( $this->handle ); -// -// $unencryptedNewBlock = fread( $this->handle, 8192 ); -// -// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR ); -// -// $block = Crypt::symmetricDecryptFileContent( $unencryptedNewBlock, $this->keyfile ); -// -// $x = substr( $block, 0, $currentPos % 8192 ); -// -// $data = $x . $data; -// -// fseek( $this->handle, - ( $currentPos % 8192 ), SEEK_CUR ); -// - } + // While there still remains some data to be processed & written + while (strlen($data) > 0) { + + // Remaining length for this iteration, not of the + // entire file (may be greater than 8192 bytes) + $remainingLength = strlen($data); + + // If data remaining to be written is less than the + // size of 1 6126 byte block + if ($remainingLength < 6126) { -// $currentPos = ftell( $this->handle ); - -// // While there still remains somed data to be processed & written - while( strlen( $data ) > 0 ) { -// -// // Remaining length for this iteration, not of the -// // entire file (may be greater than 8192 bytes) -// $remainingLength = strlen( $data ); -// -// // If data remaining to be written is less than the -// // size of 1 6126 byte block - if ( strlen( $data ) < 6126 ) { - // Set writeCache to contents of $data // The writeCache will be carried over to the // next write round, and added to the start of @@ -394,101 +316,167 @@ class Stream { // Clear $data ready for next round $data = ''; -// + } else { - + // Read the chunk from the start of $data - $chunk = substr( $data, 0, 6126 ); - - $encrypted = $this->preWriteEncrypt( $chunk, $this->keyfile ); - + $chunk = substr($data, 0, 6126); + + $encrypted = $this->preWriteEncrypt($chunk, $this->plainKey); + // Write the data chunk to disk. This will be // attended to the last data chunk if the file // being handled totals more than 6126 bytes - fwrite( $this->handle, $encrypted ); - - $writtenLen = strlen( $encrypted ); - //fseek( $this->handle, $writtenLen, SEEK_CUR ); + fwrite($this->handle, $encrypted); - // Remove the chunk we just processed from + // Remove the chunk we just processed from // $data, leaving only unprocessed data in $data // var, for handling on the next round - $data = substr( $data, 6126 ); + $data = substr($data, 6126); } - + } - $this->size = max( $this->size, $pointer + $length ); - + $this->size = max($this->size, $pointer + $length); + $this->unencryptedSize += $length; + + \OC_FileProxy::$enabled = $proxyStatus; + return $length; } - public function stream_set_option( $option, $arg1, $arg2 ) { - switch($option) { + /** + * @param $option + * @param $arg1 + * @param $arg2 + */ + public function stream_set_option($option, $arg1, $arg2) { + $return = false; + switch ($option) { case STREAM_OPTION_BLOCKING: - stream_set_blocking( $this->handle, $arg1 ); + $return = stream_set_blocking($this->handle, $arg1); break; case STREAM_OPTION_READ_TIMEOUT: - stream_set_timeout( $this->handle, $arg1, $arg2 ); + $return = stream_set_timeout($this->handle, $arg1, $arg2); break; case STREAM_OPTION_WRITE_BUFFER: - stream_set_write_buffer( $this->handle, $arg1, $arg2 ); + $return = stream_set_write_buffer($this->handle, $arg1); } + + return $return; } + /** + * @return array + */ public function stream_stat() { return fstat($this->handle); } - - public function stream_lock( $mode ) { - flock( $this->handle, $mode ); - } - - public function stream_flush() { - - return fflush( $this->handle ); - // Not a typo: http://php.net/manual/en/function.fflush.php - + + /** + * @param $mode + */ + public function stream_lock($mode) { + return flock($this->handle, $mode); } + /** + * @return bool + */ + public function stream_flush() { + + return fflush($this->handle); + // Not a typo: http://php.net/manual/en/function.fflush.php + + } + + /** + * @return bool + */ public function stream_eof() { return feof($this->handle); } private function flush() { - - if ( $this->writeCache ) { - + + if ($this->writeCache) { + // Set keyfile property for file in question $this->getKey(); - - $encrypted = $this->preWriteEncrypt( $this->writeCache, $this->keyfile ); - - fwrite( $this->handle, $encrypted ); - + + $encrypted = $this->preWriteEncrypt($this->writeCache, $this->plainKey); + + fwrite($this->handle, $encrypted); + $this->writeCache = ''; - + } - + } + /** + * @return bool + */ public function stream_close() { - + $this->flush(); - if ( - $this->meta['mode']!='r' - and $this->meta['mode']!='rb' + if ( + $this->meta['mode'] !== 'r' + and $this->meta['mode'] !== 'rb' + and $this->size > 0 ) { + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; - \OC\Files\Filesystem::putFileInfo( $this->path, array( 'encrypted' => true, 'size' => $this->size ), '' ); + // Fetch user's public key + $this->publicKey = Keymanager::getPublicKey($this->rootView, $this->userId); + // Check if OC sharing api is enabled + $sharingEnabled = \OCP\Share::isEnabled(); + + $util = new Util($this->rootView, $this->userId); + + // Get all users sharing the file includes current user + $uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $this->userId); + + // Fetch public keys for all sharing users + $publicKeys = Keymanager::getPublicKeys($this->rootView, $uniqueUserIds); + + // Encrypt enc key for all sharing users + $this->encKeyfiles = Crypt::multiKeyEncrypt($this->plainKey, $publicKeys); + + $view = new \OC_FilesystemView('/'); + + // Save the new encrypted file key + Keymanager::setFileKey($this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data']); + + // Save the sharekeys + Keymanager::setShareKeys($view, $this->relPath, $this->encKeyfiles['keys']); + + // get file info + $fileInfo = $view->getFileInfo($this->rawPath); + if (!is_array($fileInfo)) { + $fileInfo = array(); + } + + // Re-enable proxy - our work is done + \OC_FileProxy::$enabled = $proxyStatus; + + // set encryption data + $fileInfo['encrypted'] = true; + $fileInfo['size'] = $this->size; + $fileInfo['unencrypted_size'] = $this->unencryptedSize; + + // set fileinfo + $view->putFileInfo($this->rawPath, $fileInfo); } - return fclose( $this->handle ); + return fclose($this->handle); } diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 52bc74db27..04bd4dc8ac 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -3,8 +3,8 @@ * ownCloud * * @author Sam Tuke, Frank Karlitschek - * @copyright 2012 Sam Tuke samtuke@owncloud.com, - * Frank Karlitschek frank@owncloud.org + * @copyright 2012 Sam Tuke , + * Frank Karlitschek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -21,78 +21,82 @@ * */ -// Todo: +# Bugs +# ---- +# Sharing a file to a user without encryption set up will not provide them with access but won't notify the sharer +# Sharing all files to admin for recovery purposes still in progress +# Possibly public links are broken (not tested since last merge of master) + + +# Missing features +# ---------------- +# Make sure user knows if large files weren't encrypted + + +# Test +# ---- +# Test that writing files works when recovery is enabled, and sharing API is disabled +# Test trashbin support + + +// Old Todo: // - Crypt/decrypt button in the userinterface // - Setting if crypto should be on by default // - Add a setting "Don´t encrypt files larger than xx because of performance // reasons" -// - Transparent decrypt/encrypt in filesystem.php. Autodetect if a file is -// encrypted (.encrypted extension) -// - Don't use a password directly as encryption key. but a key which is -// stored on the server and encrypted with the user password. -> password -// change faster -// - IMPORTANT! Check if the block lenght of the encrypted data stays the same namespace OCA\Encryption; /** * @brief Class for utilities relating to encrypted file storage system - * @param OC_FilesystemView $view expected to have OC '/' as root path + * @param \OC_FilesystemView $view expected to have OC '/' as root path * @param string $userId ID of the logged in user * @param int $client indicating status of client side encryption. Currently * unused, likely to become obsolete shortly */ class Util { - - + // Web UI: - + //// DONE: files created via web ui are encrypted //// DONE: file created & encrypted via web ui are readable in web ui //// DONE: file created & encrypted via web ui are readable via webdav - - + + // WebDAV: - + //// DONE: new data filled files added via webdav get encrypted //// DONE: new data filled files added via webdav are readable via webdav //// DONE: reading unencrypted files when encryption is enabled works via //// webdav //// DONE: files created & encrypted via web ui are readable via webdav - - + + // Legacy support: - + //// DONE: add method to check if file is encrypted using new system //// DONE: add method to check if file is encrypted using old system //// DONE: add method to fetch legacy key //// DONE: add method to decrypt legacy encrypted data - - + + // Admin UI: - + //// DONE: changing user password also changes encryption passphrase - + //// TODO: add support for optional recovery in case of lost passphrase / keys //// TODO: add admin optional required long passphrase for users - //// TODO: add UI buttons for encrypt / decrypt everything //// TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc. - - - // Sharing: - - //// TODO: add support for encrypting to multiple public keys - //// TODO: add support for decrypting to multiple private keys - - + + // Integration testing: - + //// TODO: test new encryption with versioning - //// TODO: test new encryption with sharing + //// DONE: test new encryption with sharing //// TODO: test new encryption with proxies - - + + private $view; // OC_FilesystemView object for filesystem operations private $userId; // ID of the currently logged-in user private $pwd; // User Password @@ -103,166 +107,322 @@ class Util { private $shareKeysPath; // Dir containing env keys for shared files private $publicKeyPath; // Path to user's public key private $privateKeyPath; // Path to user's private key + private $publicShareKeyId; + private $recoveryKeyId; + private $isPublic; + + /** + * @param \OC_FilesystemView $view + * @param $userId + * @param bool $client + */ + public function __construct(\OC_FilesystemView $view, $userId, $client = false) { - public function __construct( \OC_FilesystemView $view, $userId, $client = false ) { - $this->view = $view; $this->userId = $userId; $this->client = $client; - $this->userDir = '/' . $this->userId; - $this->userFilesDir = '/' . $this->userId . '/' . 'files'; - $this->publicKeyDir = '/' . 'public-keys'; - $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; - $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; - $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys'; - $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key - $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key - - } - - public function ready() { - - if( - !$this->view->file_exists( $this->encryptionDir ) - or !$this->view->file_exists( $this->keyfilesPath ) - or !$this->view->file_exists( $this->shareKeysPath ) - or !$this->view->file_exists( $this->publicKeyPath ) - or !$this->view->file_exists( $this->privateKeyPath ) - ) { - - return false; - + $this->isPublic = false; + + $this->publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId'); + $this->recoveryKeyId = \OC_Appconfig::getValue('files_encryption', 'recoveryKeyId'); + + // if we are anonymous/public + if (\OCA\Encryption\Helper::isPublicAccess()) { + $this->userId = $this->publicShareKeyId; + + // only handle for files_sharing app + if (isset($GLOBALS['app']) && $GLOBALS['app'] === 'files_sharing') { + $this->userDir = '/' . $GLOBALS['fileOwner']; + $this->fileFolderName = 'files'; + $this->userFilesDir = '/' . $GLOBALS['fileOwner'] . '/' + . $this->fileFolderName; // TODO: Does this need to be user configurable? + $this->publicKeyDir = '/' . 'public-keys'; + $this->encryptionDir = '/' . $GLOBALS['fileOwner'] . '/' . 'files_encryption'; + $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; + $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys'; + $this->publicKeyPath = + $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key + $this->privateKeyPath = + '/owncloud_private_key/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key + $this->isPublic = true; + } + } else { - - return true; - + $this->userDir = '/' . $this->userId; + $this->fileFolderName = 'files'; + $this->userFilesDir = + '/' . $this->userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable? + $this->publicKeyDir = '/' . 'public-keys'; + $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; + $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; + $this->shareKeysPath = $this->encryptionDir . '/' . 'share-keys'; + $this->publicKeyPath = + $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key + $this->privateKeyPath = + $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key } - } - - /** - * @brief Sets up user folders and keys for serverside encryption - * @param $passphrase passphrase to encrypt server-stored private key with - */ - public function setupServerSide( $passphrase = null ) { - - // Create user dir - if( !$this->view->file_exists( $this->userDir ) ) { - - $this->view->mkdir( $this->userDir ); - - } - - // Create user files dir - if( !$this->view->file_exists( $this->userFilesDir ) ) { - - $this->view->mkdir( $this->userFilesDir ); - - } - - // Create shared public key directory - if( !$this->view->file_exists( $this->publicKeyDir ) ) { - - $this->view->mkdir( $this->publicKeyDir ); - - } - - // Create encryption app directory - if( !$this->view->file_exists( $this->encryptionDir ) ) { - - $this->view->mkdir( $this->encryptionDir ); - - } - - // Create mirrored keyfile directory - if( !$this->view->file_exists( $this->keyfilesPath ) ) { - - $this->view->mkdir( $this->keyfilesPath ); - + + /** + * @return bool + */ + public function ready() { + + if ( + !$this->view->file_exists($this->encryptionDir) + or !$this->view->file_exists($this->keyfilesPath) + or !$this->view->file_exists($this->shareKeysPath) + or !$this->view->file_exists($this->publicKeyPath) + or !$this->view->file_exists($this->privateKeyPath) + ) { + + return false; + + } else { + + return true; + } - // Create mirrored share env keys directory - if( !$this->view->file_exists( $this->shareKeysPath ) ) { - - $this->view->mkdir( $this->shareKeysPath ); - + } + + /** + * @brief Sets up user folders and keys for serverside encryption + * + * @param string $passphrase to encrypt server-stored private key with + * @return bool + */ + public function setupServerSide($passphrase = null) { + + // Set directories to check / create + $setUpDirs = array( + $this->userDir, + $this->userFilesDir, + $this->publicKeyDir, + $this->encryptionDir, + $this->keyfilesPath, + $this->shareKeysPath + ); + + // Check / create all necessary dirs + foreach ($setUpDirs as $dirPath) { + + if (!$this->view->file_exists($dirPath)) { + + $this->view->mkdir($dirPath); + + } + } - + // Create user keypair - if ( - ! $this->view->file_exists( $this->publicKeyPath ) - or ! $this->view->file_exists( $this->privateKeyPath ) + // we should never override a keyfile + if ( + !$this->view->file_exists($this->publicKeyPath) + && !$this->view->file_exists($this->privateKeyPath) ) { - + // Generate keypair $keypair = Crypt::createKeypair(); - + \OC_FileProxy::$enabled = false; - + // Save public key - $this->view->file_put_contents( $this->publicKeyPath, $keypair['publicKey'] ); - + $this->view->file_put_contents($this->publicKeyPath, $keypair['publicKey']); + // Encrypt private key with user pwd as passphrase - $encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $passphrase ); - + $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $passphrase); + // Save private key - $this->view->file_put_contents( $this->privateKeyPath, $encryptedPrivateKey ); - + $this->view->file_put_contents($this->privateKeyPath, $encryptedPrivateKey); + \OC_FileProxy::$enabled = true; - + + } else { + // check if public-key exists but private-key is missing + if ($this->view->file_exists($this->publicKeyPath) && !$this->view->file_exists($this->privateKeyPath)) { + \OCP\Util::writeLog('Encryption library', + 'public key exists but private key is missing for "' . $this->userId . '"', \OCP\Util::FATAL); + return false; + } else { + if (!$this->view->file_exists($this->publicKeyPath) && $this->view->file_exists($this->privateKeyPath) + ) { + \OCP\Util::writeLog('Encryption library', + 'private key exists but public key is missing for "' . $this->userId . '"', \OCP\Util::FATAL); + return false; + } + } } - + + // If there's no record for this user's encryption preferences + if (false === $this->recoveryEnabledForUser()) { + + // create database configuration + $sql = 'INSERT INTO `*PREFIX*encryption` (`uid`,`mode`,`recovery_enabled`) VALUES (?,?,?)'; + $args = array( + $this->userId, + 'server-side', + 0 + ); + $query = \OCP\DB::prepare($sql); + $query->execute($args); + + } + return true; - + } - + + /** + * @return string + */ + public function getPublicShareKeyId() { + return $this->publicShareKeyId; + } + + /** + * @brief Check whether pwd recovery is enabled for a given user + * @return bool 1 = yes, 0 = no, false = no record + * + * @note If records are not being returned, check for a hidden space + * at the start of the uid in db + */ + public function recoveryEnabledForUser() { + + $sql = 'SELECT `recovery_enabled` FROM `*PREFIX*encryption` WHERE uid = ?'; + + $args = array($this->userId); + + $query = \OCP\DB::prepare($sql); + + $result = $query->execute($args); + + $recoveryEnabled = array(); + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); + } else { + if($result->numRows() > 0) { + $row = $result->fetchRow(); + if (isset($row['recovery_enabled'])) { + $recoveryEnabled[] = $row['recovery_enabled']; + } + } + } + + // If no record is found + if (empty($recoveryEnabled)) { + + return false; + + // If a record is found + } else { + + return $recoveryEnabled[0]; + + } + + } + + /** + * @brief Enable / disable pwd recovery for a given user + * @param bool $enabled Whether to enable or disable recovery + * @return bool + */ + public function setRecoveryForUser($enabled) { + + $recoveryStatus = $this->recoveryEnabledForUser(); + + // If a record for this user already exists, update it + if (false === $recoveryStatus) { + + $sql = 'INSERT INTO `*PREFIX*encryption` (`uid`,`mode`,`recovery_enabled`) VALUES (?,?,?)'; + + $args = array( + $this->userId, + 'server-side', + $enabled + ); + + // Create a new record instead + } else { + + $sql = 'UPDATE `*PREFIX*encryption` SET recovery_enabled = ? WHERE uid = ?'; + + $args = array( + $enabled, + $this->userId + ); + + } + + $query = \OCP\DB::prepare($sql); + + if ($query->execute($args)) { + + return true; + + } else { + + return false; + + } + + } + /** * @brief Find all files and their encryption status within a directory * @param string $directory The path of the parent directory to search + * @param bool $found the founded files if called again * @return mixed false if 0 found, array on success. Keys: name, path - * @note $directory needs to be a path relative to OC data dir. e.g. * /admin/files NOT /backup OR /home/www/oc/data/admin/files */ - public function findFiles( $directory ) { - + public function findEncFiles($directory, &$found = false) { + // Disable proxy - we don't want files to be decrypted before // we handle them \OC_FileProxy::$enabled = false; - - $found = array( 'plain' => array(), 'encrypted' => array(), 'legacy' => array() ); - - if ( - $this->view->is_dir( $directory ) - && $handle = $this->view->opendir( $directory ) + + if ($found === false) { + $found = array( + 'plain' => array(), + 'encrypted' => array(), + 'legacy' => array() + ); + } + + if ( + $this->view->is_dir($directory) + && $handle = $this->view->opendir($directory) ) { - - while ( false !== ( $file = readdir( $handle ) ) ) { - + + while (false !== ($file = readdir($handle))) { + if ( - $file != "." - && $file != ".." + $file !== "." + && $file !== ".." ) { - - $filePath = $directory . '/' . $this->view->getRelativePath( '/' . $file ); - $relPath = $this->stripUserFilesPath( $filePath ); - + + $filePath = $directory . '/' . $this->view->getRelativePath('/' . $file); + $relPath = \OCA\Encryption\Helper::stripUserFilesPath($filePath); + // If the path is a directory, search // its contents - if ( $this->view->is_dir( $filePath ) ) { - - $this->findFiles( $filePath ); - - // If the path is a file, determine - // its encryption status - } elseif ( $this->view->is_file( $filePath ) ) { - + if ($this->view->is_dir($filePath)) { + + $this->findEncFiles($filePath, $found); + + // If the path is a file, determine + // its encryption status + } elseif ($this->view->is_file($filePath)) { + // Disable proxies again, some- // where they got re-enabled :/ \OC_FileProxy::$enabled = false; - - $data = $this->view->file_get_contents( $filePath ); - + + $data = $this->view->file_get_contents($filePath); + // If the file is encrypted // NOTE: If the userId is // empty or not set, file will @@ -270,207 +430,1118 @@ class Util { // NOTE: This is inefficient; // scanning every file like this // will eat server resources :( - if ( - Keymanager::getFileKey( $this->view, $this->userId, $file ) - && Crypt::isCatfile( $data ) + if ( + Keymanager::getFileKey($this->view, $this->userId, $relPath) + && Crypt::isCatfileContent($data) ) { - - $found['encrypted'][] = array( 'name' => $file, 'path' => $filePath ); - - // If the file uses old - // encryption system - } elseif ( Crypt::isLegacyEncryptedContent( $this->view->file_get_contents( $filePath ), $relPath ) ) { - - $found['legacy'][] = array( 'name' => $file, 'path' => $filePath ); - - // If the file is not encrypted + + $found['encrypted'][] = array( + 'name' => $file, + 'path' => $filePath + ); + + // If the file uses old + // encryption system + } elseif ( Crypt::isLegacyEncryptedContent( $data, $relPath ) ) { + + $found['legacy'][] = array( + 'name' => $file, + 'path' => $filePath + ); + + // If the file is not encrypted } else { - - $found['plain'][] = array( 'name' => $file, 'path' => $filePath ); - + + $found['plain'][] = array( + 'name' => $file, + 'path' => $relPath + ); + } - + } - + } - + } - + \OC_FileProxy::$enabled = true; - - if ( empty( $found ) ) { - + + if (empty($found)) { + return false; - + } else { - + return $found; - + } - + } - + \OC_FileProxy::$enabled = true; - + return false; } - - /** - * @brief Check if a given path identifies an encrypted file - * @return true / false - */ - public function isEncryptedPath( $path ) { - - // Disable encryption proxy so data retreived is in its - // original form + + /** + * @brief Fetch the last lines of a file efficiently + * @note Safe to use on large files; does not read entire file to memory + * @note Derivative of http://tekkie.flashbit.net/php/tail-functionality-in-php + */ + public function tail($filename, $numLines) { + \OC_FileProxy::$enabled = false; - - $data = $this->view->file_get_contents( $path ); - + + $text = ''; + $pos = -1; + $handle = $this->view->fopen($filename, 'r'); + + while ($numLines > 0) { + + --$pos; + + if (fseek($handle, $pos, SEEK_END) !== 0) { + + rewind($handle); + $numLines = 0; + + } elseif (fgetc($handle) === "\n") { + + --$numLines; + + } + + $block_size = (-$pos) % 8192; + if ($block_size === 0 || $numLines === 0) { + + $text = fread($handle, ($block_size === 0 ? 8192 : $block_size)) . $text; + + } + } + + fclose($handle); + \OC_FileProxy::$enabled = true; - - return Crypt::isCatfile( $data ); - + + return $text; } + + /** + * @brief Check if a given path identifies an encrypted file + * @param string $path + * @return boolean + */ + public function isEncryptedPath($path) { + + // Disable encryption proxy so data retrieved is in its + // original form + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // we only need 24 byte from the last chunk + $data = ''; + $handle = $this->view->fopen($path, 'r'); + if (is_resource($handle) && !fseek($handle, -24, SEEK_END)) { + $data = fgets($handle); + } + + // re-enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + + return Crypt::isCatfileContent($data); + + } + + /** + * @brief get the file size of the unencrypted file + * @param string $path absolute path + * @return bool + */ + public function getFileSize($path) { + + $result = 0; + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // split the path parts + $pathParts = explode('/', $path); + + // get relative path + $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); + + if (isset($pathParts[2]) && $pathParts[2] === 'files' && $this->view->file_exists($path) && $this->isEncryptedPath($path)) { + + // get the size from filesystem + $fullPath = $this->view->getLocalFile($path); + $size = filesize($fullPath); + + // calculate last chunk nr + $lastChunkNr = floor($size / 8192); + + // open stream + $stream = fopen('crypt://' . $relativePath, "r"); + + if (is_resource($stream)) { + // calculate last chunk position + $lastChunckPos = ($lastChunkNr * 8192); + + // seek to end + fseek($stream, $lastChunckPos); + + // get the content of the last chunk + $lastChunkContent = fread($stream, 8192); + + // calc the real file size with the size of the last chunk + $realSize = (($lastChunkNr * 6126) + strlen($lastChunkContent)); + + // store file size + $result = $realSize; + } + } + + \OC_FileProxy::$enabled = $proxyStatus; + + return $result; + } + + /** + * @brief fix the file size of the encrypted file + * @param string $path absolute path + * @return boolean true / false if file is encrypted + */ + public function fixFileSize($path) { + + $result = false; + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $realSize = $this->getFileSize($path); + + if ($realSize > 0) { + + $cached = $this->view->getFileInfo($path); + $cached['encrypted'] = true; + + // set the size + $cached['unencrypted_size'] = $realSize; + + // put file info + $this->view->putFileInfo($path, $cached); + + $result = true; + + } + + \OC_FileProxy::$enabled = $proxyStatus; + + return $result; + } + /** - * @brief Format a path to be relative to the /user/files/ directory + * @param $path + * @return bool */ - public function stripUserFilesPath( $path ) { - - $trimmed = ltrim( $path, '/' ); - $split = explode( '/', $trimmed ); - $sliced = array_slice( $split, 2 ); - $relPath = implode( '/', $sliced ); - - return $relPath; - + public function isSharedPath($path) { + + $trimmed = ltrim($path, '/'); + $split = explode('/', $trimmed); + + if (isset($split[2]) && $split[2] === 'Shared') { + + return true; + + } else { + + return false; + + } + } - + /** * @brief Encrypt all files in a directory - * @param string $publicKey the public key to encrypt files with * @param string $dirPath the directory whose files will be encrypted + * @param null $legacyPassphrase + * @param null $newPassphrase + * @return bool * @note Encryption is recursive */ - public function encryptAll( $publicKey, $dirPath, $legacyPassphrase = null, $newPassphrase = null ) { - - if ( $found = $this->findFiles( $dirPath ) ) { - + public function encryptAll($dirPath, $legacyPassphrase = null, $newPassphrase = null) { + + if ($found = $this->findEncFiles($dirPath)) { + // Disable proxy to prevent file being encrypted twice \OC_FileProxy::$enabled = false; - + // Encrypt unencrypted files - foreach ( $found['plain'] as $plainFile ) { - - // Fetch data from file - $plainData = $this->view->file_get_contents( $plainFile['path'] ); - - // Encrypt data, generate catfile - $encrypted = Crypt::keyEncryptKeyfile( $plainData, $publicKey ); - - $relPath = $this->stripUserFilesPath( $plainFile['path'] ); - - // Save keyfile - Keymanager::setFileKey( $this->view, $relPath, $this->userId, $encrypted['key'] ); - - // Overwrite the existing file with the encrypted one - $this->view->file_put_contents( $plainFile['path'], $encrypted['data'] ); - - $size = strlen( $encrypted['data'] ); - + foreach ($found['plain'] as $plainFile) { + + //relative to data//file + $relPath = $plainFile['path']; + + //relative to /data + $rawPath = '/'.$this->userId . '/files/' . $plainFile['path']; + + // Open plain file handle for binary reading + $plainHandle = $this->view->fopen( $rawPath, 'rb' ); + + // Open enc file handle for binary writing, with same filename as original plain file + $encHandle = fopen( 'crypt://' . $relPath.'.tmp', 'wb' ); + + // Move plain file to a temporary location + $size = stream_copy_to_stream( $plainHandle, $encHandle ); + + fclose($encHandle); + + $fakeRoot = $this->view->getRoot(); + $this->view->chroot('/'.$this->userId.'/files'); + + $this->view->rename($relPath . '.tmp', $relPath); + + $this->view->chroot($fakeRoot); + // Add the file to the cache - \OC\Files\Filesystem::putFileInfo( $plainFile['path'], array( 'encrypted'=>true, 'size' => $size ), '' ); - + \OC\Files\Filesystem::putFileInfo( $relPath, array( 'encrypted' => true, 'size' => $size, 'unencrypted_size' => $size ) ); } - + // Encrypt legacy encrypted files - if ( - ! empty( $legacyPassphrase ) - && ! empty( $newPassphrase ) + if ( + !empty($legacyPassphrase) + && !empty($newPassphrase) ) { - - foreach ( $found['legacy'] as $legacyFile ) { - + + foreach ($found['legacy'] as $legacyFile) { + // Fetch data from file - $legacyData = $this->view->file_get_contents( $legacyFile['path'] ); - + $legacyData = $this->view->file_get_contents($legacyFile['path']); + + $sharingEnabled = \OCP\Share::isEnabled(); + + // if file exists try to get sharing users + if ($this->view->file_exists($legacyFile['path'])) { + $uniqueUserIds = $this->getSharingUsersArray($sharingEnabled, $legacyFile['path'], $this->userId); + } else { + $uniqueUserIds[] = $this->userId; + } + + // Fetch public keys for all users who will share the file + $publicKeys = Keymanager::getPublicKeys($this->view, $uniqueUserIds); + // Recrypt data, generate catfile - $recrypted = Crypt::legacyKeyRecryptKeyfile( $legacyData, $legacyPassphrase, $publicKey, $newPassphrase ); - - $relPath = $this->stripUserFilesPath( $legacyFile['path'] ); - + $recrypted = Crypt::legacyKeyRecryptKeyfile( $legacyData, $legacyPassphrase, $publicKeys ); + + $rawPath = $legacyFile['path']; + $relPath = \OCA\Encryption\Helper::stripUserFilesPath($rawPath); + // Save keyfile - Keymanager::setFileKey( $this->view, $relPath, $this->userId, $recrypted['key'] ); - + Keymanager::setFileKey($this->view, $relPath, $this->userId, $recrypted['filekey']); + + // Save sharekeys to user folders + Keymanager::setShareKeys($this->view, $relPath, $recrypted['sharekeys']); + // Overwrite the existing file with the encrypted one - $this->view->file_put_contents( $legacyFile['path'], $recrypted['data'] ); - - $size = strlen( $recrypted['data'] ); - + $this->view->file_put_contents($rawPath, $recrypted['data']); + + $size = strlen($recrypted['data']); + // Add the file to the cache - \OC\Files\Filesystem::putFileInfo( $legacyFile['path'], array( 'encrypted'=>true, 'size' => $size ), '' ); - + \OC\Files\Filesystem::putFileInfo($rawPath, array( + 'encrypted' => true, + 'size' => $size + ), ''); } - } - + \OC_FileProxy::$enabled = true; - + // If files were found, return true return true; - } else { - + // If no files were found, return false return false; - } - } - + /** * @brief Return important encryption related paths * @param string $pathName Name of the directory to return the path of * @return string path */ - public function getPath( $pathName ) { - - switch ( $pathName ) { - + public function getPath($pathName) { + + switch ($pathName) { + case 'publicKeyDir': - + return $this->publicKeyDir; - + break; - + case 'encryptionDir': - + return $this->encryptionDir; - + break; - + case 'keyfilesPath': - + return $this->keyfilesPath; - + break; - + case 'publicKeyPath': - + return $this->publicKeyPath; - + break; - + case 'privateKeyPath': - + return $this->privateKeyPath; - + break; - } - + + return false; + + } + + /** + * @brief get path of a file. + * @param int $fileId id of the file + * @return string path of the file + */ + public static function fileIdToPath($fileId) { + + $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?'; + + $query = \OCP\DB::prepare($sql); + + $result = $query->execute(array($fileId)); + + $path = false; + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); + } else { + if($result->numRows() > 0) { + $row = $result->fetchRow(); + $path = substr($row['path'], strlen('files')); + } + } + + return $path; + + } + + /** + * @brief Filter an array of UIDs to return only ones ready for sharing + * @param array $unfilteredUsers users to be checked for sharing readiness + * @return array as multi-dimensional array. keys: ready, unready + */ + public function filterShareReadyUsers($unfilteredUsers) { + + // This array will collect the filtered IDs + $readyIds = $unreadyIds = array(); + + // Loop through users and create array of UIDs that need new keyfiles + foreach ($unfilteredUsers as $user) { + + $util = new Util($this->view, $user); + + // Check that the user is encryption capable, or is the + // public system user 'ownCloud' (for public shares) + if ( + $user === $this->publicShareKeyId + or $user === $this->recoveryKeyId + or $util->ready() + ) { + + // Construct array of ready UIDs for Keymanager{} + $readyIds[] = $user; + + } else { + + // Construct array of unready UIDs for Keymanager{} + $unreadyIds[] = $user; + + // Log warning; we can't do necessary setup here + // because we don't have the user passphrase + \OCP\Util::writeLog('Encryption library', + '"' . $user . '" is not setup for encryption', \OCP\Util::WARN); + + } + + } + + return array( + 'ready' => $readyIds, + 'unready' => $unreadyIds + ); + + } + + /** + * @brief Decrypt a keyfile without knowing how it was encrypted + * @param string $filePath + * @param string $fileOwner + * @param string $privateKey + * @return bool|string + * @note Checks whether file was encrypted with openssl_seal or + * openssl_encrypt, and decrypts accrdingly + * @note This was used when 2 types of encryption for keyfiles was used, + * but now we've switched to exclusively using openssl_seal() + */ + public function decryptUnknownKeyfile($filePath, $fileOwner, $privateKey) { + + // Get the encrypted keyfile + // NOTE: the keyfile format depends on how it was encrypted! At + // this stage we don't know how it was encrypted + $encKeyfile = Keymanager::getFileKey($this->view, $this->userId, $filePath); + + // We need to decrypt the keyfile + // Has the file been shared yet? + if ( + $this->userId === $fileOwner + && !Keymanager::getShareKey($this->view, $this->userId, $filePath) // NOTE: we can't use isShared() here because it's a post share hook so it always returns true + ) { + + // The file has no shareKey, and its keyfile must be + // decrypted conventionally + $plainKeyfile = Crypt::keyDecrypt($encKeyfile, $privateKey); + + + } else { + + // The file has a shareKey and must use it for decryption + $shareKey = Keymanager::getShareKey($this->view, $this->userId, $filePath); + + $plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); + + } + + return $plainKeyfile; + + } + + /** + * @brief Encrypt keyfile to multiple users + * @param Session $session + * @param array $users list of users which should be able to access the file + * @param string $filePath path of the file to be shared + * @return bool + */ + public function setSharedFileKeyfiles(Session $session, array $users, $filePath) { + + // Make sure users are capable of sharing + $filteredUids = $this->filterShareReadyUsers($users); + + // If we're attempting to share to unready users + if (!empty($filteredUids['unready'])) { + + \OCP\Util::writeLog('Encryption library', + 'Sharing to these user(s) failed as they are unready for encryption:"' + . print_r($filteredUids['unready'], 1), \OCP\Util::WARN); + + return false; + + } + + // Get public keys for each user, ready for generating sharekeys + $userPubKeys = Keymanager::getPublicKeys($this->view, $filteredUids['ready']); + + // Note proxy status then disable it + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // Get the current users's private key for decrypting existing keyfile + $privateKey = $session->getPrivateKey(); + + $fileOwner = \OC\Files\Filesystem::getOwner($filePath); + + // Decrypt keyfile + $plainKeyfile = $this->decryptUnknownKeyfile($filePath, $fileOwner, $privateKey); + + // Re-enc keyfile to (additional) sharekeys + $multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys); + + // Save the recrypted key to it's owner's keyfiles directory + // Save new sharekeys to all necessary user directory + if ( + !Keymanager::setFileKey($this->view, $filePath, $fileOwner, $multiEncKey['data']) + || !Keymanager::setShareKeys($this->view, $filePath, $multiEncKey['keys']) + ) { + + \OCP\Util::writeLog('Encryption library', + 'Keyfiles could not be saved for users sharing ' . $filePath, \OCP\Util::ERROR); + + return false; + + } + + // Return proxy to original status + \OC_FileProxy::$enabled = $proxyStatus; + + return true; + } + + /** + * @brief Find, sanitise and format users sharing a file + * @note This wraps other methods into a portable bundle + */ + public function getSharingUsersArray($sharingEnabled, $filePath, $currentUserId = false) { + + // Check if key recovery is enabled + if ( + \OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled') + && $this->recoveryEnabledForUser() + ) { + + $recoveryEnabled = true; + + } else { + + $recoveryEnabled = false; + + } + + // Make sure that a share key is generated for the owner too + list($owner, $ownerPath) = $this->getUidAndFilename($filePath); + + $userIds = array(); + if ($sharingEnabled) { + + // Find out who, if anyone, is sharing the file + $result = \OCP\Share::getUsersSharingFile($ownerPath, $owner, true); + $userIds = $result['users']; + if ($result['public']) { + $userIds[] = $this->publicShareKeyId; + } + + } + + // If recovery is enabled, add the + // Admin UID to list of users to share to + if ($recoveryEnabled) { + + // Find recoveryAdmin user ID + $recoveryKeyId = \OC_Appconfig::getValue('files_encryption', 'recoveryKeyId'); + + // Add recoveryAdmin to list of users sharing + $userIds[] = $recoveryKeyId; + + } + + // add current user if given + if ($currentUserId !== false) { + + $userIds[] = $currentUserId; + + } + + // Remove duplicate UIDs + $uniqueUserIds = array_unique($userIds); + + return $uniqueUserIds; + + } + + /** + * @brief Set file migration status for user + * @param $status + * @return bool + */ + public function setMigrationStatus($status) { + + $sql = 'UPDATE `*PREFIX*encryption` SET migration_status = ? WHERE uid = ?'; + + $args = array( + $status, + $this->userId + ); + + $query = \OCP\DB::prepare($sql); + + if ($query->execute($args)) { + + return true; + + } else { + + return false; + + } + + } + + /** + * @brief Check whether pwd recovery is enabled for a given user + * @return bool 1 = yes, 0 = no, false = no record + * @note If records are not being returned, check for a hidden space + * at the start of the uid in db + */ + public function getMigrationStatus() { + + $sql = 'SELECT `migration_status` FROM `*PREFIX*encryption` WHERE uid = ?'; + + $args = array($this->userId); + + $query = \OCP\DB::prepare($sql); + + $result = $query->execute($args); + + $migrationStatus = array(); + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); + } else { + if($result->numRows() > 0) { + $row = $result->fetchRow(); + if (isset($row['migration_status'])) { + $migrationStatus[] = $row['migration_status']; + } + } + } + + // If no record is found + if (empty($migrationStatus)) { + + return false; + + // If a record is found + } else { + + return $migrationStatus[0]; + + } + + } + + /** + * @brief get uid of the owners of the file and the path to the file + * @param string $path Path of the file to check + * @throws \Exception + * @note $shareFilePath must be relative to data/UID/files. Files + * relative to /Shared are also acceptable + * @return array + */ + public function getUidAndFilename($path) { + + $view = new \OC\Files\View($this->userFilesDir); + $fileOwnerUid = $view->getOwner($path); + + // handle public access + if ($this->isPublic) { + $filename = $path; + $fileOwnerUid = $GLOBALS['fileOwner']; + + return array( + $fileOwnerUid, + $filename + ); + } else { + + // Check that UID is valid + if (!\OCP\User::userExists($fileOwnerUid)) { + throw new \Exception( + 'Could not find owner (UID = "' . var_export($fileOwnerUid, 1) . '") of file "' . $path . '"'); + } + + // NOTE: Bah, this dependency should be elsewhere + \OC\Files\Filesystem::initMountPoints($fileOwnerUid); + + // If the file owner is the currently logged in user + if ($fileOwnerUid === $this->userId) { + + // Assume the path supplied is correct + $filename = $path; + + } else { + + $info = $view->getFileInfo($path); + $ownerView = new \OC\Files\View('/' . $fileOwnerUid . '/files'); + + // Fetch real file path from DB + $filename = $ownerView->getPath($info['fileid']); // TODO: Check that this returns a path without including the user data dir + + } + + return array( + $fileOwnerUid, + $filename + ); + } + + + } + + /** + * @brief go recursively through a dir and collect all files and sub files. + * @param string $dir relative to the users files folder + * @return array with list of files relative to the users files folder + */ + public function getAllFiles($dir) { + + $result = array(); + + $content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath($this->userFilesDir . '/' . $dir)); + + // handling for re shared folders + $pathSplit = explode('/', $dir); + + foreach ($content as $c) { + + $sharedPart = $pathSplit[sizeof($pathSplit) - 1]; + $targetPathSplit = array_reverse(explode('/', $c['path'])); + + $path = ''; + + // rebuild path + foreach ($targetPathSplit as $pathPart) { + + if ($pathPart !== $sharedPart) { + + $path = '/' . $pathPart . $path; + + } else { + + break; + + } + + } + + $path = $dir . $path; + + if ($c['type'] === 'dir') { + + $result = array_merge($result, $this->getAllFiles($path)); + + } else { + + $result[] = $path; + + } + } + + return $result; + + } + + /** + * @brief get shares parent. + * @param int $id of the current share + * @return array of the parent + */ + public static function getShareParent($id) { + + $sql = 'SELECT `file_target`, `item_type` FROM `*PREFIX*share` WHERE `id` = ?'; + + $query = \OCP\DB::prepare($sql); + + $result = $query->execute(array($id)); + + $row = array(); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); + } else { + if($result->numRows() > 0) { + $row = $result->fetchRow(); + } + } + + return $row; + + } + + /** + * @brief get shares parent. + * @param int $id of the current share + * @return array of the parent + */ + public static function getParentFromShare($id) { + + $sql = 'SELECT `parent` FROM `*PREFIX*share` WHERE `id` = ?'; + + $query = \OCP\DB::prepare($sql); + + $result = $query->execute(array($id)); + + $row = array(); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); + } else { + if($result->numRows() > 0) { + $row = $result->fetchRow(); + } + } + + return $row; + + } + + /** + * @brief get owner of the shared files. + * @param $id + * @internal param int $Id of a share + * @return string owner + */ + public function getOwnerFromSharedFile($id) { + + $query = \OCP\DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); + + $result = $query->execute(array($id)); + + $source = array(); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); + } else { + if($result->numRows() > 0) { + $source = $result->fetchRow(); + } + } + + $fileOwner = false; + + if (isset($source['parent'])) { + + $parent = $source['parent']; + + while (isset($parent)) { + + $query = \OCP\DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); + + $result = $query->execute(array($parent)); + + $item = array(); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); + } else { + if($result->numRows() > 0) { + $item = $result->fetchRow(); + } + } + + if (isset($item['parent'])) { + + $parent = $item['parent']; + + } else { + + $fileOwner = $item['uid_owner']; + + break; + + } + } + + } else { + + $fileOwner = $source['uid_owner']; + + } + + return $fileOwner; + + } + + /** + * @return string + */ + public function getUserId() { + return $this->userId; + } + + /** + * @return string + */ + public function getUserFilesDir() { + return $this->userFilesDir; + } + + /** + * @param $password + * @return bool + */ + public function checkRecoveryPassword($password) { + + $pathKey = '/owncloud_private_key/' . $this->recoveryKeyId . ".private.key"; + $pathControlData = '/control-file/controlfile.enc'; + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $recoveryKey = $this->view->file_get_contents($pathKey); + + $decryptedRecoveryKey = Crypt::symmetricDecryptFileContent($recoveryKey, $password); + + $controlData = $this->view->file_get_contents($pathControlData); + $decryptedControlData = Crypt::keyDecrypt($controlData, $decryptedRecoveryKey); + + \OC_FileProxy::$enabled = $proxyStatus; + + if ($decryptedControlData === 'ownCloud') { + return true; + } + + return false; + } + + /** + * @return string + */ + public function getRecoveryKeyId() { + return $this->recoveryKeyId; + } + + /** + * @brief add recovery key to all encrypted files + */ + public function addRecoveryKeys($path = '/') { + $dirContent = $this->view->getDirectoryContent($this->keyfilesPath . $path); + foreach ($dirContent as $item) { + // get relative path from files_encryption/keyfiles/ + $filePath = substr($item['path'], strlen('files_encryption/keyfiles')); + if ($item['type'] === 'dir') { + $this->addRecoveryKeys($filePath . '/'); + } else { + $session = new \OCA\Encryption\Session(new \OC_FilesystemView('/')); + $sharingEnabled = \OCP\Share::isEnabled(); + // remove '.key' extension from path e.g. 'file.txt.key' to 'file.txt' + $file = substr($filePath, 0, -4); + $usersSharing = $this->getSharingUsersArray($sharingEnabled, $file); + $this->setSharedFileKeyfiles($session, $usersSharing, $file); + } + } + } + + /** + * @brief remove recovery key to all encrypted files + */ + public function removeRecoveryKeys($path = '/') { + $dirContent = $this->view->getDirectoryContent($this->keyfilesPath . $path); + foreach ($dirContent as $item) { + // get relative path from files_encryption/keyfiles + $filePath = substr($item['path'], strlen('files_encryption/keyfiles')); + if ($item['type'] === 'dir') { + $this->removeRecoveryKeys($filePath . '/'); + } else { + // remove '.key' extension from path e.g. 'file.txt.key' to 'file.txt' + $file = substr($filePath, 0, -4); + $this->view->unlink($this->shareKeysPath . '/' . $file . '.' . $this->recoveryKeyId . '.shareKey'); + } + } + } + + /** + * @brief decrypt given file with recovery key and encrypt it again to the owner and his new key + * @param string $file + * @param string $privateKey recovery key to decrypt the file + */ + private function recoverFile($file, $privateKey) { + + $sharingEnabled = \OCP\Share::isEnabled(); + + // Find out who, if anyone, is sharing the file + if ($sharingEnabled) { + $result = \OCP\Share::getUsersSharingFile($file, $this->userId, true); + $userIds = $result['users']; + $userIds[] = $this->recoveryKeyId; + if ($result['public']) { + $userIds[] = $this->publicShareKeyId; + } + } else { + $userIds = array( + $this->userId, + $this->recoveryKeyId + ); + } + $filteredUids = $this->filterShareReadyUsers($userIds); + + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + //decrypt file key + $encKeyfile = $this->view->file_get_contents($this->keyfilesPath . $file . ".key"); + $shareKey = $this->view->file_get_contents( + $this->shareKeysPath . $file . "." . $this->recoveryKeyId . ".shareKey"); + $plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); + // encrypt file key again to all users, this time with the new public key for the recovered use + $userPubKeys = Keymanager::getPublicKeys($this->view, $filteredUids['ready']); + $multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys); + + // write new keys to filesystem TDOO! + $this->view->file_put_contents($this->keyfilesPath . $file . '.key', $multiEncKey['data']); + foreach ($multiEncKey['keys'] as $userId => $shareKey) { + $shareKeyPath = $this->shareKeysPath . $file . '.' . $userId . '.shareKey'; + $this->view->file_put_contents($shareKeyPath, $shareKey); + } + + // Return proxy to original status + \OC_FileProxy::$enabled = $proxyStatus; + } + + /** + * @brief collect all files and recover them one by one + * @param string $path to look for files keys + * @param string $privateKey private recovery key which is used to decrypt the files + */ + private function recoverAllFiles($path, $privateKey) { + $dirContent = $this->view->getDirectoryContent($this->keyfilesPath . $path); + foreach ($dirContent as $item) { + // get relative path from files_encryption/keyfiles + $filePath = substr($item['path'], strlen('files_encryption/keyfiles')); + if ($item['type'] === 'dir') { + $this->recoverAllFiles($filePath . '/', $privateKey); + } else { + // remove '.key' extension from path e.g. 'file.txt.key' to 'file.txt' + $file = substr($filePath, 0, -4); + $this->recoverFile($file, $privateKey); + } + } + } + + /** + * @brief recover users files in case of password lost + * @param string $recoveryPassword + */ + public function recoverUsersFiles($recoveryPassword) { + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $encryptedKey = $this->view->file_get_contents( + '/owncloud_private_key/' . $this->recoveryKeyId . '.private.key'); + $privateKey = Crypt::symmetricDecryptFileContent($encryptedKey, $recoveryPassword); + + \OC_FileProxy::$enabled = $proxyStatus; + + $this->recoverAllFiles('/', $privateKey); + } + + /** + * Get the path including the storage mount point + * @param int $id + * @return string the path including the mount point like AmazonS3/folder/file.txt + */ + public function getPathWithMountPoint($id) { + list($storage, $internalPath) = \OC\Files\Cache\Cache::getById($id); + $mount = \OC\Files\Filesystem::getMountByStorageId($storage); + $mountPoint = $mount[0]->getMountPoint(); + $path = \OC\Files\Filesystem::normalizePath($mountPoint.'/'.$internalPath); + + // reformat the path to be relative e.g. /user/files/folder becomes /folder/ + $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); + + return $relativePath; } } diff --git a/apps/files_encryption/settings-admin.php b/apps/files_encryption/settings-admin.php new file mode 100644 index 0000000000..5367605898 --- /dev/null +++ b/apps/files_encryption/settings-admin.php @@ -0,0 +1,23 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +\OC_Util::checkAdminUser(); + +$tmpl = new OCP\Template('files_encryption', 'settings-admin'); + +// Check if an adminRecovery account is enabled for recovering files after lost pwd +$view = new OC_FilesystemView(''); + +$recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled'); + +$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled); + +\OCP\Util::addscript('files_encryption', 'settings-admin'); +\OCP\Util::addscript('core', 'multiselect'); + +return $tmpl->fetchPage(); diff --git a/apps/files_encryption/settings-personal.php b/apps/files_encryption/settings-personal.php index af0273cfdc..3e96565949 100644 --- a/apps/files_encryption/settings-personal.php +++ b/apps/files_encryption/settings-personal.php @@ -6,12 +6,23 @@ * See the COPYING-README file. */ -$tmpl = new OCP\Template( 'files_encryption', 'settings-personal'); +// Add CSS stylesheet +\OC_Util::addStyle('files_encryption', 'settings-personal'); -$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) ); +$tmpl = new OCP\Template('files_encryption', 'settings-personal'); -$tmpl->assign( 'blacklist', $blackList ); +$user = \OCP\USER::getUser(); +$view = new \OC_FilesystemView('/'); +$util = new \OCA\Encryption\Util($view, $user); + +$recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled'); +$recoveryEnabledForUser = $util->recoveryEnabledForUser(); + +\OCP\Util::addscript('files_encryption', 'settings-personal'); +\OCP\Util::addScript('settings', 'personal'); + +$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled); +$tmpl->assign('recoveryEnabledForUser', $recoveryEnabledForUser); return $tmpl->fetchPage(); -return null; diff --git a/apps/files_encryption/settings.php b/apps/files_encryption/settings.php deleted file mode 100644 index d1260f44e9..0000000000 --- a/apps/files_encryption/settings.php +++ /dev/null @@ -1,21 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -\OC_Util::checkAdminUser(); - -$tmpl = new OCP\Template( 'files_encryption', 'settings' ); - -$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) ); - -$tmpl->assign( 'blacklist', $blackList ); -$tmpl->assign( 'encryption_mode', \OC_Appconfig::getValue( 'files_encryption', 'mode', 'none' ) ); - -\OCP\Util::addscript( 'files_encryption', 'settings' ); -\OCP\Util::addscript( 'core', 'multiselect' ); - -return $tmpl->fetchPage(); diff --git a/apps/files_encryption/templates/settings-admin.php b/apps/files_encryption/templates/settings-admin.php new file mode 100644 index 0000000000..18fea1845f --- /dev/null +++ b/apps/files_encryption/templates/settings-admin.php @@ -0,0 +1,56 @@ +
+
+ +

+ t( 'Encryption' )); ?> +
+

+

+ t( "Enable encryption passwords recovery key (allow sharing to recovery key):" )); ?> +
+
+ + +
+ /> + t( "Enabled" )); ?> +
+ + /> + t( "Disabled" )); ?> +

+

+

+ t( "Change encryption passwords recovery key:" )); ?> +

+ /> + +
+ /> + +
+ + +

+
+
diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php index 5f0accaed5..04d6e79179 100644 --- a/apps/files_encryption/templates/settings-personal.php +++ b/apps/files_encryption/templates/settings-personal.php @@ -1,22 +1,33 @@
- t( 'Encryption' )); ?> + t( 'Encryption' ) ); ?> -

- t( 'File encryption is enabled.' )); ?> -

- -

- t( 'The following file types will not be encrypted:' )); ?> -

-
    - -
  • - -
  • - -
+ + +

+ +
+ t( "Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" ) ); ?> +
+ /> + t( "Enabled" ) ); ?> +
+ + /> + t( "Disabled" ) ); ?> +

t( 'File recovery settings updated' ) ); ?>
+
t( 'Could not update file recovery' ) ); ?>
+

+
diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php deleted file mode 100644 index b873d7f5aa..0000000000 --- a/apps/files_encryption/templates/settings.php +++ /dev/null @@ -1,20 +0,0 @@ -
-
- -

- t( 'Encryption' )); ?> - - t( "Exclude the following file types from encryption:" )); ?> -
- - -

-
-
diff --git a/apps/files_encryption/test/crypt.php b/apps/files_encryption/test/crypt.php deleted file mode 100755 index aa87ec3282..0000000000 --- a/apps/files_encryption/test/crypt.php +++ /dev/null @@ -1,667 +0,0 @@ -, and - * Robin Appelman - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -//require_once "PHPUnit/Framework/TestCase.php"; -require_once realpath( dirname(__FILE__).'/../../../3rdparty/Crypt_Blowfish/Blowfish.php' ); -require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); -require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); -require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' ); -require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); -require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); -require_once realpath( dirname(__FILE__).'/../lib/util.php' ); -require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); - -use OCA\Encryption; - -// This has to go here because otherwise session errors arise, and the private -// encryption key needs to be saved in the session -\OC_User::login( 'admin', 'admin' ); - -/** - * @note It would be better to use Mockery here for mocking out the session - * handling process, and isolate calls to session class and data from the unit - * tests relating to them (stream etc.). However getting mockery to work and - * overload classes whilst also using the OC autoloader is difficult due to - * load order Pear errors. - */ - -class Test_Crypt extends \PHPUnit_Framework_TestCase { - - function setUp() { - - // set content for encrypting / decrypting in tests - $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); - $this->dataShort = 'hats'; - $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); - $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); - $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); - $this->randomKey = Encryption\Crypt::generateKey(); - - $keypair = Encryption\Crypt::createKeypair(); - $this->genPublicKey = $keypair['publicKey']; - $this->genPrivateKey = $keypair['privateKey']; - - $this->view = new \OC_FilesystemView( '/' ); - - \OC_User::setUserId( 'admin' ); - $this->userId = 'admin'; - $this->pass = 'admin'; - - \OC_Filesystem::init( '/' ); - \OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' ); - - } - - function tearDown() { - - } - - function testGenerateKey() { - - # TODO: use more accurate (larger) string length for test confirmation - - $key = Encryption\Crypt::generateKey(); - - $this->assertTrue( strlen( $key ) > 16 ); - - } - - function testGenerateIv() { - - $iv = Encryption\Crypt::generateIv(); - - $this->assertEquals( 16, strlen( $iv ) ); - - return $iv; - - } - - /** - * @depends testGenerateIv - */ - function testConcatIv( $iv ) { - - $catFile = Encryption\Crypt::concatIv( $this->dataLong, $iv ); - - // Fetch encryption metadata from end of file - $meta = substr( $catFile, -22 ); - - $identifier = substr( $meta, 0, 6); - - // Fetch IV from end of file - $foundIv = substr( $meta, 6 ); - - $this->assertEquals( '00iv00', $identifier ); - - $this->assertEquals( $iv, $foundIv ); - - // Remove IV and IV identifier text to expose encrypted content - $data = substr( $catFile, 0, -22 ); - - $this->assertEquals( $this->dataLong, $data ); - - return array( - 'iv' => $iv - , 'catfile' => $catFile - ); - - } - - /** - * @depends testConcatIv - */ - function testSplitIv( $testConcatIv ) { - - // Split catfile into components - $splitCatfile = Encryption\Crypt::splitIv( $testConcatIv['catfile'] ); - - // Check that original IV and split IV match - $this->assertEquals( $testConcatIv['iv'], $splitCatfile['iv'] ); - - // Check that original data and split data match - $this->assertEquals( $this->dataLong, $splitCatfile['encrypted'] ); - - } - - function testAddPadding() { - - $padded = Encryption\Crypt::addPadding( $this->dataLong ); - - $padding = substr( $padded, -2 ); - - $this->assertEquals( 'xx' , $padding ); - - return $padded; - - } - - /** - * @depends testAddPadding - */ - function testRemovePadding( $padded ) { - - $noPadding = Encryption\Crypt::RemovePadding( $padded ); - - $this->assertEquals( $this->dataLong, $noPadding ); - - } - - function testEncrypt() { - - $random = openssl_random_pseudo_bytes( 13 ); - - $iv = substr( base64_encode( $random ), 0, -4 ); // i.e. E5IG033j+mRNKrht - - $crypted = Encryption\Crypt::encrypt( $this->dataUrl, $iv, 'hat' ); - - $this->assertNotEquals( $this->dataUrl, $crypted ); - - } - - function testDecrypt() { - - $random = openssl_random_pseudo_bytes( 13 ); - - $iv = substr( base64_encode( $random ), 0, -4 ); // i.e. E5IG033j+mRNKrht - - $crypted = Encryption\Crypt::encrypt( $this->dataUrl, $iv, 'hat' ); - - $decrypt = Encryption\Crypt::decrypt( $crypted, $iv, 'hat' ); - - $this->assertEquals( $this->dataUrl, $decrypt ); - - } - - function testSymmetricEncryptFileContent() { - - # TODO: search in keyfile for actual content as IV will ensure this test always passes - - $crypted = Encryption\Crypt::symmetricEncryptFileContent( $this->dataShort, 'hat' ); - - $this->assertNotEquals( $this->dataShort, $crypted ); - - - $decrypt = Encryption\Crypt::symmetricDecryptFileContent( $crypted, 'hat' ); - - $this->assertEquals( $this->dataShort, $decrypt ); - - } - - // These aren't used for now -// function testSymmetricBlockEncryptShortFileContent() { -// -// $crypted = Encryption\Crypt::symmetricBlockEncryptFileContent( $this->dataShort, $this->randomKey ); -// -// $this->assertNotEquals( $this->dataShort, $crypted ); -// -// -// $decrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $crypted, $this->randomKey ); -// -// $this->assertEquals( $this->dataShort, $decrypt ); -// -// } -// -// function testSymmetricBlockEncryptLongFileContent() { -// -// $crypted = Encryption\Crypt::symmetricBlockEncryptFileContent( $this->dataLong, $this->randomKey ); -// -// $this->assertNotEquals( $this->dataLong, $crypted ); -// -// -// $decrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $crypted, $this->randomKey ); -// -// $this->assertEquals( $this->dataLong, $decrypt ); -// -// } - - function testSymmetricStreamEncryptShortFileContent() { - - $filename = 'tmp-'.time(); - - $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort ); - - // Test that data was successfully written - $this->assertTrue( is_int( $cryptedFile ) ); - - - // Get file contents without using any wrapper to get it's actual contents on disk - $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); - - // Check that the file was encrypted before being written to disk - $this->assertNotEquals( $this->dataShort, $retreivedCryptedFile ); - - // Get private key - $encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); - - $decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass ); - - - // Get keyfile - $encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename ); - - $decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey ); - - - // Manually decrypt - $manualDecrypt = Encryption\Crypt::symmetricBlockDecryptFileContent( $retreivedCryptedFile, $decryptedKeyfile ); - - // Check that decrypted data matches - $this->assertEquals( $this->dataShort, $manualDecrypt ); - - } - - /** - * @brief Test that data that is written by the crypto stream wrapper - * @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read - * @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual - * reassembly of its data - */ - function testSymmetricStreamEncryptLongFileContent() { - - // Generate a a random filename - $filename = 'tmp-'.time(); - - // Save long data as encrypted file using stream wrapper - $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong.$this->dataLong ); - - // Test that data was successfully written - $this->assertTrue( is_int( $cryptedFile ) ); - - // Get file contents without using any wrapper to get it's actual contents on disk - $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); - -// echo "\n\n\$retreivedCryptedFile = $retreivedCryptedFile\n\n"; - - // Check that the file was encrypted before being written to disk - $this->assertNotEquals( $this->dataLong.$this->dataLong, $retreivedCryptedFile ); - - // Manuallly split saved file into separate IVs and encrypted chunks - $r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE); - - //print_r($r); - - // Join IVs and their respective data chunks - $e = array( $r[0].$r[1], $r[2].$r[3], $r[4].$r[5], $r[6].$r[7], $r[8].$r[9], $r[10].$r[11], $r[12].$r[13] );//.$r[11], $r[12].$r[13], $r[14] ); - - //print_r($e); - - - // Get private key - $encryptedPrivateKey = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); - - $decryptedPrivateKey = Encryption\Crypt::symmetricDecryptFileContent( $encryptedPrivateKey, $this->pass ); - - - // Get keyfile - $encryptedKeyfile = Encryption\Keymanager::getFileKey( $this->view, $this->userId, $filename ); - - $decryptedKeyfile = Encryption\Crypt::keyDecrypt( $encryptedKeyfile, $decryptedPrivateKey ); - - - // Set var for reassembling decrypted content - $decrypt = ''; - - // Manually decrypt chunk - foreach ($e as $e) { - -// echo "\n\$e = $e"; - - $chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent( $e, $decryptedKeyfile ); - - // Assemble decrypted chunks - $decrypt .= $chunkDecrypt; - -// echo "\n\$chunkDecrypt = $chunkDecrypt"; - - } - -// echo "\n\$decrypt = $decrypt"; - - $this->assertEquals( $this->dataLong.$this->dataLong, $decrypt ); - - // Teardown - - $this->view->unlink( $filename ); - - Encryption\Keymanager::deleteFileKey( $filename ); - - } - - /** - * @brief Test that data that is read by the crypto stream wrapper - */ - function testSymmetricStreamDecryptShortFileContent() { - - $filename = 'tmp-'.time(); - - // Save long data as encrypted file using stream wrapper - $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataShort ); - - // Test that data was successfully written - $this->assertTrue( is_int( $cryptedFile ) ); - - - // Get file contents without using any wrapper to get it's actual contents on disk - $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); - - $decrypt = file_get_contents( 'crypt://' . $filename ); - - $this->assertEquals( $this->dataShort, $decrypt ); - - } - - function testSymmetricStreamDecryptLongFileContent() { - - $filename = 'tmp-'.time(); - - // Save long data as encrypted file using stream wrapper - $cryptedFile = file_put_contents( 'crypt://' . $filename, $this->dataLong ); - - // Test that data was successfully written - $this->assertTrue( is_int( $cryptedFile ) ); - - - // Get file contents without using any wrapper to get it's actual contents on disk - $retreivedCryptedFile = $this->view->file_get_contents( $this->userId . '/files/' . $filename ); - - $decrypt = file_get_contents( 'crypt://' . $filename ); - - $this->assertEquals( $this->dataLong, $decrypt ); - - } - - // Is this test still necessary? -// function testSymmetricBlockStreamDecryptFileContent() { -// -// \OC_User::setUserId( 'admin' ); -// -// // Disable encryption proxy to prevent unwanted en/decryption -// \OC_FileProxy::$enabled = false; -// -// $cryptedFile = file_put_contents( 'crypt://' . '/blockEncrypt', $this->dataUrl ); -// -// // Disable encryption proxy to prevent unwanted en/decryption -// \OC_FileProxy::$enabled = false; -// -// echo "\n\n\$cryptedFile = " . $this->view->file_get_contents( '/blockEncrypt' ); -// -// $retreivedCryptedFile = file_get_contents( 'crypt://' . '/blockEncrypt' ); -// -// $this->assertEquals( $this->dataUrl, $retreivedCryptedFile ); -// -// \OC_FileProxy::$enabled = false; -// -// } - - function testSymmetricEncryptFileContentKeyfile() { - - # TODO: search in keyfile for actual content as IV will ensure this test always passes - - $crypted = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->dataUrl ); - - $this->assertNotEquals( $this->dataUrl, $crypted['encrypted'] ); - - - $decrypt = Encryption\Crypt::symmetricDecryptFileContent( $crypted['encrypted'], $crypted['key'] ); - - $this->assertEquals( $this->dataUrl, $decrypt ); - - } - - function testIsEncryptedContent() { - - $this->assertFalse( Encryption\Crypt::isCatfile( $this->dataUrl ) ); - - $this->assertFalse( Encryption\Crypt::isCatfile( $this->legacyEncryptedData ) ); - - $keyfileContent = Encryption\Crypt::symmetricEncryptFileContent( $this->dataUrl, 'hat' ); - - $this->assertTrue( Encryption\Crypt::isCatfile( $keyfileContent ) ); - - } - - function testMultiKeyEncrypt() { - - # TODO: search in keyfile for actual content as IV will ensure this test always passes - - $pair1 = Encryption\Crypt::createKeypair(); - - $this->assertEquals( 2, count( $pair1 ) ); - - $this->assertTrue( strlen( $pair1['publicKey'] ) > 1 ); - - $this->assertTrue( strlen( $pair1['privateKey'] ) > 1 ); - - - $crypted = Encryption\Crypt::multiKeyEncrypt( $this->dataUrl, array( $pair1['publicKey'] ) ); - - $this->assertNotEquals( $this->dataUrl, $crypted['encrypted'] ); - - - $decrypt = Encryption\Crypt::multiKeyDecrypt( $crypted['encrypted'], $crypted['keys'][0], $pair1['privateKey'] ); - - $this->assertEquals( $this->dataUrl, $decrypt ); - - } - - function testKeyEncrypt() { - - // Generate keypair - $pair1 = Encryption\Crypt::createKeypair(); - - // Encrypt data - $crypted = Encryption\Crypt::keyEncrypt( $this->dataUrl, $pair1['publicKey'] ); - - $this->assertNotEquals( $this->dataUrl, $crypted ); - - // Decrypt data - $decrypt = Encryption\Crypt::keyDecrypt( $crypted, $pair1['privateKey'] ); - - $this->assertEquals( $this->dataUrl, $decrypt ); - - } - - // What is the point of this test? It doesn't use keyEncryptKeyfile() - function testKeyEncryptKeyfile() { - - # TODO: Don't repeat encryption from previous tests, use PHPUnit test interdependency instead - - // Generate keypair - $pair1 = Encryption\Crypt::createKeypair(); - - // Encrypt plain data, generate keyfile & encrypted file - $cryptedData = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->dataUrl ); - - // Encrypt keyfile - $cryptedKey = Encryption\Crypt::keyEncrypt( $cryptedData['key'], $pair1['publicKey'] ); - - // Decrypt keyfile - $decryptKey = Encryption\Crypt::keyDecrypt( $cryptedKey, $pair1['privateKey'] ); - - // Decrypt encrypted file - $decryptData = Encryption\Crypt::symmetricDecryptFileContent( $cryptedData['encrypted'], $decryptKey ); - - $this->assertEquals( $this->dataUrl, $decryptData ); - - } - - /** - * @brief test functionality of keyEncryptKeyfile() and - * keyDecryptKeyfile() - */ - function testKeyDecryptKeyfile() { - - $encrypted = Encryption\Crypt::keyEncryptKeyfile( $this->dataShort, $this->genPublicKey ); - - $this->assertNotEquals( $encrypted['data'], $this->dataShort ); - - $decrypted = Encryption\Crypt::keyDecryptKeyfile( $encrypted['data'], $encrypted['key'], $this->genPrivateKey ); - - $this->assertEquals( $decrypted, $this->dataShort ); - - } - - - /** - * @brief test encryption using legacy blowfish method - */ - function testLegacyEncryptShort() { - - $crypted = Encryption\Crypt::legacyEncrypt( $this->dataShort, $this->pass ); - - $this->assertNotEquals( $this->dataShort, $crypted ); - - # TODO: search inencrypted text for actual content to ensure it - # genuine transformation - - return $crypted; - - } - - /** - * @brief test decryption using legacy blowfish method - * @depends testLegacyEncryptShort - */ - function testLegacyDecryptShort( $crypted ) { - - $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass ); - - $this->assertEquals( $this->dataShort, $decrypted ); - - } - - /** - * @brief test encryption using legacy blowfish method - */ - function testLegacyEncryptLong() { - - $crypted = Encryption\Crypt::legacyEncrypt( $this->dataLong, $this->pass ); - - $this->assertNotEquals( $this->dataLong, $crypted ); - - # TODO: search inencrypted text for actual content to ensure it - # genuine transformation - - return $crypted; - - } - - /** - * @brief test decryption using legacy blowfish method - * @depends testLegacyEncryptLong - */ - function testLegacyDecryptLong( $crypted ) { - - $decrypted = Encryption\Crypt::legacyDecrypt( $crypted, $this->pass ); - - $this->assertEquals( $this->dataLong, $decrypted ); - - } - - /** - * @brief test generation of legacy encryption key - * @depends testLegacyDecryptShort - */ - function testLegacyCreateKey() { - - // Create encrypted key - $encKey = Encryption\Crypt::legacyCreateKey( $this->pass ); - - // Decrypt key - $key = Encryption\Crypt::legacyDecrypt( $encKey, $this->pass ); - - $this->assertTrue( is_numeric( $key ) ); - - // Check that key is correct length - $this->assertEquals( 20, strlen( $key ) ); - - } - - /** - * @brief test decryption using legacy blowfish method - * @depends testLegacyEncryptLong - */ - function testLegacyKeyRecryptKeyfileEncrypt( $crypted ) { - - $recrypted = Encryption\Crypt::LegacyKeyRecryptKeyfile( $crypted, $this->pass, $this->genPublicKey, $this->pass ); - - $this->assertNotEquals( $this->dataLong, $recrypted['data'] ); - - return $recrypted; - - # TODO: search inencrypted text for actual content to ensure it - # genuine transformation - - } - -// function testEncryption(){ -// -// $key=uniqid(); -// $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; -// $source=file_get_contents($file); //nice large text file -// $encrypted=OC_Encryption\Crypt::encrypt($source,$key); -// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key); -// $decrypted=rtrim($decrypted, "\0"); -// $this->assertNotEquals($encrypted,$source); -// $this->assertEquals($decrypted,$source); -// -// $chunk=substr($source,0,8192); -// $encrypted=OC_Encryption\Crypt::encrypt($chunk,$key); -// $this->assertEquals(strlen($chunk),strlen($encrypted)); -// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key); -// $decrypted=rtrim($decrypted, "\0"); -// $this->assertEquals($decrypted,$chunk); -// -// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key); -// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key); -// $this->assertNotEquals($encrypted,$source); -// $this->assertEquals($decrypted,$source); -// -// $tmpFileEncrypted=OCP\Files::tmpFile(); -// OC_Encryption\Crypt::encryptfile($file,$tmpFileEncrypted,$key); -// $encrypted=file_get_contents($tmpFileEncrypted); -// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key); -// $this->assertNotEquals($encrypted,$source); -// $this->assertEquals($decrypted,$source); -// -// $tmpFileDecrypted=OCP\Files::tmpFile(); -// OC_Encryption\Crypt::decryptfile($tmpFileEncrypted,$tmpFileDecrypted,$key); -// $decrypted=file_get_contents($tmpFileDecrypted); -// $this->assertEquals($decrypted,$source); -// -// $file=OC::$SERVERROOT.'/core/img/weather-clear.png'; -// $source=file_get_contents($file); //binary file -// $encrypted=OC_Encryption\Crypt::encrypt($source,$key); -// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key); -// $decrypted=rtrim($decrypted, "\0"); -// $this->assertEquals($decrypted,$source); -// -// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key); -// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key); -// $this->assertEquals($decrypted,$source); -// -// } -// -// function testBinary(){ -// $key=uniqid(); -// -// $file=__DIR__.'/binary'; -// $source=file_get_contents($file); //binary file -// $encrypted=OC_Encryption\Crypt::encrypt($source,$key); -// $decrypted=OC_Encryption\Crypt::decrypt($encrypted,$key); -// -// $decrypted=rtrim($decrypted, "\0"); -// $this->assertEquals($decrypted,$source); -// -// $encrypted=OC_Encryption\Crypt::blockEncrypt($source,$key); -// $decrypted=OC_Encryption\Crypt::blockDecrypt($encrypted,$key,strlen($source)); -// $this->assertEquals($decrypted,$source); -// } - -} diff --git a/apps/files_encryption/test/keymanager.php b/apps/files_encryption/test/keymanager.php deleted file mode 100644 index bf453fe316..0000000000 --- a/apps/files_encryption/test/keymanager.php +++ /dev/null @@ -1,130 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -//require_once "PHPUnit/Framework/TestCase.php"; -require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); -require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); -require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' ); -require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); -require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); -require_once realpath( dirname(__FILE__).'/../lib/util.php' ); -require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); - -use OCA\Encryption; - -// This has to go here because otherwise session errors arise, and the private -// encryption key needs to be saved in the session -\OC_User::login( 'admin', 'admin' ); - -class Test_Keymanager extends \PHPUnit_Framework_TestCase { - - function setUp() { - - \OC_FileProxy::$enabled = false; - - // set content for encrypting / decrypting in tests - $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); - $this->dataShort = 'hats'; - $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); - $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); - $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); - $this->randomKey = Encryption\Crypt::generateKey(); - - $keypair = Encryption\Crypt::createKeypair(); - $this->genPublicKey = $keypair['publicKey']; - $this->genPrivateKey = $keypair['privateKey']; - - $this->view = new \OC_FilesystemView( '/' ); - - \OC_User::setUserId( 'admin' ); - $this->userId = 'admin'; - $this->pass = 'admin'; - - \OC_Filesystem::init( '/' ); - \OC_Filesystem::mount( 'OC_Filestorage_Local', array('datadir' => \OC_User::getHome($this->userId)), '/' ); - - } - - function tearDown(){ - - \OC_FileProxy::$enabled = true; - - } - - function testGetPrivateKey() { - - $key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); - - // Will this length vary? Perhaps we should use a range instead - $this->assertEquals( 2296, strlen( $key ) ); - - } - - function testGetPublicKey() { - - $key = Encryption\Keymanager::getPublicKey( $this->view, $this->userId ); - - $this->assertEquals( 451, strlen( $key ) ); - - $this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $key, 0, 26 ) ); - } - - function testSetFileKey() { - - # NOTE: This cannot be tested until we are able to break out - # of the FileSystemView data directory root - - $key = Encryption\Crypt::symmetricEncryptFileContentKeyfile( $this->randomKey, 'hat' ); - - $path = 'unittest-'.time().'txt'; - - //$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' ); - - Encryption\Keymanager::setFileKey( $this->view, $path, $this->userId, $key['key'] ); - - } - -// /** -// * @depends testGetPrivateKey -// */ -// function testGetPrivateKey_decrypt() { -// -// $key = Encryption\Keymanager::getPrivateKey( $this->view, $this->userId ); -// -// # TODO: replace call to Crypt with a mock object? -// $decrypted = Encryption\Crypt::symmetricDecryptFileContent( $key, $this->passphrase ); -// -// $this->assertEquals( 1704, strlen( $decrypted ) ); -// -// $this->assertEquals( '-----BEGIN PRIVATE KEY-----', substr( $decrypted, 0, 27 ) ); -// -// } - - function testGetUserKeys() { - - $keys = Encryption\Keymanager::getUserKeys( $this->view, $this->userId ); - - $this->assertEquals( 451, strlen( $keys['publicKey'] ) ); - $this->assertEquals( '-----BEGIN PUBLIC KEY-----', substr( $keys['publicKey'], 0, 26 ) ); - $this->assertEquals( 2296, strlen( $keys['privateKey'] ) ); - - } - - function testGetPublicKeys() { - - # TODO: write me - - } - - function testGetFileKey() { - -// Encryption\Keymanager::getFileKey( $this->view, $this->userId, $this->filePath ); - - } - -} diff --git a/apps/files_encryption/test/legacy-encrypted-text.txt b/apps/files_encryption/test/legacy-encrypted-text.txt deleted file mode 100644 index cb5bf50550..0000000000 Binary files a/apps/files_encryption/test/legacy-encrypted-text.txt and /dev/null differ diff --git a/apps/files_encryption/test/stream.php b/apps/files_encryption/test/stream.php deleted file mode 100644 index ba82ac80ea..0000000000 --- a/apps/files_encryption/test/stream.php +++ /dev/null @@ -1,226 +0,0 @@ -// -// * This file is licensed under the Affero General Public License version 3 or -// * later. -// * See the COPYING-README file. -// */ -// -// namespace OCA\Encryption; -// -// class Test_Stream extends \PHPUnit_Framework_TestCase { -// -// function setUp() { -// -// \OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' ); -// -// $this->empty = ''; -// -// $this->stream = new Stream(); -// -// $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); -// $this->dataShort = 'hats'; -// -// $this->emptyTmpFilePath = \OCP\Files::tmpFile(); -// -// $this->dataTmpFilePath = \OCP\Files::tmpFile(); -// -// file_put_contents( $this->dataTmpFilePath, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est." ); -// -// } -// -// function testStreamOpen() { -// -// $stream1 = new Stream(); -// -// $handle1 = $stream1->stream_open( $this->emptyTmpFilePath, 'wb', array(), $this->empty ); -// -// // Test that resource was returned successfully -// $this->assertTrue( $handle1 ); -// -// // Test that file has correct size -// $this->assertEquals( 0, $stream1->size ); -// -// // Test that path is correct -// $this->assertEquals( $this->emptyTmpFilePath, $stream1->rawPath ); -// -// $stream2 = new Stream(); -// -// $handle2 = $stream2->stream_open( 'crypt://' . $this->emptyTmpFilePath, 'wb', array(), $this->empty ); -// -// // Test that protocol identifier is removed from path -// $this->assertEquals( $this->emptyTmpFilePath, $stream2->rawPath ); -// -// // "Stat failed error" prevents this test from executing -// // $stream3 = new Stream(); -// // -// // $handle3 = $stream3->stream_open( $this->dataTmpFilePath, 'r', array(), $this->empty ); -// // -// // $this->assertEquals( 0, $stream3->size ); -// -// } -// -// function testStreamWrite() { -// -// $stream1 = new Stream(); -// -// $handle1 = $stream1->stream_open( $this->emptyTmpFilePath, 'r+b', array(), $this->empty ); -// -// # what about the keymanager? there is no key for the newly created temporary file! -// -// $stream1->stream_write( $this->dataShort ); -// -// } -// -// // function getStream( $id, $mode, $size ) { -// // -// // if ( $id === '' ) { -// // -// // $id = uniqid(); -// // } -// // -// // -// // if ( !isset( $this->tmpFiles[$id] ) ) { -// // -// // // If tempfile with given name does not already exist, create it -// // -// // $file = OCP\Files::tmpFile(); -// // -// // $this->tmpFiles[$id] = $file; -// // -// // } else { -// // -// // $file = $this->tmpFiles[$id]; -// // -// // } -// // -// // $stream = fopen( $file, $mode ); -// // -// // Stream::$sourceStreams[$id] = array( 'path' => 'dummy' . $id, 'stream' => $stream, 'size' => $size ); -// // -// // return fopen( 'crypt://streams/'.$id, $mode ); -// // -// // } -// // -// // function testStream( ){ -// // -// // $stream = $this->getStream( 'test1', 'w', strlen( 'foobar' ) ); -// // -// // fwrite( $stream, 'foobar' ); -// // -// // fclose( $stream ); -// // -// // -// // $stream = $this->getStream( 'test1', 'r', strlen( 'foobar' ) ); -// // -// // $data = fread( $stream, 6 ); -// // -// // fclose( $stream ); -// // -// // $this->assertEquals( 'foobar', $data ); -// // -// // -// // $file = OC::$SERVERROOT.'/3rdparty/MDB2.php'; -// // -// // $source = fopen( $file, 'r' ); -// // -// // $target = $this->getStream( 'test2', 'w', 0 ); -// // -// // OCP\Files::streamCopy( $source, $target ); -// // -// // fclose( $target ); -// // -// // fclose( $source ); -// // -// // -// // $stream = $this->getStream( 'test2', 'r', filesize( $file ) ); -// // -// // $data = stream_get_contents( $stream ); -// // -// // $original = file_get_contents( $file ); -// // -// // $this->assertEquals( strlen( $original ), strlen( $data ) ); -// // -// // $this->assertEquals( $original, $data ); -// // -// // } -// -// } -// -// // class Test_CryptStream extends PHPUnit_Framework_TestCase { -// // private $tmpFiles=array(); -// // -// // function testStream(){ -// // $stream=$this->getStream('test1','w',strlen('foobar')); -// // fwrite($stream,'foobar'); -// // fclose($stream); -// // -// // $stream=$this->getStream('test1','r',strlen('foobar')); -// // $data=fread($stream,6); -// // fclose($stream); -// // $this->assertEquals('foobar',$data); -// // -// // $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; -// // $source=fopen($file,'r'); -// // $target=$this->getStream('test2','w',0); -// // OCP\Files::streamCopy($source,$target); -// // fclose($target); -// // fclose($source); -// // -// // $stream=$this->getStream('test2','r',filesize($file)); -// // $data=stream_get_contents($stream); -// // $original=file_get_contents($file); -// // $this->assertEquals(strlen($original),strlen($data)); -// // $this->assertEquals($original,$data); -// // } -// // -// // /** -// // * get a cryptstream to a temporary file -// // * @param string $id -// // * @param string $mode -// // * @param int size -// // * @return resource -// // */ -// // function getStream($id,$mode,$size){ -// // if($id===''){ -// // $id=uniqid(); -// // } -// // if(!isset($this->tmpFiles[$id])){ -// // $file=OCP\Files::tmpFile(); -// // $this->tmpFiles[$id]=$file; -// // }else{ -// // $file=$this->tmpFiles[$id]; -// // } -// // $stream=fopen($file,$mode); -// // OC_CryptStream::$sourceStreams[$id]=array('path'=>'dummy'.$id,'stream'=>$stream,'size'=>$size); -// // return fopen('crypt://streams/'.$id,$mode); -// // } -// // -// // function testBinary(){ -// // $file=__DIR__.'/binary'; -// // $source=file_get_contents($file); -// // -// // $stream=$this->getStream('test','w',strlen($source)); -// // fwrite($stream,$source); -// // fclose($stream); -// // -// // $stream=$this->getStream('test','r',strlen($source)); -// // $data=stream_get_contents($stream); -// // fclose($stream); -// // $this->assertEquals(strlen($data),strlen($source)); -// // $this->assertEquals($source,$data); -// // -// // $file=__DIR__.'/zeros'; -// // $source=file_get_contents($file); -// // -// // $stream=$this->getStream('test2','w',strlen($source)); -// // fwrite($stream,$source); -// // fclose($stream); -// // -// // $stream=$this->getStream('test2','r',strlen($source)); -// // $data=stream_get_contents($stream); -// // fclose($stream); -// // $this->assertEquals(strlen($data),strlen($source)); -// // $this->assertEquals($source,$data); -// // } -// // } diff --git a/apps/files_encryption/test/util.php b/apps/files_encryption/test/util.php deleted file mode 100755 index 1cdeff8008..0000000000 --- a/apps/files_encryption/test/util.php +++ /dev/null @@ -1,225 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -//require_once "PHPUnit/Framework/TestCase.php"; -require_once realpath( dirname(__FILE__).'/../../../lib/base.php' ); -require_once realpath( dirname(__FILE__).'/../lib/crypt.php' ); -require_once realpath( dirname(__FILE__).'/../lib/keymanager.php' ); -require_once realpath( dirname(__FILE__).'/../lib/proxy.php' ); -require_once realpath( dirname(__FILE__).'/../lib/stream.php' ); -require_once realpath( dirname(__FILE__).'/../lib/util.php' ); -require_once realpath( dirname(__FILE__).'/../appinfo/app.php' ); - -// Load mockery files -require_once 'Mockery/Loader.php'; -require_once 'Hamcrest/Hamcrest.php'; -$loader = new \Mockery\Loader; -$loader->register(); - -use \Mockery as m; -use OCA\Encryption; - -class Test_Enc_Util extends \PHPUnit_Framework_TestCase { - - function setUp() { - - \OC_Filesystem::mount( 'OC_Filestorage_Local', array(), '/' ); - - // set content for encrypting / decrypting in tests - $this->dataUrl = realpath( dirname(__FILE__).'/../lib/crypt.php' ); - $this->dataShort = 'hats'; - $this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) ); - $this->legacyData = realpath( dirname(__FILE__).'/legacy-text.txt' ); - $this->legacyEncryptedData = realpath( dirname(__FILE__).'/legacy-encrypted-text.txt' ); - - $this->userId = 'admin'; - $this->pass = 'admin'; - - $keypair = Encryption\Crypt::createKeypair(); - - $this->genPublicKey = $keypair['publicKey']; - $this->genPrivateKey = $keypair['privateKey']; - - $this->publicKeyDir = '/' . 'public-keys'; - $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; - $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; - $this->publicKeyPath = $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key - $this->privateKeyPath = $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key - - $this->view = new \OC_FilesystemView( '/' ); - - $this->mockView = m::mock('OC_FilesystemView'); - $this->util = new Encryption\Util( $this->mockView, $this->userId ); - - } - - function tearDown(){ - - m::close(); - - } - - /** - * @brief test that paths set during User construction are correct - */ - function testKeyPaths() { - - $mockView = m::mock('OC_FilesystemView'); - - $util = new Encryption\Util( $mockView, $this->userId ); - - $this->assertEquals( $this->publicKeyDir, $util->getPath( 'publicKeyDir' ) ); - $this->assertEquals( $this->encryptionDir, $util->getPath( 'encryptionDir' ) ); - $this->assertEquals( $this->keyfilesPath, $util->getPath( 'keyfilesPath' ) ); - $this->assertEquals( $this->publicKeyPath, $util->getPath( 'publicKeyPath' ) ); - $this->assertEquals( $this->privateKeyPath, $util->getPath( 'privateKeyPath' ) ); - - } - - /** - * @brief test setup of encryption directories when they don't yet exist - */ - function testSetupServerSideNotSetup() { - - $mockView = m::mock('OC_FilesystemView'); - - $mockView->shouldReceive( 'file_exists' )->times(5)->andReturn( false ); - $mockView->shouldReceive( 'mkdir' )->times(4)->andReturn( true ); - $mockView->shouldReceive( 'file_put_contents' )->withAnyArgs(); - - $util = new Encryption\Util( $mockView, $this->userId ); - - $this->assertEquals( true, $util->setupServerSide( $this->pass ) ); - - } - - /** - * @brief test setup of encryption directories when they already exist - */ - function testSetupServerSideIsSetup() { - - $mockView = m::mock('OC_FilesystemView'); - - $mockView->shouldReceive( 'file_exists' )->times(6)->andReturn( true ); - $mockView->shouldReceive( 'file_put_contents' )->withAnyArgs(); - - $util = new Encryption\Util( $mockView, $this->userId ); - - $this->assertEquals( true, $util->setupServerSide( $this->pass ) ); - - } - - /** - * @brief test checking whether account is ready for encryption, when it isn't ready - */ - function testReadyNotReady() { - - $mockView = m::mock('OC_FilesystemView'); - - $mockView->shouldReceive( 'file_exists' )->times(1)->andReturn( false ); - - $util = new Encryption\Util( $mockView, $this->userId ); - - $this->assertEquals( false, $util->ready() ); - - # TODO: Add more tests here to check that if any of the dirs are - # then false will be returned. Use strict ordering? - - } - - /** - * @brief test checking whether account is ready for encryption, when it is ready - */ - function testReadyIsReady() { - - $mockView = m::mock('OC_FilesystemView'); - - $mockView->shouldReceive( 'file_exists' )->times(3)->andReturn( true ); - - $util = new Encryption\Util( $mockView, $this->userId ); - - $this->assertEquals( true, $util->ready() ); - - # TODO: Add more tests here to check that if any of the dirs are - # then false will be returned. Use strict ordering? - - } - - function testFindFiles() { - -// $this->view->chroot( "/data/{$this->userId}/files" ); - - $util = new Encryption\Util( $this->view, $this->userId ); - - $files = $util->findFiles( '/', 'encrypted' ); - - var_dump( $files ); - - # TODO: Add more tests here to check that if any of the dirs are - # then false will be returned. Use strict ordering? - - } - -// /** -// * @brief test decryption using legacy blowfish method -// * @depends testLegacyEncryptLong -// */ -// function testLegacyKeyRecryptKeyfileDecrypt( $recrypted ) { -// -// $decrypted = Encryption\Crypt::keyDecryptKeyfile( $recrypted['data'], $recrypted['key'], $this->genPrivateKey ); -// -// $this->assertEquals( $this->dataLong, $decrypted ); -// -// } - -// // Cannot use this test for now due to hidden dependencies in OC_FileCache -// function testIsLegacyEncryptedContent() { -// -// $keyfileContent = OCA\Encryption\Crypt::symmetricEncryptFileContent( $this->legacyEncryptedData, 'hat' ); -// -// $this->assertFalse( OCA\Encryption\Crypt::isLegacyEncryptedContent( $keyfileContent, '/files/admin/test.txt' ) ); -// -// OC_FileCache::put( '/admin/files/legacy-encrypted-test.txt', $this->legacyEncryptedData ); -// -// $this->assertTrue( OCA\Encryption\Crypt::isLegacyEncryptedContent( $this->legacyEncryptedData, '/files/admin/test.txt' ) ); -// -// } - -// // Cannot use this test for now due to need for different root in OC_Filesystem_view class -// function testGetLegacyKey() { -// -// $c = new \OCA\Encryption\Util( $view, false ); -// -// $bool = $c->getLegacyKey( 'admin' ); -// -// $this->assertTrue( $bool ); -// -// $this->assertTrue( $c->legacyKey ); -// -// $this->assertTrue( is_int( $c->legacyKey ) ); -// -// $this->assertTrue( strlen( $c->legacyKey ) == 20 ); -// -// } - -// // Cannot use this test for now due to need for different root in OC_Filesystem_view class -// function testLegacyDecrypt() { -// -// $c = new OCA\Encryption\Util( $this->view, false ); -// -// $bool = $c->getLegacyKey( 'admin' ); -// -// $encrypted = $c->legacyEncrypt( $this->data, $c->legacyKey ); -// -// $decrypted = $c->legacyDecrypt( $encrypted, $c->legacyKey ); -// -// $this->assertEquals( $decrypted, $this->data ); -// -// } - -} \ No newline at end of file diff --git a/apps/files_encryption/test/binary b/apps/files_encryption/tests/binary similarity index 100% rename from apps/files_encryption/test/binary rename to apps/files_encryption/tests/binary diff --git a/apps/files_encryption/tests/crypt.php b/apps/files_encryption/tests/crypt.php new file mode 100755 index 0000000000..32156eea27 --- /dev/null +++ b/apps/files_encryption/tests/crypt.php @@ -0,0 +1,809 @@ +, and + * Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +require_once realpath(dirname(__FILE__) . '/../3rdparty/Crypt_Blowfish/Blowfish.php'); +require_once realpath(dirname(__FILE__) . '/../../../lib/base.php'); +require_once realpath(dirname(__FILE__) . '/../lib/crypt.php'); +require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php'); +require_once realpath(dirname(__FILE__) . '/../lib/proxy.php'); +require_once realpath(dirname(__FILE__) . '/../lib/stream.php'); +require_once realpath(dirname(__FILE__) . '/../lib/util.php'); +require_once realpath(dirname(__FILE__) . '/../lib/helper.php'); +require_once realpath(dirname(__FILE__) . '/../appinfo/app.php'); +require_once realpath(dirname(__FILE__) . '/util.php'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Crypt + */ +class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase { + + const TEST_ENCRYPTION_CRYPT_USER1 = "test-crypt-user1"; + + public $userId; + public $pass; + public $stateFilesTrashbin; + public $dataLong; + public $dataUrl; + public $dataShort; + /** + * @var OC_FilesystemView + */ + public $view; + public $legacyEncryptedData; + public $genPrivateKey; + public $genPublicKey; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerUserHooks(); + + // clear and register hooks + \OC_FileProxy::clearProxies(); + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // create test user + \Test_Encryption_Util::loginHelper(\Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1, true); + } + + function setUp() { + // set user id + \OC_User::setUserId(\Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1); + $this->userId = \Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1; + $this->pass = \Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1; + + // set content for encrypting / decrypting in tests + $this->dataLong = file_get_contents(realpath(dirname(__FILE__) . '/../lib/crypt.php')); + $this->dataShort = 'hats'; + $this->dataUrl = realpath(dirname(__FILE__) . '/../lib/crypt.php'); + $this->legacyData = realpath(dirname(__FILE__) . '/legacy-text.txt'); + $this->legacyEncryptedData = realpath(dirname(__FILE__) . '/legacy-encrypted-text.txt'); + $this->legacyEncryptedDataKey = realpath(dirname(__FILE__) . '/encryption.key'); + $this->randomKey = Encryption\Crypt::generateKey(); + + $keypair = Encryption\Crypt::createKeypair(); + $this->genPublicKey = $keypair['publicKey']; + $this->genPrivateKey = $keypair['privateKey']; + + $this->view = new \OC_FilesystemView('/'); + + // remember files_trashbin state + $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we don't want to tests with app files_trashbin enabled + \OC_App::disable('files_trashbin'); + } + + function tearDown() { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + + public static function tearDownAfterClass() { + // cleanup test user + \OC_User::deleteUser(\Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1); + } + + function testGenerateKey() { + + # TODO: use more accurate (larger) string length for test confirmation + + $key = Encryption\Crypt::generateKey(); + + $this->assertTrue(strlen($key) > 16); + + } + + /** + * @return String + */ + function testGenerateIv() { + + $iv = Encryption\Crypt::generateIv(); + + $this->assertEquals(16, strlen($iv)); + + return $iv; + + } + + /** + * @depends testGenerateIv + */ + function testConcatIv($iv) { + + $catFile = Encryption\Crypt::concatIv($this->dataLong, $iv); + + // Fetch encryption metadata from end of file + $meta = substr($catFile, -22); + + $identifier = substr($meta, 0, 6); + + // Fetch IV from end of file + $foundIv = substr($meta, 6); + + $this->assertEquals('00iv00', $identifier); + + $this->assertEquals($iv, $foundIv); + + // Remove IV and IV identifier text to expose encrypted content + $data = substr($catFile, 0, -22); + + $this->assertEquals($this->dataLong, $data); + + return array( + 'iv' => $iv + , + 'catfile' => $catFile + ); + + } + + /** + * @depends testConcatIv + */ + function testSplitIv($testConcatIv) { + + // Split catfile into components + $splitCatfile = Encryption\Crypt::splitIv($testConcatIv['catfile']); + + // Check that original IV and split IV match + $this->assertEquals($testConcatIv['iv'], $splitCatfile['iv']); + + // Check that original data and split data match + $this->assertEquals($this->dataLong, $splitCatfile['encrypted']); + + } + + /** + * @return string padded + */ + function testAddPadding() { + + $padded = Encryption\Crypt::addPadding($this->dataLong); + + $padding = substr($padded, -2); + + $this->assertEquals('xx', $padding); + + return $padded; + + } + + /** + * @depends testAddPadding + */ + function testRemovePadding($padded) { + + $noPadding = Encryption\Crypt::RemovePadding($padded); + + $this->assertEquals($this->dataLong, $noPadding); + + } + + function testEncrypt() { + + $random = openssl_random_pseudo_bytes(13); + + $iv = substr(base64_encode($random), 0, -4); // i.e. E5IG033j+mRNKrht + + $crypted = Encryption\Crypt::encrypt($this->dataUrl, $iv, 'hat'); + + $this->assertNotEquals($this->dataUrl, $crypted); + + } + + function testDecrypt() { + + $random = openssl_random_pseudo_bytes(13); + + $iv = substr(base64_encode($random), 0, -4); // i.e. E5IG033j+mRNKrht + + $crypted = Encryption\Crypt::encrypt($this->dataUrl, $iv, 'hat'); + + $decrypt = Encryption\Crypt::decrypt($crypted, $iv, 'hat'); + + $this->assertEquals($this->dataUrl, $decrypt); + + } + + function testSymmetricEncryptFileContent() { + + # TODO: search in keyfile for actual content as IV will ensure this test always passes + + $crypted = Encryption\Crypt::symmetricEncryptFileContent($this->dataShort, 'hat'); + + $this->assertNotEquals($this->dataShort, $crypted); + + + $decrypt = Encryption\Crypt::symmetricDecryptFileContent($crypted, 'hat'); + + $this->assertEquals($this->dataShort, $decrypt); + + } + + function testSymmetricStreamEncryptShortFileContent() { + + $filename = 'tmp-' . time() . '.test'; + + $cryptedFile = file_put_contents('crypt://' . $filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // Get file contents without using any wrapper to get it's actual contents on disk + $retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename); + + // Re-enable proxy - our work is done + \OC_FileProxy::$enabled = $proxyStatus; + + // Check that the file was encrypted before being written to disk + $this->assertNotEquals($this->dataShort, $retreivedCryptedFile); + + // Get the encrypted keyfile + $encKeyfile = Encryption\Keymanager::getFileKey($this->view, $this->userId, $filename); + + // Attempt to fetch the user's shareKey + $shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $filename); + + // get session + $session = new \OCA\Encryption\Session($this->view); + + // get private key + $privateKey = $session->getPrivateKey($this->userId); + + // Decrypt keyfile with shareKey + $plainKeyfile = Encryption\Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); + + // Manually decrypt + $manualDecrypt = Encryption\Crypt::symmetricDecryptFileContent($retreivedCryptedFile, $plainKeyfile); + + // Check that decrypted data matches + $this->assertEquals($this->dataShort, $manualDecrypt); + + // Teardown + $this->view->unlink($this->userId . '/files/' . $filename); + + Encryption\Keymanager::deleteFileKey($this->view, $this->userId, $filename); + } + + /** + * @brief Test that data that is written by the crypto stream wrapper + * @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read + * @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual + * reassembly of its data + */ + function testSymmetricStreamEncryptLongFileContent() { + + // Generate a a random filename + $filename = 'tmp-' . time() . '.test'; + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong . $this->dataLong); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // Get file contents without using any wrapper to get it's actual contents on disk + $retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename); + + // Re-enable proxy - our work is done + \OC_FileProxy::$enabled = $proxyStatus; + + + // Check that the file was encrypted before being written to disk + $this->assertNotEquals($this->dataLong . $this->dataLong, $retreivedCryptedFile); + + // Manuallly split saved file into separate IVs and encrypted chunks + $r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE); + + //print_r($r); + + // Join IVs and their respective data chunks + $e = array( + $r[0] . $r[1], + $r[2] . $r[3], + $r[4] . $r[5], + $r[6] . $r[7], + $r[8] . $r[9], + $r[10] . $r[11] + ); //.$r[11], $r[12].$r[13], $r[14] ); + + //print_r($e); + + // Get the encrypted keyfile + $encKeyfile = Encryption\Keymanager::getFileKey($this->view, $this->userId, $filename); + + // Attempt to fetch the user's shareKey + $shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $filename); + + // get session + $session = new \OCA\Encryption\Session($this->view); + + // get private key + $privateKey = $session->getPrivateKey($this->userId); + + // Decrypt keyfile with shareKey + $plainKeyfile = Encryption\Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); + + // Set var for reassembling decrypted content + $decrypt = ''; + + // Manually decrypt chunk + foreach ($e as $chunk) { + + $chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent($chunk, $plainKeyfile); + + // Assemble decrypted chunks + $decrypt .= $chunkDecrypt; + + } + + $this->assertEquals($this->dataLong . $this->dataLong, $decrypt); + + // Teardown + + $this->view->unlink($this->userId . '/files/' . $filename); + + Encryption\Keymanager::deleteFileKey($this->view, $this->userId, $filename); + + } + + /** + * @brief Test that data that is read by the crypto stream wrapper + */ + function testSymmetricStreamDecryptShortFileContent() { + + $filename = 'tmp-' . time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents('crypt://' . $filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $this->assertTrue(Encryption\Crypt::isEncryptedMeta($filename)); + + \OC_FileProxy::$enabled = $proxyStatus; + + // Get file decrypted contents + $decrypt = file_get_contents('crypt://' . $filename); + + $this->assertEquals($this->dataShort, $decrypt); + + // tear down + $this->view->unlink($this->userId . '/files/' . $filename); + } + + function testSymmetricStreamDecryptLongFileContent() { + + $filename = 'tmp-' . time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Get file decrypted contents + $decrypt = file_get_contents('crypt://' . $filename); + + $this->assertEquals($this->dataLong, $decrypt); + + // tear down + $this->view->unlink($this->userId . '/files/' . $filename); + } + + function testSymmetricEncryptFileContentKeyfile() { + + # TODO: search in keyfile for actual content as IV will ensure this test always passes + + $crypted = Encryption\Crypt::symmetricEncryptFileContentKeyfile($this->dataUrl); + + $this->assertNotEquals($this->dataUrl, $crypted['encrypted']); + + + $decrypt = Encryption\Crypt::symmetricDecryptFileContent($crypted['encrypted'], $crypted['key']); + + $this->assertEquals($this->dataUrl, $decrypt); + + } + + function testIsEncryptedContent() { + + $this->assertFalse(Encryption\Crypt::isCatfileContent($this->dataUrl)); + + $this->assertFalse(Encryption\Crypt::isCatfileContent($this->legacyEncryptedData)); + + $keyfileContent = Encryption\Crypt::symmetricEncryptFileContent($this->dataUrl, 'hat'); + + $this->assertTrue(Encryption\Crypt::isCatfileContent($keyfileContent)); + + } + + function testMultiKeyEncrypt() { + + # TODO: search in keyfile for actual content as IV will ensure this test always passes + + $pair1 = Encryption\Crypt::createKeypair(); + + $this->assertEquals(2, count($pair1)); + + $this->assertTrue(strlen($pair1['publicKey']) > 1); + + $this->assertTrue(strlen($pair1['privateKey']) > 1); + + + $crypted = Encryption\Crypt::multiKeyEncrypt($this->dataShort, array($pair1['publicKey'])); + + $this->assertNotEquals($this->dataShort, $crypted['data']); + + + $decrypt = Encryption\Crypt::multiKeyDecrypt($crypted['data'], $crypted['keys'][0], $pair1['privateKey']); + + $this->assertEquals($this->dataShort, $decrypt); + + } + + function testKeyEncrypt() { + + // Generate keypair + $pair1 = Encryption\Crypt::createKeypair(); + + // Encrypt data + $crypted = Encryption\Crypt::keyEncrypt($this->dataUrl, $pair1['publicKey']); + + $this->assertNotEquals($this->dataUrl, $crypted); + + // Decrypt data + $decrypt = Encryption\Crypt::keyDecrypt($crypted, $pair1['privateKey']); + + $this->assertEquals($this->dataUrl, $decrypt); + + } + + /** + * @brief test encryption using legacy blowfish method + */ + function testLegacyEncryptShort() { + + $crypted = Encryption\Crypt::legacyEncrypt($this->dataShort, $this->pass); + + $this->assertNotEquals($this->dataShort, $crypted); + + # TODO: search inencrypted text for actual content to ensure it + # genuine transformation + + return $crypted; + + } + + /** + * @brief test decryption using legacy blowfish method + * @depends testLegacyEncryptShort + */ + function testLegacyDecryptShort($crypted) { + + $decrypted = Encryption\Crypt::legacyBlockDecrypt($crypted, $this->pass); + + $this->assertEquals($this->dataShort, $decrypted); + + } + + /** + * @brief test encryption using legacy blowfish method + */ + function testLegacyEncryptLong() { + + $crypted = Encryption\Crypt::legacyEncrypt($this->dataLong, $this->pass); + + $this->assertNotEquals($this->dataLong, $crypted); + + # TODO: search inencrypted text for actual content to ensure it + # genuine transformation + + return $crypted; + + } + + /** + * @brief test decryption using legacy blowfish method + * @depends testLegacyEncryptLong + */ + function testLegacyDecryptLong($crypted) { + + $decrypted = Encryption\Crypt::legacyBlockDecrypt($crypted, $this->pass); + + $this->assertEquals($this->dataLong, $decrypted); + + $this->assertFalse(Encryption\Crypt::getBlowfish('')); + } + + /** + * @brief test generation of legacy encryption key + * @depends testLegacyDecryptShort + */ + function testLegacyCreateKey() { + + // Create encrypted key + $encKey = Encryption\Crypt::legacyCreateKey($this->pass); + + // Decrypt key + $key = Encryption\Crypt::legacyBlockDecrypt($encKey, $this->pass); + + $this->assertTrue(is_numeric($key)); + + // Check that key is correct length + $this->assertEquals(20, strlen($key)); + + } + + /** + * @brief test decryption using legacy blowfish method + * @depends testLegacyEncryptLong + */ + function testLegacyKeyRecryptKeyfileEncrypt($crypted) { + + $recrypted = Encryption\Crypt::LegacyKeyRecryptKeyfile($crypted, $this->pass, array($this->genPublicKey)); + + $this->assertNotEquals($this->dataLong, $recrypted['data']); + + return $recrypted; + + # TODO: search inencrypted text for actual content to ensure it + # genuine transformation + + } + + function testRenameFile() { + + $filename = 'tmp-' . time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Get file decrypted contents + $decrypt = file_get_contents('crypt://' . $filename); + + $this->assertEquals($this->dataLong, $decrypt); + + $newFilename = 'tmp-new-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + $view->rename($filename, $newFilename); + + // Get file decrypted contents + $newDecrypt = file_get_contents('crypt://' . $newFilename); + + $this->assertEquals($this->dataLong, $newDecrypt); + + // tear down + $view->unlink($newFilename); + } + + function testMoveFileIntoFolder() { + + $filename = 'tmp-' . time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Get file decrypted contents + $decrypt = file_get_contents('crypt://' . $filename); + + $this->assertEquals($this->dataLong, $decrypt); + + $newFolder = '/newfolder' . time(); + $newFilename = 'tmp-new-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + $view->mkdir($newFolder); + $view->rename($filename, $newFolder . '/' . $newFilename); + + // Get file decrypted contents + $newDecrypt = file_get_contents('crypt://' . $newFolder . '/' . $newFilename); + + $this->assertEquals($this->dataLong, $newDecrypt); + + // tear down + $view->unlink($newFolder); + } + + function testMoveFolder() { + + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + $filename = '/tmp-' . time(); + $folder = '/folder' . time(); + + $view->mkdir($folder); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents('crypt://' . $folder . $filename, $this->dataLong); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Get file decrypted contents + $decrypt = file_get_contents('crypt://' . $folder . $filename); + + $this->assertEquals($this->dataLong, $decrypt); + + $newFolder = '/newfolder/subfolder' . time(); + $view->mkdir('/newfolder'); + + $view->rename($folder, $newFolder); + + // Get file decrypted contents + $newDecrypt = file_get_contents('crypt://' . $newFolder . $filename); + + $this->assertEquals($this->dataLong, $newDecrypt); + + // tear down + $view->unlink($newFolder); + $view->unlink('/newfolder'); + } + + function testChangePassphrase() { + $filename = 'tmp-' . time(); + + // Save long data as encrypted file using stream wrapper + $cryptedFile = file_put_contents('crypt://' . $filename, $this->dataLong); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Get file decrypted contents + $decrypt = file_get_contents('crypt://' . $filename); + + $this->assertEquals($this->dataLong, $decrypt); + + // change password + \OC_User::setPassword($this->userId, 'test', null); + + // relogin + $params['uid'] = $this->userId; + $params['password'] = 'test'; + OCA\Encryption\Hooks::login($params); + + // Get file decrypted contents + $newDecrypt = file_get_contents('crypt://' . $filename); + + $this->assertEquals($this->dataLong, $newDecrypt); + + // tear down + // change password back + \OC_User::setPassword($this->userId, $this->pass); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + $view->unlink($filename); + } + + function testViewFilePutAndGetContents() { + + $filename = '/tmp-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + // Save short data as encrypted file using stream wrapper + $cryptedFile = $view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Get file decrypted contents + $decrypt = $view->file_get_contents($filename); + + $this->assertEquals($this->dataShort, $decrypt); + + // Save long data as encrypted file using stream wrapper + $cryptedFileLong = $view->file_put_contents($filename, $this->dataLong); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFileLong)); + + // Get file decrypted contents + $decryptLong = $view->file_get_contents($filename); + + $this->assertEquals($this->dataLong, $decryptLong); + + // tear down + $view->unlink($filename); + } + + function testTouchExistingFile() { + $filename = '/tmp-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + // Save short data as encrypted file using stream wrapper + $cryptedFile = $view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + $view->touch($filename); + + // Get file decrypted contents + $decrypt = $view->file_get_contents($filename); + + $this->assertEquals($this->dataShort, $decrypt); + + // tear down + $view->unlink($filename); + } + + function testTouchFile() { + $filename = '/tmp-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + $view->touch($filename); + + // Save short data as encrypted file using stream wrapper + $cryptedFile = $view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // Get file decrypted contents + $decrypt = $view->file_get_contents($filename); + + $this->assertEquals($this->dataShort, $decrypt); + + // tear down + $view->unlink($filename); + } + + function testFopenFile() { + $filename = '/tmp-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + // Save short data as encrypted file using stream wrapper + $cryptedFile = $view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + $handle = $view->fopen($filename, 'r'); + + // Get file decrypted contents + $decrypt = fgets($handle); + + $this->assertEquals($this->dataShort, $decrypt); + + // tear down + $view->unlink($filename); + } +} diff --git a/apps/files_encryption/tests/encryption.key b/apps/files_encryption/tests/encryption.key new file mode 100644 index 0000000000..4ee962145c Binary files /dev/null and b/apps/files_encryption/tests/encryption.key differ diff --git a/apps/files_encryption/tests/keymanager.php b/apps/files_encryption/tests/keymanager.php new file mode 100644 index 0000000000..40ae1659a5 --- /dev/null +++ b/apps/files_encryption/tests/keymanager.php @@ -0,0 +1,245 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +require_once realpath(dirname(__FILE__) . '/../../../lib/base.php'); +require_once realpath(dirname(__FILE__) . '/../lib/crypt.php'); +require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php'); +require_once realpath(dirname(__FILE__) . '/../lib/proxy.php'); +require_once realpath(dirname(__FILE__) . '/../lib/stream.php'); +require_once realpath(dirname(__FILE__) . '/../lib/util.php'); +require_once realpath(dirname(__FILE__) . '/../lib/helper.php'); +require_once realpath(dirname(__FILE__) . '/../appinfo/app.php'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Keymanager + */ +class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase { + + public $userId; + public $pass; + public $stateFilesTrashbin; + /** + * @var OC_FilesystemView + */ + public $view; + public $randomKey; + public $dataShort; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // clear and register hooks + \OC_FileProxy::clearProxies(); + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // disable file proxy by default + \OC_FileProxy::$enabled = false; + + // setup filesystem + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_Util::setupFS('admin'); + \OC_User::setUserId('admin'); + + // login admin + $params['uid'] = 'admin'; + $params['password'] = 'admin'; + OCA\Encryption\Hooks::login($params); + } + + function setUp() { + // set content for encrypting / decrypting in tests + $this->dataLong = file_get_contents(realpath(dirname(__FILE__) . '/../lib/crypt.php')); + $this->dataShort = 'hats'; + $this->dataUrl = realpath(dirname(__FILE__) . '/../lib/crypt.php'); + $this->legacyData = realpath(dirname(__FILE__) . '/legacy-text.txt'); + $this->legacyEncryptedData = realpath(dirname(__FILE__) . '/legacy-encrypted-text.txt'); + $this->randomKey = Encryption\Crypt::generateKey(); + + $keypair = Encryption\Crypt::createKeypair(); + $this->genPublicKey = $keypair['publicKey']; + $this->genPrivateKey = $keypair['privateKey']; + + $this->view = new \OC_FilesystemView('/'); + + \OC_User::setUserId('admin'); + $this->userId = 'admin'; + $this->pass = 'admin'; + + $userHome = \OC_User::getHome($this->userId); + $this->dataDir = str_replace('/' . $this->userId, '', $userHome); + + // remember files_trashbin state + $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we don't want to tests with app files_trashbin enabled + \OC_App::disable('files_trashbin'); + } + + function tearDown() { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + + public static function tearDownAfterClass() { + \OC_FileProxy::$enabled = true; + } + + function testGetPrivateKey() { + + $key = Encryption\Keymanager::getPrivateKey($this->view, $this->userId); + + $privateKey = Encryption\Crypt::symmetricDecryptFileContent($key, $this->pass); + + $res = openssl_pkey_get_private($privateKey); + + $this->assertTrue(is_resource($res)); + + $sslInfo = openssl_pkey_get_details($res); + + $this->assertArrayHasKey('key', $sslInfo); + + } + + function testGetPublicKey() { + + $publiceKey = Encryption\Keymanager::getPublicKey($this->view, $this->userId); + + $res = openssl_pkey_get_public($publiceKey); + + $this->assertTrue(is_resource($res)); + + $sslInfo = openssl_pkey_get_details($res); + + $this->assertArrayHasKey('key', $sslInfo); + } + + function testSetFileKey() { + + # NOTE: This cannot be tested until we are able to break out + # of the FileSystemView data directory root + + $key = Encryption\Crypt::symmetricEncryptFileContentKeyfile($this->randomKey, 'hat'); + + $file = 'unittest-' . time() . '.txt'; + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $this->view->file_put_contents($this->userId . '/files/' . $file, $key['encrypted']); + + // Re-enable proxy - our work is done + \OC_FileProxy::$enabled = $proxyStatus; + + //$view = new \OC_FilesystemView( '/' . $this->userId . '/files_encryption/keyfiles' ); + Encryption\Keymanager::setFileKey($this->view, $file, $this->userId, $key['key']); + + // enable encryption proxy + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = true; + + // cleanup + $this->view->unlink('/' . $this->userId . '/files/' . $file); + + // change encryption proxy to previous state + \OC_FileProxy::$enabled = $proxyStatus; + + } + + function testGetUserKeys() { + + $keys = Encryption\Keymanager::getUserKeys($this->view, $this->userId); + + $resPublic = openssl_pkey_get_public($keys['publicKey']); + + $this->assertTrue(is_resource($resPublic)); + + $sslInfoPublic = openssl_pkey_get_details($resPublic); + + $this->assertArrayHasKey('key', $sslInfoPublic); + + $privateKey = Encryption\Crypt::symmetricDecryptFileContent($keys['privateKey'], $this->pass); + + $resPrivate = openssl_pkey_get_private($privateKey); + + $this->assertTrue(is_resource($resPrivate)); + + $sslInfoPrivate = openssl_pkey_get_details($resPrivate); + + $this->assertArrayHasKey('key', $sslInfoPrivate); + } + + function testFixPartialFilePath() { + + $partFilename = 'testfile.txt.part'; + $filename = 'testfile.txt'; + + $this->assertTrue(Encryption\Keymanager::isPartialFilePath($partFilename)); + + $this->assertEquals('testfile.txt', Encryption\Keymanager::fixPartialFilePath($partFilename)); + + $this->assertFalse(Encryption\Keymanager::isPartialFilePath($filename)); + + $this->assertEquals('testfile.txt', Encryption\Keymanager::fixPartialFilePath($filename)); + } + + function testRecursiveDelShareKeys() { + + // generate filename + $filename = '/tmp-' . time() . '.txt'; + + // create folder structure + $this->view->mkdir('/admin/files/folder1'); + $this->view->mkdir('/admin/files/folder1/subfolder'); + $this->view->mkdir('/admin/files/folder1/subfolder/subsubfolder'); + + // enable encryption proxy + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = true; + + // save file with content + $cryptedFile = file_put_contents('crypt:///folder1/subfolder/subsubfolder/' . $filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // change encryption proxy to previous state + \OC_FileProxy::$enabled = $proxyStatus; + + // recursive delete keys + Encryption\Keymanager::delShareKey($this->view, array('admin'), '/folder1/'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/admin/files_encryption/share-keys/folder1/subfolder/subsubfolder/' . $filename . '.admin.shareKey')); + + // enable encryption proxy + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = true; + + // cleanup + $this->view->unlink('/admin/files/folder1'); + + // change encryption proxy to previous state + \OC_FileProxy::$enabled = $proxyStatus; + } +} diff --git a/apps/files_encryption/tests/legacy-encrypted-text.txt b/apps/files_encryption/tests/legacy-encrypted-text.txt new file mode 100644 index 0000000000..1f5087178c --- /dev/null +++ b/apps/files_encryption/tests/legacy-encrypted-text.txt @@ -0,0 +1 @@ +5ǡiZgESlF= \ No newline at end of file diff --git a/apps/files_encryption/test/proxy.php b/apps/files_encryption/tests/proxy.php similarity index 98% rename from apps/files_encryption/test/proxy.php rename to apps/files_encryption/tests/proxy.php index 709730f760..5a2d851ff7 100644 --- a/apps/files_encryption/test/proxy.php +++ b/apps/files_encryption/tests/proxy.php @@ -52,7 +52,7 @@ // $this->userId = 'admin'; // $this->pass = 'admin'; // -// $this->session = new Encryption\Session(); +// $this->session = new Encryption\Session( $view ); // FIXME: Provide a $view object for use here // // $this->session->setPrivateKey( // '-----BEGIN PRIVATE KEY----- diff --git a/apps/files_encryption/tests/share.php b/apps/files_encryption/tests/share.php new file mode 100755 index 0000000000..816bc709f2 --- /dev/null +++ b/apps/files_encryption/tests/share.php @@ -0,0 +1,911 @@ + + * + * 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 . + * + */ + +require_once realpath(dirname(__FILE__) . '/../3rdparty/Crypt_Blowfish/Blowfish.php'); +require_once realpath(dirname(__FILE__) . '/../../../lib/base.php'); +require_once realpath(dirname(__FILE__) . '/../lib/crypt.php'); +require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php'); +require_once realpath(dirname(__FILE__) . '/../lib/proxy.php'); +require_once realpath(dirname(__FILE__) . '/../lib/stream.php'); +require_once realpath(dirname(__FILE__) . '/../lib/util.php'); +require_once realpath(dirname(__FILE__) . '/../lib/helper.php'); +require_once realpath(dirname(__FILE__) . '/../appinfo/app.php'); +require_once realpath(dirname(__FILE__) . '/util.php'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Share + */ +class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { + + const TEST_ENCRYPTION_SHARE_USER1 = "test-share-user1"; + const TEST_ENCRYPTION_SHARE_USER2 = "test-share-user2"; + const TEST_ENCRYPTION_SHARE_USER3 = "test-share-user3"; + const TEST_ENCRYPTION_SHARE_USER4 = "test-share-user4"; + const TEST_ENCRYPTION_SHARE_GROUP1 = "test-share-group1"; + + public $stateFilesTrashbin; + public $filename; + public $dataShort; + /** + * @var OC_FilesystemView + */ + public $view; + public $folder1; + public $subfolder; + public $subsubfolder; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + // enable resharing + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + // clear share hooks + \OC_Hook::clear('OCP\\Share'); + \OC::registerShareHooks(); + \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); + + // Sharing related hooks + \OCA\Encryption\Helper::registerShareHooks(); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // clear and register hooks + \OC_FileProxy::clearProxies(); + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // create users + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1, true); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, true); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, true); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, true); + + // create group and assign users + \OC_Group::createGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + \OC_Group::addToGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + \OC_Group::addToGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + } + + function setUp() { + $this->dataShort = 'hats'; + $this->view = new \OC_FilesystemView('/'); + + $this->folder1 = '/folder1'; + $this->subfolder = '/subfolder1'; + $this->subsubfolder = '/subsubfolder1'; + + $this->filename = 'share-tmp.test'; + + // we don't want to tests with app files_trashbin enabled + \OC_App::disable('files_trashbin'); + + // remember files_trashbin state + $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + } + + function tearDown() { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + + public static function tearDownAfterClass() { + // clean group + \OC_Group::deleteGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + + // cleanup users + \OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + \OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + \OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + \OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4); + } + + /** + * @param bool $withTeardown + */ + function testShareFile($withTeardown = true) { + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // save file with content + $cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get the file info from previous created file + $fileInfo = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if we have a valid file info + $this->assertTrue(is_array($fileInfo)); + + // check if the unencrypted file size is stored + $this->assertGreaterThan(0, $fileInfo['unencrypted_size']); + + // re-enable the file proxy + \OC_FileProxy::$enabled = $proxyStatus; + + // share the file + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if share key for user1 exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename); + + // check if data is the same as we previously written + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // cleanup + if ($withTeardown) { + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // unshare the file + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + + // cleanup + $this->view->unlink( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey')); + } + } + + /** + * @param bool $withTeardown + */ + function testReShareFile($withTeardown = true) { + $this->testShareFile(false); + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // get the file info + $fileInfo = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename); + + // share the file with user2 + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, OCP\PERMISSION_ALL); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if share key for user2 exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); + + // login as user2 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/Shared/' . $this->filename); + + // check if data is the same as previously written + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // cleanup + if ($withTeardown) { + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // unshare the file with user2 + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); + + // unshare the file with user1 + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + + // cleanup + $this->view->unlink( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey')); + } + } + + /** + * @param bool $withTeardown + * @return array + */ + function testShareFolder($withTeardown = true) { + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // create folder structure + $this->view->mkdir('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1); + $this->view->mkdir( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1 . $this->subfolder); + $this->view->mkdir( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1 . $this->subfolder + . $this->subsubfolder); + + // save file with content + $cryptedFile = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get the file info from previous created folder + $fileInfo = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1); + + // check if we have a valid file info + $this->assertTrue(is_array($fileInfo)); + + // re-enable the file proxy + \OC_FileProxy::$enabled = $proxyStatus; + + // share the folder with user1 + \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if share key for user1 exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' . $this->filename); + + // check if data is the same + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // cleanup + if ($withTeardown) { + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // unshare the folder with user1 + \OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' + . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + + // cleanup + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' + . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey')); + } + + return $fileInfo; + } + + /** + * @param bool $withTeardown + */ + function testReShareFolder($withTeardown = true) { + $fileInfoFolder1 = $this->testShareFolder(false); + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get the file info from previous created folder + $fileInfoSubFolder = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared' . $this->folder1 + . $this->subfolder); + + // check if we have a valid file info + $this->assertTrue(is_array($fileInfoSubFolder)); + + // re-enable the file proxy + \OC_FileProxy::$enabled = $proxyStatus; + + // share the file with user2 + \OCP\Share::shareItem('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, OCP\PERMISSION_ALL); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if share key for user2 exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); + + // login as user2 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/Shared' . $this->subfolder + . $this->subsubfolder . '/' . $this->filename); + + // check if data is the same + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // get the file info + $fileInfo = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/Shared' . $this->subfolder + . $this->subsubfolder . '/' . $this->filename); + + // check if we have fileInfos + $this->assertTrue(is_array($fileInfo)); + + // share the file with user3 + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, OCP\PERMISSION_ALL); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if share key for user3 exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey')); + + // login as user3 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '/files/Shared/' . $this->filename); + + // check if data is the same + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // cleanup + if ($withTeardown) { + + // login as user2 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + + // unshare the file with user3 + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' + . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey')); + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // unshare the folder with user2 + \OCP\Share::unshare('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' + . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // unshare the folder1 with user1 + \OCP\Share::unshare('folder', $fileInfoFolder1['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' + . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + + // cleanup + $this->view->unlink( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1 . $this->subfolder + . $this->subsubfolder . '/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' + . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey')); + } + } + + function testPublicShareFile() { + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // save file with content + $cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get the file info from previous created file + $fileInfo = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if we have a valid file info + $this->assertTrue(is_array($fileInfo)); + + // check if the unencrypted file size is stored + $this->assertGreaterThan(0, $fileInfo['unencrypted_size']); + + // re-enable the file proxy + \OC_FileProxy::$enabled = $proxyStatus; + + // share the file + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, false, OCP\PERMISSION_ALL); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + $publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId'); + + // check if share key for public exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . $publicShareKeyId . '.shareKey')); + + // some hacking to simulate public link + $GLOBALS['app'] = 'files_sharing'; + $GLOBALS['fileOwner'] = \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1; + \OC_User::setUserId(false); + + // get file contents + $retrievedCryptedFile = file_get_contents('crypt://' . $this->filename); + + // check if data is the same as we previously written + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // tear down + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // unshare the file + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . $publicShareKeyId . '.shareKey')); + + // cleanup + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey')); + } + + function testShareFileWithGroup() { + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // save file with content + $cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get the file info from previous created file + $fileInfo = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if we have a valid file info + $this->assertTrue(is_array($fileInfo)); + + // check if the unencrypted file size is stored + $this->assertGreaterThan(0, $fileInfo['unencrypted_size']); + + // re-enable the file proxy + \OC_FileProxy::$enabled = $proxyStatus; + + // share the file + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1, OCP\PERMISSION_ALL); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if share key for user2 and user3 exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey')); + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/Shared/' . $this->filename); + + // check if data is the same as we previously written + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // unshare the file + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey')); + + // cleanup + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey')); + + } + + function testRecoveryFile() { + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + \OCA\Encryption\Helper::adminEnableRecovery(null, 'test123'); + $recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId'); + + // check if control file created + $this->assertTrue($this->view->file_exists('/control-file/controlfile.enc')); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if recovery password match + $this->assertTrue($util->checkRecoveryPassword('test123')); + + // enable recovery for admin + $this->assertTrue($util->setRecoveryForUser(1)); + + // create folder structure + $this->view->mkdir('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1); + $this->view->mkdir( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1 . $this->subfolder); + $this->view->mkdir( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1 . $this->subfolder + . $this->subsubfolder); + + // save file with content + $cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort); + $cryptedFile2 = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile1)); + $this->assertTrue(is_int($cryptedFile2)); + + // check if share key for admin and recovery exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey')); + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey')); + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + + // disable recovery for admin + $this->assertTrue($util->setRecoveryForUser(0)); + + // remove all recovery keys + $util->removeRecoveryKeys('/'); + + // check if share key for recovery not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + + // enable recovery for admin + $this->assertTrue($util->setRecoveryForUser(1)); + + // remove all recovery keys + $util->addRecoveryKeys('/'); + + // check if share key for admin and recovery exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + + // cleanup + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->folder1); + + // check if share key for recovery not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + + $this->assertTrue(\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123')); + $this->assertTrue(\OCA\Encryption\Helper::adminDisableRecovery('test123')); + $this->assertEquals(0, \OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled')); + } + + function testRecoveryForUser() { + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + \OCA\Encryption\Helper::adminEnableRecovery(null, 'test123'); + $recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId'); + + // check if control file created + $this->assertTrue($this->view->file_exists('/control-file/controlfile.enc')); + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + // enable recovery for admin + $this->assertTrue($util->setRecoveryForUser(1)); + + // create folder structure + $this->view->mkdir('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files' . $this->folder1); + $this->view->mkdir( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files' . $this->folder1 . $this->subfolder); + $this->view->mkdir( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files' . $this->folder1 . $this->subfolder + . $this->subsubfolder); + + // save file with content + $cryptedFile1 = file_put_contents('crypt://' . $this->filename, $this->dataShort); + $cryptedFile2 = file_put_contents('crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' + . $this->filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile1)); + $this->assertTrue(is_int($cryptedFile2)); + + // check if share key for user and recovery exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // change password + \OC_User::setPassword(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, 'test', 'test123'); + + // login as user1 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, false, 'test'); + + // get file contents + $retrievedCryptedFile1 = file_get_contents('crypt://' . $this->filename); + $retrievedCryptedFile2 = file_get_contents( + 'crypt://' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename); + + // check if data is the same as we previously written + $this->assertEquals($this->dataShort, $retrievedCryptedFile1); + $this->assertEquals($this->dataShort, $retrievedCryptedFile2); + + // cleanup + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files' . $this->folder1); + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files' . $this->filename); + + // check if share key for user and recovery exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files_encryption/share-keys/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files_encryption/share-keys/' . $this->folder1 + . $this->subfolder . $this->subsubfolder . '/' + . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + + // enable recovery for admin + $this->assertTrue($util->setRecoveryForUser(0)); + + \OCA\Encryption\Helper::adminDisableRecovery('test123'); + $this->assertEquals(0, \OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled')); + } + + function testFailShareFile() { + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // save file with content + $cryptedFile = file_put_contents('crypt://' . $this->filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get the file info from previous created file + $fileInfo = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if we have a valid file info + $this->assertTrue(is_array($fileInfo)); + + // check if the unencrypted file size is stored + $this->assertGreaterThan(0, $fileInfo['unencrypted_size']); + + // break users public key + $this->view->rename('/public-keys/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.public.key', + '/public-keys/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.public.key_backup'); + + // re-enable the file proxy + \OC_FileProxy::$enabled = $proxyStatus; + + // share the file + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1, OCP\PERMISSION_ALL); + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // check if share key for user1 not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // break user1 public key + $this->view->rename( + '/public-keys/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.public.key_backup', + '/public-keys/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.public.key'); + + // remove share file + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 + . '.shareKey'); + + // re-enable the file proxy + \OC_FileProxy::$enabled = $proxyStatus; + + // unshare the file with user1 + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + + // check if share key not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); + + // cleanup + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + } + +} diff --git a/apps/files_encryption/tests/stream.php b/apps/files_encryption/tests/stream.php new file mode 100644 index 0000000000..3d97876754 --- /dev/null +++ b/apps/files_encryption/tests/stream.php @@ -0,0 +1,180 @@ + + * + * 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 . + * + */ + +require_once realpath(dirname(__FILE__) . '/../../../lib/base.php'); +require_once realpath(dirname(__FILE__) . '/../lib/crypt.php'); +require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php'); +require_once realpath(dirname(__FILE__) . '/../lib/proxy.php'); +require_once realpath(dirname(__FILE__) . '/../lib/stream.php'); +require_once realpath(dirname(__FILE__) . '/../lib/util.php'); +require_once realpath(dirname(__FILE__) . '/../appinfo/app.php'); +require_once realpath(dirname(__FILE__) . '/util.php'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Stream + * @brief this class provide basic stream tests + */ +class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase { + + const TEST_ENCRYPTION_STREAM_USER1 = "test-stream-user1"; + + public $userId; + public $pass; + /** + * @var \OC_FilesystemView + */ + public $view; + public $dataShort; + public $stateFilesTrashbin; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // clear and register hooks + \OC_FileProxy::clearProxies(); + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // create test user + \Test_Encryption_Util::loginHelper(\Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1, true); + } + + function setUp() { + // set user id + \OC_User::setUserId(\Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1); + $this->userId = \Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1; + $this->pass = \Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1; + + // init filesystem view + $this->view = new \OC_FilesystemView('/'); + + // init short data + $this->dataShort = 'hats'; + + // remember files_trashbin state + $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we don't want to tests with app files_trashbin enabled + \OC_App::disable('files_trashbin'); + } + + function tearDown() { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + + public static function tearDownAfterClass() { + // cleanup test user + \OC_User::deleteUser(\Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1); + } + + function testStreamOptions() { + $filename = '/tmp-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + // Save short data as encrypted file using stream wrapper + $cryptedFile = $view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + $handle = $view->fopen($filename, 'r'); + + // check if stream is at position zero + $this->assertEquals(0, ftell($handle)); + + // set stream options + $this->assertTrue(flock($handle, LOCK_SH)); + $this->assertTrue(flock($handle, LOCK_UN)); + + // tear down + $view->unlink($filename); + } + + function testStreamSetBlocking() { + $filename = '/tmp-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + // Save short data as encrypted file using stream wrapper + $cryptedFile = $view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + $handle = $view->fopen($filename, 'r'); + + // set stream options + $this->assertTrue(stream_set_blocking($handle, 1)); + + // tear down + $view->unlink($filename); + } + + function testStreamSetTimeout() { + $filename = '/tmp-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + // Save short data as encrypted file using stream wrapper + $cryptedFile = $view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + $handle = $view->fopen($filename, 'r'); + + // set stream options + $this->assertFalse(stream_set_timeout($handle, 1)); + + // tear down + $view->unlink($filename); + } + + function testStreamSetWriteBuffer() { + $filename = '/tmp-' . time(); + $view = new \OC\Files\View('/' . $this->userId . '/files'); + + // Save short data as encrypted file using stream wrapper + $cryptedFile = $view->file_put_contents($filename, $this->dataShort); + + // Test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + $handle = $view->fopen($filename, 'r'); + + // set stream options + $this->assertEquals(0, stream_set_write_buffer($handle, 1024)); + + // tear down + $view->unlink($filename); + } +} \ No newline at end of file diff --git a/apps/files_encryption/tests/trashbin.php b/apps/files_encryption/tests/trashbin.php new file mode 100755 index 0000000000..29f8fb5a39 --- /dev/null +++ b/apps/files_encryption/tests/trashbin.php @@ -0,0 +1,300 @@ + + * + * 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 . + * + */ + +require_once realpath(dirname(__FILE__) . '/../../../lib/base.php'); +require_once realpath(dirname(__FILE__) . '/../lib/crypt.php'); +require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php'); +require_once realpath(dirname(__FILE__) . '/../lib/proxy.php'); +require_once realpath(dirname(__FILE__) . '/../lib/stream.php'); +require_once realpath(dirname(__FILE__) . '/../lib/util.php'); +require_once realpath(dirname(__FILE__) . '/../appinfo/app.php'); +require_once realpath(dirname(__FILE__) . '/../../files_trashbin/appinfo/app.php'); +require_once realpath(dirname(__FILE__) . '/util.php'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Trashbin + * @brief this class provide basic trashbin app tests + */ +class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase { + + const TEST_ENCRYPTION_TRASHBIN_USER1 = "test-trashbin-user1"; + + public $userId; + public $pass; + /** + * @var \OC_FilesystemView + */ + public $view; + public $dataShort; + public $stateFilesTrashbin; + public $folder1; + public $subfolder; + public $subsubfolder; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + \OC_Hook::clear('OC_Filesystem'); + \OC_Hook::clear('OC_User'); + + // trashbin hooks + \OCA\Files_Trashbin\Trashbin::registerHooks(); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // clear and register hooks + \OC_FileProxy::clearProxies(); + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // create test user + \Test_Encryption_Util::loginHelper(\Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1, true); + } + + function setUp() { + // set user id + \OC_User::setUserId(\Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1); + $this->userId = \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1; + $this->pass = \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1; + + // init filesystem view + $this->view = new \OC_FilesystemView('/'); + + // init short data + $this->dataShort = 'hats'; + + $this->folder1 = '/folder1'; + $this->subfolder = '/subfolder1'; + $this->subsubfolder = '/subsubfolder1'; + + // remember files_trashbin state + $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we want to tests with app files_trashbin enabled + \OC_App::enable('files_trashbin'); + } + + function tearDown() { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + + public static function tearDownAfterClass() { + // cleanup test user + \OC_User::deleteUser(\Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1); + } + + /** + * @brief test delete file + */ + function testDeleteFile() { + + // generate filename + $filename = 'tmp-' . time() . '.txt'; + + // save file with content + $cryptedFile = file_put_contents('crypt:///' . $filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // check if key for admin exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename + . '.key')); + + // check if share key for admin exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/' + . $filename . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey')); + + // delete file + \OC\FIles\Filesystem::unlink($filename); + + // check if file not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename)); + + // check if key for admin not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename + . '.key')); + + // check if share key for admin not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/' + . $filename . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey')); + + // get files + $trashFiles = $this->view->getDirectoryContent( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/files/'); + + $trashFileSuffix = null; + // find created file with timestamp + foreach ($trashFiles as $file) { + if (strncmp($file['path'], $filename, strlen($filename))) { + $path_parts = pathinfo($file['name']); + $trashFileSuffix = $path_parts['extension']; + } + } + + // check if we found the file we created + $this->assertNotNull($trashFileSuffix); + + // check if key for admin not exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename + . '.key.' . $trashFileSuffix)); + + // check if share key for admin not exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename + . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix)); + + // return filename for next test + return $filename . '.' . $trashFileSuffix; + } + + /** + * @brief test restore file + * + * @depends testDeleteFile + */ + function testRestoreFile($filename) { + + // prepare file information + $path_parts = pathinfo($filename); + $trashFileSuffix = $path_parts['extension']; + $timestamp = str_replace('d', '', $trashFileSuffix); + $fileNameWithoutSuffix = str_replace('.' . $trashFileSuffix, '', $filename); + + // restore file + $this->assertTrue(\OCA\Files_Trashbin\Trashbin::restore($filename, $fileNameWithoutSuffix, $timestamp)); + + // check if file exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $fileNameWithoutSuffix)); + + // check if key for admin exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' + . $fileNameWithoutSuffix . '.key')); + + // check if share key for admin exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/' + . $fileNameWithoutSuffix . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey')); + } + + /** + * @brief test delete file forever + */ + function testPermanentDeleteFile() { + + // generate filename + $filename = 'tmp-' . time() . '.txt'; + + // save file with content + $cryptedFile = file_put_contents('crypt:///' . $filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // check if key for admin exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename + . '.key')); + + // check if share key for admin exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/' + . $filename . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey')); + + // delete file + \OC\FIles\Filesystem::unlink($filename); + + // check if file not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename)); + + // check if key for admin not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename + . '.key')); + + // check if share key for admin not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/' + . $filename . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey')); + + // find created file with timestamp + $query = \OC_DB::prepare('SELECT `timestamp`,`type` FROM `*PREFIX*files_trash`' + . ' WHERE `id`=?'); + $result = $query->execute(array($filename))->fetchRow(); + + $this->assertTrue(is_array($result)); + + // build suffix + $trashFileSuffix = 'd' . $result['timestamp']; + + // check if key for admin exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename + . '.key.' . $trashFileSuffix)); + + // check if share key for admin exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename + . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix)); + + // get timestamp from file + $timestamp = str_replace('d', '', $trashFileSuffix); + + // delete file forever + $this->assertGreaterThan(0, \OCA\Files_Trashbin\Trashbin::delete($filename, $timestamp)); + + // check if key for admin not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/files/' . $filename . '.' + . $trashFileSuffix)); + + // check if key for admin not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename + . '.key.' . $trashFileSuffix)); + + // check if share key for admin not exists + $this->assertFalse($this->view->file_exists( + '/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename + . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix)); + } + +} \ No newline at end of file diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php new file mode 100755 index 0000000000..0dc452a41c --- /dev/null +++ b/apps/files_encryption/tests/util.php @@ -0,0 +1,317 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +require_once realpath(dirname(__FILE__) . '/../../../lib/base.php'); +require_once realpath(dirname(__FILE__) . '/../lib/crypt.php'); +require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php'); +require_once realpath(dirname(__FILE__) . '/../lib/proxy.php'); +require_once realpath(dirname(__FILE__) . '/../lib/stream.php'); +require_once realpath(dirname(__FILE__) . '/../lib/util.php'); +require_once realpath(dirname(__FILE__) . '/../appinfo/app.php'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Util + */ +class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { + + const TEST_ENCRYPTION_UTIL_USER1 = "test-util-user1"; + const TEST_ENCRYPTION_UTIL_LEGACY_USER = "test-legacy-user"; + + public $userId; + public $encryptionDir; + public $publicKeyDir; + public $pass; + /** + * @var OC_FilesystemView + */ + public $view; + public $keyfilesPath; + public $publicKeyPath; + public $privateKeyPath; + /** + * @var \OCA\Encryption\Util + */ + public $util; + public $dataShort; + public $legacyEncryptedData; + public $legacyEncryptedDataKey; + public $legacyKey; + public $stateFilesTrashbin; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // clear and register hooks + \OC_FileProxy::clearProxies(); + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // create test user + \Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1, true); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER, true); + } + + + function setUp() { + \OC_User::setUserId(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1); + $this->userId = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1; + $this->pass = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1; + + // set content for encrypting / decrypting in tests + $this->dataUrl = realpath(dirname(__FILE__) . '/../lib/crypt.php'); + $this->dataShort = 'hats'; + $this->dataLong = file_get_contents(realpath(dirname(__FILE__) . '/../lib/crypt.php')); + $this->legacyData = realpath(dirname(__FILE__) . '/legacy-text.txt'); + $this->legacyEncryptedData = realpath(dirname(__FILE__) . '/legacy-encrypted-text.txt'); + $this->legacyEncryptedDataKey = realpath(dirname(__FILE__) . '/encryption.key'); + $this->legacyKey = '30943623843030686906'; + + $keypair = Encryption\Crypt::createKeypair(); + + $this->genPublicKey = $keypair['publicKey']; + $this->genPrivateKey = $keypair['privateKey']; + + $this->publicKeyDir = '/' . 'public-keys'; + $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; + $this->keyfilesPath = $this->encryptionDir . '/' . 'keyfiles'; + $this->publicKeyPath = + $this->publicKeyDir . '/' . $this->userId . '.public.key'; // e.g. data/public-keys/admin.public.key + $this->privateKeyPath = + $this->encryptionDir . '/' . $this->userId . '.private.key'; // e.g. data/admin/admin.private.key + + $this->view = new \OC_FilesystemView('/'); + + $this->util = new Encryption\Util($this->view, $this->userId); + + // remember files_trashbin state + $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we don't want to tests with app files_trashbin enabled + \OC_App::disable('files_trashbin'); + } + + function tearDown() { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + + public static function tearDownAfterClass() { + // cleanup test user + \OC_User::deleteUser(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1); + \OC_User::deleteUser(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER); + } + + /** + * @brief test that paths set during User construction are correct + */ + function testKeyPaths() { + $util = new Encryption\Util($this->view, $this->userId); + + $this->assertEquals($this->publicKeyDir, $util->getPath('publicKeyDir')); + $this->assertEquals($this->encryptionDir, $util->getPath('encryptionDir')); + $this->assertEquals($this->keyfilesPath, $util->getPath('keyfilesPath')); + $this->assertEquals($this->publicKeyPath, $util->getPath('publicKeyPath')); + $this->assertEquals($this->privateKeyPath, $util->getPath('privateKeyPath')); + + } + + /** + * @brief test setup of encryption directories + */ + function testSetupServerSide() { + $this->assertEquals(true, $this->util->setupServerSide($this->pass)); + } + + /** + * @brief test checking whether account is ready for encryption, + */ + function testUserIsReady() { + $this->assertEquals(true, $this->util->ready()); + } + + /** + * @brief test checking whether account is not ready for encryption, + */ +// function testUserIsNotReady() { +// $this->view->unlink($this->publicKeyDir); +// +// $params['uid'] = $this->userId; +// $params['password'] = $this->pass; +// $this->assertFalse(OCA\Encryption\Hooks::login($params)); +// +// $this->view->unlink($this->privateKeyPath); +// } + + /** + * @brief test checking whether account is not ready for encryption, + */ + function testIsLegacyUser() { + \Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER); + + $userView = new \OC_FilesystemView('/' . \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER); + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $encryptionKeyContent = file_get_contents($this->legacyEncryptedDataKey); + $userView->file_put_contents('/encryption.key', $encryptionKeyContent); + + \OC_FileProxy::$enabled = $proxyStatus; + + $params['uid'] = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER; + $params['password'] = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER; + + $util = new Encryption\Util($this->view, \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER); + $util->setMigrationStatus(0); + + $this->assertTrue(OCA\Encryption\Hooks::login($params)); + + $this->assertEquals($this->legacyKey, \OC::$session->get('legacyKey')); + } + + function testRecoveryEnabledForUser() { + + $util = new Encryption\Util($this->view, $this->userId); + + // Record the value so we can return it to it's original state later + $enabled = $util->recoveryEnabledForUser(); + + $this->assertTrue($util->setRecoveryForUser(1)); + + $this->assertEquals(1, $util->recoveryEnabledForUser()); + + $this->assertTrue($util->setRecoveryForUser(0)); + + $this->assertEquals(0, $util->recoveryEnabledForUser()); + + // Return the setting to it's previous state + $this->assertTrue($util->setRecoveryForUser($enabled)); + + } + + function testGetUidAndFilename() { + + \OC_User::setUserId(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1); + + $filename = 'tmp-' . time() . '.test'; + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort); + + // Re-enable proxy - our work is done + \OC_FileProxy::$enabled = $proxyStatus; + + $util = new Encryption\Util($this->view, $this->userId); + + list($fileOwnerUid, $file) = $util->getUidAndFilename($filename); + + $this->assertEquals(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1, $fileOwnerUid); + + $this->assertEquals($file, $filename); + + $this->view->unlink($this->userId . '/files/' . $filename); + } + + function testIsSharedPath() { + $sharedPath = '/user1/files/Shared/test'; + $path = '/user1/files/test'; + + $this->assertTrue($this->util->isSharedPath($sharedPath)); + + $this->assertFalse($this->util->isSharedPath($path)); + } + + function testEncryptLegacyFiles() { + \Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER); + + $userView = new \OC_FilesystemView('/' . \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER); + $view = new \OC_FilesystemView('/' . \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER . '/files'); + + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $encryptionKeyContent = file_get_contents($this->legacyEncryptedDataKey); + $userView->file_put_contents('/encryption.key', $encryptionKeyContent); + + $legacyEncryptedData = file_get_contents($this->legacyEncryptedData); + $view->mkdir('/test/'); + $view->mkdir('/test/subtest/'); + $view->file_put_contents('/test/subtest/legacy-encrypted-text.txt', $legacyEncryptedData); + + $fileInfo = $view->getFileInfo('/test/subtest/legacy-encrypted-text.txt'); + $fileInfo['encrypted'] = true; + $view->putFileInfo('/test/subtest/legacy-encrypted-text.txt', $fileInfo); + + \OC_FileProxy::$enabled = $proxyStatus; + + $params['uid'] = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER; + $params['password'] = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER; + + $util = new Encryption\Util($this->view, \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER); + $util->setMigrationStatus(0); + + $this->assertTrue(OCA\Encryption\Hooks::login($params)); + + $this->assertEquals($this->legacyKey, \OC::$session->get('legacyKey')); + + $files = $util->findEncFiles('/' . \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER . '/files/'); + + $this->assertTrue(is_array($files)); + + $found = false; + foreach ($files['encrypted'] as $encryptedFile) { + if ($encryptedFile['name'] === 'legacy-encrypted-text.txt') { + $found = true; + break; + } + } + + $this->assertTrue($found); + } + + /** + * @param $user + * @param bool $create + * @param bool $password + */ + public static function loginHelper($user, $create = false, $password = false) { + if ($create) { + \OC_User::createUser($user, $user); + } + + if ($password === false) { + $password = $user; + } + + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_Util::setupFS($user); + \OC_User::setUserId($user); + + $params['uid'] = $user; + $params['password'] = $password; + OCA\Encryption\Hooks::login($params); + } +} diff --git a/apps/files_encryption/tests/webdav.php b/apps/files_encryption/tests/webdav.php new file mode 100755 index 0000000000..1d406789f0 --- /dev/null +++ b/apps/files_encryption/tests/webdav.php @@ -0,0 +1,262 @@ + + * + * 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 . + * + */ + +require_once realpath(dirname(__FILE__) . '/../../../lib/base.php'); +require_once realpath(dirname(__FILE__) . '/../lib/crypt.php'); +require_once realpath(dirname(__FILE__) . '/../lib/keymanager.php'); +require_once realpath(dirname(__FILE__) . '/../lib/proxy.php'); +require_once realpath(dirname(__FILE__) . '/../lib/stream.php'); +require_once realpath(dirname(__FILE__) . '/../lib/util.php'); +require_once realpath(dirname(__FILE__) . '/../appinfo/app.php'); +require_once realpath(dirname(__FILE__) . '/util.php'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Webdav + * @brief this class provide basic webdav tests for PUT,GET and DELETE + */ +class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { + + const TEST_ENCRYPTION_WEBDAV_USER1 = "test-webdav-user1"; + + public $userId; + public $pass; + /** + * @var \OC_FilesystemView + */ + public $view; + public $dataShort; + public $stateFilesTrashbin; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerUserHooks(); + + // clear and register hooks + \OC_FileProxy::clearProxies(); + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // create test user + \Test_Encryption_Util::loginHelper(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1, true); + } + + function setUp() { + // reset backend + \OC_User::useBackend('database'); + + // set user id + \OC_User::setUserId(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1); + $this->userId = \Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1; + $this->pass = \Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1; + + // init filesystem view + $this->view = new \OC_FilesystemView('/'); + + // init short data + $this->dataShort = 'hats'; + + // remember files_trashbin state + $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we don't want to tests with app files_trashbin enabled + \OC_App::disable('files_trashbin'); + + // create test user + \Test_Encryption_Util::loginHelper(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1); + } + + function tearDown() { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } + else { + OC_App::disable('files_trashbin'); + } + } + + public static function tearDownAfterClass() { + // cleanup test user + \OC_User::deleteUser(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1); + } + + /** + * @brief test webdav put random file + */ + function testWebdavPUT() { + + // generate filename + $filename = '/tmp-' . time() . '.txt'; + + // set server vars + $_SERVER['REQUEST_METHOD'] = 'OPTIONS'; + + $_SERVER['REQUEST_METHOD'] = 'PUT'; + $_SERVER['REQUEST_URI'] = '/remote.php/webdav' . $filename; + $_SERVER['HTTP_AUTHORIZATION'] = 'Basic dGVzdC13ZWJkYXYtdXNlcjE6dGVzdC13ZWJkYXYtdXNlcjE='; + $_SERVER['CONTENT_TYPE'] = 'application/octet-stream'; + $_SERVER['PATH_INFO'] = '/webdav' . $filename; + $_SERVER['CONTENT_LENGTH'] = strlen($this->dataShort); + + // handle webdav request + $this->handleWebdavRequest($this->dataShort); + + // check if file was created + $this->assertTrue($this->view->file_exists('/' . $this->userId . '/files' . $filename)); + + // check if key-file was created + $this->assertTrue($this->view->file_exists( + '/' . $this->userId . '/files_encryption/keyfiles/' . $filename . '.key')); + + // check if shareKey-file was created + $this->assertTrue($this->view->file_exists( + '/' . $this->userId . '/files_encryption/share-keys/' . $filename . '.' . $this->userId . '.shareKey')); + + // disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // get encrypted file content + $encryptedContent = $this->view->file_get_contents('/' . $this->userId . '/files' . $filename); + + // restore proxy state + \OC_FileProxy::$enabled = $proxyStatus; + + // check if encrypted content is valid + $this->assertTrue(Encryption\Crypt::isCatfileContent($encryptedContent)); + + // get decrypted file contents + $decrypt = file_get_contents('crypt://' . $filename); + + // check if file content match with the written content + $this->assertEquals($this->dataShort, $decrypt); + + // return filename for next test + return $filename; + } + + /** + * @brief test webdav get random file + * + * @depends testWebdavPUT + */ + function testWebdavGET($filename) { + + // set server vars + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = '/remote.php/webdav' . $filename; + $_SERVER['HTTP_AUTHORIZATION'] = 'Basic dGVzdC13ZWJkYXYtdXNlcjE6dGVzdC13ZWJkYXYtdXNlcjE='; + $_SERVER['PATH_INFO'] = '/webdav' . $filename; + + // handle webdav request + $content = $this->handleWebdavRequest(); + + // check if file content match with the written content + $this->assertEquals($this->dataShort, $content); + + // return filename for next test + return $filename; + } + + /** + * @brief test webdav delete random file + * @depends testWebdavGET + */ + function testWebdavDELETE($filename) { + // set server vars + $_SERVER['REQUEST_METHOD'] = 'DELETE'; + $_SERVER['REQUEST_URI'] = '/remote.php/webdav' . $filename; + $_SERVER['HTTP_AUTHORIZATION'] = 'Basic dGVzdC13ZWJkYXYtdXNlcjE6dGVzdC13ZWJkYXYtdXNlcjE='; + $_SERVER['PATH_INFO'] = '/webdav' . $filename; + + // handle webdav request + $content = $this->handleWebdavRequest(); + + // check if file was removed + $this->assertFalse($this->view->file_exists('/' . $this->userId . '/files' . $filename)); + + // check if key-file was removed + $this->assertFalse($this->view->file_exists( + '/' . $this->userId . '/files_encryption/keyfiles' . $filename . '.key')); + + // check if shareKey-file was removed + $this->assertFalse($this->view->file_exists( + '/' . $this->userId . '/files_encryption/share-keys' . $filename . '.' . $this->userId . '.shareKey')); + } + + /** + * @brief handle webdav request + * + * @param bool $body + * + * @note this init procedure is copied from /apps/files/appinfo/remote.php + */ + function handleWebdavRequest($body = false) { + // Backends + $authBackend = new OC_Connector_Sabre_Auth(); + $lockBackend = new OC_Connector_Sabre_Locks(); + $requestBackend = new OC_Connector_Sabre_Request(); + + // Create ownCloud Dir + $publicDir = new OC_Connector_Sabre_Directory(''); + + // Fire up server + $server = new Sabre_DAV_Server($publicDir); + $server->httpRequest = $requestBackend; + $server->setBaseUri('/remote.php/webdav/'); + + // Load plugins + $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, 'ownCloud')); + $server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend)); + $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload + $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin()); + $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); + + // And off we go! + if ($body) { + $server->httpRequest->setBody($body); + } + + // turn on output buffering + ob_start(); + + // handle request + $server->exec(); + + // file content is written in the output buffer + $content = ob_get_contents(); + + // flush the output buffer and turn off output buffering + ob_end_clean(); + + // return captured content + return $content; + } +} \ No newline at end of file diff --git a/apps/files_encryption/test/zeros b/apps/files_encryption/tests/zeros similarity index 100% rename from apps/files_encryption/test/zeros rename to apps/files_encryption/tests/zeros diff --git a/apps/files_external/l10n/ar.php b/apps/files_external/l10n/ar.php index 06837d5085..a53bfe48bc 100644 --- a/apps/files_external/l10n/ar.php +++ b/apps/files_external/l10n/ar.php @@ -1,5 +1,5 @@ "مجموعات", "Users" => "المستخدمين", -"Delete" => "حذف" +"Delete" => "إلغاء" ); diff --git a/apps/files_external/l10n/bg_BG.php b/apps/files_external/l10n/bg_BG.php index 66ad4a879d..fcb01152bf 100644 --- a/apps/files_external/l10n/bg_BG.php +++ b/apps/files_external/l10n/bg_BG.php @@ -2,6 +2,7 @@ "Access granted" => "Достъпът е даден", "Grant access" => "Даване на достъп", "External Storage" => "Външно хранилище", +"Folder name" => "Име на папката", "Configuration" => "Конфигурация", "Options" => "Опции", "Applicable" => "Приложимо", diff --git a/apps/files_external/l10n/bn_BD.php b/apps/files_external/l10n/bn_BD.php index 07ccd50074..0f032df9f0 100644 --- a/apps/files_external/l10n/bn_BD.php +++ b/apps/files_external/l10n/bn_BD.php @@ -12,7 +12,7 @@ "All Users" => "সমস্ত ব্যবহারকারী", "Groups" => "গোষ্ঠীসমূহ", "Users" => "ব্যবহারকারী", -"Delete" => "মুছে ফেল", +"Delete" => "মুছে", "Enable User External Storage" => "ব্যবহারকারীর বাহ্যিক সংরক্ষণাগার সক্রিয় কর", "Allow users to mount their own external storage" => "ব্যবহারকারীদেরকে তাদের নিজস্ব বাহ্যিক সংরক্ষনাগার সাউন্ট করতে অনুমোদন দাও", "SSL root certificates" => "SSL রুট সনদপত্র", diff --git a/apps/files_external/l10n/ca.php b/apps/files_external/l10n/ca.php index aa9304d330..90ac954301 100644 --- a/apps/files_external/l10n/ca.php +++ b/apps/files_external/l10n/ca.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Error en configurar l'emmagatzemament Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Avís: \"smbclient\" no està instal·lat. No es pot muntar la compartició CIFS/SMB. Demaneu a l'administrador del sistema que l'instal·li.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Avís: El suport FTP per PHP no està activat o no està instal·lat. No es pot muntar la compartició FTP. Demaneu a l'administrador del sistema que l'instal·li.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Avís:El suport Curl de PHP no està activat o instal·lat. No es pot muntar ownCloud / WebDAV o GoogleDrive. Demaneu a l'administrador que l'instal·li.", "External Storage" => "Emmagatzemament extern", "Folder name" => "Nom de la carpeta", "External storage" => "Emmagatzemament extern", @@ -17,7 +18,7 @@ "All Users" => "Tots els usuaris", "Groups" => "Grups", "Users" => "Usuaris", -"Delete" => "Elimina", +"Delete" => "Esborra", "Enable User External Storage" => "Habilita l'emmagatzemament extern d'usuari", "Allow users to mount their own external storage" => "Permet als usuaris muntar el seu emmagatzemament extern propi", "SSL root certificates" => "Certificats SSL root", diff --git a/apps/files_external/l10n/cs_CZ.php b/apps/files_external/l10n/cs_CZ.php index 20bbe8acba..12603044d6 100644 --- a/apps/files_external/l10n/cs_CZ.php +++ b/apps/files_external/l10n/cs_CZ.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Chyba při nastavení úložiště Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Varování: není nainstalován program \"smbclient\". Není možné připojení oddílů CIFS/SMB. Prosím požádejte svého správce systému ať jej nainstaluje.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Varování: není nainstalována, nebo povolena, podpora FTP v PHP. Není možné připojení oddílů FTP. Prosím požádejte svého správce systému ať ji nainstaluje.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Varování: není nainstalována, nebo povolena, podpora Curl v PHP. Není možné připojení oddílů ownCloud, WebDAV, či GoogleDrive. Prosím požádejte svého správce systému ať ji nainstaluje.", "External Storage" => "Externí úložiště", "Folder name" => "Název složky", "External storage" => "Externí úložiště", diff --git a/apps/files_external/l10n/cy_GB.php b/apps/files_external/l10n/cy_GB.php new file mode 100644 index 0000000000..78bbb987eb --- /dev/null +++ b/apps/files_external/l10n/cy_GB.php @@ -0,0 +1,5 @@ + "Grwpiau", +"Users" => "Defnyddwyr", +"Delete" => "Dileu" +); diff --git a/apps/files_external/l10n/da.php b/apps/files_external/l10n/da.php index c1c070e3d7..f2c1e45778 100644 --- a/apps/files_external/l10n/da.php +++ b/apps/files_external/l10n/da.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Fejl ved konfiguration af Google Drive plads", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => " Advarsel: \"smbclient\" ikke er installeret. Montering af CIFS / SMB delinger er ikke muligt. Spørg din systemadministrator om at installere det.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => " Advarsel: FTP-understøttelse i PHP ikke er aktiveret eller installeret. Montering af FTP delinger er ikke muligt. Spørg din systemadministrator om at installere det.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Advarsel: Understøttelsen for Curl i PHP er enten ikke aktiveret eller ikke installeret. Det er ikke muligt, at montere ownCloud / WebDAV eller GoogleDrive. Spørg din system administrator om at installere det. ", "External Storage" => "Ekstern opbevaring", "Folder name" => "Mappenavn", "External storage" => "Eksternt lager", diff --git a/apps/files_external/l10n/de.php b/apps/files_external/l10n/de.php index 2418377221..8dfa0eafbb 100644 --- a/apps/files_external/l10n/de.php +++ b/apps/files_external/l10n/de.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Fehler beim Einrichten von Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Warnung: \"smbclient\" ist nicht installiert. Das Einhängen von CIFS/SMB-Freigaben ist nicht möglich. Bitte Deinen System-Administrator, dies zu installieren.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Warnung:: Die FTP Unterstützung von PHP ist nicht aktiviert oder installiert. Bitte wende Dich an Deinen Systemadministrator.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Warnung: Die Curl-Unterstützung in PHP ist nicht aktiviert oder installiert. Das Einbinden von ownCloud / WebDav der GoogleDrive-Freigaben ist nicht möglich. Bitte Deinen Systemadminstrator um die Installation. ", "External Storage" => "Externer Speicher", "Folder name" => "Ordnername", "External storage" => "Externer Speicher", diff --git a/apps/files_external/l10n/de_DE.php b/apps/files_external/l10n/de_DE.php index d55c0c6909..9b7ab4d53c 100644 --- a/apps/files_external/l10n/de_DE.php +++ b/apps/files_external/l10n/de_DE.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Fehler beim Einrichten von Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Warnung: \"smbclient\" ist nicht installiert. Das Einhängen von CIFS/SMB-Freigaben ist nicht möglich. Bitten Sie Ihren Systemadministrator, dies zu installieren.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Warnung:: Die FTP Unterstützung von PHP ist nicht aktiviert oder installiert. Bitte wenden Sie sich an Ihren Systemadministrator.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Achtung: Die Curl-Unterstützung von PHP ist nicht aktiviert oder installiert. Das Laden von ownCloud / WebDAV oder GoogleDrive Freigaben ist nicht möglich. Bitte Sie Ihren Systemadministrator, das Modul zu installieren.", "External Storage" => "Externer Speicher", "Folder name" => "Ordnername", "External storage" => "Externer Speicher", @@ -19,7 +20,7 @@ "Users" => "Benutzer", "Delete" => "Löschen", "Enable User External Storage" => "Externen Speicher für Benutzer aktivieren", -"Allow users to mount their own external storage" => "Erlaubt Benutzern ihre eigenen externen Speicher einzubinden", +"Allow users to mount their own external storage" => "Erlaubt Benutzern, ihre eigenen externen Speicher einzubinden", "SSL root certificates" => "SSL-Root-Zertifikate", "Import Root Certificate" => "Root-Zertifikate importieren" ); diff --git a/apps/files_external/l10n/el.php b/apps/files_external/l10n/el.php index 6c519a1b41..62703b08fb 100644 --- a/apps/files_external/l10n/el.php +++ b/apps/files_external/l10n/el.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Σφάλμα ρυθμίζωντας αποθήκευση Google Drive ", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Προσοχή: Ο \"smbclient\" δεν εγκαταστάθηκε. Δεν είναι δυνατή η προσάρτηση CIFS/SMB. Παρακαλώ ενημερώστε τον διαχειριστή συστήματος να το εγκαταστήσει.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Προσοχή: Η υποστήριξη FTP στην PHP δεν ενεργοποιήθηκε ή εγκαταστάθηκε. Δεν είναι δυνατή η προσάρτηση FTP. Παρακαλώ ενημερώστε τον διαχειριστή συστήματος να το εγκαταστήσει.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "<Προειδοποίηση Η υποστήριξη του συστήματος Curl στο PHP δεν είναι ενεργοποιημένη ή εγκαταστημένη. Η αναπαραγωγή του ownCloud/WebDAV ή GoogleDrive δεν είναι δυνατή. Παρακαλώ ρωτήστε τον διαχειριστλη του συστήματος για την εγκατάσταση. ", "External Storage" => "Εξωτερικό Αποθηκευτικό Μέσο", "Folder name" => "Όνομα φακέλου", "External storage" => "Εξωτερική αποθήκευση", diff --git a/apps/files_external/l10n/es.php b/apps/files_external/l10n/es.php index da22f41032..f83562dd64 100644 --- a/apps/files_external/l10n/es.php +++ b/apps/files_external/l10n/es.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Error configurando el almacenamiento de Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Advertencia: El cliente smb (smbclient) no se encuentra instalado. El montado de archivos o ficheros CIFS/SMB no es posible. Por favor pida al administrador de su sistema que lo instale.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Advertencia: El soporte de FTP en PHP no se encuentra instalado. El montado de archivos o ficheros FTP no es posible. Por favor pida al administrador de su sistema que lo instale.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Advertencia: El soporte de Curl en PHP no está activado ni instalado. El montado de ownCloud, WebDAV o GoogleDrive no es posible. Pida al administrador de su sistema que lo instale.", "External Storage" => "Almacenamiento externo", "Folder name" => "Nombre de la carpeta", "External storage" => "Almacenamiento externo", @@ -17,7 +18,7 @@ "All Users" => "Todos los usuarios", "Groups" => "Grupos", "Users" => "Usuarios", -"Delete" => "Eliiminar", +"Delete" => "Eliminar", "Enable User External Storage" => "Habilitar almacenamiento de usuario externo", "Allow users to mount their own external storage" => "Permitir a los usuarios montar su propio almacenamiento externo", "SSL root certificates" => "Raíz de certificados SSL ", diff --git a/apps/files_external/l10n/et_EE.php b/apps/files_external/l10n/et_EE.php index 5d1eb0887b..465201df4d 100644 --- a/apps/files_external/l10n/et_EE.php +++ b/apps/files_external/l10n/et_EE.php @@ -5,7 +5,8 @@ "Please provide a valid Dropbox app key and secret." => "Palun sisesta korrektne Dropboxi rakenduse võti ja salasõna.", "Error configuring Google Drive storage" => "Viga Google Drive'i salvestusruumi seadistamisel", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Hoiatus: \"smbclient\" pole paigaldatud. Jagatud CIFS/SMB hoidlate ühendamine pole võimalik. Palu oma süsteemihalduril paigaldata SAMBA tugi.", -"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Hoiatus: FTP tugi puudub PHP paigalduses. Jagatud FTP hoidlate ühendamine pole võimalik. Palu oma süsteemihalduril paigaldata FTP tugi.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Hoiatus: PHP-s puudub FTP tugi. Jagatud FTP hoidlate ühendamine pole võimalik. Palu oma süsteemihalduril paigaldata FTP tugi.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Hoiatus: PHP-s puudub Curl tugi. Jagatud ownCloud / WebDAV või GoogleDrive ühendamine pole võimalik. Palu oma süsteemihalduril see paigaldada.", "External Storage" => "Väline salvestuskoht", "Folder name" => "Kausta nimi", "External storage" => "Väline andmehoidla", diff --git a/apps/files_external/l10n/fi_FI.php b/apps/files_external/l10n/fi_FI.php index 2f7d65fbe9..ba39d140fb 100644 --- a/apps/files_external/l10n/fi_FI.php +++ b/apps/files_external/l10n/fi_FI.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Virhe Google Drive levyn asetuksia tehtäessä", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Varoitus: \"smbclient\" ei ole asennettuna. CIFS-/SMB-jakojen liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää asentamaan smbclient.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Varoitus: PHP:n FTP-tuki ei ole käytössä tai sitä ei ole asennettu. FTP-jakojen liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää ottamaan FTP-tuki käyttöön.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Varoitus: PHP:n Curl-tuki ei ole käytössä tai sitä ei ole lainkaan asennettu. ownCloudin, WebDAV:in tai Google Driven liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää ottamaan Curl-tuki käyttöön.", "External Storage" => "Erillinen tallennusväline", "Folder name" => "Kansion nimi", "External storage" => "Ulkoinen tallennustila", diff --git a/apps/files_external/l10n/fr.php b/apps/files_external/l10n/fr.php index c42c89f857..5006133a7b 100644 --- a/apps/files_external/l10n/fr.php +++ b/apps/files_external/l10n/fr.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Erreur lors de la configuration du support de stockage Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Attention : \"smbclient\" n'est pas installé. Le montage des partages CIFS/SMB n'est pas disponible. Contactez votre administrateur système pour l'installer.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Attention : Le support FTP de PHP n'est pas activé ou installé. Le montage des partages FTP n'est pas disponible. Contactez votre administrateur système pour l'installer.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Attention : Le support de Curl n'est pas activé ou installé dans PHP. Le montage de ownCloud / WebDAV ou GoogleDrive n'est pas possible. Contactez votre administrateur système pour l'installer.", "External Storage" => "Stockage externe", "Folder name" => "Nom du dossier", "External storage" => "Stockage externe", diff --git a/apps/files_external/l10n/gl.php b/apps/files_external/l10n/gl.php index 715417e25a..77f23c39bc 100644 --- a/apps/files_external/l10n/gl.php +++ b/apps/files_external/l10n/gl.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Produciuse un erro ao configurar o almacenamento en Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Aviso: «smbclient» non está instalado. Non é posibel a montaxe de comparticións CIFS/SMB. Consulte co administrador do sistema para instalalo.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Aviso: A compatibilidade de FTP en PHP non está activada ou instalada. Non é posibel a montaxe de comparticións FTP. Consulte co administrador do sistema para instalalo.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Aviso: A compatibilidade de Curl en PHP non está activada ou instalada. Non é posíbel a montaxe de ownCloud / WebDAV ou GoogleDrive. Consulte co administrador do sistema para instalala.", "External Storage" => "Almacenamento externo", "Folder name" => "Nome do cartafol", "External storage" => "Almacenamento externo", diff --git a/apps/files_external/l10n/hu_HU.php b/apps/files_external/l10n/hu_HU.php index 9ecd2d1088..b88737a19a 100644 --- a/apps/files_external/l10n/hu_HU.php +++ b/apps/files_external/l10n/hu_HU.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "A Google Drive tárolót nem sikerült beállítani", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Figyelem: az \"smbclient\" nincs telepítve a kiszolgálón. Emiatt nem lehet CIFS/SMB megosztásokat fölcsatolni. Kérje meg a rendszergazdát, hogy telepítse a szükséges programot.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Figyelem: a PHP FTP támogatása vagy nincs telepítve, vagy nincs engedélyezve a kiszolgálón. Emiatt nem lehetséges FTP-tárolókat fölcsatolni. Kérje meg a rendszergazdát, hogy telepítse a szükséges programot.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Figyelmeztetés: A PHP-ben nincs telepítve vagy engedélyezve a Curl támogatás. Nem lehetséges ownCloud / WebDAV ill. GoogleDrive tárolók becsatolása. Kérje meg a rendszergazdát, hogy telepítse a szükséges programot!", "External Storage" => "Külső tárolási szolgáltatások becsatolása", "Folder name" => "Mappanév", "External storage" => "Külső tárolók", diff --git a/apps/files_external/l10n/it.php b/apps/files_external/l10n/it.php index d7e0c81a0b..4c70a04022 100644 --- a/apps/files_external/l10n/it.php +++ b/apps/files_external/l10n/it.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Errore durante la configurazione dell'archivio Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Avviso: \"smbclient\" non è installato. Impossibile montare condivisioni CIFS/SMB. Chiedi all'amministratore di sistema di installarlo.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Avviso: il supporto FTP di PHP non è abilitato o non è installato. Impossibile montare condivisioni FTP. Chiedi all'amministratore di sistema di installarlo.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Avviso: il supporto Curl di PHP non è abilitato o non è installato. Impossibile montare condivisioni ownCloud / WebDAV o GoogleDrive. Chiedi all'amministratore di sistema di installarlo.", "External Storage" => "Archiviazione esterna", "Folder name" => "Nome della cartella", "External storage" => "Archiviazione esterna", diff --git a/apps/files_external/l10n/ja_JP.php b/apps/files_external/l10n/ja_JP.php index 12a0b30938..97dd4e119d 100644 --- a/apps/files_external/l10n/ja_JP.php +++ b/apps/files_external/l10n/ja_JP.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Googleドライブストレージの設定エラー", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "警告: \"smbclient\" はインストールされていません。CIFS/SMB 共有のマウントはできません。システム管理者にインストールをお願いして下さい。", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "警告: PHPのFTPサポートは無効もしくはインストールされていません。FTP共有のマウントはできません。システム管理者にインストールをお願いして下さい。", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "警告: PHP の Curl サポートは無効もしくはインストールされていません。ownCloud / WebDAV もしくは GoogleDrive のマウントはできません。システム管理者にインストールをお願いして下さい。", "External Storage" => "外部ストレージ", "Folder name" => "フォルダ名", "External storage" => "外部ストレージ", diff --git a/apps/files_external/l10n/ka_GE.php b/apps/files_external/l10n/ka_GE.php index d10f82849d..b0845555b4 100644 --- a/apps/files_external/l10n/ka_GE.php +++ b/apps/files_external/l10n/ka_GE.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "შეცდომა Google Drive საცავის კონფიგურირების დროს", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "გაფრთხილება: \"smbclient\" არ არის ინსტალირებული. CIFS/SMB ზიარების მონტირება შეუძლებელია. გთხოვთ თხოვოთ თქვენს სისტემურ ადმინისტრატორებს დააინსტალიროს ის.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "გაფრთხილება: FTP მხარდაჭერა არ არის აქტიური ან დაინსტალირებული. FTP ზიარის მონტირება შეუძლებელია. გთხოვთ თხოვოთ თქვენს სისტემურ ადმინისტრატორებს დააინსტალიროს ის.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "გაფრთხილება:PHP–ის Curl მხარდაჭერა არ არის ჩართული ან ინსტალირებული. ownCloud / WebDAV ან GoogleDrive–ის მონტირება შეუძლებელია. თხოვეთ თქვენს ადმინისტრატორს დააინსტალიროს ის.", "External Storage" => "ექსტერნალ საცავი", "Folder name" => "ფოლდერის სახელი", "External storage" => "ექსტერნალ საცავი", diff --git a/apps/files_external/l10n/lb.php b/apps/files_external/l10n/lb.php index 2a62cad3fe..4e78227ec4 100644 --- a/apps/files_external/l10n/lb.php +++ b/apps/files_external/l10n/lb.php @@ -1,5 +1,6 @@ "Dossiers Numm:", "Groups" => "Gruppen", +"Users" => "Benotzer", "Delete" => "Läschen" ); diff --git a/apps/files_external/l10n/lt_LT.php b/apps/files_external/l10n/lt_LT.php index 9bf997d87c..29c962d9a8 100644 --- a/apps/files_external/l10n/lt_LT.php +++ b/apps/files_external/l10n/lt_LT.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Klaida nustatinėjant Google Drive talpyklą", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Įspėjimas: \"smbclient\" nėra įdiegtas. CIFS/SMB dalinimasis nėra galimas. Prašome susisiekti su sistemos administratoriumi kad būtų įdiegtas \"smbclient\"", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Įspėjimas: FTP palaikymas PHP sistemoje nėra įjungtas arba nėra įdiegtas. FTP dalinimosi įjungimas nėra galimas. Prašome susisiekti su sistemos administratoriumi kad būtų įdiegtas FTP palaikymas. ", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Įspėjimas: \"Curl\" palaikymas PHP terpėje nėra įjungtas arba įdiegtas. ownCloud/WebDAV ar GoogleDrive įjungimas nebus įmanomas. Prašome susisiekti su sistemos administratoriumi kad būtų įdiegtas arba įjungtas \"Curl\" palaikymas.", "External Storage" => "Išorinės saugyklos", "Folder name" => "Katalogo pavadinimas", "External storage" => "Išorinė saugykla", diff --git a/apps/files_external/l10n/nb_NO.php b/apps/files_external/l10n/nb_NO.php index 961ef2b104..ea8648303d 100644 --- a/apps/files_external/l10n/nb_NO.php +++ b/apps/files_external/l10n/nb_NO.php @@ -2,6 +2,11 @@ "Access granted" => "Tilgang innvilget", "Error configuring Dropbox storage" => "Feil ved konfigurering av Dropbox-lagring", "Grant access" => "Gi tilgang", +"Please provide a valid Dropbox app key and secret." => "Vær vennlig å oppgi gyldig Dropbox appnøkkel og hemmelighet.", +"Error configuring Google Drive storage" => "Feil med konfigurering av Google Drive", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Advarsel: \"smbclient\" er ikke installert. Kan ikke montere CIFS/SMB mapper. Ta kontakt med din systemadministrator for å installere det.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Advarsel: FTP støtte i PHP er ikke slått på eller innstallert. Kan ikke montere FTP mapper. Ta kontakt med din systemadministrator for å innstallere det.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Advarsel: Curl støtte i PHP er ikke aktivert eller innstallert. Kan ikke montere owncloud/WebDAV eller Googledrive. Ta kontakt med din systemadministrator for å innstallerer det.", "External Storage" => "Ekstern lagring", "Folder name" => "Mappenavn", "External storage" => "Ekstern lagringsplass", diff --git a/apps/files_external/l10n/nl.php b/apps/files_external/l10n/nl.php index ad3eda9747..ded5a861a8 100644 --- a/apps/files_external/l10n/nl.php +++ b/apps/files_external/l10n/nl.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Fout tijdens het configureren van Google Drive opslag", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Waarschuwing: \"smbclient\" is niet geïnstalleerd. Mounten van CIFS/SMB shares is niet mogelijk. Vraag uw beheerder om smbclient te installeren.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Waarschuwing: FTP ondersteuning in PHP is niet geactiveerd of geïnstalleerd. Mounten van FTP shares is niet mogelijk. Vraag uw beheerder FTP ondersteuning te installeren.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Waarschuwing: Curl ondersteuning in PHP is niet geactiveerd of geïnstalleerd. Mounten van ownCloud / WebDAV of GoogleDrive is niet mogelijk. Vraag uw systeembeheerder dit te installeren.", "External Storage" => "Externe opslag", "Folder name" => "Mapnaam", "External storage" => "Externe opslag", diff --git a/apps/files_external/l10n/nn_NO.php b/apps/files_external/l10n/nn_NO.php index 4b4b6167d8..998c3f8245 100644 --- a/apps/files_external/l10n/nn_NO.php +++ b/apps/files_external/l10n/nn_NO.php @@ -1,4 +1,5 @@ "Innstillingar", "Groups" => "Grupper", "Users" => "Brukarar", "Delete" => "Slett" diff --git a/apps/files_external/l10n/pl.php b/apps/files_external/l10n/pl.php index cd1b1fe84a..e03ded1e70 100644 --- a/apps/files_external/l10n/pl.php +++ b/apps/files_external/l10n/pl.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Wystąpił błąd podczas konfigurowania zasobu Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Ostrzeżenie: \"smbclient\" nie jest zainstalowany. Zamontowanie katalogów CIFS/SMB nie jest możliwe. Skontaktuj sie z administratorem w celu zainstalowania.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Ostrzeżenie: Wsparcie dla FTP w PHP nie jest zainstalowane lub włączone. Skontaktuj sie z administratorem w celu zainstalowania lub włączenia go.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Ostrzeżenie: Wsparcie dla Curl w PHP nie jest zainstalowane lub włączone. Montowanie WebDAV lub GoogleDrive nie będzie możliwe. Skontaktuj się z administratorem w celu zainstalowania lub włączenia tej opcji.", "External Storage" => "Zewnętrzna zasoby dyskowe", "Folder name" => "Nazwa folderu", "External storage" => "Zewnętrzne zasoby dyskowe", diff --git a/apps/files_external/l10n/pt_BR.php b/apps/files_external/l10n/pt_BR.php index a358d56913..bc3c356a51 100644 --- a/apps/files_external/l10n/pt_BR.php +++ b/apps/files_external/l10n/pt_BR.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Erro ao configurar armazenamento do Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Aviso: \"smbclient\" não está instalado. Impossível montar compartilhamentos de CIFS/SMB. Por favor, peça ao seu administrador do sistema para instalá-lo.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Aviso: O suporte para FTP do PHP não está ativado ou instalado. Impossível montar compartilhamentos FTP. Por favor, peça ao seu administrador do sistema para instalá-lo.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => " Aviso: O suport a Curl em PHP não está habilitado ou instalado. A montagem do ownCloud / WebDAV ou GoogleDrive não é possível. Por favor, solicite ao seu administrador do sistema instalá-lo.", "External Storage" => "Armazenamento Externo", "Folder name" => "Nome da pasta", "External storage" => "Armazenamento Externo", @@ -17,7 +18,7 @@ "All Users" => "Todos os Usuários", "Groups" => "Grupos", "Users" => "Usuários", -"Delete" => "Remover", +"Delete" => "Excluir", "Enable User External Storage" => "Habilitar Armazenamento Externo do Usuário", "Allow users to mount their own external storage" => "Permitir usuários a montar seus próprios armazenamentos externos", "SSL root certificates" => "Certificados SSL raíz", diff --git a/apps/files_external/l10n/pt_PT.php b/apps/files_external/l10n/pt_PT.php index aac3c1c2ca..0a05d1f882 100644 --- a/apps/files_external/l10n/pt_PT.php +++ b/apps/files_external/l10n/pt_PT.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Erro ao configurar o armazenamento do Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Atenção: O cliente \"smbclient\" não está instalado. Não é possível montar as partilhas CIFS/SMB . Peça ao seu administrador para instalar.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Aviso: O suporte FTP no PHP não está activate ou instalado. Não é possível montar as partilhas FTP. Peça ao seu administrador para instalar.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Atenção:
O suporte PHP para o Curl não está activado ou instalado. A montagem do ownCloud/WebDav ou GoolgeDriver não é possível. Por favor contacte o administrador para o instalar.", "External Storage" => "Armazenamento Externo", "Folder name" => "Nome da pasta", "External storage" => "Armazenamento Externo", diff --git a/apps/files_external/l10n/ro.php b/apps/files_external/l10n/ro.php index 5747205dc0..ed23b4cca8 100644 --- a/apps/files_external/l10n/ro.php +++ b/apps/files_external/l10n/ro.php @@ -6,11 +6,14 @@ "Error configuring Google Drive storage" => "Eroare la configurarea mediului de stocare Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Atenție: \"smbclient\" nu este instalat. Montarea mediilor CIFS/SMB partajate nu este posibilă. Solicită administratorului sistemului tău să îl instaleaze.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Atenție: suportul pentru FTP în PHP nu este activat sau instalat. Montarea mediilor FPT partajate nu este posibilă. Solicită administratorului sistemului tău să îl instaleze.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Atentie: Suportul Curl nu este pornit / instalat in configuratia PHP! Montarea ownCloud / WebDAV / GoogleDrive nu este posibila! Intrebati administratorul sistemului despre aceasta problema!", "External Storage" => "Stocare externă", "Folder name" => "Denumire director", +"External storage" => "Stocare externă", "Configuration" => "Configurație", "Options" => "Opțiuni", "Applicable" => "Aplicabil", +"Add storage" => "Adauga stocare", "None set" => "Niciunul", "All Users" => "Toți utilizatorii", "Groups" => "Grupuri", diff --git a/apps/files_external/l10n/ru.php b/apps/files_external/l10n/ru.php index 46b73a67f0..d2c5292bac 100644 --- a/apps/files_external/l10n/ru.php +++ b/apps/files_external/l10n/ru.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Ошибка при настройке хранилища Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Внимание: \"smbclient\" не установлен. Подключение по CIFS/SMB невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить его.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Внимание: Поддержка FTP не включена в PHP. Подключение по FTP невозможно. Пожалуйста, обратитесь к системному администратору, чтобы включить.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Внимание: Поддержка Curl в PHP не включена или не установлена. Подключение ownCloud / WebDAV или GoogleDrive невозможно. Попросите вашего системного администратора установить его.", "External Storage" => "Внешний носитель", "Folder name" => "Имя папки", "External storage" => "Внешний носитель данных", diff --git a/apps/files_external/l10n/ru_RU.php b/apps/files_external/l10n/ru_RU.php index 406e284b27..a43417dfbc 100644 --- a/apps/files_external/l10n/ru_RU.php +++ b/apps/files_external/l10n/ru_RU.php @@ -1,23 +1,4 @@ "Доступ разрешен", -"Error configuring Dropbox storage" => "Ошибка при конфигурировании хранилища Dropbox", -"Grant access" => "Предоставить доступ", -"Please provide a valid Dropbox app key and secret." => "Пожалуйста представьте допустимый ключ приложения Dropbox и пароль.", -"Error configuring Google Drive storage" => "Ошибка настройки хранилища Google Drive", -"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Предупреждение: \"smbclient\" не установлен. Подключение общих папок CIFS/SMB невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить его.", -"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Предупреждение: Поддержка FTP в PHP не включена или не установлена. Подключение по FTP невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить ее.", -"External Storage" => "Внешние системы хранения данных", -"Folder name" => "Имя папки", -"Configuration" => "Конфигурация", -"Options" => "Опции", -"Applicable" => "Применимый", -"None set" => "Не задан", -"All Users" => "Все пользователи", "Groups" => "Группы", -"Users" => "Пользователи", -"Delete" => "Удалить", -"Enable User External Storage" => "Включить пользовательскую внешнюю систему хранения данных", -"Allow users to mount their own external storage" => "Разрешить пользователям монтировать их собственную внешнюю систему хранения данных", -"SSL root certificates" => "Корневые сертификаты SSL", -"Import Root Certificate" => "Импортировать корневые сертификаты" +"Delete" => "Удалить" ); diff --git a/apps/files_external/l10n/sk_SK.php b/apps/files_external/l10n/sk_SK.php index af6b7b4ae6..33edcb9d4c 100644 --- a/apps/files_external/l10n/sk_SK.php +++ b/apps/files_external/l10n/sk_SK.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "Chyba pri konfigurácii úložiska Google drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Upozornenie: \"smbclient\" nie je nainštalovaný. Nie je možné pripojenie oddielov CIFS/SMB. Požiadajte administrátora systému, nech ho nainštaluje.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Upozornenie: Podpora FTP v PHP nie je povolená alebo nainštalovaná. Nie je možné pripojenie oddielov FTP. Požiadajte administrátora systému, nech ho nainštaluje.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Varovanie: nie je nainštalovaná, alebo povolená, podpora Curl v PHP. Nie je možné pripojenie oddielov ownCloud, WebDAV, či GoogleDrive. Prosím požiadajte svojho administrátora systému, nech ju nainštaluje.", "External Storage" => "Externé úložisko", "Folder name" => "Meno priečinka", "External storage" => "Externé úložisko", @@ -17,7 +18,7 @@ "All Users" => "Všetci používatelia", "Groups" => "Skupiny", "Users" => "Používatelia", -"Delete" => "Odstrániť", +"Delete" => "Zmazať", "Enable User External Storage" => "Povoliť externé úložisko", "Allow users to mount their own external storage" => "Povoliť používateľom pripojiť ich vlastné externé úložisko", "SSL root certificates" => "Koreňové SSL certifikáty", diff --git a/apps/files_external/l10n/sl.php b/apps/files_external/l10n/sl.php index 4ff2eed3bf..09b91b913e 100644 --- a/apps/files_external/l10n/sl.php +++ b/apps/files_external/l10n/sl.php @@ -5,7 +5,8 @@ "Please provide a valid Dropbox app key and secret." => "Vpisati je treba veljaven ključ programa in kodo za Dropbox", "Error configuring Google Drive storage" => "Napaka nastavljanja shrambe Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Opozorilo: paket \"smbclient\" ni nameščen. Priklapljanje pogonov CIFS/SMB ne bo mogoče.", -"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Opozorilo: podpora FTP v PHP ni omogočena ali pa ni nameščena. Priklapljanje pogonov FTP zato ni mogoče.", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Opozorilo: podpora FTP v PHP ni omogočena ali pa ni nameščena. Priklapljanje pogonov FTP zato ne bo mogoče.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Opozorilo: podpora za Curl v PHP ni omogočena ali pa ni nameščena. Priklapljanje točke ownCloud / WebDAV ali GoogleDrive zato ne bo mogoče. Zahtevane pakete je treba pred uporabo namestiti.", "External Storage" => "Zunanja podatkovna shramba", "Folder name" => "Ime mape", "External storage" => "Zunanja shramba", @@ -18,7 +19,7 @@ "Groups" => "Skupine", "Users" => "Uporabniki", "Delete" => "Izbriši", -"Enable User External Storage" => "Omogoči uporabniško zunanjo podatkovno shrambo", +"Enable User External Storage" => "Omogoči zunanjo uporabniško podatkovno shrambo", "Allow users to mount their own external storage" => "Dovoli uporabnikom priklop lastne zunanje podatkovne shrambe", "SSL root certificates" => "Korenska potrdila SSL", "Import Root Certificate" => "Uvozi korensko potrdilo" diff --git a/apps/files_external/l10n/tr.php b/apps/files_external/l10n/tr.php index 79dc3a8589..3c3c0c24f9 100644 --- a/apps/files_external/l10n/tr.php +++ b/apps/files_external/l10n/tr.php @@ -4,6 +4,9 @@ "Grant access" => "Erişim sağlandı", "Please provide a valid Dropbox app key and secret." => "Lütfen Dropbox app key ve secret temin ediniz", "Error configuring Google Drive storage" => "Google Drive depo yapılandırma hatası", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Uyari.''smbclient''yüklü değil. Mont etme CIFS/SMB hissenin mümkün değildir. Lutfen kullanici sistemin sormak onu yuklemek ici, ", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => ". Sistem FTP PHPden aktif degil veya yuklemedi. Monte etme hissenin FTP mumkun degildir. Lutfen kullaniici sistemin sormak onu yuklemek icin.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => " Ihbar . Dayanma Curl PHPden aktif veya yuklemedi degil. Monte ownClouden/WebDay veya GoogleDrive mumkun degil. Lutfen sistm yonetici sormak yuklemek icin. ", "External Storage" => "Harici Depolama", "Folder name" => "Dizin ismi", "External storage" => "Harici Depolama", diff --git a/apps/files_external/l10n/ug.php b/apps/files_external/l10n/ug.php new file mode 100644 index 0000000000..2d1dea9890 --- /dev/null +++ b/apps/files_external/l10n/ug.php @@ -0,0 +1,9 @@ + "قىسقۇچ ئاتى", +"External storage" => "سىرتقى ساقلىغۇچ", +"Configuration" => "سەپلىمە", +"Options" => "تاللانما", +"Groups" => "گۇرۇپپا", +"Users" => "ئىشلەتكۈچىلەر", +"Delete" => "ئۆچۈر" +); diff --git a/apps/files_external/l10n/vi.php b/apps/files_external/l10n/vi.php index 84f31e8892..769f9e2a09 100644 --- a/apps/files_external/l10n/vi.php +++ b/apps/files_external/l10n/vi.php @@ -6,11 +6,14 @@ "Error configuring Google Drive storage" => "Lỗi cấu hình lưu trữ Google Drive", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "Cảnh báo: \"smbclient\" chưa được cài đặt. Mount CIFS/SMB shares là không thể thực hiện được. Hãy hỏi người quản trị hệ thống để cài đặt nó.", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "Cảnh báo: FTP trong PHP chưa được cài đặt hoặc chưa được mở. Mount FTP shares là không thể. Xin hãy yêu cầu quản trị hệ thống của bạn cài đặt nó.", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "Cảnh báo: Tính năng Curl trong PHP chưa được kích hoạt hoặc cài đặt. Việc gắn kết ownCloud / WebDAV hay GoogleDrive không thực hiện được. Vui lòng liên hệ người quản trị để cài đặt nó.", "External Storage" => "Lưu trữ ngoài", "Folder name" => "Tên thư mục", +"External storage" => "Lưu trữ ngoài", "Configuration" => "Cấu hình", "Options" => "Tùy chọn", "Applicable" => "Áp dụng", +"Add storage" => "Thêm bộ nhớ", "None set" => "không", "All Users" => "Tất cả người dùng", "Groups" => "Nhóm", diff --git a/apps/files_external/l10n/zh_CN.php b/apps/files_external/l10n/zh_CN.php index 7f95320511..8157923183 100644 --- a/apps/files_external/l10n/zh_CN.php +++ b/apps/files_external/l10n/zh_CN.php @@ -6,6 +6,7 @@ "Error configuring Google Drive storage" => "配置Google Drive存储时出错", "Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "警告:“smbclient” 尚未安装。CIFS/SMB 分享挂载无法实现。请咨询系统管理员进行安装。", "Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "警告:PHP中尚未启用或安装FTP。FTP 分享挂载无法实现。请咨询系统管理员进行安装。", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "警告: PHP中未启用或未安装Curl支持。ownCloud / WebDAV 或 GoogleDrive 不能挂载。请请求您的系统管理员安装该它。", "External Storage" => "外部存储", "Folder name" => "目录名称", "External storage" => "外部存储", diff --git a/apps/files_external/l10n/zh_TW.php b/apps/files_external/l10n/zh_TW.php index 873b555348..a8314dcef0 100644 --- a/apps/files_external/l10n/zh_TW.php +++ b/apps/files_external/l10n/zh_TW.php @@ -1,16 +1,26 @@ "訪問權已被准許", -"Grant access" => "准許訪問權", -"External Storage" => "外部儲存裝置", +"Access granted" => "允許存取", +"Error configuring Dropbox storage" => "設定 Dropbox 儲存時發生錯誤", +"Grant access" => "允許存取", +"Please provide a valid Dropbox app key and secret." => "請提供有效的 Dropbox app key 和 app secret 。", +"Error configuring Google Drive storage" => "設定 Google Drive 儲存時發生錯誤", +"Warning: \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "警告:未安裝 \"smbclient\" ,因此無法掛載 CIFS/SMB 分享,請洽您的系統管理員將其安裝。", +"Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "警告:PHP 並未啓用 FTP 的支援,因此無法掛載 FTP 分享,請洽您的系統管理員將其安裝並啓用。", +"Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "警告:PHP 並未啓用 Curl 的支援,因此無法掛載 ownCloud/WebDAV 或 Google Drive 分享,請洽您的系統管理員將其安裝並啓用。", +"External Storage" => "外部儲存", "Folder name" => "資料夾名稱", -"External storage" => "外部儲存裝置", +"External storage" => "外部儲存", "Configuration" => "設定", "Options" => "選項", -"Add storage" => "添加儲存區", +"Applicable" => "可用的", +"Add storage" => "增加儲存區", "None set" => "尚未設定", "All Users" => "所有使用者", "Groups" => "群組", "Users" => "使用者", "Delete" => "刪除", +"Enable User External Storage" => "啓用使用者外部儲存", +"Allow users to mount their own external storage" => "允許使用者自行掛載他們的外部儲存", +"SSL root certificates" => "SSL 根憑證", "Import Root Certificate" => "匯入根憑證" ); diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 01462cb6f8..4cb9b7c8ec 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -70,7 +70,7 @@ class OC_Mount_Config { 'root' => '&Root', 'secure' => '!Secure ftps://')); - $backends['\OC\Files\Storage\Google']=array( + if(OC_Mount_Config::checkcurl()) $backends['\OC\Files\Storage\Google']=array( 'backend' => 'Google Drive', 'configuration' => array( 'configured' => '#configured', @@ -96,7 +96,7 @@ class OC_Mount_Config { 'share' => 'Share', 'root' => '&Root')); - $backends['\OC\Files\Storage\DAV']=array( + if(OC_Mount_Config::checkcurl()) $backends['\OC\Files\Storage\DAV']=array( 'backend' => 'ownCloud / WebDAV', 'configuration' => array( 'host' => 'URL', @@ -414,6 +414,13 @@ class OC_Mount_Config { } } + /** + * check if curl is installed + */ + public static function checkcurl() { + return (function_exists('curl_init')); + } + /** * check dependencies */ @@ -426,6 +433,9 @@ class OC_Mount_Config { if(!OC_Mount_Config::checkphpftp()) { $txt.=$l->t('Warning: The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it.').'
'; } + if(!OC_Mount_Config::checkcurl()) { + $txt.=$l->t('Warning: The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it.').'
'; + } return($txt); } diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index cb04e557f8..081c547888 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -45,7 +45,6 @@ class Dropbox extends \OC\Files\Storage\Common { $oauth = new \Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); $oauth->setToken($params['token'], $params['token_secret']); $this->dropbox = new \Dropbox_API($oauth, 'dropbox'); - $this->mkdir(''); } else { throw new \Exception('Creating \OC\Files\Storage\Dropbox storage failed'); } diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index 8a7375ebe3..ca6c635eb2 100644 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -35,10 +35,6 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ if ( ! $this->root || $this->root[0]!='/') { $this->root='/'.$this->root; } - //create the root folder if necessary - if ( ! $this->is_dir('')) { - $this->mkdir(''); - } } else { throw new \Exception(); } @@ -63,7 +59,6 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ return $url; } public function fopen($path,$mode) { - $this->init(); switch($mode) { case 'r': case 'rb': @@ -100,7 +95,6 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ } public function writeBack($tmpFile) { - $this->init(); if (isset(self::$tempFiles[$tmpFile])) { $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]); unlink($tmpFile); diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php index ede6c251fd..4fd3609646 100644 --- a/apps/files_external/lib/sftp.php +++ b/apps/files_external/lib/sftp.php @@ -50,10 +50,6 @@ class SFTP extends \OC\Files\Storage\Common { $host_keys[$this->host] = $current_host_key; $this->write_host_keys($host_keys); } - - if(!$this->file_exists('')){ - $this->mkdir(''); - } } public function test() { diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index 961efb1a50..81a6c95638 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -57,12 +57,22 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ public function stat($path) { if ( ! $path and $this->root=='/') {//mtime doesn't work for shares - $mtime=$this->shareMTime(); $stat=stat($this->constructUrl($path)); + if (empty($stat)) { + return false; + } + $mtime=$this->shareMTime(); $stat['mtime']=$mtime; return $stat; } else { - return stat($this->constructUrl($path)); + $stat = stat($this->constructUrl($path)); + + // smb4php can return an empty array if the connection could not be established + if (empty($stat)) { + return false; + } + + return $stat; } } @@ -73,7 +83,6 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ * @return bool */ public function hasUpdated($path,$time) { - $this->init(); if(!$path and $this->root=='/') { // mtime doesn't work for shares, but giving the nature of the backend, // doing a full update is still just fast enough diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php index 4685877f26..09041f335b 100644 --- a/apps/files_external/lib/streamwrapper.php +++ b/apps/files_external/lib/streamwrapper.php @@ -8,46 +8,28 @@ namespace OC\Files\Storage; -abstract class StreamWrapper extends \OC\Files\Storage\Common{ - private $ready = false; - - protected function init(){ - if($this->ready) { - return; - } - $this->ready = true; - - //create the root folder if necesary - if(!$this->is_dir('')) { - $this->mkdir(''); - } - } - +abstract class StreamWrapper extends Common{ abstract public function constructUrl($path); public function mkdir($path) { - $this->init(); return mkdir($this->constructUrl($path)); } public function rmdir($path) { - $this->init(); if($this->file_exists($path)) { - $succes = rmdir($this->constructUrl($path)); + $success = rmdir($this->constructUrl($path)); clearstatcache(); - return $succes; + return $success; } else { return false; } } public function opendir($path) { - $this->init(); return opendir($this->constructUrl($path)); } public function filetype($path) { - $this->init(); return filetype($this->constructUrl($path)); } @@ -60,24 +42,20 @@ abstract class StreamWrapper extends \OC\Files\Storage\Common{ } public function file_exists($path) { - $this->init(); return file_exists($this->constructUrl($path)); } public function unlink($path) { - $this->init(); - $succes = unlink($this->constructUrl($path)); + $success = unlink($this->constructUrl($path)); clearstatcache(); - return $succes; + return $success; } public function fopen($path, $mode) { - $this->init(); return fopen($this->constructUrl($path), $mode); } public function touch($path, $mtime=null) { - $this->init(); if(is_null($mtime)) { $fh = $this->fopen($path, 'a'); fwrite($fh, ''); @@ -88,22 +66,18 @@ abstract class StreamWrapper extends \OC\Files\Storage\Common{ } public function getFile($path, $target) { - $this->init(); return copy($this->constructUrl($path), $target); } public function uploadFile($path, $target) { - $this->init(); return copy($path, $this->constructUrl($target)); } public function rename($path1, $path2) { - $this->init(); return rename($this->constructUrl($path1), $this->constructUrl($path2)); } public function stat($path) { - $this->init(); return stat($this->constructUrl($path)); } diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index 68c4b48f17..a9cfe5bd20 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -287,6 +287,7 @@ class SWIFT extends \OC\Files\Storage\Common{ if ( ! $this->root || $this->root[0]!='/') { $this->root='/'.$this->root; } + $this->id = 'swift:' . $this->host . ':'.$this->root . ':' . $this->user; } else { throw new \Exception(); } diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 3ba7c48cd5..c2fe7c67b5 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -73,8 +73,6 @@ class DAV extends \OC\Files\Storage\Common{ $this->client->addTrustedCertificates($certPath); } } - //create the root folder if necessary - $this->mkdir(''); } public function getId(){ diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php index 923b5e3968..e146725473 100644 --- a/apps/files_external/tests/ftp.php +++ b/apps/files_external/tests/ftp.php @@ -19,6 +19,7 @@ class FTP extends Storage { } $this->config['ftp']['root'] .= '/' . $id; //make sure we have an new empty folder to work in $this->instance = new \OC\Files\Storage\FTP($this->config['ftp']); + $this->instance->mkdir('/'); } public function tearDown() { diff --git a/apps/files_external/tests/sftp.php b/apps/files_external/tests/sftp.php index 16964e2087..efea7f075f 100644 --- a/apps/files_external/tests/sftp.php +++ b/apps/files_external/tests/sftp.php @@ -33,6 +33,7 @@ class SFTP extends Storage { } $this->config['sftp']['root'] .= '/' . $id; //make sure we have an new empty folder to work in $this->instance = new \OC\Files\Storage\SFTP($this->config['sftp']); + $this->instance->mkdir('/'); } public function tearDown() { @@ -40,4 +41,4 @@ class SFTP extends Storage { $this->instance->rmdir('/'); } } -} \ No newline at end of file +} diff --git a/apps/files_external/tests/smb.php b/apps/files_external/tests/smb.php index be3ea5a830..ca2a93c894 100644 --- a/apps/files_external/tests/smb.php +++ b/apps/files_external/tests/smb.php @@ -20,6 +20,7 @@ class SMB extends Storage { } $this->config['smb']['root'] .= $id; //make sure we have an new empty folder to work in $this->instance = new \OC\Files\Storage\SMB($this->config['smb']); + $this->instance->mkdir('/'); } public function tearDown() { diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php index 1702898045..1f9b767eca 100644 --- a/apps/files_external/tests/webdav.php +++ b/apps/files_external/tests/webdav.php @@ -20,6 +20,7 @@ class DAV extends Storage { } $this->config['webdav']['root'] .= '/' . $id; //make sure we have an new empty folder to work in $this->instance = new \OC\Files\Storage\DAV($this->config['webdav']); + $this->instance->mkdir('/'); } public function tearDown() { diff --git a/apps/files_sharing/l10n/bn_BD.php b/apps/files_sharing/l10n/bn_BD.php index c3af434ee2..5fdf6de50c 100644 --- a/apps/files_sharing/l10n/bn_BD.php +++ b/apps/files_sharing/l10n/bn_BD.php @@ -1,6 +1,6 @@ "কূটশব্দ", -"Submit" => "জমা দাও", +"Submit" => "জমা দিন", "%s shared the folder %s with you" => "%s আপনার সাথে %s ফোল্ডারটি ভাগাভাগি করেছেন", "%s shared the file %s with you" => "%s আপনার সাথে %s ফাইলটি ভাগাভাগি করেছেন", "Download" => "ডাউনলোড", diff --git a/apps/files_sharing/l10n/cy_GB.php b/apps/files_sharing/l10n/cy_GB.php new file mode 100644 index 0000000000..dec9af4ebe --- /dev/null +++ b/apps/files_sharing/l10n/cy_GB.php @@ -0,0 +1,9 @@ + "Cyfrinair", +"Submit" => "Cyflwyno", +"%s shared the folder %s with you" => "Rhannodd %s blygell %s â chi", +"%s shared the file %s with you" => "Rhannodd %s ffeil %s â chi", +"Download" => "Llwytho i lawr", +"No preview available for" => "Does dim rhagolwg ar gael ar gyfer", +"web services under your control" => "gwasanaethau gwe a reolir gennych" +); diff --git a/apps/files_sharing/l10n/de_DE.php b/apps/files_sharing/l10n/de_DE.php index b92d6d478c..ab81589b0e 100644 --- a/apps/files_sharing/l10n/de_DE.php +++ b/apps/files_sharing/l10n/de_DE.php @@ -1,9 +1,9 @@ "Passwort", -"Submit" => "Absenden", +"Submit" => "Bestätigen", "%s shared the folder %s with you" => "%s hat den Ordner %s mit Ihnen geteilt", "%s shared the file %s with you" => "%s hat die Datei %s mit Ihnen geteilt", -"Download" => "Download", +"Download" => "Herunterladen", "No preview available for" => "Es ist keine Vorschau verfügbar für", "web services under your control" => "Web-Services unter Ihrer Kontrolle" ); diff --git a/apps/files_sharing/l10n/en@pirate.php b/apps/files_sharing/l10n/en@pirate.php new file mode 100644 index 0000000000..02ee844048 --- /dev/null +++ b/apps/files_sharing/l10n/en@pirate.php @@ -0,0 +1,9 @@ + "Secret Code", +"Submit" => "Submit", +"%s shared the folder %s with you" => "%s shared the folder %s with you", +"%s shared the file %s with you" => "%s shared the file %s with you", +"Download" => "Download", +"No preview available for" => "No preview available for", +"web services under your control" => "web services under your control" +); diff --git a/apps/files_sharing/l10n/he.php b/apps/files_sharing/l10n/he.php index ff7be88af8..2ea5ba76ab 100644 --- a/apps/files_sharing/l10n/he.php +++ b/apps/files_sharing/l10n/he.php @@ -1,5 +1,5 @@ "ססמה", +"Password" => "סיסמא", "Submit" => "שליחה", "%s shared the folder %s with you" => "%s שיתף עמך את התיקייה %s", "%s shared the file %s with you" => "%s שיתף עמך את הקובץ %s", diff --git a/apps/files_sharing/l10n/hi.php b/apps/files_sharing/l10n/hi.php new file mode 100644 index 0000000000..560df54fc9 --- /dev/null +++ b/apps/files_sharing/l10n/hi.php @@ -0,0 +1,3 @@ + "पासवर्ड" +); diff --git a/apps/files_sharing/l10n/hr.php b/apps/files_sharing/l10n/hr.php new file mode 100644 index 0000000000..b2dca866bb --- /dev/null +++ b/apps/files_sharing/l10n/hr.php @@ -0,0 +1,6 @@ + "Lozinka", +"Submit" => "Pošalji", +"Download" => "Preuzimanje", +"web services under your control" => "web usluge pod vašom kontrolom" +); diff --git a/apps/files_sharing/l10n/hy.php b/apps/files_sharing/l10n/hy.php new file mode 100644 index 0000000000..438e8a7433 --- /dev/null +++ b/apps/files_sharing/l10n/hy.php @@ -0,0 +1,4 @@ + "Հաստատել", +"Download" => "Բեռնել" +); diff --git a/apps/files_sharing/l10n/ia.php b/apps/files_sharing/l10n/ia.php new file mode 100644 index 0000000000..d229135a71 --- /dev/null +++ b/apps/files_sharing/l10n/ia.php @@ -0,0 +1,6 @@ + "Contrasigno", +"Submit" => "Submitter", +"Download" => "Discargar", +"web services under your control" => "servicios web sub tu controlo" +); diff --git a/apps/files_sharing/l10n/ku_IQ.php b/apps/files_sharing/l10n/ku_IQ.php index f139b0a064..675fc372e1 100644 --- a/apps/files_sharing/l10n/ku_IQ.php +++ b/apps/files_sharing/l10n/ku_IQ.php @@ -1,5 +1,5 @@ "تێپه‌ڕه‌وشه", +"Password" => "وشەی تێپەربو", "Submit" => "ناردن", "%s shared the folder %s with you" => "%s دابه‌شی کردووه‌ بوخچه‌ی %s له‌گه‌ڵ تۆ", "%s shared the file %s with you" => "%s دابه‌شی کردووه‌ په‌ڕگه‌یی %s له‌گه‌ڵ تۆ", diff --git a/apps/files_sharing/l10n/lb.php b/apps/files_sharing/l10n/lb.php index 8aba5806aa..630866ab4c 100644 --- a/apps/files_sharing/l10n/lb.php +++ b/apps/files_sharing/l10n/lb.php @@ -1,3 +1,6 @@ "Passwuert" +"Password" => "Passwuert", +"Submit" => "Fortschécken", +"Download" => "Download", +"web services under your control" => "Web Servicer ënnert denger Kontroll" ); diff --git a/apps/files_sharing/l10n/lt_LT.php b/apps/files_sharing/l10n/lt_LT.php index d21a3c14f4..96ab48cd2c 100644 --- a/apps/files_sharing/l10n/lt_LT.php +++ b/apps/files_sharing/l10n/lt_LT.php @@ -1,6 +1,6 @@ "Dydis", -"Modified" => "Pakeista", -"Delete all" => "Ištrinti viską", -"Delete" => "Ištrinti" +"Password" => "Slaptažodis", +"Submit" => "Išsaugoti", +"Download" => "Atsisiųsti", +"web services under your control" => "jūsų valdomos web paslaugos" ); diff --git a/apps/files_sharing/l10n/lv.php b/apps/files_sharing/l10n/lv.php index 0b22486708..88faeaf9f1 100644 --- a/apps/files_sharing/l10n/lv.php +++ b/apps/files_sharing/l10n/lv.php @@ -5,5 +5,5 @@ "%s shared the file %s with you" => "%s ar jums dalījās ar datni %s", "Download" => "Lejupielādēt", "No preview available for" => "Nav pieejams priekšskatījums priekš", -"web services under your control" => "jūsu vadībā esošie tīmekļa servisi" +"web services under your control" => "tīmekļa servisi tavā varā" ); diff --git a/apps/files_sharing/l10n/ms_MY.php b/apps/files_sharing/l10n/ms_MY.php new file mode 100644 index 0000000000..879524afce --- /dev/null +++ b/apps/files_sharing/l10n/ms_MY.php @@ -0,0 +1,6 @@ + "Kata laluan", +"Submit" => "Hantar", +"Download" => "Muat turun", +"web services under your control" => "Perkhidmatan web di bawah kawalan anda" +); diff --git a/apps/files_sharing/l10n/nn_NO.php b/apps/files_sharing/l10n/nn_NO.php new file mode 100644 index 0000000000..aeba545dab --- /dev/null +++ b/apps/files_sharing/l10n/nn_NO.php @@ -0,0 +1,9 @@ + "Passord", +"Submit" => "Send", +"%s shared the folder %s with you" => "%s delte mappa %s med deg", +"%s shared the file %s with you" => "%s delte fila %s med deg", +"Download" => "Last ned", +"No preview available for" => "Inga førehandsvising tilgjengeleg for", +"web services under your control" => "Vev tjenester under din kontroll" +); diff --git a/apps/files_sharing/l10n/oc.php b/apps/files_sharing/l10n/oc.php new file mode 100644 index 0000000000..07bc26ecdd --- /dev/null +++ b/apps/files_sharing/l10n/oc.php @@ -0,0 +1,6 @@ + "Senhal", +"Submit" => "Sosmetre", +"Download" => "Avalcarga", +"web services under your control" => "Services web jos ton contraròtle" +); diff --git a/apps/files_sharing/l10n/pt_BR.php b/apps/files_sharing/l10n/pt_BR.php index 4dde4bb5ad..ce4c28ddcb 100644 --- a/apps/files_sharing/l10n/pt_BR.php +++ b/apps/files_sharing/l10n/pt_BR.php @@ -5,5 +5,5 @@ "%s shared the file %s with you" => "%s compartilhou o arquivo %s com você", "Download" => "Baixar", "No preview available for" => "Nenhuma visualização disponível para", -"web services under your control" => "web services sob seu controle" +"web services under your control" => "serviços web sob seu controle" ); diff --git a/apps/files_sharing/l10n/pt_PT.php b/apps/files_sharing/l10n/pt_PT.php index b8e700e380..43e8f3c4b6 100644 --- a/apps/files_sharing/l10n/pt_PT.php +++ b/apps/files_sharing/l10n/pt_PT.php @@ -1,9 +1,9 @@ "Palavra-Passe", +"Password" => "Password", "Submit" => "Submeter", "%s shared the folder %s with you" => "%s partilhou a pasta %s consigo", "%s shared the file %s with you" => "%s partilhou o ficheiro %s consigo", -"Download" => "Descarregar", +"Download" => "Transferir", "No preview available for" => "Não há pré-visualização para", "web services under your control" => "serviços web sob o seu controlo" ); diff --git a/apps/files_sharing/l10n/ru_RU.php b/apps/files_sharing/l10n/ru_RU.php index 36e4b2fd0e..2cadd16346 100644 --- a/apps/files_sharing/l10n/ru_RU.php +++ b/apps/files_sharing/l10n/ru_RU.php @@ -1,9 +1,3 @@ "Пароль", -"Submit" => "Передать", -"%s shared the folder %s with you" => "%s имеет общий с Вами доступ к папке %s ", -"%s shared the file %s with you" => "%s имеет общий с Вами доступ к файлу %s ", -"Download" => "Загрузка", -"No preview available for" => "Предварительный просмотр недоступен", -"web services under your control" => "веб-сервисы под Вашим контролем" +"Download" => "Загрузка" ); diff --git a/apps/files_sharing/l10n/si_LK.php b/apps/files_sharing/l10n/si_LK.php index 1c69c60817..580f7b1990 100644 --- a/apps/files_sharing/l10n/si_LK.php +++ b/apps/files_sharing/l10n/si_LK.php @@ -1,9 +1,9 @@ "මුරපදය", +"Password" => "මුර පදය", "Submit" => "යොමු කරන්න", "%s shared the folder %s with you" => "%s ඔබව %s ෆෝල්ඩරයට හවුල් කරගත්තේය", "%s shared the file %s with you" => "%s ඔබ සමඟ %s ගොනුව බෙදාහදාගත්තේය", -"Download" => "භාගත කරන්න", +"Download" => "බාන්න", "No preview available for" => "පූර්වදර්ශනයක් නොමැත", "web services under your control" => "ඔබට පාලනය කළ හැකි වෙබ් සේවාවන්" ); diff --git a/apps/files_sharing/l10n/sk_SK.php b/apps/files_sharing/l10n/sk_SK.php index 2e781f76f3..14124eeb87 100644 --- a/apps/files_sharing/l10n/sk_SK.php +++ b/apps/files_sharing/l10n/sk_SK.php @@ -3,7 +3,7 @@ "Submit" => "Odoslať", "%s shared the folder %s with you" => "%s zdieľa s vami priečinok %s", "%s shared the file %s with you" => "%s zdieľa s vami súbor %s", -"Download" => "Stiahnuť", +"Download" => "Sťahovanie", "No preview available for" => "Žiaden náhľad k dispozícii pre", "web services under your control" => "webové služby pod Vašou kontrolou" ); diff --git a/apps/files_sharing/l10n/sr.php b/apps/files_sharing/l10n/sr.php index 6e277f6771..be24c06e46 100644 --- a/apps/files_sharing/l10n/sr.php +++ b/apps/files_sharing/l10n/sr.php @@ -1,5 +1,6 @@ "Лозинка", "Submit" => "Пошаљи", -"Download" => "Преузми" +"Download" => "Преузми", +"web services under your control" => "веб сервиси под контролом" ); diff --git a/apps/files_sharing/l10n/sr@latin.php b/apps/files_sharing/l10n/sr@latin.php new file mode 100644 index 0000000000..cce6bd1f77 --- /dev/null +++ b/apps/files_sharing/l10n/sr@latin.php @@ -0,0 +1,5 @@ + "Lozinka", +"Submit" => "Pošalji", +"Download" => "Preuzmi" +); diff --git a/apps/files_sharing/l10n/tr.php b/apps/files_sharing/l10n/tr.php index f2e6e5697d..42dfec8cc6 100644 --- a/apps/files_sharing/l10n/tr.php +++ b/apps/files_sharing/l10n/tr.php @@ -1,5 +1,5 @@ "Şifre", +"Password" => "Parola", "Submit" => "Gönder", "%s shared the folder %s with you" => "%s sizinle paylaşılan %s klasör", "%s shared the file %s with you" => "%s sizinle paylaşılan %s klasör", diff --git a/apps/files_sharing/l10n/ug.php b/apps/files_sharing/l10n/ug.php new file mode 100644 index 0000000000..348acc4a89 --- /dev/null +++ b/apps/files_sharing/l10n/ug.php @@ -0,0 +1,5 @@ + "ئىم", +"Submit" => "تاپشۇر", +"Download" => "چۈشۈر" +); diff --git a/apps/files_sharing/l10n/uk.php b/apps/files_sharing/l10n/uk.php index cdc103ad46..8e1fa4bc98 100644 --- a/apps/files_sharing/l10n/uk.php +++ b/apps/files_sharing/l10n/uk.php @@ -1,6 +1,6 @@ "Пароль", -"Submit" => "Submit", +"Submit" => "Передати", "%s shared the folder %s with you" => "%s опублікував каталог %s для Вас", "%s shared the file %s with you" => "%s опублікував файл %s для Вас", "Download" => "Завантажити", diff --git a/apps/files_sharing/l10n/zh_TW.php b/apps/files_sharing/l10n/zh_TW.php index f1d28731a7..14e4466ecb 100644 --- a/apps/files_sharing/l10n/zh_TW.php +++ b/apps/files_sharing/l10n/zh_TW.php @@ -1,9 +1,9 @@ "密碼", "Submit" => "送出", -"%s shared the folder %s with you" => "%s 分享了資料夾 %s 給您", -"%s shared the file %s with you" => "%s 分享了檔案 %s 給您", +"%s shared the folder %s with you" => "%s 和您分享了資料夾 %s ", +"%s shared the file %s with you" => "%s 和您分享了檔案 %s", "Download" => "下載", "No preview available for" => "無法預覽", -"web services under your control" => "在您掌控之下的網路服務" +"web services under your control" => "由您控制的網路服務" ); diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 9fccd0b46f..2160fe9a39 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -44,9 +44,9 @@ class Shared_Cache extends Cache { $source = \OC_Share_Backend_File::getSource($target); if (isset($source['path']) && isset($source['fileOwner'])) { \OC\Files\Filesystem::initMountPoints($source['fileOwner']); - $mount = \OC\Files\Mount::findByNumericId($source['storage']); - if ($mount) { - $fullPath = $mount->getMountPoint().$source['path']; + $mount = \OC\Files\Filesystem::getMountByNumericId($source['storage']); + if (is_array($mount)) { + $fullPath = $mount[key($mount)]->getMountPoint().$source['path']; list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath); if ($storage) { $this->files[$target] = $internalPath; @@ -60,6 +60,14 @@ class Shared_Cache extends Cache { return false; } + public function getNumericStorageId() { + if (isset($this->numericId)) { + return $this->numericId; + } else { + return false; + } + } + /** * get the stored metadata of a file or folder * @@ -182,12 +190,10 @@ class Shared_Cache extends Cache { */ public function move($source, $target) { if ($cache = $this->getSourceCache($source)) { - $targetPath = \OC_Share_Backend_File::getSourcePath(dirname($target)); - if ($targetPath) { - $targetPath .= '/' . basename($target); - $cache->move($this->files[$source], $targetPath); + $file = \OC_Share_Backend_File::getSource($target); + if ($file && isset($file['path'])) { + $cache->move($this->files[$source], $file['path']); } - } } @@ -269,4 +275,17 @@ class Shared_Cache extends Cache { return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); } -} + /** + * find a folder in the cache which has not been fully scanned + * + * If multiply incomplete folders are in the cache, the one with the highest id will be returned, + * use the one with the highest id gives the best result with the background scanner, since that is most + * likely the folder where we stopped scanning previously + * + * @return string|bool the path of the folder or false when no folder matched + */ + public function getIncomplete() { + return false; + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index 6747faa4d4..b6638564cd 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -70,6 +70,28 @@ class Shared_Permissions extends Permissions { return $filePermissions; } + /** + * get the permissions for all files in a folder + * + * @param int $parentId + * @param string $user + * @return int[] + */ + public function getDirectoryPermissions($parentId, $user) { + // Root of the Shared folder + if ($parentId === -1) { + return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_PERMISSIONS); + } + $permissions = $this->get($parentId, $user); + $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `parent` = ?'); + $result = $query->execute(array($parentId)); + $filePermissions = array(); + while ($row = $result->fetchRow()) { + $filePermissions[$row['fileid']] = $permissions; + } + return $filePermissions; + } + /** * remove the permissions for a file * @@ -83,4 +105,5 @@ class Shared_Permissions extends Permissions { public function removeMultiple($fileIds, $user) { // Not a valid action for Shared Permissions } -} + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 6294865180..07e7a4ca0c 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -26,6 +26,7 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { const FORMAT_FILE_APP_ROOT = 2; const FORMAT_OPENDIR = 3; const FORMAT_GET_ALL = 4; + const FORMAT_PERMISSIONS = 5; private $path; @@ -125,6 +126,12 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $ids[] = $item['file_source']; } return $ids; + } else if ($format === self::FORMAT_PERMISSIONS) { + $filePermissions = array(); + foreach ($items as $item) { + $filePermissions[$item['file_source']] = $item['permissions']; + } + return $filePermissions; } return array(); } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index ffd4e5ced2..5c23a9eb0d 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -71,9 +71,9 @@ class Shared extends \OC\Files\Storage\Common { if ($source) { if (!isset($source['fullPath'])) { \OC\Files\Filesystem::initMountPoints($source['fileOwner']); - $mount = \OC\Files\Mount::findByNumericId($source['storage']); - if ($mount) { - $this->files[$target]['fullPath'] = $mount->getMountPoint().$source['path']; + $mount = \OC\Files\Filesystem::getMountByNumericId($source['storage']); + if (is_array($mount)) { + $this->files[$target]['fullPath'] = $mount[key($mount)]->getMountPoint().$source['path']; } else { $this->files[$target]['fullPath'] = false; } diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index c8aca498f8..98d2a84fb6 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -1,8 +1,14 @@ printPage(); + exit(); +} + function fileCmp($a, $b) { if ($a['type'] == 'dir' and $b['type'] != 'dir') { return -1; @@ -39,6 +45,7 @@ if (isset($_GET['t'])) { $fileOwner = $shareOwner; } if (isset($fileOwner)) { + OC_Util::tearDownFS(); OC_Util::setupFS($fileOwner); $path = \OC\Files\Filesystem::getPath($linkItem['file_source']); } @@ -77,7 +84,7 @@ if (isset($path)) { exit(); } else { // Save item id in session for future requests - $_SESSION['public_link_authenticated'] = $linkItem['id']; + \OC::$session->set('public_link_authenticated', $linkItem['id']); } } else { OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type'] @@ -90,8 +97,8 @@ if (isset($path)) { } else { // Check if item id is set in session - if (!isset($_SESSION['public_link_authenticated']) - || $_SESSION['public_link_authenticated'] !== $linkItem['id'] + if ( ! \OC::$session->exists('public_link_authenticated') + || \OC::$session->get('public_link_authenticated') !== $linkItem['id'] ) { // Prompt for password $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php index e83d3b8fbb..3b1e0ac30c 100644 --- a/apps/files_trashbin/appinfo/app.php +++ b/apps/files_trashbin/appinfo/app.php @@ -3,7 +3,5 @@ OC::$CLASSPATH['OCA\Files_Trashbin\Hooks'] = 'files_trashbin/lib/hooks.php'; OC::$CLASSPATH['OCA\Files_Trashbin\Trashbin'] = 'files_trashbin/lib/trash.php'; -//Listen to delete file signal -OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Trashbin\Hooks", "remove_hook"); -//Listen to delete user signal -OCP\Util::connectHook('OC_User', 'pre_deleteUser', "OCA\Files_Trashbin\Hooks", "deleteUser_hook"); \ No newline at end of file +// register hooks +\OCA\Files_Trashbin\Trashbin::registerHooks(); \ No newline at end of file diff --git a/apps/files_trashbin/appinfo/info.xml b/apps/files_trashbin/appinfo/info.xml index 7f807da579..e9cbdafc1c 100644 --- a/apps/files_trashbin/appinfo/info.xml +++ b/apps/files_trashbin/appinfo/info.xml @@ -2,7 +2,20 @@ files_trashbin Deleted files - Keep a copy of deleted files so that they can be restored if needed + + ownCloud keeps a copy of your deleted files in case you need them again. + To make sure that the user doesn't run out of memory the deleted files app + manages the size of the deleted files for the user. By default deleted files + stay in the trash bin for 180 days. ownCloud checks the age of the files + every time a new files gets moved to the deleted files and remove all files + older than 180 days. The user can adjust this value in the config.php by + setting the "trashbin_retention_obligation" value. + + Beside that the delted files app take care to never use more that 50% of + your currently available free space. If your deleted files exceed this limit + ownCloud deletes the oldest versions until it meets the memory usage limit + again. + AGPL Bjoern Schiessle true diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 8a5875b9ce..a32b7414ac 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -21,8 +21,7 @@ $dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : ''; $result = array(); if ($dir) { $dirlisting = true; - $fullpath = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($dir); - $dirContent = opendir($fullpath); + $dirContent = $view->opendir($dir); $i = 0; while($entryName = readdir($dirContent)) { if ( $entryName != '.' && $entryName != '..' ) { diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index eed253d660..691642811b 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -93,6 +93,7 @@ $(document).ready(function() { }); $('.undelete').click('click',function(event) { + event.preventDefault(); var spinner = ''; var files=getSelectedFiles('file'); var fileslist = JSON.stringify(files); @@ -117,6 +118,7 @@ $(document).ready(function() { }); $('.delete').click('click',function(event) { + event.preventDefault(); console.log("delete selected"); var spinner = ''; var files=getSelectedFiles('file'); diff --git a/apps/files_trashbin/l10n/bg_BG.php b/apps/files_trashbin/l10n/bg_BG.php index 31c5dcb4ef..288518e1a4 100644 --- a/apps/files_trashbin/l10n/bg_BG.php +++ b/apps/files_trashbin/l10n/bg_BG.php @@ -13,5 +13,6 @@ "{count} files" => "{count} файла", "Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!", "Restore" => "Възтановяване", -"Delete" => "Изтриване" +"Delete" => "Изтриване", +"Deleted Files" => "Изтрити файлове" ); diff --git a/apps/files_trashbin/l10n/cy_GB.php b/apps/files_trashbin/l10n/cy_GB.php new file mode 100644 index 0000000000..055e8d8654 --- /dev/null +++ b/apps/files_trashbin/l10n/cy_GB.php @@ -0,0 +1,18 @@ + "Methwyd dileu %s yn barhaol", +"Couldn't restore %s" => "Methwyd adfer %s", +"perform restore operation" => "gweithrediad adfer", +"Error" => "Gwall", +"delete file permanently" => "dileu ffeil yn barhaol", +"Delete permanently" => "Dileu'n barhaol", +"Name" => "Enw", +"Deleted" => "Wedi dileu", +"1 folder" => "1 blygell", +"{count} folders" => "{count} plygell", +"1 file" => "1 ffeil", +"{count} files" => "{count} ffeil", +"Nothing in here. Your trash bin is empty!" => "Does dim byd yma. Mae eich bin sbwriel yn wag!", +"Restore" => "Adfer", +"Delete" => "Dileu", +"Deleted Files" => "Ffeiliau Ddilewyd" +); diff --git a/apps/files_trashbin/l10n/ia.php b/apps/files_trashbin/l10n/ia.php index 0a51752312..dea25b30ba 100644 --- a/apps/files_trashbin/l10n/ia.php +++ b/apps/files_trashbin/l10n/ia.php @@ -1,4 +1,5 @@ "Error", "Name" => "Nomine", "Delete" => "Deler" ); diff --git a/apps/files_trashbin/l10n/id.php b/apps/files_trashbin/l10n/id.php index e06c66784f..62a63d515a 100644 --- a/apps/files_trashbin/l10n/id.php +++ b/apps/files_trashbin/l10n/id.php @@ -2,13 +2,13 @@ "Couldn't delete %s permanently" => "Tidak dapat menghapus permanen %s", "Couldn't restore %s" => "Tidak dapat memulihkan %s", "perform restore operation" => "jalankan operasi pemulihan", -"Error" => "kesalahan", +"Error" => "Galat", "delete file permanently" => "hapus berkas secara permanen", -"Delete permanently" => "hapus secara permanen", +"Delete permanently" => "Hapus secara permanen", "Name" => "Nama", "Deleted" => "Dihapus", -"1 folder" => "1 map", -"{count} folders" => "{count} map", +"1 folder" => "1 folder", +"{count} folders" => "{count} folder", "1 file" => "1 berkas", "{count} files" => "{count} berkas", "Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!", diff --git a/apps/files_trashbin/l10n/ko.php b/apps/files_trashbin/l10n/ko.php index f06c90962e..42ad87e98d 100644 --- a/apps/files_trashbin/l10n/ko.php +++ b/apps/files_trashbin/l10n/ko.php @@ -1,5 +1,6 @@ "오류", +"Delete permanently" => "영원히 삭제", "Name" => "이름", "1 folder" => "폴더 1개", "{count} folders" => "폴더 {count}개", diff --git a/apps/files_trashbin/l10n/nb_NO.php b/apps/files_trashbin/l10n/nb_NO.php index e1dce4b399..43ad018049 100644 --- a/apps/files_trashbin/l10n/nb_NO.php +++ b/apps/files_trashbin/l10n/nb_NO.php @@ -13,5 +13,6 @@ "{count} files" => "{count} filer", "Nothing in here. Your trash bin is empty!" => "Ingenting her. Søppelkassen din er tom!", "Restore" => "Gjenopprett", -"Delete" => "Slett" +"Delete" => "Slett", +"Deleted Files" => "Slettet filer" ); diff --git a/apps/files_trashbin/l10n/nn_NO.php b/apps/files_trashbin/l10n/nn_NO.php index 14345ddcc4..454ea2b057 100644 --- a/apps/files_trashbin/l10n/nn_NO.php +++ b/apps/files_trashbin/l10n/nn_NO.php @@ -1,5 +1,18 @@ "Klarte ikkje sletta %s for godt", +"Couldn't restore %s" => "Klarte ikkje gjenoppretta %s", +"perform restore operation" => "utfør gjenoppretting", "Error" => "Feil", +"delete file permanently" => "slett fila for godt", +"Delete permanently" => "Slett for godt", "Name" => "Namn", -"Delete" => "Slett" +"Deleted" => "Sletta", +"1 folder" => "1 mappe", +"{count} folders" => "{count} mapper", +"1 file" => "1 fil", +"{count} files" => "{count} filer", +"Nothing in here. Your trash bin is empty!" => "Ingenting her. Papirkorga di er tom!", +"Restore" => "Gjenopprett", +"Delete" => "Slett", +"Deleted Files" => "Sletta filer" ); diff --git a/apps/files_trashbin/l10n/pl.php b/apps/files_trashbin/l10n/pl.php index 7fd1ab21ec..5c9f558f11 100644 --- a/apps/files_trashbin/l10n/pl.php +++ b/apps/files_trashbin/l10n/pl.php @@ -8,9 +8,9 @@ "Name" => "Nazwa", "Deleted" => "Usunięte", "1 folder" => "1 folder", -"{count} folders" => "{count} foldery", +"{count} folders" => "Ilość folderów: {count}", "1 file" => "1 plik", -"{count} files" => "{count} pliki", +"{count} files" => "Ilość plików: {count}", "Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!", "Restore" => "Przywróć", "Delete" => "Usuń", diff --git a/apps/files_trashbin/l10n/pt_PT.php b/apps/files_trashbin/l10n/pt_PT.php index 84a07fb0d0..ba85158b70 100644 --- a/apps/files_trashbin/l10n/pt_PT.php +++ b/apps/files_trashbin/l10n/pt_PT.php @@ -1,7 +1,7 @@ "Não foi possível eliminar %s de forma permanente", "Couldn't restore %s" => "Não foi possível restaurar %s", -"perform restore operation" => "Restaurar", +"perform restore operation" => "executar a operação de restauro", "Error" => "Erro", "delete file permanently" => "Eliminar permanentemente o(s) ficheiro(s)", "Delete permanently" => "Eliminar permanentemente", @@ -11,8 +11,8 @@ "{count} folders" => "{count} pastas", "1 file" => "1 ficheiro", "{count} files" => "{count} ficheiros", -"Nothing in here. Your trash bin is empty!" => "Não ha ficheiros. O lixo está vazio", +"Nothing in here. Your trash bin is empty!" => "Não hà ficheiros. O lixo está vazio!", "Restore" => "Restaurar", -"Delete" => "Apagar", +"Delete" => "Eliminar", "Deleted Files" => "Ficheiros Apagados" ); diff --git a/apps/files_trashbin/l10n/ro.php b/apps/files_trashbin/l10n/ro.php index c03ef600f3..3af21b7e3f 100644 --- a/apps/files_trashbin/l10n/ro.php +++ b/apps/files_trashbin/l10n/ro.php @@ -1,5 +1,6 @@ "Eroare", +"Delete permanently" => "Stergere permanenta", "Name" => "Nume", "1 folder" => "1 folder", "{count} folders" => "{count} foldare", diff --git a/apps/files_trashbin/l10n/ru_RU.php b/apps/files_trashbin/l10n/ru_RU.php index 178eb53107..8636e417ec 100644 --- a/apps/files_trashbin/l10n/ru_RU.php +++ b/apps/files_trashbin/l10n/ru_RU.php @@ -1,18 +1,5 @@ "%s не может быть удалён навсегда", -"Couldn't restore %s" => "%s не может быть восстановлен", -"perform restore operation" => "выполнить операцию восстановления", "Error" => "Ошибка", -"delete file permanently" => "удалить файл навсегда", -"Delete permanently" => "Удалить навсегда", "Name" => "Имя", -"Deleted" => "Удалён", -"1 folder" => "1 папка", -"{count} folders" => "{количество} папок", -"1 file" => "1 файл", -"{count} files" => "{количество} файлов", -"Nothing in here. Your trash bin is empty!" => "Здесь ничего нет. Ваша корзина пуста!", -"Restore" => "Восстановить", -"Delete" => "Удалить", -"Deleted Files" => "Удаленные файлы" +"Delete" => "Удалить" ); diff --git a/apps/files_trashbin/l10n/sk_SK.php b/apps/files_trashbin/l10n/sk_SK.php index 7203f4c75f..7cef36ef1c 100644 --- a/apps/files_trashbin/l10n/sk_SK.php +++ b/apps/files_trashbin/l10n/sk_SK.php @@ -5,7 +5,7 @@ "Error" => "Chyba", "delete file permanently" => "trvalo zmazať súbor", "Delete permanently" => "Zmazať trvalo", -"Name" => "Meno", +"Name" => "Názov", "Deleted" => "Zmazané", "1 folder" => "1 priečinok", "{count} folders" => "{count} priečinkov", diff --git a/apps/files_trashbin/l10n/sr.php b/apps/files_trashbin/l10n/sr.php index 81a65b819f..280c2b0282 100644 --- a/apps/files_trashbin/l10n/sr.php +++ b/apps/files_trashbin/l10n/sr.php @@ -1,6 +1,7 @@ "врати у претходно стање", "Error" => "Грешка", +"Delete permanently" => "Обриши за стално", "Name" => "Име", "Deleted" => "Обрисано", "1 folder" => "1 фасцикла", diff --git a/apps/files_trashbin/l10n/ug.php b/apps/files_trashbin/l10n/ug.php new file mode 100644 index 0000000000..c369e385f7 --- /dev/null +++ b/apps/files_trashbin/l10n/ug.php @@ -0,0 +1,11 @@ + "خاتالىق", +"Delete permanently" => "مەڭگۈلۈك ئۆچۈر", +"Name" => "ئاتى", +"Deleted" => "ئۆچۈرۈلدى", +"1 folder" => "1 قىسقۇچ", +"1 file" => "1 ھۆججەت", +"{count} files" => "{count} ھۆججەت", +"Nothing in here. Your trash bin is empty!" => "بۇ جايدا ھېچنېمە يوق. Your trash bin is empty!", +"Delete" => "ئۆچۈر" +); diff --git a/apps/files_trashbin/l10n/zh_TW.php b/apps/files_trashbin/l10n/zh_TW.php index adfde6c06e..a9dcba8f7d 100644 --- a/apps/files_trashbin/l10n/zh_TW.php +++ b/apps/files_trashbin/l10n/zh_TW.php @@ -1,9 +1,9 @@ "無法永久刪除%s", -"Couldn't restore %s" => "無法復原%s", +"Couldn't delete %s permanently" => "無法永久刪除 %s", +"Couldn't restore %s" => "無法復原 %s", "perform restore operation" => "進行復原動作", "Error" => "錯誤", -"delete file permanently" => "永久刪除文件", +"delete file permanently" => "永久刪除檔案", "Delete permanently" => "永久刪除", "Name" => "名稱", "Deleted" => "已刪除", @@ -11,7 +11,7 @@ "{count} folders" => "{count} 個資料夾", "1 file" => "1 個檔案", "{count} files" => "{count} 個檔案", -"Nothing in here. Your trash bin is empty!" => "這裏沒東西。您的垃圾桶是空的!", +"Nothing in here. Your trash bin is empty!" => "您的垃圾桶是空的!", "Restore" => "復原", "Delete" => "刪除", "Deleted Files" => "已刪除的檔案" diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 9efb041bb9..2d1830a38f 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -29,6 +29,17 @@ class Trashbin { // unit: percentage; 50% of available disk space/quota const DEFAULTMAXSIZE=50; + public static function getUidAndFilename($filename) { + $uid = \OC\Files\Filesystem::getOwner($filename); + \OC\Files\Filesystem::initMountPoints($uid); + if ( $uid != \OCP\User::getUser() ) { + $info = \OC\Files\Filesystem::getFileInfo($filename); + $ownerView = new \OC\Files\View('/'.$uid.'/files'); + $filename = $ownerView->getPath($info['fileid']); + } + return array($uid, $filename); + } + /** * move file to the trash bin * @@ -39,14 +50,15 @@ class Trashbin { $view = new \OC\Files\View('/'. $user); if (!$view->is_dir('files_trashbin')) { $view->mkdir('files_trashbin'); - $view->mkdir("files_trashbin/files"); - $view->mkdir("files_trashbin/versions"); - $view->mkdir("files_trashbin/keyfiles"); + $view->mkdir('files_trashbin/files'); + $view->mkdir('files_trashbin/versions'); + $view->mkdir('files_trashbin/keyfiles'); + $view->mkdir('files_trashbin/share-keys'); } $path_parts = pathinfo($file_path); - $deleted = $path_parts['basename']; + $filename = $path_parts['basename']; $location = $path_parts['dirname']; $timestamp = time(); $mime = $view->getMimeType('files'.$file_path); @@ -61,84 +73,174 @@ class Trashbin { if ( $trashbinSize === false || $trashbinSize < 0 ) { $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin')); } - - $sizeOfAddedFiles = self::copy_recursive($file_path, 'files_trashbin/files/'.$deleted.'.d'.$timestamp, $view); - - if ( $view->file_exists('files_trashbin/files/'.$deleted.'.d'.$timestamp) ) { + + // disable proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + $sizeOfAddedFiles = self::copy_recursive($file_path, 'files_trashbin/files/'.$filename.'.d'.$timestamp, $view); + \OC_FileProxy::$enabled = $proxyStatus; + + if ( $view->file_exists('files_trashbin/files/'.$filename.'.d'.$timestamp) ) { $trashbinSize += $sizeOfAddedFiles; $query = \OC_DB::prepare("INSERT INTO `*PREFIX*files_trash` (`id`,`timestamp`,`location`,`type`,`mime`,`user`) VALUES (?,?,?,?,?,?)"); - $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); + $result = $query->execute(array($filename, $timestamp, $location, $type, $mime, $user)); if ( !$result ) { // if file couldn't be added to the database than also don't store it in the trash bin. - $view->deleteAll('files_trashbin/files/'.$deleted.'.d'.$timestamp); + $view->deleteAll('files_trashbin/files/'.$filename.'.d'.$timestamp); \OC_Log::write('files_trashbin', 'trash bin database couldn\'t be updated', \OC_log::ERROR); return; } \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_moveToTrash', array('filePath' => \OC\Files\Filesystem::normalizePath($file_path), - 'trashPath' => \OC\Files\Filesystem::normalizePath($deleted.'.d'.$timestamp))); - - // Take care of file versions - if ( \OCP\App::isEnabled('files_versions') ) { - if ( $view->is_dir('files_versions/'.$file_path) ) { - $trashbinSize += self::calculateSize(new \OC\Files\View('/'. $user.'/files_versions/'.$file_path)); - $view->rename('files_versions/'.$file_path, 'files_trashbin/versions'. $deleted.'.d'.$timestamp); - } else if ( $versions = \OCA\Files_Versions\Storage::getVersions($user, $file_path) ) { - foreach ($versions as $v) { - $trashbinSize += $view->filesize('files_versions'.$v['path'].'.v'.$v['version']); - $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'files_trashbin/versions/'. $deleted.'.v'.$v['version'].'.d'.$timestamp); - } - } - } - - // Take care of encryption keys - $keyfile = \OC\Files\Filesystem::normalizePath('files_encryption/keyfiles/'.$file_path); - if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile.'.key') ) { - if ( $view->is_dir('files'.$file_path) ) { - $trashbinSize += self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile)); - $view->rename($keyfile, 'files_trashbin/keyfiles/'. $deleted.'.d'.$timestamp); - } else { - $trashbinSize += $view->filesize($keyfile.'.key'); - $view->rename($keyfile.'.key', 'files_trashbin/keyfiles/'. $deleted.'.key.d'.$timestamp); - } - } + 'trashPath' => \OC\Files\Filesystem::normalizePath($filename.'.d'.$timestamp))); + + $trashbinSize += self::retainVersions($view, $file_path, $filename, $timestamp); + $trashbinSize += self::retainEncryptionKeys($view, $file_path, $filename, $timestamp); + } else { \OC_Log::write('files_trashbin', 'Couldn\'t move '.$file_path.' to the trash bin', \OC_log::ERROR); } - - // get available disk space for user - $quota = \OC_Preferences::getValue($user, 'files', 'quota'); - if ( $quota === null || $quota === 'default') { - $quota = \OC_Appconfig::getValue('files', 'default_quota'); - } - if ( $quota === null || $quota === 'none' ) { - $quota = \OC\Files\Filesystem::free_space('/') / count(\OCP\User::getUsers()); - } else { - $quota = \OCP\Util::computerFileSize($quota); - } - - // calculate available space for trash bin - $rootInfo = $view->getFileInfo('/files'); - $free = $quota-$rootInfo['size']; // remaining free space for user - if ( $free > 0 ) { - $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $trashbinSize; // how much space can be used for versions - } else { - $availableSpace = $free-$trashbinSize; - } - $trashbinSize -= self::expire($availableSpace); + + $trashbinSize -= self::expire($trashbinSize); self::setTrashbinSize($user, $trashbinSize); } + /** + * Move file versions to trash so that they can be restored later + * + * @param \OC\Files\View $view + * @param $file_path path to original file + * @param $filename of deleted file + * @param $timestamp when the file was deleted + * + * @return size of stored versions + */ + private static function retainVersions($view, $file_path, $filename, $timestamp) { + $size = 0; + if (\OCP\App::isEnabled('files_versions')) { + + // disable proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $user = \OCP\User::getUser(); + $rootView = new \OC\Files\View('/'); + + list($owner, $ownerPath) = self::getUidAndFilename($file_path); + + if ($rootView->is_dir($owner.'/files_versions/' . $ownerPath)) { + $size += self::calculateSize(new \OC\Files\View('/' . $owner . '/files_versions/' . $ownerPath)); + $rootView->rename($owner.'/files_versions/' . $ownerPath, $user.'/files_trashbin/versions/' . $filename . '.d' . $timestamp); + } else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) { + foreach ($versions as $v) { + $size += $rootView->filesize($owner.'/files_versions' . $v['path'] . '.v' . $v['version']); + $rootView->rename($owner.'/files_versions' . $v['path'] . '.v' . $v['version'], $user.'/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp); + } + } + + // enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + } + + return $size; + } + + /** + * Move encryption keys to trash so that they can be restored later + * + * @param \OC\Files\View $view + * @param $file_path path to original file + * @param $filename of deleted file + * @param $timestamp when the file was deleted + * + * @return size of encryption keys + */ + private static function retainEncryptionKeys($view, $file_path, $filename, $timestamp) { + $size = 0; + + if (\OCP\App::isEnabled('files_encryption')) { + + $user = \OCP\User::getUser(); + $rootView = new \OC\Files\View('/'); + + list($owner, $ownerPath) = self::getUidAndFilename($file_path); + + + // disable proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // retain key files + $keyfile = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/keyfiles/' . $ownerPath); + + if ($rootView->is_dir($keyfile) || $rootView->file_exists($keyfile . '.key')) { + // move keyfiles + if ($rootView->is_dir($keyfile)) { + $size += self::calculateSize(new \OC\Files\View($keyfile)); + $rootView->rename($keyfile, $user.'/files_trashbin/keyfiles/' . $filename . '.d' . $timestamp); + } else { + $size += $rootView->filesize($keyfile . '.key'); + $rootView->rename($keyfile . '.key', $user.'/files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp); + } + } + + // retain share keys + $sharekeys = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/share-keys/' . $ownerPath); + + if ($rootView->is_dir($sharekeys)) { + $size += self::calculateSize(new \OC\Files\View($sharekeys)); + $rootView->rename($sharekeys, $user.'/files_trashbin/share-keys/' . $filename . '.d' . $timestamp); + } else { + // get local path to share-keys + $localShareKeysPath = $rootView->getLocalFile($sharekeys); + + // handle share-keys + $matches = glob(preg_quote($localShareKeysPath).'*.shareKey'); + foreach ($matches as $src) { + // get source file parts + $pathinfo = pathinfo($src); + + // we only want to keep the owners key so we can access the private key + $ownerShareKey = $filename . '.' . $user. '.shareKey'; + + // if we found the share-key for the owner, we need to move it to files_trashbin + if($pathinfo['basename'] == $ownerShareKey) { + + // calculate size + $size += $rootView->filesize($sharekeys. '.' . $user. '.shareKey'); + + // move file + $rootView->rename($sharekeys. '.' . $user. '.shareKey', $user.'/files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp); + } else { + + // calculate size + $size += filesize($src); + + // don't keep other share-keys + unlink($src); + } + } + + } + + // enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + } + return $size; + } /** * restore files from trash bin * @param $file path to the deleted file * @param $filename name of the file * @param $timestamp time when the file was deleted - */ + * + * @return bool + */ public static function restore($file, $filename, $timestamp) { - $user = \OCP\User::getUser(); + + $user = \OCP\User::getUser(); $view = new \OC\Files\View('/'.$user); $trashbinSize = self::getTrashbinSize($user); @@ -176,8 +278,20 @@ class Trashbin { // we need a extension in case a file/dir with the same name already exists $ext = self::getUniqueExtension($location, $filename, $view); $mtime = $view->filemtime($source); - if( $view->rename($source, $target.$ext) ) { - $view->touch($target.$ext, $mtime); + + // disable proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + // restore file + $restoreResult = $view->rename($source, $target.$ext); + + // handle the restore result + if( $restoreResult ) { + $fakeRoot = $view->getRoot(); + $view->chroot('/'.$user.'/files'); + $view->touch('/'.$location.'/'.$filename.$ext, $mtime); + $view->chroot($fakeRoot); \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', array('filePath' => \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext), 'trashPath' => \OC\Files\Filesystem::normalizePath($file))); @@ -186,68 +300,195 @@ class Trashbin { } else { $trashbinSize -= $view->filesize($target.$ext); } - // if versioning app is enabled, copy versions from the trash bin back to the original location - if ( \OCP\App::isEnabled('files_versions') ) { - if ($timestamp ) { - $versionedFile = $filename; - } else { - $versionedFile = $file; - } - if ( $result[0]['type'] === 'dir' ) { - $trashbinSize -= self::calculateSize(new \OC\Files\View('/'.$user.'/'.'files_trashbin/versions/'. $file)); - $view->rename(\OC\Files\Filesystem::normalizePath('files_trashbin/versions/'. $file), \OC\Files\Filesystem::normalizePath('files_versions/'.$location.'/'.$filename.$ext)); - } else if ( $versions = self::getVersionsFromTrash($versionedFile, $timestamp) ) { - foreach ($versions as $v) { - if ($timestamp ) { - $trashbinSize -= $view->filesize('files_trashbin/versions/'.$versionedFile.'.v'.$v.'.d'.$timestamp); - $view->rename('files_trashbin/versions/'.$versionedFile.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); - } else { - $trashbinSize -= $view->filesize('files_trashbin/versions/'.$versionedFile.'.v'.$v); - $view->rename('files_trashbin/versions/'.$versionedFile.'.v'.$v, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); - } - } - } - } - - // Take care of encryption keys TODO! Get '.key' in file between file name and delete date (also for permanent delete!) - $parts = pathinfo($file); - if ( $result[0]['type'] === 'dir' ) { - $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$parts['dirname'].'/'.$filename); - } else { - $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$parts['dirname'].'/'.$filename.'.key'); - } - if ($timestamp) { - $keyfile .= '.d'.$timestamp; - } - if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) { - if ( $result[0]['type'] === 'dir' ) { - $trashbinSize -= self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile)); - $view->rename($keyfile, 'files_encryption/keyfiles/'. $location.'/'.$filename); - } else { - $trashbinSize -= $view->filesize($keyfile); - $view->rename($keyfile, 'files_encryption/keyfiles/'. $location.'/'.$filename.'.key'); - } - } - + + $trashbinSize -= self::restoreVersions($view, $file, $filename, $ext, $location, $timestamp); + $trashbinSize -= self::restoreEncryptionKeys($view, $file, $filename, $ext, $location, $timestamp); + if ( $timestamp ) { $query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=? AND `id`=? AND `timestamp`=?'); $query->execute(array($user,$filename,$timestamp)); } self::setTrashbinSize($user, $trashbinSize); - + + // enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + return true; - } else { - \OC_Log::write('files_trashbin', 'Couldn\'t restore file from trash bin, '.$filename, \OC_log::ERROR); } + // enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + return false; } + /** + * @brief restore versions from trash bin + * + * @param \OC\Files\View $view file view + * @param $file complete path to file + * @param $filename name of file + * @param $ext file extension in case a file with the same $filename already exists + * @param $location location if file + * @param $timestamp deleteion time + * + * @return size of restored versions + */ + private static function restoreVersions($view, $file, $filename, $ext, $location, $timestamp) { + $size = 0; + if (\OCP\App::isEnabled('files_versions')) { + // disable proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + $user = \OCP\User::getUser(); + $rootView = new \OC\Files\View('/'); + + $target = \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext); + + list($owner, $ownerPath) = self::getUidAndFilename($target); + + if ($timestamp) { + $versionedFile = $filename; + } else { + $versionedFile = $file; + } + + if ($view->is_dir('/files_trashbin/versions/'.$file)) { + $size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . 'files_trashbin/versions/' . $file)); + $rootView->rename(\OC\Files\Filesystem::normalizePath($user.'/files_trashbin/versions/' . $file), \OC\Files\Filesystem::normalizePath($owner.'/files_versions/' . $ownerPath)); + } else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp)) { + foreach ($versions as $v) { + if ($timestamp) { + $size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp); + $rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, $owner.'/files_versions/' . $ownerPath . '.v' . $v); + } else { + $size += $view->filesize('files_trashbin/versions/' . $versionedFile . '.v' . $v); + $rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v, $owner.'/files_versions/' . $ownerPath . '.v' . $v); + } + } + } + + // enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + } + return $size; + } + + + /** + * @brief restore encryption keys from trash bin + * + * @param \OC\Files\View $view + * @param $file complete path to file + * @param $filename name of file + * @param $ext file extension in case a file with the same $filename already exists + * @param $location location of file + * @param $timestamp deleteion time + * + * @return size of restored encrypted file + */ + private static function restoreEncryptionKeys($view, $file, $filename, $ext, $location, $timestamp) { + // Take care of encryption keys TODO! Get '.key' in file between file name and delete date (also for permanent delete!) + $size = 0; + if (\OCP\App::isEnabled('files_encryption')) { + $user = \OCP\User::getUser(); + $rootView = new \OC\Files\View('/'); + + $target = \OC\Files\Filesystem::normalizePath('/'.$location.'/'.$filename.$ext); + + list($owner, $ownerPath) = self::getUidAndFilename($target); + + $path_parts = pathinfo($file); + $source_location = $path_parts['dirname']; + + if ($view->is_dir('/files_trashbin/keyfiles/'.$file)) { + if($source_location != '.') { + $keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename); + $sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename); + } else { + $keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $filename); + $sharekey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $filename); + } + } else { + $keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key'); + } + + if ($timestamp) { + $keyfile .= '.d' . $timestamp; + } + + // disable proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + + if ($rootView->file_exists($keyfile)) { + // handle directory + if ($rootView->is_dir($keyfile)) { + + // handle keyfiles + $size += self::calculateSize(new \OC\Files\View($keyfile)); + $rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath); + + // handle share-keys + if ($timestamp) { + $sharekey .= '.d' . $timestamp; + } + $size += self::calculateSize(new \OC\Files\View($sharekey)); + $rootView->rename($sharekey, $owner.'/files_encryption/share-keys/' . $ownerPath); + + } else { + // handle keyfiles + $size += $rootView->filesize($keyfile); + $rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath . '.key'); + + // handle share-keys + $ownerShareKey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey'); + if ($timestamp) { + $ownerShareKey .= '.d' . $timestamp; + } + + $size += $rootView->filesize($ownerShareKey); + + // move only owners key + $rootView->rename($ownerShareKey, $owner.'/files_encryption/share-keys/' . $ownerPath . '.' . $user. '.shareKey'); + + // try to re-share if file is shared + $filesystemView = new \OC_FilesystemView('/'); + $session = new \OCA\Encryption\Session($filesystemView); + $util = new \OCA\Encryption\Util($filesystemView, $user); + + // fix the file size + $absolutePath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files/'. $ownerPath); + $util->fixFileSize($absolutePath); + + // get current sharing state + $sharingEnabled = \OCP\Share::isEnabled(); + + // get the final filename + $target = \OC\Files\Filesystem::normalizePath($location.'/'.$filename); + + // get users sharing this file + $usersSharing = $util->getSharingUsersArray($sharingEnabled, $target.$ext, $user); + + // Attempt to set shareKey + $util->setSharedFileKeyfiles($session, $usersSharing, $target.$ext); + } + } + + // enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + } + return $size; + } + /** - * delete file from trash bin permanently + * @brief delete file from trash bin permanently + * * @param $filename path to the file * @param $timestamp of deletion time + * * @return size of deleted files */ public static function delete($filename, $timestamp=null) { @@ -268,7 +509,25 @@ class Trashbin { $file = $filename; } + $size += self::deleteVersions($view, $file, $filename, $timestamp); + $size += self::deleteEncryptionKeys($view, $file, $filename, $timestamp); + + if ($view->is_dir('/files_trashbin/files/'.$file)) { + $size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file)); + } else { + $size += $view->filesize('/files_trashbin/files/'.$file); + } + $view->unlink('/files_trashbin/files/'.$file); + $trashbinSize -= $size; + self::setTrashbinSize($user, $trashbinSize); + + return $size; + } + + private static function deleteVersions($view, $file, $filename, $timestamp) { + $size = 0; if ( \OCP\App::isEnabled('files_versions') ) { + $user = \OCP\User::getUser(); if ($view->is_dir('files_trashbin/versions/'.$file)) { $size += self::calculateSize(new \OC\Files\view('/'.$user.'/files_trashbin/versions/'.$file)); $view->unlink('files_trashbin/versions/'.$file); @@ -284,35 +543,37 @@ class Trashbin { } } } - - // Take care of encryption keys - $parts = pathinfo($file); - if ( $view->is_dir('/files_trashbin/files/'.$file) ) { - $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename); - } else { - $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/'.$filename.'.key'); - } - if ($timestamp) { - $keyfile .= '.d'.$timestamp; - } - if ( \OCP\App::isEnabled('files_encryption') && $view->file_exists($keyfile) ) { - if ( $view->is_dir($keyfile) ) { - $size += self::calculateSize(new \OC\Files\View('/'.$user.'/'.$keyfile)); + return $size; + } + + private static function deleteEncryptionKeys($view, $file, $filename, $timestamp) { + $size = 0; + if (\OCP\App::isEnabled('files_encryption')) { + $user = \OCP\User::getUser(); + + if ($view->is_dir('/files_trashbin/files/' . $file)) { + $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename); + $sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename); } else { - $size += $view->filesize($keyfile); + $keyfile = \OC\Files\Filesystem::normalizePath('files_trashbin/keyfiles/' . $filename . '.key'); + $sharekeys = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename . '.' . $user . '.shareKey'); + } + if ($timestamp) { + $keyfile .= '.d' . $timestamp; + $sharekeys .= '.d' . $timestamp; + } + if ($view->file_exists($keyfile)) { + if ($view->is_dir($keyfile)) { + $size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile)); + $size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys)); + } else { + $size += $view->filesize($keyfile); + $size += $view->filesize($sharekeys); + } + $view->unlink($keyfile); + $view->unlink($sharekeys); } - $view->unlink($keyfile); } - - if ($view->is_dir('/files_trashbin/files/'.$file)) { - $size += self::calculateSize(new \OC\Files\View('/'.$user.'/files_trashbin/files/'.$file)); - } else { - $size += $view->filesize('/files_trashbin/files/'.$file); - } - $view->unlink('/files_trashbin/files/'.$file); - $trashbinSize -= $size; - self::setTrashbinSize($user, $trashbinSize); - return $size; } @@ -353,13 +614,52 @@ class Trashbin { } /** - * clean up the trash bin - * @param max. available disk space for trashbin + * calculate remaining free space for trash bin + * + * @param $trashbinSize current size of the trash bin + * @return available free space for trash bin */ - private static function expire($availableSpace) { + private static function calculateFreeSpace($trashbinSize) { + $softQuota = true; + $user = \OCP\User::getUser(); + $quota = \OC_Preferences::getValue($user, 'files', 'quota'); + $view = new \OC\Files\View('/'.$user); + if ( $quota === null || $quota === 'default') { + $quota = \OC_Appconfig::getValue('files', 'default_quota'); + } + if ( $quota === null || $quota === 'none' ) { + $quota = \OC\Files\Filesystem::free_space('/'); + $softQuota = false; + } else { + $quota = \OCP\Util::computerFileSize($quota); + } + + // calculate available space for trash bin + // subtract size of files and current trash bin size from quota + if ($softQuota) { + $rootInfo = $view->getFileInfo('/files/'); + $free = $quota-$rootInfo['size']; // remaining free space for user + if ( $free > 0 ) { + $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $trashbinSize; // how much space can be used for versions + } else { + $availableSpace = $free-$trashbinSize; + } + } else { + $availableSpace = $quota; + } + + return $availableSpace; + } + + /** + * clean up the trash bin + * @param current size of the trash bin + */ + private static function expire($trashbinSize) { $user = \OCP\User::getUser(); $view = new \OC\Files\View('/'.$user); + $availableSpace = self::calculateFreeSpace($trashbinSize); $size = 0; $query = \OC_DB::prepare('SELECT `location`,`type`,`id`,`timestamp` FROM `*PREFIX*files_trash` WHERE `user`=?'); @@ -375,6 +675,7 @@ class Trashbin { $filename = $r['id']; if ( $r['timestamp'] < $limit ) { $size += self::delete($filename, $timestamp); + \OC_Log::write('files_trashbin', 'remove "'.$filename.'" fom trash bin because it is older than '.$retention_obligation, \OC_log::INFO); } } $availableSpace = $availableSpace + $size; @@ -387,6 +688,7 @@ class Trashbin { $i = 0; while ( $i < $length && $availableSpace < 0 ) { $tmp = self::delete($result[$i]['id'], $result[$i]['timestamp']); + \OC_Log::write('files_trashbin', 'remove "'.$result[$i]['id'].'" ('.$tmp.'B) to meet the limit of trash bin size (50% of available quota)', \OC_log::INFO); $availableSpace += $tmp; $size += $tmp; $i++; @@ -531,5 +833,14 @@ class Trashbin { } $query->execute(array($size, $user)); } - + + /** + * register hooks + */ + public static function registerHooks() { + //Listen to delete file signal + \OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Trashbin\Hooks", "remove_hook"); + //Listen to delete user signal + \OCP\Util::connectHook('OC_User', 'pre_deleteUser', "OCA\Files_Trashbin\Hooks", "deleteUser_hook"); + } } diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml index 44878da5e4..661d64aa97 100644 --- a/apps/files_versions/appinfo/info.xml +++ b/apps/files_versions/appinfo/info.xml @@ -6,7 +6,25 @@ Frank Karlitschek 4.93 true - Versioning of files + + ownCloud supports simple version control for files. The versioning app + expires old versions automatically to make sure that + the user doesn't run out of space. Following pattern is used to delete + old versions: + For the first 10 seconds ownCloud keeps one version every 2 seconds; + For the first hour ownCloud keeps one version every minute; + For the first 24 hours ownCloud keeps one version every hour; + For the first 30 days ownCloud keeps one version every day; + After the first 30 days ownCloud keeps one version every week. + + The versions are adjusted along this pattern every time a new version gets + created. + + Beside that the version app takes care to never use more that 50% of the users + currently available free space. If the stored versions exceed this limit + ownCloud deletes the oldest versions until it meets the memory usage limit + again. + diff --git a/apps/files_versions/l10n/bg_BG.php b/apps/files_versions/l10n/bg_BG.php index 6a1882c2bf..a03d9adcf0 100644 --- a/apps/files_versions/l10n/bg_BG.php +++ b/apps/files_versions/l10n/bg_BG.php @@ -1,3 +1,5 @@ "успешно", +"File %s was reverted to version %s" => "Файлът %s бе върнат към версия %s", "Versions" => "Версии" ); diff --git a/apps/files_versions/l10n/et_EE.php b/apps/files_versions/l10n/et_EE.php index 930cfbc33a..c8d2f7cfac 100644 --- a/apps/files_versions/l10n/et_EE.php +++ b/apps/files_versions/l10n/et_EE.php @@ -7,5 +7,5 @@ "No old versions available" => "Vanu versioone pole saadaval", "No path specified" => "Asukohta pole määratud", "Versions" => "Versioonid", -"Revert a file to a previous version by clicking on its revert button" => "Taasta fail varasemale versioonile klikkides \"Revert\" nupule" +"Revert a file to a previous version by clicking on its revert button" => "Taasta fail varasemale versioonile klikkides nupule \"Taasta\"" ); diff --git a/apps/files_versions/l10n/he.php b/apps/files_versions/l10n/he.php index 9eb4df6485..ad2e261d53 100644 --- a/apps/files_versions/l10n/he.php +++ b/apps/files_versions/l10n/he.php @@ -1,5 +1,3 @@ "היסטוריה", -"Files Versioning" => "שמירת הבדלי גרסאות של קבצים", -"Enable" => "הפעלה" +"Versions" => "גרסאות" ); diff --git a/apps/files_versions/l10n/ku_IQ.php b/apps/files_versions/l10n/ku_IQ.php index db5dbad49f..9132caf75e 100644 --- a/apps/files_versions/l10n/ku_IQ.php +++ b/apps/files_versions/l10n/ku_IQ.php @@ -1,5 +1,3 @@ "مێژوو", -"Files Versioning" => "وه‌شانی په‌ڕگه", -"Enable" => "چالاککردن" +"Versions" => "وه‌شان" ); diff --git a/apps/files_versions/l10n/nb_NO.php b/apps/files_versions/l10n/nb_NO.php index 18c7250610..df59dfe4c8 100644 --- a/apps/files_versions/l10n/nb_NO.php +++ b/apps/files_versions/l10n/nb_NO.php @@ -1,5 +1,3 @@ "Historie", -"Files Versioning" => "Fil versjonering", -"Enable" => "Aktiver" +"Versions" => "Versjoner" ); diff --git a/apps/files_versions/l10n/nn_NO.php b/apps/files_versions/l10n/nn_NO.php new file mode 100644 index 0000000000..940cc2371a --- /dev/null +++ b/apps/files_versions/l10n/nn_NO.php @@ -0,0 +1,11 @@ + "Klarte ikkje å tilbakestilla: %s", +"success" => "vellukka", +"File %s was reverted to version %s" => "Tilbakestilte fila %s til utgåva %s", +"failure" => "feil", +"File %s could not be reverted to version %s" => "Klarte ikkje tilbakestilla fila %s til utgåva %s", +"No old versions available" => "Ingen eldre utgåver tilgjengelege", +"No path specified" => "Ingen sti gjeve", +"Versions" => "Utgåver", +"Revert a file to a previous version by clicking on its revert button" => "Tilbakestill ei fil til ei tidlegare utgåve ved å klikka tilbakestill-knappen" +); diff --git a/apps/files_versions/l10n/ro.php b/apps/files_versions/l10n/ro.php index 7dfaee3672..cd9fc89dcc 100644 --- a/apps/files_versions/l10n/ro.php +++ b/apps/files_versions/l10n/ro.php @@ -1,5 +1,11 @@ "Istoric", -"Files Versioning" => "Versionare fișiere", -"Enable" => "Activare" +"Could not revert: %s" => "Nu a putut reveni: %s", +"success" => "success", +"File %s was reverted to version %s" => "Fisierul %s a revenit la versiunea %s", +"failure" => "eșec", +"File %s could not be reverted to version %s" => "Fisierele %s nu au putut reveni la versiunea %s", +"No old versions available" => "Versiunile vechi nu sunt disponibile", +"No path specified" => "Nici un dosar specificat", +"Versions" => "Versiuni", +"Revert a file to a previous version by clicking on its revert button" => "Readuceti un fișier la o versiune anterioară, făcând clic pe butonul revenire" ); diff --git a/apps/files_versions/l10n/si_LK.php b/apps/files_versions/l10n/si_LK.php index 37debf869b..c7ee63d8ef 100644 --- a/apps/files_versions/l10n/si_LK.php +++ b/apps/files_versions/l10n/si_LK.php @@ -1,5 +1,3 @@ "ඉතිහාසය", -"Files Versioning" => "ගොනු අනුවාදයන්", -"Enable" => "සක්‍රිය කරන්න" +"Versions" => "අනුවාද" ); diff --git a/apps/files_versions/l10n/ta_LK.php b/apps/files_versions/l10n/ta_LK.php index aca76dcc26..61a47e42f0 100644 --- a/apps/files_versions/l10n/ta_LK.php +++ b/apps/files_versions/l10n/ta_LK.php @@ -1,5 +1,3 @@ "வரலாறு", -"Files Versioning" => "கோப்பு பதிப்புகள்", -"Enable" => "இயலுமைப்படுத்துக" +"Versions" => "பதிப்புகள்" ); diff --git a/apps/files_versions/l10n/th_TH.php b/apps/files_versions/l10n/th_TH.php index e1e996903a..2998f74838 100644 --- a/apps/files_versions/l10n/th_TH.php +++ b/apps/files_versions/l10n/th_TH.php @@ -1,5 +1,3 @@ "ประวัติ", -"Files Versioning" => "การกำหนดเวอร์ชั่นของไฟล์", -"Enable" => "เปิดใช้งาน" +"Versions" => "รุ่น" ); diff --git a/apps/files_versions/l10n/ug.php b/apps/files_versions/l10n/ug.php new file mode 100644 index 0000000000..024f326b03 --- /dev/null +++ b/apps/files_versions/l10n/ug.php @@ -0,0 +1,9 @@ + "ئەسلىگە قايتۇرالمايدۇ: %s", +"success" => "مۇۋەپپەقىيەتلىك", +"File %s was reverted to version %s" => "ھۆججەت %s نى %s نەشرىگە ئەسلىگە قايتۇردى", +"failure" => "مەغلۇپ بولدى", +"No old versions available" => "كونا نەشرى يوق", +"No path specified" => "يول بەلگىلەنمىگەن", +"Versions" => "نەشرى" +); diff --git a/apps/files_versions/l10n/vi.php b/apps/files_versions/l10n/vi.php index f2499e7bf3..33b045f2e3 100644 --- a/apps/files_versions/l10n/vi.php +++ b/apps/files_versions/l10n/vi.php @@ -6,5 +6,6 @@ "File %s could not be reverted to version %s" => "File %s không thể khôi phục về phiên bản %s", "No old versions available" => "Không có phiên bản cũ nào", "No path specified" => "Không chỉ ra đường dẫn rõ ràng", +"Versions" => "Phiên bản", "Revert a file to a previous version by clicking on its revert button" => "Khôi phục một file về phiên bản trước đó bằng cách click vào nút Khôi phục tương ứng" ); diff --git a/apps/files_versions/l10n/zh_TW.php b/apps/files_versions/l10n/zh_TW.php index a191d59452..2ae9ce657c 100644 --- a/apps/files_versions/l10n/zh_TW.php +++ b/apps/files_versions/l10n/zh_TW.php @@ -5,7 +5,7 @@ "failure" => "失敗", "File %s could not be reverted to version %s" => "檔案 %s 無法復原至版本 %s", "No old versions available" => "沒有舊的版本", -"No path specified" => "沒有指定路線", +"No path specified" => "沒有指定路徑", "Versions" => "版本", -"Revert a file to a previous version by clicking on its revert button" => "按一按復原的按鈕,就能把一個檔案復原至以前的版本" +"Revert a file to a previous version by clicking on its revert button" => "按一下復原的按鈕即可把檔案復原至以前的版本" ); diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index 47d044873a..4beb9e0fe5 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -113,8 +113,16 @@ class Storage { mkdir($versionsFolderName.'/'.$info['dirname'], 0750, true); } + // disable proxy to prevent multiple fopen calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + // store a new version of a file $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename)); + + // reset proxy state + \OC_FileProxy::$enabled = $proxyStatus; + $versionsSize = self::getVersionsSize($uid); if ( $versionsSize === false || $versionsSize < 0 ) { $versionsSize = self::calculateSize($uid); @@ -184,24 +192,34 @@ class Storage { /** * rollback to an old version of a file. */ - public static function rollback($filename, $revision) { + public static function rollback($file, $revision) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - list($uid, $filename) = self::getUidAndFilename($filename); + list($uid, $filename) = self::getUidAndFilename($file); $users_view = new \OC\Files\View('/'.$uid); + $files_view = new \OC\Files\View('/'.\OCP\User::getUser().'/files'); $versionCreated = false; //first create a new version $version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename); if ( !$users_view->file_exists($version)) { + + // disable proxy to prevent multiple fopen calls + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename)); + + // reset proxy state + \OC_FileProxy::$enabled = $proxyStatus; + $versionCreated = true; } // rollback - if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) { - $users_view->touch('files'.$filename, $revision); - Storage::expire($filename); + if( @$users_view->rename('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) { + $files_view->touch($file, $revision); + Storage::expire($file); return true; }else if ( $versionCreated ) { @@ -378,12 +396,14 @@ class Storage { $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); // get available disk space for user + $softQuota = true; $quota = \OC_Preferences::getValue($uid, 'files', 'quota'); if ( $quota === null || $quota === 'default') { $quota = \OC_Appconfig::getValue('files', 'default_quota'); } if ( $quota === null || $quota === 'none' ) { - $quota = \OC\Files\Filesystem::free_space('/') / count(\OCP\User::getUsers()); + $quota = \OC\Files\Filesystem::free_space('/'); + $softQuota = false; } else { $quota = \OCP\Util::computerFileSize($quota); } @@ -397,15 +417,21 @@ class Storage { } // calculate available space for version history - $files_view = new \OC\Files\View('/'.$uid.'/files'); - $rootInfo = $files_view->getFileInfo('/'); - $free = $quota-$rootInfo['size']; // remaining free space for user - if ( $free > 0 ) { - $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions + // subtract size of files and current versions size from quota + if ($softQuota) { + $files_view = new \OC\Files\View('/'.$uid.'/files'); + $rootInfo = $files_view->getFileInfo('/'); + $free = $quota-$rootInfo['size']; // remaining free space for user + if ( $free > 0 ) { + $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions + } else { + $availableSpace = $free-$versionsSize; + } } else { - $availableSpace = $free-$versionsSize; + $availableSpace = $quota; } + // after every 1000s run reduce the number of all versions not only for the current file $random = rand(0, 1000); if ($random == 0) { diff --git a/apps/files_versions/templates/history.php b/apps/files_versions/templates/history.php index f728443904..3a6d5f0c9e 100644 --- a/apps/files_versions/templates/history.php +++ b/apps/files_versions/templates/history.php @@ -5,18 +5,18 @@ if( isset( $_['message'] ) ) { - if( isset($_['path'] ) ) print_unescaped('File: '.OC_Util::sanitizeHTML($_['path'])).'
'; - print_unescaped(''.OC_Util::sanitizeHTML($_['message']) ).'
'; + if( isset($_['path'] ) ) print_unescaped('File: '.OC_Util::sanitizeHTML($_['path']).'
'); + print_unescaped(''.OC_Util::sanitizeHTML($_['message']) .'
'); }else{ if( isset( $_['outcome_stat'] ) ) { - print_unescaped( '

'.OC_Util::sanitizeHTML($_['outcome_msg']) ).'


'; + print_unescaped( '

'.OC_Util::sanitizeHTML($_['outcome_msg']).'


'); } - print_unescaped( 'Versions of '.OC_Util::sanitizeHTML($_['path']) ).'
'; + print_unescaped( 'Versions of '.OC_Util::sanitizeHTML($_['path']).'
'); print_unescaped('

'.OC_Util::sanitizeHTML($l->t('Revert a file to a previous version by clicking on its revert button')).'


'); foreach ( $_['versions'] as $v ) { diff --git a/apps/user_ldap/ajax/clearMappings.php b/apps/user_ldap/ajax/clearMappings.php new file mode 100644 index 0000000000..5dab39839b --- /dev/null +++ b/apps/user_ldap/ajax/clearMappings.php @@ -0,0 +1,35 @@ +. + * + */ + +// Check user and app status +OCP\JSON::checkAdminUser(); +OCP\JSON::checkAppEnabled('user_ldap'); +OCP\JSON::callCheck(); + +$subject = $_POST['ldap_clear_mapping']; +if(\OCA\user_ldap\lib\Helper::clearMapping($subject)) { + OCP\JSON::success(); +} else { + $l=OC_L10N::get('user_ldap'); + OCP\JSON::error(array('message' => $l->t('Failed to clear the mappings.'))); +} \ No newline at end of file diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index 89410b5ef0..593e846bc0 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -24,7 +24,7 @@ OCP\App::registerAdmin('user_ldap', 'settings'); $configPrefixes = OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes(true); -if(count($configPrefixes) == 1) { +if(count($configPrefixes) === 1) { $connector = new OCA\user_ldap\lib\Connection($configPrefixes[0]); $userBackend = new OCA\user_ldap\USER_LDAP(); $userBackend->setConnector($connector); @@ -49,7 +49,7 @@ $entry = array( 'name' => 'LDAP' ); -OCP\Backgroundjob::addRegularTask('OCA\user_ldap\lib\Jobs', 'updateGroups'); +OCP\Backgroundjob::registerJob('OCA\user_ldap\lib\Jobs'); if(OCP\App::isEnabled('user_webdavauth')) { OCP\Util::writeLog('user_ldap', 'user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour', diff --git a/apps/user_ldap/appinfo/install.php b/apps/user_ldap/appinfo/install.php index 378957ec40..c0c33a25c7 100644 --- a/apps/user_ldap/appinfo/install.php +++ b/apps/user_ldap/appinfo/install.php @@ -1,6 +1,6 @@ getUUID($newDN); //fix home folder to avoid new ones depending on the configuration $userBE->getHome($dn['owncloud_name']); diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css index 84ada0832a..185952e14b 100644 --- a/apps/user_ldap/css/settings.css +++ b/apps/user_ldap/css/settings.css @@ -11,6 +11,10 @@ display: inline-block; } +.ldapIndent { + margin-left: 50px; +} + .ldapwarning { margin-left: 1.4em; color: #FF3B3B; diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 432ddd215d..04ff392f92 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -66,7 +66,7 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { //extra work if we don't get back user DNs //TODO: this can be done with one LDAP query - if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { + if(strtolower($this->connection->ldapGroupMemberAssocAttr) === 'memberuid') { $dns = array(); foreach($members as $mid) { $filter = str_replace('%uid', $mid, $this->connection->ldapLoginFilter); @@ -108,11 +108,11 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { } //uniqueMember takes DN, memberuid the uid, so we need to distinguish - if((strtolower($this->connection->ldapGroupMemberAssocAttr) == 'uniquemember') - || (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'member') + if((strtolower($this->connection->ldapGroupMemberAssocAttr) === 'uniquemember') + || (strtolower($this->connection->ldapGroupMemberAssocAttr) === 'member') ) { $uid = $userDN; - } else if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { + } else if(strtolower($this->connection->ldapGroupMemberAssocAttr) === 'memberuid') { $result = $this->readAttribute($userDN, 'uid'); $uid = $result[0]; } else { @@ -157,7 +157,7 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { return $groupUsers; } - if($limit == -1) { + if($limit === -1) { $limit = null; } $groupDN = $this->groupname2dn($gid); @@ -175,7 +175,7 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { } $groupUsers = array(); - $isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid'); + $isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) === 'memberuid'); foreach($members as $member) { if($isMemberUid) { //we got uids, need to get their DNs to 'tranlsate' them to usernames diff --git a/apps/user_ldap/group_proxy.php b/apps/user_ldap/group_proxy.php index 68d2efe387..75e7cd4633 100644 --- a/apps/user_ldap/group_proxy.php +++ b/apps/user_ldap/group_proxy.php @@ -76,8 +76,15 @@ class Group_Proxy extends lib\Proxy implements \OCP\GroupInterface { if(isset($this->backends[$prefix])) { $result = call_user_func_array(array($this->backends[$prefix], $method), $parameters); if(!$result) { - //not found here, reset cache to null - $this->writeToCache($cacheKey, null); + //not found here, reset cache to null if group vanished + //because sometimes methods return false with a reason + $groupExists = call_user_func_array( + array($this->backends[$prefix], 'groupExists'), + array($gid) + ); + if(!$groupExists) { + $this->writeToCache($cacheKey, null); + } } return $result; } diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index e34849ec88..52d5dbc48d 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -8,13 +8,13 @@ var LdapConfiguration = { OC.filePath('user_ldap','ajax','getConfiguration.php'), $('#ldap_serverconfig_chooser').serialize(), function (result) { - if(result.status == 'success') { + if(result.status === 'success') { $.each(result.configuration, function(configkey, configvalue) { elementID = '#'+configkey; //deal with Checkboxes if($(elementID).is('input[type=checkbox]')) { - if(configvalue == 1) { + if(parseInt(configvalue) === 1) { $(elementID).attr('checked', 'checked'); } else { $(elementID).removeAttr('checked'); @@ -37,13 +37,13 @@ var LdapConfiguration = { resetDefaults: function() { $('#ldap').find('input[type=text], input[type=number], input[type=password], textarea, select').each(function() { - if($(this).attr('id') == 'ldap_serverconfig_chooser') { + if($(this).attr('id') === 'ldap_serverconfig_chooser') { return; } $(this).val($(this).attr('data-default')); }); $('#ldap').find('input[type=checkbox]').each(function() { - if($(this).attr('data-default') == 1) { + if($(this).attr('data-default') === 1) { $(this).attr('checked', 'checked'); } else { $(this).removeAttr('checked'); @@ -56,7 +56,7 @@ var LdapConfiguration = { OC.filePath('user_ldap','ajax','deleteConfiguration.php'), $('#ldap_serverconfig_chooser').serialize(), function (result) { - if(result.status == 'success') { + if(result.status === 'success') { $('#ldap_serverconfig_chooser option:selected').remove(); $('#ldap_serverconfig_chooser option:first').select(); LdapConfiguration.refreshConfig(); @@ -74,7 +74,7 @@ var LdapConfiguration = { $.post( OC.filePath('user_ldap','ajax','getNewServerConfigPrefix.php'), function (result) { - if(result.status == 'success') { + if(result.status === 'success') { if(doNotAsk) { LdapConfiguration.resetDefaults(); } else { @@ -99,6 +99,26 @@ var LdapConfiguration = { } } ); + }, + + clearMappings: function(mappingSubject) { + $.post( + OC.filePath('user_ldap','ajax','clearMappings.php'), + 'ldap_clear_mapping='+mappingSubject, + function(result) { + if(result.status == 'success') { + OC.dialogs.info( + t('user_ldap', 'mappings cleared'), + t('user_ldap', 'Success') + ); + } else { + OC.dialogs.alert( + result.message, + t('user_ldap', 'Error') + ); + } + } + ); } } @@ -115,7 +135,7 @@ $(document).ready(function() { OC.filePath('user_ldap','ajax','testConfiguration.php'), $('#ldap').serialize(), function (result) { - if (result.status == 'success') { + if (result.status === 'success') { OC.dialogs.alert( result.message, t('user_ldap', 'Connection test succeeded') @@ -150,7 +170,7 @@ $(document).ready(function() { $('#ldap').serialize(), function (result) { bgcolor = $('#ldap_submit').css('background'); - if (result.status == 'success') { + if (result.status === 'success') { //the dealing with colors is a but ugly, but the jQuery version in use has issues with rgba colors $('#ldap_submit').css('background', '#fff'); $('#ldap_submit').effect('highlight', {'color':'#A8FA87'}, 5000, function() { @@ -166,9 +186,19 @@ $(document).ready(function() { ); }); + $('#ldap_action_clear_user_mappings').click(function(event) { + event.preventDefault(); + LdapConfiguration.clearMappings('user'); + }); + + $('#ldap_action_clear_group_mappings').click(function(event) { + event.preventDefault(); + LdapConfiguration.clearMappings('group'); + }); + $('#ldap_serverconfig_chooser').change(function(event) { value = $('#ldap_serverconfig_chooser option:selected:first').attr('value'); - if(value == 'NEW') { + if(value === 'NEW') { LdapConfiguration.addConfiguration(false); } else { LdapConfiguration.refreshConfig(); diff --git a/apps/user_ldap/l10n/ar.php b/apps/user_ldap/l10n/ar.php index 4d7b7ac4ad..5f8b6b8145 100644 --- a/apps/user_ldap/l10n/ar.php +++ b/apps/user_ldap/l10n/ar.php @@ -1,5 +1,6 @@ "فشل الحذف", +"Error" => "خطأ", "Password" => "كلمة المرور", "Help" => "المساعدة" ); diff --git a/apps/user_ldap/l10n/bg_BG.php b/apps/user_ldap/l10n/bg_BG.php index c064534a6b..0330046d80 100644 --- a/apps/user_ldap/l10n/bg_BG.php +++ b/apps/user_ldap/l10n/bg_BG.php @@ -1,4 +1,5 @@ "Грешка", "Password" => "Парола", "Help" => "Помощ" ); diff --git a/apps/user_ldap/l10n/bn_BD.php b/apps/user_ldap/l10n/bn_BD.php index 69dfc89617..4cee35777d 100644 --- a/apps/user_ldap/l10n/bn_BD.php +++ b/apps/user_ldap/l10n/bn_BD.php @@ -1,4 +1,5 @@ "সমস্যা", "Host" => "হোস্ট", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL আবশ্যক না হলে আপনি এই প্রটোকলটি মুছে ফেলতে পারেন । এরপর শুরু করুন এটা দিয়ে ldaps://", "Base DN" => "ভিত্তি DN", diff --git a/apps/user_ldap/l10n/ca.php b/apps/user_ldap/l10n/ca.php index abdecb164e..7f0849b238 100644 --- a/apps/user_ldap/l10n/ca.php +++ b/apps/user_ldap/l10n/ca.php @@ -1,4 +1,5 @@ "Ha fallat en eliminar els mapatges", "Failed to delete the server configuration" => "Ha fallat en eliminar la configuració del servidor", "The configuration is valid and the connection could be established!" => "La configuració és vàlida i s'ha pogut establir la comunicació!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuració és vàlida, però ha fallat el Bind. Comproveu les credencials i l'arranjament del servidor.", @@ -7,6 +8,9 @@ "Take over settings from recent server configuration?" => "Voleu prendre l'arranjament de la configuració actual del servidor?", "Keep settings?" => "Voleu mantenir la configuració?", "Cannot add server configuration" => "No es pot afegir la configuració del servidor", +"mappings cleared" => "s'han eliminat els mapatges", +"Success" => "Èxit", +"Error" => "Error", "Connection test succeeded" => "La prova de connexió ha reeixit", "Connection test failed" => "La prova de connexió ha fallat", "Do you really want to delete the current Server Configuration?" => "Voleu eliminar la configuració actual del servidor?", @@ -15,7 +19,7 @@ "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Avís: El mòdul PHP LDAP no està instal·lat, el dorsal no funcionarà. Demaneu a l'administrador del sistema que l'instal·li.", "Server configuration" => "Configuració del servidor", "Add Server Configuration" => "Afegeix la configuració del servidor", -"Host" => "Màquina", +"Host" => "Equip remot", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Podeu ometre el protocol, excepte si requeriu SSL. Llavors comenceu amb ldaps://", "Base DN" => "DN Base", "One Base DN per line" => "Una DN Base per línia", @@ -70,6 +74,16 @@ "Email Field" => "Camp de correu electrònic", "User Home Folder Naming Rule" => "Norma per anomenar la carpeta arrel d'usuari", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixeu-ho buit pel nom d'usuari (per defecte). Altrament, especifiqueu un atribut LDAP/AD.", +"Internal Username" => "Nom d'usuari intern", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "Per defecte el nom d'usuari intern es crearà a partir de l'atribut UUID. Això assegura que el nom d'usuari és únic i que els caràcters no s'han de convertir. El nom d'usuari intern té la restricció que només estan permesos els caràcters: [ a-zA-Z0-9_.@- ]. Els altres caràcters es substitueixen pel seu corresponent ASCII o simplement s'ometen. En cas de col·lisió s'incrementa/decrementa en un. El nom d'usuari intern s'utilitza per identificar un usuari internament. També és el nom per defecte de la carpeta home a ownCloud. És també un port de URLs remotes, per exemple tots els serveis *DAV. Amb aquest arranjament es pot variar el comportament per defecte. Per obtenir un comportament similar al d'abans de ownCloud 5, escriviu el nom d'usuari a mostrar en el camp següent. Deixei-lo en blanc si preferiu el comportament per defecte. Els canvis tindran efecte només en els nous usuaris LDAP mapats (afegits).", +"Internal Username Attribute:" => "Atribut nom d'usuari intern:", +"Override UUID detection" => "Sobrescriu la detecció UUID", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "Per defecte, owncloud autodetecta l'atribut UUID. L'atribut UUID s'utilitza per identificar usuaris i grups de forma indubtable. També el nom d'usuari intern es crearà en base a la UUIS, si no heu especificat res diferent a dalt. Podeu sobreescriure l'arranjament i passar l'atribut que desitgeu. Heu d'assegurar-vos que l'atribut que escolliu pot ser recollit tant pels usuaris com pels grups i que és únic. Deixeu-ho en blanc si preferiu el comportament per defecte. els canvis s'aplicaran en els usuaris i grups LDAP mapats de nou (afegits).", +"UUID Attribute:" => "Atribut UUID:", +"Username-LDAP User Mapping" => "Mapatge d'usuari Nom d'usuari-LDAP", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloud utilitza els noms d'usuari per emmagatzemar i assignar (meta)dades. per tal d'identificar usuaris de forma precisa, cada usuari LDAP tindrà un nom d'usuari intern. Això requereix un mapatge del nom d'usuari ownCloud a l'usuari LDAP. El nom d'usuari creat es mapa a la UUID de l'usuari LDAP. Addicionalment, la DN es desa a la memòria de cau per reduïr la interacció LDAP, però no s'usa per a identificació. Si la DN canvia, els canvis són detectats per ownCloud. El nom d'usuari intern ownCloud s'utilitza internament arreu de ownCloud. Eliminar els mapatges tindrà efectues per tot arreu. L'eliminació dels mapatges no és sensible a la configuració, afecta a totes les configuracions LDAP! No elimineu mai els mapatges en un entorn de producció. Elimineu-los només en un estadi experimental o de prova.", +"Clear Username-LDAP User Mapping" => "Elimina el mapatge d'usuari Nom d'usuari-LDAP", +"Clear Groupname-LDAP Group Mapping" => "Elimina el mapatge de grup Nom de grup-LDAP", "Test Configuration" => "Comprovació de la configuració", "Help" => "Ajuda" ); diff --git a/apps/user_ldap/l10n/cs_CZ.php b/apps/user_ldap/l10n/cs_CZ.php index c5d77026b9..dd7373eb69 100644 --- a/apps/user_ldap/l10n/cs_CZ.php +++ b/apps/user_ldap/l10n/cs_CZ.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "Převzít nastavení z nedávného nastavení serveru?", "Keep settings?" => "Ponechat nastavení?", "Cannot add server configuration" => "Nelze přidat nastavení serveru", +"Success" => "Úspěch", +"Error" => "Chyba", "Connection test succeeded" => "Test spojení byl úspěšný", "Connection test failed" => "Test spojení selhal", "Do you really want to delete the current Server Configuration?" => "Opravdu si přejete smazat současné nastavení serveru?", diff --git a/apps/user_ldap/l10n/cy_GB.php b/apps/user_ldap/l10n/cy_GB.php new file mode 100644 index 0000000000..abe2336b2b --- /dev/null +++ b/apps/user_ldap/l10n/cy_GB.php @@ -0,0 +1,6 @@ + "Methwyd dileu", +"Error" => "Gwall", +"Password" => "Cyfrinair", +"Help" => "Cymorth" +); diff --git a/apps/user_ldap/l10n/da.php b/apps/user_ldap/l10n/da.php index 9329c4e8a2..0a77f46647 100644 --- a/apps/user_ldap/l10n/da.php +++ b/apps/user_ldap/l10n/da.php @@ -1,5 +1,7 @@ "Fejl ved sletning", +"Success" => "Succes", +"Error" => "Fejl", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du kan udelade protokollen, medmindre du skal bruge SSL. Start i så fald med ldaps://", "Base DN" => "Base DN", diff --git a/apps/user_ldap/l10n/de.php b/apps/user_ldap/l10n/de.php index e86d877ecd..f001081842 100644 --- a/apps/user_ldap/l10n/de.php +++ b/apps/user_ldap/l10n/de.php @@ -1,31 +1,33 @@ "Löschen der Serverkonfiguration fehlgeschlagen", -"The configuration is valid and the connection could be established!" => "Die Konfiguration war erfolgreich, die Verbindung konnte hergestellt werden!", -"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist gültig aber die Verbindung ist fehlgeschlagen. Bitte überprüfen Sie die Servereinstellungen und die Anmeldeinformationen.", -"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist ungültig, bitte sehen Sie für weitere Details im ownCloud Log nach", +"The configuration is valid and the connection could be established!" => "Die Konfiguration ist gültig und die Verbindung konnte hergestellt werden!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist gültig aber die Verbindung ist fehlgeschlagen. Bitte überprüfe die Servereinstellungen und Anmeldeinformationen.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist ungültig, sieh für weitere Details bitte im ownCloud Log nach", "Deletion failed" => "Löschen fehlgeschlagen", "Take over settings from recent server configuration?" => "Einstellungen von letzter Konfiguration übernehmen?", "Keep settings?" => "Einstellungen beibehalten?", -"Cannot add server configuration" => "Serverkonfiguration konnte nicht hinzugefügt werden.", +"Cannot add server configuration" => "Das Hinzufügen der Serverkonfiguration schlug fehl", +"Success" => "Erfolgreich", +"Error" => "Fehler", "Connection test succeeded" => "Verbindungstest erfolgreich", "Connection test failed" => "Verbindungstest fehlgeschlagen", -"Do you really want to delete the current Server Configuration?" => "Wollen Sie die aktuelle Serverkonfiguration wirklich löschen?", +"Do you really want to delete the current Server Configuration?" => "Möchtest Du die aktuelle Serverkonfiguration wirklich löschen?", "Confirm Deletion" => "Löschung bestätigen", -"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Warnung: Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwarteten Verhalten kommen. Bitte Deinen Systemadministator eine der beiden Anwendungen zu deaktivieren.", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Warnung: Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwartetem Verhalten kommen. Bitte Deinen Systemadministator eine der beiden Anwendungen zu deaktivieren.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Warnung: Da das PHP-Modul für LDAP nicht installiert ist, wird das Backend nicht funktionieren. Bitte Deinen Systemadministrator das Modul zu installieren.", "Server configuration" => "Serverkonfiguration", "Add Server Configuration" => "Serverkonfiguration hinzufügen", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du kannst das Protokoll auslassen, außer wenn Du SSL benötigst. Beginne dann mit ldaps://", "Base DN" => "Basis-DN", -"One Base DN per line" => "Ein Base DN pro Zeile", +"One Base DN per line" => "Ein Basis-DN pro Zeile", "You can specify Base DN for users and groups in the Advanced tab" => "Du kannst Basis-DN für Benutzer und Gruppen in dem \"Erweitert\"-Reiter konfigurieren", "User DN" => "Benutzer-DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Der DN des Benutzers für LDAP-Bind, z.B.: uid=agent,dc=example,dc=com. Für anonymen Zugriff lasse DN und Passwort leer.", "Password" => "Passwort", -"For anonymous access, leave DN and Password empty." => "Lasse die Felder von DN und Passwort für anonymen Zugang leer.", +"For anonymous access, leave DN and Password empty." => "Lasse die Felder DN und Passwort für anonymen Zugang leer.", "User Login Filter" => "Benutzer-Login-Filter", -"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Bestimmt den angewendeten Filter, wenn eine Anmeldung versucht wird. %%uid ersetzt den Benutzernamen bei dem Anmeldeversuch.", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Bestimmt den angewendeten Filter, wenn eine Anmeldung versucht wird. %%uid ersetzt den Benutzernamen beim Anmeldeversuch.", "use %%uid placeholder, e.g. \"uid=%%uid\"" => "verwende %%uid Platzhalter, z. B. \"uid=%%uid\"", "User List Filter" => "Benutzer-Filter-Liste", "Defines the filter to apply, when retrieving users." => "Definiert den Filter für die Anfrage der Benutzer.", @@ -54,13 +56,13 @@ "User Display Name Field" => "Feld für den Anzeigenamen des Benutzers", "The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ", "Base User Tree" => "Basis-Benutzerbaum", -"One User Base DN per line" => "Ein Benutzer Base DN pro Zeile", +"One User Base DN per line" => "Ein Benutzer Basis-DN pro Zeile", "User Search Attributes" => "Benutzersucheigenschaften", -"Optional; one attribute per line" => "Optional; eine Eigenschaft pro Zeile", +"Optional; one attribute per line" => "Optional; ein Attribut pro Zeile", "Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ", "Base Group Tree" => "Basis-Gruppenbaum", -"One Group Base DN per line" => "Ein Gruppen Base DN pro Zeile", +"One Group Base DN per line" => "Ein Gruppen Basis-DN pro Zeile", "Group Search Attributes" => "Gruppensucheigenschaften", "Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", "Special Attributes" => "Spezielle Eigenschaften", diff --git a/apps/user_ldap/l10n/de_DE.php b/apps/user_ldap/l10n/de_DE.php index 3b5d60387a..8aa64477e4 100644 --- a/apps/user_ldap/l10n/de_DE.php +++ b/apps/user_ldap/l10n/de_DE.php @@ -1,31 +1,33 @@ "Das Löschen der Server-Konfiguration schlug fehl", +"Failed to delete the server configuration" => "Löschen der Serverkonfiguration fehlgeschlagen", "The configuration is valid and the connection could be established!" => "Die Konfiguration ist gültig und die Verbindung konnte hergestellt werden!", -"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist gültig, aber das Herstellen der Verbindung schlug fehl. Bitte überprüfen Sie die Server-Einstellungen und Zertifikate.", -"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist ungültig. Weitere Details können Sie im ownCloud-Log nachlesen.", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Die Konfiguration ist gültig aber die Verbindung ist fehlgeschlagen. Bitte überprüfen Sie die Servereinstellungen und die Anmeldeinformationen.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Die Konfiguration ist ungültig, sehen Sie für weitere Details bitte im ownCloud Log nach", "Deletion failed" => "Löschen fehlgeschlagen", -"Take over settings from recent server configuration?" => "Sollen die Einstellungen der letzten Serverkonfiguration übernommen werden?", -"Keep settings?" => "Einstellungen behalten?", +"Take over settings from recent server configuration?" => "Einstellungen von letzter Konfiguration übernehmen?", +"Keep settings?" => "Einstellungen beibehalten?", "Cannot add server configuration" => "Das Hinzufügen der Serverkonfiguration schlug fehl", +"Success" => "Erfolg", +"Error" => "Fehler", "Connection test succeeded" => "Verbindungstest erfolgreich", "Connection test failed" => "Verbindungstest fehlgeschlagen", -"Do you really want to delete the current Server Configuration?" => "Möchten Sie die Serverkonfiguration wirklich löschen?", +"Do you really want to delete the current Server Configuration?" => "Möchten Sie die aktuelle Serverkonfiguration wirklich löschen?", "Confirm Deletion" => "Löschung bestätigen", -"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Warnung: Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwarteten Verhalten kommen. Bitten Sie Ihren Systemadministator eine der beiden Anwendungen zu deaktivieren.", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Warnung: Die Anwendungen user_ldap und user_webdavauth sind inkompatibel. Es kann demzufolge zu unerwartetem Verhalten kommen. Bitten Sie Ihren Systemadministator eine der beiden Anwendungen zu deaktivieren.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Warnung: Da das PHP-Modul für LDAP nicht installiert ist, wird das Backend nicht funktionieren. Bitten Sie Ihren Systemadministrator das Modul zu installieren.", "Server configuration" => "Serverkonfiguration", "Add Server Configuration" => "Serverkonfiguration hinzufügen", "Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Sie können das Protokoll auslassen, außer wenn Sie SSL benötigen. Beginnen Sie dann mit ldaps://", "Base DN" => "Basis-DN", -"One Base DN per line" => "Ein Base DN pro Zeile", +"One Base DN per line" => "Ein Basis-DN pro Zeile", "You can specify Base DN for users and groups in the Advanced tab" => "Sie können Basis-DN für Benutzer und Gruppen in dem \"Erweitert\"-Reiter konfigurieren", "User DN" => "Benutzer-DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Der DN des Benutzers für LDAP-Bind, z.B.: uid=agent,dc=example,dc=com. Für einen anonymen Zugriff lassen Sie DN und Passwort leer.", "Password" => "Passwort", -"For anonymous access, leave DN and Password empty." => "Lassen Sie die Felder von DN und Passwort für einen anonymen Zugang leer.", +"For anonymous access, leave DN and Password empty." => "Lassen Sie die Felder DN und Passwort für einen anonymen Zugang leer.", "User Login Filter" => "Benutzer-Login-Filter", -"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Bestimmt den angewendeten Filter, wenn eine Anmeldung durchgeführt wird. %%uid ersetzt den Benutzernamen bei dem Anmeldeversuch.", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Bestimmt den angewendeten Filter, wenn eine Anmeldung durchgeführt wird. %%uid ersetzt den Benutzernamen beim Anmeldeversuch.", "use %%uid placeholder, e.g. \"uid=%%uid\"" => "verwenden Sie %%uid Platzhalter, z. B. \"uid=%%uid\"", "User List Filter" => "Benutzer-Filter-Liste", "Defines the filter to apply, when retrieving users." => "Definiert den Filter für die Anfrage der Benutzer.", @@ -37,12 +39,12 @@ "Configuration Active" => "Konfiguration aktiv", "When unchecked, this configuration will be skipped." => "Wenn nicht angehakt, wird diese Konfiguration übersprungen.", "Port" => "Port", -"Backup (Replica) Host" => "Back-Up (Replikation) Host", -"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Geben Sie einen optionalen Backup-Host an. Es muss ein Replikat des Haupt-LDAP/AD Servers sein.", -"Backup (Replica) Port" => "Back-Up (Replikation) Port", +"Backup (Replica) Host" => "Backup Host (Kopie)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Geben Sie einen optionalen Backup Host an. Es muss sich um eine Kopie des Haupt LDAP/AD Servers handeln.", +"Backup (Replica) Port" => "Backup Port", "Disable Main Server" => "Hauptserver deaktivieren", -"When switched on, ownCloud will only connect to the replica server." => "Wenn eingeschaltet, wird sich die ownCloud nur mit dem Replikat-Server verbinden.", -"Use TLS" => "Benutze TLS", +"When switched on, ownCloud will only connect to the replica server." => "Wenn aktiviert, wird ownCloud ausschließlich den Backupserver verwenden.", +"Use TLS" => "Nutze TLS", "Do not use it additionally for LDAPS connections, it will fail." => "Benutzen Sie es nicht in Verbindung mit LDAPS Verbindungen, es wird fehlschlagen.", "Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", "Turn off SSL certificate validation." => "Schalten Sie die SSL-Zertifikatsprüfung aus.", @@ -50,26 +52,30 @@ "Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.", "Cache Time-To-Live" => "Speichere Time-To-Live zwischen", "in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", -"Directory Settings" => "Verzeichniseinstellungen", +"Directory Settings" => "Ordnereinstellungen", "User Display Name Field" => "Feld für den Anzeigenamen des Benutzers", "The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ", "Base User Tree" => "Basis-Benutzerbaum", -"One User Base DN per line" => "Ein Benutzer Base DN pro Zeile", -"User Search Attributes" => "Eigenschaften der Benutzer-Suche", +"One User Base DN per line" => "Ein Benutzer Basis-DN pro Zeile", +"User Search Attributes" => "Benutzersucheigenschaften", "Optional; one attribute per line" => "Optional; ein Attribut pro Zeile", "Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ", "Base Group Tree" => "Basis-Gruppenbaum", -"One Group Base DN per line" => "Ein Gruppen Base DN pro Zeile", -"Group Search Attributes" => "Eigenschaften der Gruppen-Suche", +"One Group Base DN per line" => "Ein Gruppen Basis-DN pro Zeile", +"Group Search Attributes" => "Gruppensucheigenschaften", "Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", -"Special Attributes" => "Besondere Eigenschaften", +"Special Attributes" => "Spezielle Eigenschaften", "Quota Field" => "Kontingent-Feld", "Quota Default" => "Standard-Kontingent", "in bytes" => "in Bytes", "Email Field" => "E-Mail-Feld", "User Home Folder Naming Rule" => "Benennungsregel für das Home-Verzeichnis des Benutzers", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Ohne Eingabe wird der Benutzername (Standard) verwendet. Anderenfalls tragen Sie bitte ein LDAP/AD-Attribut ein.", +"Internal Username" => "Interner Benutzername", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "Standardmäßig wird der interne Benutzername mittels des UUID-Attributes erzeugt. Dies stellt sicher, dass der Benutzername einzigartig ist und keinerlei Zeichen konvertiert werden müssen. Der interne Benutzername unterliegt Beschränkungen, die nur die nachfolgenden Zeichen erlauben: [ a-zA-Z0-9_.@- ]. Andere Zeichenwerden mittels ihrer korrespondierenden Zeichen ersetzt oder einfach ausgelassen. Bei Übereinstimmungen wird ein Zähler hinzugefügt bzw. der Zähler um einen Wert erhöht. Der interne Benutzername wird benutzt, um einen Benutzer intern zu identifizieren. Es ist ebenso der standardmäßig vorausgewählte Namen des Heimatverzeichnisses in ownCloud. Es dient weiterhin als Port für Remote-URLs - zum Beispiel für alle *DAV-Dienste Mit dieser Einstellung kann das Standardverhalten überschrieben werden. Um ein ähnliches Verhalten wie vor ownCloud 5 zu erzielen, fügen Sie das anzuzeigende Attribut des Benutzernamens in das nachfolgende Feld ein. Lassen Sie dies hingegen für das Standardverhalten leer. Die Änderungen werden sich einzig und allein nur auf neu gemappte (hinzugefügte) LDAP-Benutzer auswirken.", +"Override UUID detection" => "UUID-Erkennung überschreiben", +"UUID Attribute:" => "UUID-Attribut:", "Test Configuration" => "Testkonfiguration", "Help" => "Hilfe" ); diff --git a/apps/user_ldap/l10n/el.php b/apps/user_ldap/l10n/el.php index 96ec818043..acecf27125 100644 --- a/apps/user_ldap/l10n/el.php +++ b/apps/user_ldap/l10n/el.php @@ -4,8 +4,11 @@ "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Οι ρυθμίσεις είναι έγκυρες, αλλά απέτυχε η σύνδεση. Παρακαλώ ελέγξτε τις ρυθμίσεις του διακομιστή και τα διαπιστευτήρια.", "The configuration is invalid. Please look in the ownCloud log for further details." => "Μη έγκυρες ρυθμίσεις. Παρακαλώ ελέγξτε τις καταγραφές του ownCloud για περισσότερες λεπτομέρειες.", "Deletion failed" => "Η διαγραφή απέτυχε", +"Take over settings from recent server configuration?" => "Πάρτε πάνω από τις πρόσφατες ρυθμίσεις διαμόρφωσης του διακομιστή?", "Keep settings?" => "Διατήρηση ρυθμίσεων;", "Cannot add server configuration" => "Αδυναμία προσθήκης ρυθμίσεων διακομιστή", +"Success" => "Επιτυχία", +"Error" => "Σφάλμα", "Connection test succeeded" => "Επιτυχημένη δοκιμαστική σύνδεση", "Connection test failed" => "Αποτυχημένη δοκιμαστική σύνδεσης.", "Do you really want to delete the current Server Configuration?" => "Θέλετε να διαγράψετε τις τρέχουσες ρυθμίσεις του διακομιστή;", @@ -17,6 +20,7 @@ "Host" => "Διακομιστής", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Μπορείτε να παραλείψετε το πρωτόκολλο, εκτός αν απαιτείται SSL. Σε αυτή την περίπτωση ξεκινήστε με ldaps://", "Base DN" => "Base DN", +"One Base DN per line" => "Ένα DN Βάσης ανά γραμμή ", "You can specify Base DN for users and groups in the Advanced tab" => "Μπορείτε να καθορίσετε το Base DN για χρήστες και ομάδες από την καρτέλα Προηγμένες ρυθμίσεις", "User DN" => "User DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Το DN του χρήστη πελάτη με το οποίο θα πρέπει να γίνει η σύνδεση, π.χ. uid=agent,dc=example,dc=com. Για χρήση χωρίς πιστοποίηση, αφήστε το DN και τον Κωδικό κενά.", @@ -32,22 +36,42 @@ "Defines the filter to apply, when retrieving groups." => "Καθορίζει το φίλτρο που θα ισχύει κατά την ανάκτηση ομάδων.", "without any placeholder, e.g. \"objectClass=posixGroup\"." => "χωρίς κάποια μεταβλητή, π.χ. \"objectClass=ΟμάδαPosix\".", "Connection Settings" => "Ρυθμίσεις Σύνδεσης", +"Configuration Active" => "Ενεργοποιηση ρυθμισεων", +"When unchecked, this configuration will be skipped." => "Όταν δεν είναι επιλεγμένο, αυτή η ρύθμιση θα πρέπει να παραλειφθεί. ", "Port" => "Θύρα", +"Backup (Replica) Host" => "Δημιουργία αντιγράφων ασφαλείας (Replica) Host ", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Δώστε μια προαιρετική εφεδρική υποδοχή. Πρέπει να είναι ένα αντίγραφο του κύριου LDAP / AD διακομιστη.", +"Backup (Replica) Port" => "Δημιουργία αντιγράφων ασφαλείας (Replica) Υποδοχη", +"Disable Main Server" => "Απενεργοποιηση του κεντρικου διακομιστη", +"When switched on, ownCloud will only connect to the replica server." => "Όταν ενεργοποιηθεί, με το ownCloud θα συνδεθείτε με το διακομιστή ρεπλίκα.", "Use TLS" => "Χρήση TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "Μην το χρησιμοποιήσετε επιπροσθέτως, για LDAPS συνδέσεις , θα αποτύχει.", "Case insensitve LDAP server (Windows)" => "LDAP server (Windows) με διάκριση πεζών-ΚΕΦΑΛΑΙΩΝ", "Turn off SSL certificate validation." => "Απενεργοποίηση επικύρωσης πιστοποιητικού SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Εάν η σύνδεση δουλεύει μόνο με αυτή την επιλογή, εισάγετε το LDAP SSL πιστοποιητικό του διακομιστή στον ownCloud server σας.", "Not recommended, use for testing only." => "Δεν προτείνεται, χρήση μόνο για δοκιμές.", +"Cache Time-To-Live" => "Cache Time-To-Live", "in seconds. A change empties the cache." => "σε δευτερόλεπτα. Μια αλλαγή αδειάζει την μνήμη cache.", "Directory Settings" => "Ρυθμίσεις Καταλόγου", "User Display Name Field" => "Πεδίο Ονόματος Χρήστη", "The LDAP attribute to use to generate the user`s ownCloud name." => "Η ιδιότητα LDAP που θα χρησιμοποιείται για τη δημιουργία του ονόματος χρήστη του ownCloud.", "Base User Tree" => "Base User Tree", +"One User Base DN per line" => "Ένα DN βάσης χρηστών ανά γραμμή", +"User Search Attributes" => "Χαρακτηριστικά αναζήτησης των χρηστών ", +"Optional; one attribute per line" => "Προαιρετικά? Ένα χαρακτηριστικό ανά γραμμή ", "Group Display Name Field" => "Group Display Name Field", "The LDAP attribute to use to generate the groups`s ownCloud name." => "Η ιδιότητα LDAP που θα χρησιμοποιείται για τη δημιουργία του ονόματος ομάδας του ownCloud.", "Base Group Tree" => "Base Group Tree", +"One Group Base DN per line" => "Μια ομαδικη Βάση DN ανά γραμμή", +"Group Search Attributes" => "Ομάδα Χαρακτηριστικων Αναζήτηση", "Group-Member association" => "Group-Member association", +"Special Attributes" => "Ειδικά Χαρακτηριστικά ", +"Quota Field" => "Ποσοσταση πεδιου", +"Quota Default" => "Προκαθισμενο πεδιο", "in bytes" => "σε bytes", +"Email Field" => "Email τυπος", +"User Home Folder Naming Rule" => "Χρήστης Προσωπικόςφάκελος Ονομασία Κανόνας ", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Αφήστε το κενό για το όνομα χρήστη (προεπιλογή). Διαφορετικά, συμπληρώστε μία ιδιότητα LDAP/AD.", +"Test Configuration" => "Δοκιμαστικες ρυθμισεις", "Help" => "Βοήθεια" ); diff --git a/apps/user_ldap/l10n/en@pirate.php b/apps/user_ldap/l10n/en@pirate.php new file mode 100644 index 0000000000..482632f3fd --- /dev/null +++ b/apps/user_ldap/l10n/en@pirate.php @@ -0,0 +1,3 @@ + "Passcode" +); diff --git a/apps/user_ldap/l10n/eo.php b/apps/user_ldap/l10n/eo.php index 3ffcbddb3e..c9a9ba130c 100644 --- a/apps/user_ldap/l10n/eo.php +++ b/apps/user_ldap/l10n/eo.php @@ -1,5 +1,7 @@ "Forigo malsukcesis", +"Success" => "Sukceso", +"Error" => "Eraro", "Host" => "Gastigo", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vi povas neglekti la protokolon, escepte se vi bezonas SSL-on. Tiuokaze, komencu per ldaps://", "Base DN" => "Bazo-DN", diff --git a/apps/user_ldap/l10n/es.php b/apps/user_ldap/l10n/es.php index 098e16a5d1..31d43288e5 100644 --- a/apps/user_ldap/l10n/es.php +++ b/apps/user_ldap/l10n/es.php @@ -1,17 +1,21 @@ "Ocurrió un fallo al borrar las asignaciones.", "Failed to delete the server configuration" => "No se pudo borrar la configuración del servidor", "The configuration is valid and the connection could be established!" => "La configuración es válida y la conexión puede establecerse!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuración es válida, pero falló el Enlace. Por favor, compruebe la configuración del servidor y las credenciales.", "The configuration is invalid. Please look in the ownCloud log for further details." => "La configuración no es válida. Por favor, busque en el log de ownCloud para más detalles.", "Deletion failed" => "Falló el borrado", -"Take over settings from recent server configuration?" => "Hacerse cargo de los ajustes de configuración del servidor reciente?", +"Take over settings from recent server configuration?" => "¿Asumir los ajustes actuales de la configuración del servidor?", "Keep settings?" => "Mantener la configuración?", "Cannot add server configuration" => "No se puede añadir la configuración del servidor", +"mappings cleared" => "Asignaciones borradas", +"Success" => "Éxito", +"Error" => "Error", "Connection test succeeded" => "La prueba de conexión fue exitosa", "Connection test failed" => "La prueba de conexión falló", "Do you really want to delete the current Server Configuration?" => "¿Realmente desea eliminar la configuración actual del servidor?", "Confirm Deletion" => "Confirmar eliminación", -"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Advertencia: Los Apps user_ldap y user_webdavauth son incompatibles. Puede que experimente un comportamiento inesperado. Pregunte al administrador del sistema para desactivar uno de ellos.", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Advertencia: Las aplicaciones user_ldap y user_webdavauth son incompatibles. Puede que experimente un comportamiento inesperado. Pregunte al administrador del sistema para desactivar uno de ellos.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Advertencia: El módulo LDAP de PHP no está instalado, el sistema no funcionará. Por favor consulte al administrador del sistema para instalarlo.", "Server configuration" => "Configuración del Servidor", "Add Server Configuration" => "Agregar configuracion del servidor", @@ -26,30 +30,30 @@ "For anonymous access, leave DN and Password empty." => "Para acceso anónimo, deje DN y contraseña vacíos.", "User Login Filter" => "Filtro de inicio de sesión de usuario", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Define el filtro a aplicar cuando se ha realizado un login. %%uid remplazrá el nombre de usuario en el proceso de login.", -"use %%uid placeholder, e.g. \"uid=%%uid\"" => "usar %%uid como placeholder, ej: \"uid=%%uid\"", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "usar %%uid como comodín, ej: \"uid=%%uid\"", "User List Filter" => "Lista de filtros de usuario", "Defines the filter to apply, when retrieving users." => "Define el filtro a aplicar, cuando se obtienen usuarios.", -"without any placeholder, e.g. \"objectClass=person\"." => "Sin placeholder, ej: \"objectClass=person\".", +"without any placeholder, e.g. \"objectClass=person\"." => "Sin comodines, ej: \"objectClass=person\".", "Group Filter" => "Filtro de grupo", "Defines the filter to apply, when retrieving groups." => "Define el filtro a aplicar, cuando se obtienen grupos.", -"without any placeholder, e.g. \"objectClass=posixGroup\"." => "Con cualquier placeholder, ej: \"objectClass=posixGroup\".", -"Connection Settings" => "Configuracion de coneccion", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sin comodines, ej: \"objectClass=posixGroup\".", +"Connection Settings" => "Configuración de conexión", "Configuration Active" => "Configuracion activa", "When unchecked, this configuration will be skipped." => "Cuando deseleccione, esta configuracion sera omitida.", "Port" => "Puerto", -"Backup (Replica) Host" => "Host para backup (Replica)", -"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Dar un host de copia de seguridad opcional. Debe ser una réplica del servidor principal LDAP / AD.", -"Backup (Replica) Port" => "Puerto para backup (Replica)", +"Backup (Replica) Host" => "Servidor de copia de seguridad (Replica)", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Dar un servidor de copia de seguridad opcional. Debe ser una réplica del servidor principal LDAP / AD.", +"Backup (Replica) Port" => "Puerto para copias de seguridad (Replica)", "Disable Main Server" => "Deshabilitar servidor principal", -"When switched on, ownCloud will only connect to the replica server." => "Cuando se inicie, ownCloud unicamente estara conectado al servidor replica", +"When switched on, ownCloud will only connect to the replica server." => "Cuando se inicie, ownCloud unicamente conectará al servidor replica", "Use TLS" => "Usar TLS", -"Do not use it additionally for LDAPS connections, it will fail." => "No usar adicionalmente para conecciones LDAPS, estas fallaran", -"Case insensitve LDAP server (Windows)" => "Servidor de LDAP sensible a mayúsculas/minúsculas (Windows)", +"Do not use it additionally for LDAPS connections, it will fail." => "No lo use para conexiones LDAPS, Fallará.", +"Case insensitve LDAP server (Windows)" => "Servidor de LDAP no sensible a mayúsculas/minúsculas (Windows)", "Turn off SSL certificate validation." => "Apagar la validación por certificado SSL.", "If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la conexión sólo funciona con esta opción, importe el certificado SSL del servidor LDAP en su servidor ownCloud.", "Not recommended, use for testing only." => "No recomendado, sólo para pruebas.", "Cache Time-To-Live" => "Cache TTL", -"in seconds. A change empties the cache." => "en segundos. Un cambio vacía la cache.", +"in seconds. A change empties the cache." => "en segundos. Un cambio vacía la caché.", "Directory Settings" => "Configuracion de directorio", "User Display Name Field" => "Campo de nombre de usuario a mostrar", "The LDAP attribute to use to generate the user`s ownCloud name." => "El atributo LDAP a usar para generar el nombre de usuario de ownCloud.", @@ -70,6 +74,16 @@ "Email Field" => "E-mail", "User Home Folder Naming Rule" => "Regla para la carpeta Home de usuario", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Vacío para el nombre de usuario (por defecto). En otro caso, especifique un atributo LDAP/AD.", +"Internal Username" => "Nombre de usuario interno", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "Por defecto el nombre de usuario interno será creado desde el atributo UUID. Esto asegura que el nombre de usuario es único y los caracteres no necesitan ser convertidos. En el nombre de usuario interno sólo se pueden usar estos caracteres: [a-zA-Z0-9_.@-]. Otros caracteres son sustituidos por su correspondiente en ASCII o simplemente quitados. En coincidencias un número será añadido o incrementado. El nombre de usuario interno es usado para identificar un usuario internamente. Es también el nombre por defecto para la carpeta personal del usuario in ownCloud. También es un puerto de URLs remotas, por ejemplo, para todos los servicios *DAV. Con esta configuración el comportamiento por defecto puede ser cambiado. Para conseguir un comportamiento similar a como era antes de ownCloud 5, introduce el atributo del nombre en pantalla del usuario en el siguiente campo. Déjalo vacío para el comportamiento por defecto. Los cambios solo tendrán efecto en los nuevos usuarios LDAP.", +"Internal Username Attribute:" => "Atributo Nombre de usuario Interno:", +"Override UUID detection" => "Sobrescribir la detección UUID", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "Por defecto, ownCloud autodetecta el atributo UUID. El atributo UUID es usado para identificar indudablemente usuarios y grupos LDAP. Además, el nombre de usuario interno será creado en base al UUID, si no ha sido especificado otro comportamiento arriba. Puedes sobrescribir la configuración y pasar un atributo de tu elección. Debes asegurarte de que el atributo de tu elección sea accesible por los usuarios y grupos y ser único. Déjalo en blanco para usar el comportamiento por defecto. Los cambios tendrán efecto solo en los nuevos usuarios y grupos de LDAP.", +"UUID Attribute:" => "Atributo UUID:", +"Username-LDAP User Mapping" => "Asignación del Nombre de usuario de un usuario LDAP", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloud utiliza nombres de usuario para almacenar y asignar (meta) datos. Con el fin de identificar con precisión y reconocer usuarios, cada usuario LDAP tendrá un nombre de usuario interno. Esto requiere una asignación de nombre de usuario de ownCloud a usuario LDAP. El nombre de usuario creado se asigna al UUID del usuario LDAP. Además el DN se almacena en caché más bien para reducir la interacción de LDAP, pero no se utiliza para la identificación. Si la DN cambia, los cambios serán encontrados por ownCloud. El nombre interno de ownCloud se utiliza para todo en ownCloud. Eliminando las asignaciones tendrá restos por todas partes. Eliminando las asignaciones no es sensible a la configuración, que afecta a todas las configuraciones de LDAP! No limpiar nunca las asignaciones en un entorno de producción. Sólo borrar asignaciones en una situación de prueba o experimental.", +"Clear Username-LDAP User Mapping" => "Borrar la asignación de los Nombres de usuario de los usuarios LDAP", +"Clear Groupname-LDAP Group Mapping" => "Borrar la asignación de los Nombres de grupo de los grupos de LDAP", "Test Configuration" => "Configuración de prueba", "Help" => "Ayuda" ); diff --git a/apps/user_ldap/l10n/es_AR.php b/apps/user_ldap/l10n/es_AR.php index c8aec0cd41..98fb32b1d2 100644 --- a/apps/user_ldap/l10n/es_AR.php +++ b/apps/user_ldap/l10n/es_AR.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "Tomar los valores de la anterior configuración de servidor?", "Keep settings?" => "¿Mantener preferencias?", "Cannot add server configuration" => "No se pudo añadir la configuración del servidor", +"Success" => "Éxito", +"Error" => "Error", "Connection test succeeded" => "El este de conexión ha sido completado satisfactoriamente", "Connection test failed" => "Falló es test de conexión", "Do you really want to delete the current Server Configuration?" => "¿Realmente desea borrar la configuración actual del servidor?", diff --git a/apps/user_ldap/l10n/et_EE.php b/apps/user_ldap/l10n/et_EE.php index 665e9d6fa2..39346def03 100644 --- a/apps/user_ldap/l10n/et_EE.php +++ b/apps/user_ldap/l10n/et_EE.php @@ -1,14 +1,18 @@ "Vastendususte puhastamine ebaõnnestus.", "Failed to delete the server configuration" => "Serveri seadistuse kustutamine ebaõnnestus", "The configuration is valid and the connection could be established!" => "Seadistus on korrektne ning ühendus on olemas!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Seadistus on korrektne, kuid ühendus ebaõnnestus. Palun kontrolli serveri seadeid ja ühenduseks kasutatavaid kasutajatunnuseid.", "The configuration is invalid. Please look in the ownCloud log for further details." => "Seadistus on vigane. Palun vaata ownCloud logist täpsemalt.", "Deletion failed" => "Kustutamine ebaõnnestus", "Take over settings from recent server configuration?" => "Võta sätted viimasest serveri seadistusest?", -"Keep settings?" => "Säilitada seadistus?", +"Keep settings?" => "Säilitada seadistused?", "Cannot add server configuration" => "Ei suuda lisada serveri seadistust", -"Connection test succeeded" => "Test ühendus õnnestus", -"Connection test failed" => "Test ühendus ebaõnnestus", +"mappings cleared" => "vastendused puhastatud", +"Success" => "Korras", +"Error" => "Viga", +"Connection test succeeded" => "Ühenduse testimine õnnestus", +"Connection test failed" => "Ühenduse testimine ebaõnnestus", "Do you really want to delete the current Server Configuration?" => "Oled kindel, et tahad kustutada praegust serveri seadistust?", "Confirm Deletion" => "Kinnita kustutamine", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Hoiatus: rakendused user_ldap ja user_webdavauht ei ole ühilduvad. Töös võib esineda ootamatuid tõrkeid.\nPalu oma süsteemihalduril üks neist rakendustest kasutusest eemaldada.", @@ -39,10 +43,10 @@ "Port" => "Port", "Backup (Replica) Host" => "Varuserver", "Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Lisa täiendav LDAP/AD server, mida replikeeritakse peaserveriga.", -"Backup (Replica) Port" => "Varuserveri (replika) ldap port", +"Backup (Replica) Port" => "Varuserveri (replika) port", "Disable Main Server" => "Ära kasuta peaserverit", "When switched on, ownCloud will only connect to the replica server." => "Märgituna ownCloud ühendub ainult varuserverisse.", -"Use TLS" => "Kasutaja TLS", +"Use TLS" => "Kasuta TLS-i", "Do not use it additionally for LDAPS connections, it will fail." => "LDAPS puhul ära kasuta. Ühendus ei toimi.", "Case insensitve LDAP server (Windows)" => "Mittetõstutundlik LDAP server (Windows)", "Turn off SSL certificate validation." => "Lülita SSL sertifikaadi kontrollimine välja.", @@ -70,6 +74,16 @@ "Email Field" => "Email atribuut", "User Home Folder Naming Rule" => "Kasutaja kodukataloogi nimetamise reegel", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Kasutajanime (vaikeväärtus) kasutamiseks jäta tühjaks. Vastasel juhul määra LDAP/AD omadus.", +"Internal Username" => "Sisemine kasutajanimi", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "Vaikimisi tekitatakse sisemine kasutajanimi UUID atribuudist. See tagab, et kasutajanimi on unikaalne ja sümboleid pole vaja muuta. Sisemisel kasutajatunnuse puhul on lubatud ainult järgmised sümbolid: [ a-zA-Z0-9_.@- ]. Muud sümbolid asendatakse nende ASCII vastega või lihtsalt hüljatakse. Tõrgete korral lisatakse number või suurendatakse seda. Sisemist kasutajatunnust kasutatakse kasutaja sisemiseks tuvastamiseks. Ühtlasi on see ownCloudis kasutaja vaikimisi kodukataloogi nimeks. See on ka serveri URL pordiks, näiteks kõikidel *DAV teenustel.Selle seadistusega saab tühistada vaikimisi käitumise. Saavutamaks sarnast käitumist eelnevate ownCloud 5 versioonidega, sisesta kasutaja kuvatava nime atribuut järgnevale väljale. Vaikimisi seadistuseks jäta tühjaks. Muudatused mõjutavad ainult uusi LDAP kasutajate vastendusi (lisatud).", +"Internal Username Attribute:" => "Sisemise kasutajatunnuse atribuut:", +"Override UUID detection" => "Tühista UUID tuvastus", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "Vaikimis ownCloud tuvastab automaatlselt UUID atribuudi. UUID atribuuti kasutatakse LDAP kasutajate ja gruppide kindlaks tuvastamiseks. Samuti tekitatakse sisemine kasutajanimi UUID alusel, kui pole määratud teisiti. Sa saad tühistada selle seadistuse ning määrata atribuudi omal valikul. Pead veenduma, et valitud atribuut toimib nii kasutajate kui gruppide puhul ning on unikaalne. Vaikimisi seadistuseks jäta tühjaks. Muudatused mõjutavad ainult uusi LDAP kasutajate vastendusi (lisatud).", +"UUID Attribute:" => "UUID atribuut:", +"Username-LDAP User Mapping" => "LDAP-Kasutajatunnus Kasutaja Vastendus", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloud kasutab kasutajanime talletamaks ja omistamaks (pseudo) andmeid. Et täpselt tuvastada ja määratleda kasutajaid, iga LDAP kasutaja peab omama sisemist kasutajatunnust. See vajab ownCloud kasutajatunnuse vastendust LDAP kasutajaks. Tekitatud kasutanimi vastendatakse LDAP kasutaja UUID-iks. Lisaks puhverdatakse DN vähendamaks LDAP päringuid, kuid seda ei kasutata tuvastamisel. ownCloud suudab tuvastada ka DN muutumise. ownCloud sisemist kasutajatunnust kasutatakse üle kogu ownCloudi. Eemaldates vastenduse tekivad kõikjal andmejäägid. Vastenduste eemaldamine ei ole konfiguratsiooni tundlik, see mõjutab kõiki LDAP seadistusi! Ära kunagi eemalda vastendusi produktsioonis! Seda võid teha ainult testis või katsetuste masinas.", +"Clear Username-LDAP User Mapping" => "Puhasta LDAP-Kasutajatunnus Kasutaja Vastendus", +"Clear Groupname-LDAP Group Mapping" => "Puhasta LDAP-Grupinimi Grupp Vastendus", "Test Configuration" => "Testi seadistust", "Help" => "Abiinfo" ); diff --git a/apps/user_ldap/l10n/eu.php b/apps/user_ldap/l10n/eu.php index 5e9fd014c6..42f184e539 100644 --- a/apps/user_ldap/l10n/eu.php +++ b/apps/user_ldap/l10n/eu.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "oraintsuko zerbitzariaren konfigurazioaren ezarpenen ardura hartu?", "Keep settings?" => "Mantendu ezarpenak?", "Cannot add server configuration" => "Ezin da zerbitzariaren konfigurazioa gehitu", +"Success" => "Arrakasta", +"Error" => "Errorea", "Connection test succeeded" => "Konexio froga ongi burutu da", "Connection test failed" => "Konexio frogak huts egin du", "Do you really want to delete the current Server Configuration?" => "Ziur zaude Zerbitzariaren Konfigurazioa ezabatu nahi duzula?", diff --git a/apps/user_ldap/l10n/fa.php b/apps/user_ldap/l10n/fa.php index 9a01a67703..bef13457ad 100644 --- a/apps/user_ldap/l10n/fa.php +++ b/apps/user_ldap/l10n/fa.php @@ -3,6 +3,7 @@ "The configuration is valid and the connection could be established!" => "پیکربندی معتبر است و ارتباط می تواند برقرار شود", "Deletion failed" => "حذف کردن انجام نشد", "Keep settings?" => "آیا تنظیمات ذخیره شود ؟", +"Error" => "خطا", "Connection test succeeded" => "تست اتصال با موفقیت انجام گردید", "Connection test failed" => "تست اتصال ناموفق بود", "Do you really want to delete the current Server Configuration?" => "آیا واقعا می خواهید پیکربندی کنونی سرور را حذف کنید؟", @@ -10,7 +11,7 @@ "Server configuration" => "پیکربندی سرور", "Add Server Configuration" => "افزودن پیکربندی سرور", "Host" => "میزبانی", -"Password" => "رمز عبور", +"Password" => "گذرواژه", "Group Filter" => "فیلتر گروه", "Port" => "درگاه", "in bytes" => "در بایت", diff --git a/apps/user_ldap/l10n/fi_FI.php b/apps/user_ldap/l10n/fi_FI.php index 38ecb5d82a..38a8b99cf7 100644 --- a/apps/user_ldap/l10n/fi_FI.php +++ b/apps/user_ldap/l10n/fi_FI.php @@ -2,6 +2,8 @@ "Deletion failed" => "Poisto epäonnistui", "Keep settings?" => "Säilytetäänkö asetukset?", "Cannot add server configuration" => "Palvelinasetusten lisäys epäonnistui", +"Success" => "Onnistui!", +"Error" => "Virhe", "Connection test succeeded" => "Yhteystesti onnistui", "Connection test failed" => "Yhteystesti epäonnistui", "Confirm Deletion" => "Vahvista poisto", diff --git a/apps/user_ldap/l10n/fr.php b/apps/user_ldap/l10n/fr.php index 990658e147..11f8fbaaf4 100644 --- a/apps/user_ldap/l10n/fr.php +++ b/apps/user_ldap/l10n/fr.php @@ -1,14 +1,18 @@ "Erreur lors de la suppression des associations.", "Failed to delete the server configuration" => "Échec de la suppression de la configuration du serveur", -"The configuration is valid and the connection could be established!" => "La configuration est valide est la connexion peut être établie !", +"The configuration is valid and the connection could be established!" => "La configuration est valide et la connexion peut être établie !", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configuration est valide, mais le lien ne peut être établi. Veuillez vérifier les paramètres du serveur ainsi que vos identifiants de connexion.", "The configuration is invalid. Please look in the ownCloud log for further details." => "La configuration est invalide. Veuillez vous référer aux fichiers de journaux ownCloud pour plus d'information.", "Deletion failed" => "La suppression a échoué", "Take over settings from recent server configuration?" => "Récupérer les paramètres depuis une configuration récente du serveur ?", "Keep settings?" => "Garder ces paramètres ?", -"Cannot add server configuration" => "Impossible d'ajouter la configuration du serveur.", +"Cannot add server configuration" => "Impossible d'ajouter la configuration du serveur", +"mappings cleared" => "associations supprimées", +"Success" => "Succès", +"Error" => "Erreur", "Connection test succeeded" => "Test de connexion réussi", -"Connection test failed" => "Le test de connexion a échoué", +"Connection test failed" => "Test de connexion échoué", "Do you really want to delete the current Server Configuration?" => "Êtes-vous vraiment sûr de vouloir effacer la configuration actuelle du serveur ?", "Confirm Deletion" => "Confirmer la suppression", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Avertissement: Les applications user_ldap et user_webdavauth sont incompatibles. Des disfonctionnements peuvent survenir. Contactez votre administrateur système pour qu'il désactive l'une d'elles.", @@ -17,13 +21,13 @@ "Add Server Configuration" => "Ajouter une configuration du serveur", "Host" => "Hôte", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vous pouvez omettre le protocole, sauf si vous avez besoin de SSL. Dans ce cas préfixez avec ldaps://", -"Base DN" => "DN Racine", +"Base DN" => "DN racine", "One Base DN per line" => "Un DN racine par ligne", "You can specify Base DN for users and groups in the Advanced tab" => "Vous pouvez spécifier les DN Racines de vos utilisateurs et groupes via l'onglet Avancé", "User DN" => "DN Utilisateur (Autorisé à consulter l'annuaire)", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN de l'utilisateur client pour lequel la liaison doit se faire, par exemple uid=agent,dc=example,dc=com. Pour un accès anonyme, laisser le DN et le mot de passe vides.", "Password" => "Mot de passe", -"For anonymous access, leave DN and Password empty." => "Pour un accès anonyme, laisser le DN Utilisateur et le mot de passe vides.", +"For anonymous access, leave DN and Password empty." => "Pour un accès anonyme, laisser le DN utilisateur et le mot de passe vides.", "User Login Filter" => "Modèle d'authentification utilisateurs", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Définit le motif à appliquer, lors d'une tentative de connexion. %%uid est remplacé par le nom d'utilisateur lors de la connexion.", "use %%uid placeholder, e.g. \"uid=%%uid\"" => "veuillez utiliser le champ %%uid , ex.: \"uid=%%uid\"", @@ -66,10 +70,20 @@ "Special Attributes" => "Attributs spéciaux", "Quota Field" => "Champ du quota", "Quota Default" => "Quota par défaut", -"in bytes" => "en octets", +"in bytes" => "en bytes", "Email Field" => "Champ Email", "User Home Folder Naming Rule" => "Convention de nommage du répertoire utilisateur", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Laisser vide ", +"Internal Username" => "Nom d'utilisateur interne", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "Par défaut le nom d'utilisateur interne sera créé à partir de l'attribut UUID. Ceci permet d'assurer que le nom d'utilisateur est unique et que les caractères ne nécessitent pas de convertion. Le nom d'utilisateur interne doit contenir seulement les caractères suivants: [ a-zA-Z0-9_.@- ]. Les autres caractères sont remplacés par leur correspondance ASCII ou simplement omis. En cas de collision le nombre est incrémenté/décrémenté. Le nom d'utilisateur interne est utilisé pour identifier l'utilisateur au sein du système. C'est aussi le nom par défaut du répertoire utilisateur dans ownCloud. C'est aussi le port d'URLs distants, par exemple pour tous les services *DAV. Le comportement par défaut peut être modifié à l'aide de ce paramètre. Pour obtenir un comportement similaire aux versions précédentes à ownCloud 5, saisir le nom d'utilisateur à afficher dans le champ suivant. Laissez à blanc pour le comportement par défaut. Les modifications prendront effet seulement pour les nouveaux (ajoutés) utilisateurs LDAP.", +"Internal Username Attribute:" => "Nom d'utilisateur interne:", +"Override UUID detection" => "Surcharger la détection d'UUID", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "Par défaut, ownCloud détecte automatiquement l'attribut UUID. L'attribut UUID est utilisé pour identifier les utilisateurs et groupes de façon prédictive. De plus, le nom d'utilisateur interne sera créé basé sur l'UUID s'il n'est pas explicité ci-dessus. Vous pouvez modifier ce comportement et définir l'attribut de votre choix. Vous devez alors vous assurer que l'attribut de votre choix peut être récupéré pour les utilisateurs ainsi que pour les groupes et qu'il soit unique. Laisser à blanc pour le comportement par défaut. Les modifications seront effectives uniquement pour les nouveaux (ajoutés) utilisateurs et groupes LDAP.", +"UUID Attribute:" => "Attribut UUID :", +"Username-LDAP User Mapping" => "Association Nom d'utilisateur-Utilisateur LDAP", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloud utilise les noms d'utilisateurs pour le stockage et l'assignation de (meta) data. Pour identifier et reconnaitre précisément les utilisateurs, chaque utilisateur aura un nom interne spécifique. Cela requiert l'association d'un nom d'utilisateur ownCloud à un nom d'utilisateur LDAP. Le nom d'utilisateur créé est associé à l'attribut UUID de l'utilisateur LDAP. Par ailleurs le DN est mémorisé en cache pour limiter les interactions LDAP mais il n'est pas utilisé pour l'identification. ownCloud détectera le changement de DN, le cas échéant. Seul le nom interne à ownCloud est utilisé au sein du produit. Supprimer les associations créera des orphelins et l'action affectera toutes les configurations LDAP. NE JAMAIS SUPPRIMER LES ASSOCIATIONS EN ENVIRONNEMENT DE PRODUCTION. Le faire seulement sur les environnements de tests et d'expérimentation.", +"Clear Username-LDAP User Mapping" => "Supprimer l'association utilisateur interne-utilisateur LDAP", +"Clear Groupname-LDAP Group Mapping" => "Supprimer l'association nom de groupe-groupe LDAP", "Test Configuration" => "Tester la configuration", "Help" => "Aide" ); diff --git a/apps/user_ldap/l10n/gl.php b/apps/user_ldap/l10n/gl.php index deb6dbb555..3f44ccd9bd 100644 --- a/apps/user_ldap/l10n/gl.php +++ b/apps/user_ldap/l10n/gl.php @@ -1,12 +1,16 @@ "Non foi posíbel limpar as asignacións.", "Failed to delete the server configuration" => "Non foi posíbel eliminar a configuración do servidor", "The configuration is valid and the connection could be established!" => "A configuración é correcta e pode estabelecerse a conexión.", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A configuración é correcta, mais a ligazón non. Comprobe a configuración do servidor e as credenciais.", "The configuration is invalid. Please look in the ownCloud log for further details." => "A configuración non é correcta. Vexa o rexistro de ownCloud para máis detalles", -"Deletion failed" => "Fallou o borrado", +"Deletion failed" => "Produciuse un fallo ao eliminar", "Take over settings from recent server configuration?" => "Tomar os recentes axustes de configuración do servidor?", "Keep settings?" => "Manter os axustes?", "Cannot add server configuration" => "Non é posíbel engadir a configuración do servidor", +"mappings cleared" => "limpadas as asignacións", +"Success" => "Correcto", +"Error" => "Erro", "Connection test succeeded" => "A proba de conexión foi satisfactoria", "Connection test failed" => "A proba de conexión fracasou", "Do you really want to delete the current Server Configuration?" => "Confirma que quere eliminar a configuración actual do servidor?", @@ -70,6 +74,16 @@ "Email Field" => "Campo do correo", "User Home Folder Naming Rule" => "Regra de nomeado do cartafol do usuario", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixar baleiro para o nome de usuario (predeterminado). Noutro caso, especifique un atributo LDAP/AD.", +"Internal Username" => "Nome de usuario interno", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "De xeito predeterminado, o nome de usuario interno crease a partires do atributo UUID. Asegurase de que o nome de usuario é único e de non ter que converter os caracteres. O nome de usuario interno ten a limitación de que só están permitidos estes caracteres: [ a-zA-Z0-9_.@- ]. Os outros caracteres substitúense pola súa correspondencia ASCII ou simplemente omítense. Nas colisións engadirase/incrementarase un número. O nome de usuario interno utilizase para identificar a un usuario interno. É tamén o nome predeterminado do cartafol persoal do usuario en ownCloud. Tamén é un porto de URL remoto, por exemplo, para todos os servizos *DAV. Con este axuste, o comportamento predeterminado pode ser sobrescrito. Para lograr un comportamento semellante ao anterior ownCloud 5 introduza o atributo do nome para amosar do usuario no seguinte campo. Déixeo baleiro para o comportamento predeterminado. Os cambios terán efecto só nas novas asignacións (engadidos) de usuarios de LDAP.", +"Internal Username Attribute:" => "Atributo do nome de usuario interno:", +"Override UUID detection" => "Ignorar a detección do UUID", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "De xeito predeterminado, ownCloud detecta automaticamente o atributo UUID. O atributo UUID utilizase para identificar, sen dúbida, aos usuarios e grupos LDAP. Ademais, crearase o usuario interno baseado no UUID, se non se especifica anteriormente o contrario. Pode anular a configuración e pasar un atributo da súa escolla. Vostede debe asegurarse de que o atributo da súa escolla pode ser recuperado polos usuarios e grupos e de que é único. Déixeo baleiro para o comportamento predeterminado. Os cambios terán efecto só nas novas asignacións (engadidos) de usuarios de LDAP.", +"UUID Attribute:" => "Atributo do UUID:", +"Username-LDAP User Mapping" => "Asignación do usuario ao «nome de usuario LDAP»", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloud utiliza os nomes de usuario para almacenar e asignar (meta) datos. Coa fin de identificar con precisión e recoñecer aos usuarios, cada usuario LDAP terá un nome de usuario interno. Isto require unha asignación de ownCloud nome de usuario a usuario LDAP. O nome de usuario creado asignase ao UUID do usuario LDAP. Ademais o DN almacenase na caché, para así reducir a interacción do LDAP, mais non se utiliza para a identificación. Se o DN cambia, os cambios poden ser atopados polo ownCloud. O nome interno no ownCloud utilizase en todo o ownCloud. A limpeza das asignacións deixará rastros en todas partes. A limpeza das asignacións non é sensíbel á configuración, afecta a todas as configuracións de LDAP! Non limpar nunca as asignacións nun entorno de produción. Limpar as asignacións só en fases de proba ou experimentais.", +"Clear Username-LDAP User Mapping" => "Limpar a asignación do usuario ao «nome de usuario LDAP»", +"Clear Groupname-LDAP Group Mapping" => "Limpar a asignación do grupo ao «nome de grupo LDAP»", "Test Configuration" => "Probar a configuración", "Help" => "Axuda" ); diff --git a/apps/user_ldap/l10n/he.php b/apps/user_ldap/l10n/he.php index c9b0e282f1..0d60768dcf 100644 --- a/apps/user_ldap/l10n/he.php +++ b/apps/user_ldap/l10n/he.php @@ -1,5 +1,14 @@ "מחיקה נכשלה", +"Keep settings?" => "האם לשמור את ההגדרות?", +"Cannot add server configuration" => "לא ניתן להוסיף את הגדרות השרת", +"Error" => "שגיאה", +"Connection test succeeded" => "בדיקת החיבור עברה בהצלחה", +"Connection test failed" => "בדיקת החיבור נכשלה", +"Do you really want to delete the current Server Configuration?" => "האם אכן למחוק את הגדרות השרת הנוכחיות?", +"Confirm Deletion" => "אישור המחיקה", +"Server configuration" => "הגדרות השרת", +"Add Server Configuration" => "הוספת הגדרות השרת", "Host" => "מארח", "User DN" => "DN משתמש", "Password" => "סיסמא", diff --git a/apps/user_ldap/l10n/hi.php b/apps/user_ldap/l10n/hi.php index 60d4ea98e8..45166eb0e3 100644 --- a/apps/user_ldap/l10n/hi.php +++ b/apps/user_ldap/l10n/hi.php @@ -1,3 +1,4 @@ "पासवर्ड", "Help" => "सहयोग" ); diff --git a/apps/user_ldap/l10n/hr.php b/apps/user_ldap/l10n/hr.php index 9150331506..cc8918301f 100644 --- a/apps/user_ldap/l10n/hr.php +++ b/apps/user_ldap/l10n/hr.php @@ -1,3 +1,5 @@ "Greška", +"Password" => "Lozinka", "Help" => "Pomoć" ); diff --git a/apps/user_ldap/l10n/hu_HU.php b/apps/user_ldap/l10n/hu_HU.php index a82a64ab32..361ae5e040 100644 --- a/apps/user_ldap/l10n/hu_HU.php +++ b/apps/user_ldap/l10n/hu_HU.php @@ -1,4 +1,5 @@ "Nem sikerült törölni a hozzárendeléseket.", "Failed to delete the server configuration" => "Nem sikerült törölni a kiszolgáló konfigurációját", "The configuration is valid and the connection could be established!" => "A konfiguráció érvényes, és a kapcsolat létrehozható!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A konfiguráció érvényes, de a kapcsolat nem hozható létre. Kérem ellenőrizze a kiszolgáló beállításait, és az elérési adatokat.", @@ -7,6 +8,9 @@ "Take over settings from recent server configuration?" => "Vegyük át a beállításokat az előző konfigurációból?", "Keep settings?" => "Tartsuk meg a beállításokat?", "Cannot add server configuration" => "Az új kiszolgáló konfigurációja nem hozható létre", +"mappings cleared" => "Töröltük a hozzárendeléseket", +"Success" => "Sikeres végrehajtás", +"Error" => "Hiba", "Connection test succeeded" => "A kapcsolatellenőrzés eredménye: sikerült", "Connection test failed" => "A kapcsolatellenőrzés eredménye: nem sikerült", "Do you really want to delete the current Server Configuration?" => "Tényleg törölni szeretné a kiszolgáló beállításait?", @@ -70,6 +74,13 @@ "Email Field" => "Email mező", "User Home Folder Naming Rule" => "A home könyvtár elérési útvonala", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Hagyja üresen, ha a felhasználónevet kívánja használni. Ellenkező esetben adjon meg egy LDAP/AD attribútumot!", +"Internal Username" => "Belső felhasználónév", +"Internal Username Attribute:" => "A belső felhasználónév attribútuma:", +"Override UUID detection" => "Az UUID-felismerés felülbírálása", +"UUID Attribute:" => "UUID attribútum:", +"Username-LDAP User Mapping" => "Felhasználó - LDAP felhasználó hozzárendelés", +"Clear Username-LDAP User Mapping" => "A felhasználó - LDAP felhasználó hozzárendelés törlése", +"Clear Groupname-LDAP Group Mapping" => "A csoport - LDAP csoport hozzárendelés törlése", "Test Configuration" => "A beállítások tesztelése", "Help" => "Súgó" ); diff --git a/apps/user_ldap/l10n/ia.php b/apps/user_ldap/l10n/ia.php index 3586bf5a2e..624fd4fa0e 100644 --- a/apps/user_ldap/l10n/ia.php +++ b/apps/user_ldap/l10n/ia.php @@ -1,3 +1,5 @@ "Error", +"Password" => "Contrasigno", "Help" => "Adjuta" ); diff --git a/apps/user_ldap/l10n/id.php b/apps/user_ldap/l10n/id.php index 1f6d8fcffe..c04d09fc67 100644 --- a/apps/user_ldap/l10n/id.php +++ b/apps/user_ldap/l10n/id.php @@ -3,10 +3,12 @@ "The configuration is valid and the connection could be established!" => "Konfigurasi valid dan koneksi dapat dilakukan!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfigurasi valid, tetapi Bind gagal. Silakan cek pengaturan server dan keamanan.", "The configuration is invalid. Please look in the ownCloud log for further details." => "Konfigurasi salah. Silakan lihat log ownCloud untuk lengkapnya.", -"Deletion failed" => "penghapusan gagal", +"Deletion failed" => "Penghapusan gagal", "Take over settings from recent server configuration?" => "Ambil alih pengaturan dari konfigurasi server saat ini?", "Keep settings?" => "Biarkan pengaturan?", "Cannot add server configuration" => "Gagal menambah konfigurasi server", +"Success" => "Sukses", +"Error" => "Galat", "Connection test succeeded" => "Tes koneksi sukses", "Connection test failed" => "Tes koneksi gagal", "Do you really want to delete the current Server Configuration?" => "Anda ingin menghapus Konfigurasi Server saat ini?", @@ -15,14 +17,14 @@ "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Peringatan: Modul LDAP PHP tidak terpasang, perangkat tidak akan bekerja. Silakan minta administrator sistem untuk memasangnya.", "Server configuration" => "Konfigurasi server", "Add Server Configuration" => "Tambah Konfigurasi Server", -"Host" => "host", +"Host" => "Host", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol dapat tidak ditulis, kecuali anda menggunakan SSL. Lalu jalankan dengan ldaps://", "Base DN" => "Base DN", "One Base DN per line" => "Satu Base DN per baris", "You can specify Base DN for users and groups in the Advanced tab" => "Anda dapat menetapkan Base DN untuk pengguna dan grup dalam tab Lanjutan", "User DN" => "User DN", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN dari klien pengguna yang dengannya tautan akan diterapkan, mis. uid=agen,dc=contoh,dc=com. Untuk akses anonim, biarkan DN dan kata sandi kosong.", -"Password" => "kata kunci", +"Password" => "Sandi", "For anonymous access, leave DN and Password empty." => "Untuk akses anonim, biarkan DN dan Kata sandi kosong.", "User Login Filter" => "gunakan saringan login", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definisikan filter untuk diterapkan, saat login dilakukan. %%uid menggantikan username saat login.", @@ -71,5 +73,5 @@ "User Home Folder Naming Rule" => "Aturan Penamaan Folder Home Pengguna", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Biarkan nama pengguna kosong (default). Atau tetapkan atribut LDAP/AD.", "Test Configuration" => "Uji Konfigurasi", -"Help" => "bantuan" +"Help" => "Bantuan" ); diff --git a/apps/user_ldap/l10n/is.php b/apps/user_ldap/l10n/is.php index 29bc769279..dadac9eeda 100644 --- a/apps/user_ldap/l10n/is.php +++ b/apps/user_ldap/l10n/is.php @@ -1,4 +1,5 @@ "Villa", "Host" => "Netþjónn", "Password" => "Lykilorð", "Help" => "Hjálp" diff --git a/apps/user_ldap/l10n/it.php b/apps/user_ldap/l10n/it.php index a2790fd1de..48bcbdf589 100644 --- a/apps/user_ldap/l10n/it.php +++ b/apps/user_ldap/l10n/it.php @@ -1,4 +1,5 @@ "Cancellazione delle associazioni non riuscita.", "Failed to delete the server configuration" => "Eliminazione della configurazione del server non riuscita", "The configuration is valid and the connection could be established!" => "La configurazione è valida e la connessione può essere stabilita.", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "La configurazione è valida, ma il Bind non è riuscito. Controlla le impostazioni del server e le credenziali.", @@ -7,6 +8,9 @@ "Take over settings from recent server configuration?" => "Vuoi recuperare le impostazioni dalla configurazione recente del server?", "Keep settings?" => "Vuoi mantenere le impostazioni?", "Cannot add server configuration" => "Impossibile aggiungere la configurazione del server", +"mappings cleared" => "associazioni cancellate", +"Success" => "Riuscito", +"Error" => "Errore", "Connection test succeeded" => "Prova di connessione riuscita", "Connection test failed" => "Prova di connessione non riuscita", "Do you really want to delete the current Server Configuration?" => "Vuoi davvero eliminare la configurazione attuale del server?", @@ -70,6 +74,16 @@ "Email Field" => "Campo Email", "User Home Folder Naming Rule" => "Regola di assegnazione del nome della cartella utente", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Lascia vuoto per il nome utente (predefinito). Altrimenti, specifica un attributo LDAP/AD.", +"Internal Username" => "Nome utente interno", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "In modo predefinito, il nome utente interno sarà creato dall'attributo UUID. Ciò assicura che il nome utente sia univoco e che non sia necessario convertire i caratteri. Il nome utente interno consente l'uso di determinati caratteri: [ a-zA-Z0-9_.@- ]. Altri caratteri sono sostituiti con il corrispondente ASCII o sono semplicemente omessi. In caso di conflitto, sarà incrementato/decrementato un numero. Il nome utente interno è utilizzato per identificare un utente internamente. Rappresenta, inoltre, il nome predefinito per la cartella home dell'utente in ownCloud. Costituisce anche una porta di URL remoti, ad esempio per tutti i servizi *DAV. Con questa impostazione, il comportamento predefinito può essere scavalcato. Per ottenere un comportamento simile alle versioni precedenti ownCloud 5, inserisci l'attributo del nome visualizzato dell'utente nel campo seguente. Lascialo vuoto per il comportamento predefinito. Le modifiche avranno effetto solo sui nuovo utenti LDAP associati (aggiunti).", +"Internal Username Attribute:" => "Attributo nome utente interno:", +"Override UUID detection" => "Ignora rilevamento UUID", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "In modo predefinito, ownCloud rileva automaticamente l'attributo UUID. L'attributo UUID è utilizzato per identificare senza alcun dubbio gli utenti e i gruppi LDAP. Inoltre, il nome utente interno sarà creato sulla base dell'UUID, se non è specificato in precedenza. Puoi ignorare l'impostazione e fornire un attributo di tua scelta. Assicurati che l'attributo scelto possa essere ottenuto sia per gli utenti che per i gruppi e che sia univoco. Lascialo vuoto per ottenere il comportamento predefinito. Le modifiche avranno effetto solo sui nuovi utenti e gruppi LDAP associati (aggiunti).", +"UUID Attribute:" => "Attributo UUID:", +"Username-LDAP User Mapping" => "Associazione Nome utente-Utente LDAP", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloud utilizza i nomi utente per archiviare e assegnare i (meta) dati. Per identificare con precisione e riconoscere gli utenti, ogni utente LDAP avrà un nome utente interno. Ciò richiede un'associazione tra il nome utente di ownCloud e l'utente LDAP. In aggiunta, il DN viene mantenuto in cache per ridurre l'interazione con LDAP, ma non è utilizzato per l'identificazione. Se il DN cambia, le modifiche saranno rilevate da ownCloud. Il nome utente interno di ownCloud è utilizzato dappertutto in ownCloud. La cancellazione delle associazioni lascerà tracce residue ovunque e interesserà esclusivamente la configurazione LDAP. Non cancellare mai le associazioni in un ambiente di produzione. Procedere alla cancellazione delle associazioni solo in una fase sperimentale o di test.", +"Clear Username-LDAP User Mapping" => "Cancella associazione Nome utente-Utente LDAP", +"Clear Groupname-LDAP Group Mapping" => "Cancella associazione Nome gruppo-Gruppo LDAP", "Test Configuration" => "Prova configurazione", "Help" => "Aiuto" ); diff --git a/apps/user_ldap/l10n/ja_JP.php b/apps/user_ldap/l10n/ja_JP.php index 3ae7d2e639..53fa9ae697 100644 --- a/apps/user_ldap/l10n/ja_JP.php +++ b/apps/user_ldap/l10n/ja_JP.php @@ -1,4 +1,5 @@ "マッピングのクリアに失敗しました。", "Failed to delete the server configuration" => "サーバ設定の削除に失敗しました", "The configuration is valid and the connection could be established!" => "設定は有効であり、接続を確立しました!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "設定は有効ですが、接続に失敗しました。サーバ設定と資格情報を確認して下さい。", @@ -7,6 +8,9 @@ "Take over settings from recent server configuration?" => "最近のサーバ設定から設定を引き継ぎますか?", "Keep settings?" => "設定を保持しますか?", "Cannot add server configuration" => "サーバ設定を追加できません", +"mappings cleared" => "マッピングをクリアしました", +"Success" => "成功", +"Error" => "エラー", "Connection test succeeded" => "接続テストに成功しました", "Connection test failed" => "接続テストに失敗しました", "Do you really want to delete the current Server Configuration?" => "現在のサーバ設定を本当に削除してもよろしいですか?", @@ -70,6 +74,16 @@ "Email Field" => "メールフィールド", "User Home Folder Naming Rule" => "ユーザのホームフォルダ命名規則", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "ユーザ名を空のままにしてください(デフォルト)。そうでない場合は、LDAPもしくはADの属性を指定してください。", -"Test Configuration" => "テスト設定", +"Internal Username" => "内部ユーザ名", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "デフォルトでは、内部ユーザ名はUUID属性から作成されます。これにより、ユーザ名がユニークであり、かつ文字の変換が必要ないことを保証します。内部ユーザ名には、[ a-zA-Z0-9_.@- ] の文字のみが有効であるという制限があり、その他の文字は対応する ASCII コードに変換されるか単に無視されます。そのため、他のユーザ名との衝突の回数が増加するでしょう。内部ユーザ名は、内部的にユーザを識別するために用いられ、また、ownCloudにおけるデフォルトのホームフォルダ名としても用いられます。例えば*DAVサービスのように、リモートURLのポートでもあります。この設定により、デフォルトの振る舞いを再定義します。ownCloud 5 以前と同じような振る舞いにするためには、以下のフィールドにユーザ表示名の属性を入力します。空にするとデフォルトの振る舞いとなります。変更は新しくマッピング(追加)されたLDAPユーザにおいてのみ有効となります。", +"Internal Username Attribute:" => "内部ユーザ名属性:", +"Override UUID detection" => "UUID検出を再定義する", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "デフォルトでは、ownCloud は UUID 属性を自動的に検出します。UUID属性は、LDAPユーザとLDAPグループを間違いなく識別するために利用されます。また、もしこれを指定しない場合は、内部ユーザ名はUUIDに基づいて作成されます。この設定は再定義することができ、あなたの選択した属性を用いることができます。選択した属性がユーザとグループの両方に対して適用でき、かつユニークであることを確認してください。空であればデフォルトの振る舞いとなります。変更は、新しくマッピング(追加)されたLDAPユーザとLDAPグループに対してのみ有効となります。", +"UUID Attribute:" => "UUID属性:", +"Username-LDAP User Mapping" => "ユーザ名とLDAPユーザのマッピング", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloudは(メタ) データの保存と割り当てにユーザ名を使用します。ユーザを正確に識別して認識するために、個々のLDAPユーザは内部ユーザ名を持っています。これは、ownCloudユーザ名とLDAPユーザ名の間のマッピングが必要であることを意味しています。生成されたユーザ名は、LDAPユーザのUUIDとマッピングされます。加えて、DNがLDAPとのインタラクションを削減するためにキャッシュされますが、識別には利用されません。DNが変わった場合は、変更をownCloudが見つけます。内部のownCloud名はownCloud全体に亘って利用されます。マッピングをクリアすると、いたるところに使われないままの物が残るでしょう。マッピングのクリアは設定に敏感ではありませんが、全てのLDAPの設定に影響を与えます!本番の環境では決してマッピングをクリアしないでください。テストもしくは実験の段階でのみマッピングのクリアを行なってください。", +"Clear Username-LDAP User Mapping" => "ユーザ名とLDAPユーザのマッピングをクリアする", +"Clear Groupname-LDAP Group Mapping" => "グループ名とLDAPグループのマッピングをクリアする", +"Test Configuration" => "設定をテスト", "Help" => "ヘルプ" ); diff --git a/apps/user_ldap/l10n/ka_GE.php b/apps/user_ldap/l10n/ka_GE.php index 0385103f9b..8057f7c845 100644 --- a/apps/user_ldap/l10n/ka_GE.php +++ b/apps/user_ldap/l10n/ka_GE.php @@ -1,6 +1,77 @@ "წაშლის ველი", +"Failed to delete the server configuration" => "შეცდომა სერვერის კონფიგურაციის წაშლისას", +"The configuration is valid and the connection could be established!" => "კონფიგურაცია მართებულია და კავშირი დამყარდება!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "კონფიგურაცია მართებულია, მაგრამ მიერთება ვერ მოხერხდა. გთხოვთ შეამოწმოთ სერვერის პარამეტრები და აუთენთიკაციის პარამეტრები.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "კონფიგურაცია არ არის მართებული. გთხოვთ ჩაიხედოთ დეტალური ინფორმაციისთვის ownCloud –ის ლოგში.", +"Deletion failed" => "წაშლა ვერ განხორციელდა", +"Take over settings from recent server configuration?" => "დაბრუნდებით სერვერის წინა კონფიგურაციაში?", +"Keep settings?" => "დავტოვოთ პარამეტრები?", +"Cannot add server configuration" => "სერვერის პარამეტრების დამატება ვერ მოხერხდა", +"Success" => "დასრულდა", +"Error" => "შეცდომა", +"Connection test succeeded" => "კავშირის ტესტირება მოხერხდა", +"Connection test failed" => "კავშირის ტესტირება ვერ მოხერხდა", +"Do you really want to delete the current Server Configuration?" => "ნამდვილად გინდათ წაშალოთ სერვერის მიმდინარე პარამეტრები?", +"Confirm Deletion" => "წაშლის დადასტურება", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "გაფრთხილება: აპლიკაციის user_ldap და user_webdavauth არათავსებადია. თქვენ შეიძლება შეეჩეხოთ მოულოდნელ შშედეგებს. თხოვეთ თქვენს ადმინისტრატორს ჩათიშოს ერთერთი.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "გაფრთხილება: PHP LDAP მოდული არ არის ინსტალირებული, ბექენდი არ იმუშავებს. თხოვეთ თქვენს ადმინისტრატორს დააინსტალიროს ის.", +"Server configuration" => "სერვერის პარამეტრები", +"Add Server Configuration" => "სერვერის პარამეტრების დამატება", "Host" => "ჰოსტი", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "თქვენ შეგიძლიათ გამოტოვოთ პროტოკოლი. გარდა ამისა გჭირდებათ SSL. შემდეგ დაიწყეთ ldaps://", +"Base DN" => "საწყისი DN", +"One Base DN per line" => "ერთი საწყისი DN ერთ ხაზზე", +"You can specify Base DN for users and groups in the Advanced tab" => "თქვენ შეგიძლიათ მიუთითოთ საწყისი DN მომხმარებლებისთვის და ჯგუფებისთვის Advanced ტაბში", +"User DN" => "მომხმარებლის DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "მომხმარებლის DN რომელთანაც უნდა მოხდეს დაკავშირება მოხდება შემდეგნაირად მაგ: uid=agent,dc=example,dc=com. ხოლო ანონიმური დაშვებისთვის, დატოვეთ DN–ის და პაროლის ველები ცარიელი.", +"Password" => "პაროლი", +"For anonymous access, leave DN and Password empty." => "ანონიმური დაშვებისთვის, დატოვეთ DN–ის და პაროლის ველები ცარიელი.", +"User Login Filter" => "მომხმარებლის ფილტრი", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "როცა შემოსვლა განხორციელდება ასეიძლება მოვახდინოთ გაფილტვრა. %%uid შეიცვლება იუზერნეიმით მომხმარებლის ველში.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "გამოიყენეთ %%uid დამასრულებელი მაგ: \"uid=%%uid\"", +"User List Filter" => "მომხმარებლებიის სიის ფილტრი", +"Defines the filter to apply, when retrieving users." => "გაფილტვრა განხორციელდება, როცა მომხმარებლების სია ჩამოიტვირთება.", +"without any placeholder, e.g. \"objectClass=person\"." => "ყოველგვარი დამასრულებელის გარეშე, მაგ: \"objectClass=person\".", +"Group Filter" => "ჯგუფის ფილტრი", +"Defines the filter to apply, when retrieving groups." => "გაფილტვრა განხორციელდება, როცა ჯგუფის სია ჩამოიტვირთება.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ყოველგვარი დამასრულებელის გარეშე, მაგ: \"objectClass=posixGroup\".", +"Connection Settings" => "კავშირის პარამეტრები", +"Configuration Active" => "კონფიგურაცია აქტიურია", +"When unchecked, this configuration will be skipped." => "როცა გადანიშნულია, ეს კონფიგურაცია გამოტოვებული იქნება.", "Port" => "პორტი", +"Backup (Replica) Host" => "ბექაფ (რეპლიკა) ჰოსტი", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "მიუთითეთ რაიმე ბექაფ ჰოსტი. ის უნდა იყოს ძირითადი LDAP/AD სერვერის რეპლიკა.", +"Backup (Replica) Port" => "ბექაფ (რეპლიკა) პორტი", +"Disable Main Server" => "გამორთეთ ძირითადი სერვერი", +"When switched on, ownCloud will only connect to the replica server." => "როცა მონიშნულია, ownCloud დაუკავშირდება მხოლოდ რეპლიკა სერვერს.", +"Use TLS" => "გამოიყენეთ TLS", +"Do not use it additionally for LDAPS connections, it will fail." => "არ გამოიყენოთ დამატებით LDAPS კავშირი. ის წარუმატებლად დასრულდება.", +"Case insensitve LDAP server (Windows)" => "LDAP server (Windows)", +"Turn off SSL certificate validation." => "გამორთეთ SSL სერთიფიკატის ვალიდაცია.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "იმ შემთხვევაში თუ მუშაობს მხოლოდ ეს ოფცია, დააიმპორტეთ LDAP სერვერის SSL სერთიფიკატი თქვენს ownCloud სერვერზე.", +"Not recommended, use for testing only." => "არ არის რეკომენდირებული, გამოიყენეთ მხოლოდ სატესტოდ.", +"Cache Time-To-Live" => "ქეშის სიცოცხლის ხანგრძლივობა", +"in seconds. A change empties the cache." => "წამებში. ცვლილება ასუფთავებს ქეშს.", +"Directory Settings" => "დირექტორიის პარამეტრები", +"User Display Name Field" => "მომხმარებლის დისფლეის სახელის ფილდი", +"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP ატრიბუტი მომხმარებლის ownCloud სახელის გენერაციისთვის.", +"Base User Tree" => "ძირითად მომხმარებელთა სია", +"One User Base DN per line" => "ერთი მომხმარებლის საწყისი DN ერთ ხაზზე", +"User Search Attributes" => "მომხმარებლის ძებნის ატრიბუტი", +"Optional; one attribute per line" => "ოფციონალური; თითო ატრიბუტი თითო ხაზზე", +"Group Display Name Field" => "ჯგუფის დისფლეის სახელის ფილდი", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP ატრიბუტი ჯგუფის ownCloud სახელის გენერაციისთვის.", +"Base Group Tree" => "ძირითად ჯგუფთა სია", +"One Group Base DN per line" => "ერთი ჯგუფის საწყისი DN ერთ ხაზზე", +"Group Search Attributes" => "ჯგუფური ძებნის ატრიბუტი", +"Group-Member association" => "ჯგუფის წევრობის ასოციაცია", +"Special Attributes" => "სპეციალური ატრიბუტები", +"Quota Field" => "ქვოტას ველი", +"Quota Default" => "საწყისი ქვოტა", +"in bytes" => "ბაიტებში", +"Email Field" => "იმეილის ველი", +"User Home Folder Naming Rule" => "მომხმარებლის Home დირექტორიის სახელების დარქმევის წესი", +"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "დატოვეთ ცარიელი მომხმარებლის სახელი (default). სხვა დანარჩენში მიუთითეთ LDAP/AD ატრიბუტი.", +"Test Configuration" => "კავშირის ტესტირება", "Help" => "დახმარება" ); diff --git a/apps/user_ldap/l10n/ko.php b/apps/user_ldap/l10n/ko.php index 8aa9fe74b3..b8196e09d0 100644 --- a/apps/user_ldap/l10n/ko.php +++ b/apps/user_ldap/l10n/ko.php @@ -1,6 +1,7 @@ "삭제 실패", "Keep settings?" => "설정을 유지합니까?", +"Error" => "오류", "Connection test succeeded" => "연결 시험 성공", "Connection test failed" => "연결 시험 실패", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "경고: user_ldap 앱과 user_webdavauth 앱은 호환되지 않습니다. 오동작을 일으킬 수 있으므로, 시스템 관리자에게 요청하여 둘 중 하나만 사용하도록 하십시오.", diff --git a/apps/user_ldap/l10n/ku_IQ.php b/apps/user_ldap/l10n/ku_IQ.php index 1ae808ddd9..00602ae5d7 100644 --- a/apps/user_ldap/l10n/ku_IQ.php +++ b/apps/user_ldap/l10n/ku_IQ.php @@ -1,3 +1,6 @@ "سه‌رکه‌وتن", +"Error" => "هه‌ڵه", +"Password" => "وشەی تێپەربو", "Help" => "یارمەتی" ); diff --git a/apps/user_ldap/l10n/lb.php b/apps/user_ldap/l10n/lb.php index 39ed627ce2..cf58c9ec5b 100644 --- a/apps/user_ldap/l10n/lb.php +++ b/apps/user_ldap/l10n/lb.php @@ -1,5 +1,6 @@ "Konnt net läschen", +"Error" => "Fehler", "Password" => "Passwuert", "Help" => "Hëllef" ); diff --git a/apps/user_ldap/l10n/lt_LT.php b/apps/user_ldap/l10n/lt_LT.php index aa21dd2d3c..6f396847b8 100644 --- a/apps/user_ldap/l10n/lt_LT.php +++ b/apps/user_ldap/l10n/lt_LT.php @@ -1,5 +1,6 @@ "Ištrinti nepavyko", +"Error" => "Klaida", "Password" => "Slaptažodis", "Group Filter" => "Grupės filtras", "Port" => "Prievadas", diff --git a/apps/user_ldap/l10n/lv.php b/apps/user_ldap/l10n/lv.php index 50126664e5..73ffedcb13 100644 --- a/apps/user_ldap/l10n/lv.php +++ b/apps/user_ldap/l10n/lv.php @@ -7,6 +7,7 @@ "Take over settings from recent server configuration?" => "Paņemt iestatījumus no nesenas servera konfigurācijas?", "Keep settings?" => "Paturēt iestatījumus?", "Cannot add server configuration" => "Nevar pievienot servera konfigurāciju", +"Error" => "Kļūda", "Connection test succeeded" => "Savienojuma tests ir veiksmīgs", "Connection test failed" => "Savienojuma tests cieta neveiksmi", "Do you really want to delete the current Server Configuration?" => "Vai tiešām vēlaties dzēst pašreizējo servera konfigurāciju?", diff --git a/apps/user_ldap/l10n/mk.php b/apps/user_ldap/l10n/mk.php index 7d34ff4949..6a060aca41 100644 --- a/apps/user_ldap/l10n/mk.php +++ b/apps/user_ldap/l10n/mk.php @@ -1,5 +1,6 @@ "Бришењето е неуспешно", +"Error" => "Грешка", "Host" => "Домаќин", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Може да го скокнете протколот освен ако не ви треба SSL. Тогаш ставете ldaps://", "Password" => "Лозинка", diff --git a/apps/user_ldap/l10n/ms_MY.php b/apps/user_ldap/l10n/ms_MY.php index 17a6cbe2cb..b3004028c5 100644 --- a/apps/user_ldap/l10n/ms_MY.php +++ b/apps/user_ldap/l10n/ms_MY.php @@ -1,4 +1,6 @@ "Pemadaman gagal", +"Error" => "Ralat", +"Password" => "Kata laluan", "Help" => "Bantuan" ); diff --git a/apps/user_ldap/l10n/nb_NO.php b/apps/user_ldap/l10n/nb_NO.php index c4700245f2..f8cdf694ff 100644 --- a/apps/user_ldap/l10n/nb_NO.php +++ b/apps/user_ldap/l10n/nb_NO.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "Hent innstillinger fra tidligere tjener-konfigurasjon?", "Keep settings?" => "Behold innstillinger?", "Cannot add server configuration" => "Kan ikke legge til tjener-konfigurasjon", +"Success" => "Suksess", +"Error" => "Feil", "Connection test succeeded" => "Tilkoblingstest lyktes", "Connection test failed" => "Tilkoblingstest mislyktes", "Do you really want to delete the current Server Configuration?" => "Er du sikker på at du vil slette aktiv tjener-konfigurasjon?", diff --git a/apps/user_ldap/l10n/nl.php b/apps/user_ldap/l10n/nl.php index 7973c66cd1..c75aae3ea4 100644 --- a/apps/user_ldap/l10n/nl.php +++ b/apps/user_ldap/l10n/nl.php @@ -1,4 +1,5 @@ "Niet gelukt de vertalingen leeg te maken.", "Failed to delete the server configuration" => "Verwijderen serverconfiguratie mislukt", "The configuration is valid and the connection could be established!" => "De configuratie is geldig en de verbinding is geslaagd!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "De configuratie is geldig, maar Bind mislukte. Controleer de serverinstellingen en inloggegevens.", @@ -7,6 +8,9 @@ "Take over settings from recent server configuration?" => "Overnemen instellingen van de recente serverconfiguratie?", "Keep settings?" => "Instellingen bewaren?", "Cannot add server configuration" => "Kon de serverconfiguratie niet toevoegen", +"mappings cleared" => "vertaaltabel leeggemaakt", +"Success" => "Succes", +"Error" => "Fout", "Connection test succeeded" => "Verbindingstest geslaagd", "Connection test failed" => "Verbindingstest mislukt", "Do you really want to delete the current Server Configuration?" => "Wilt u werkelijk de huidige Serverconfiguratie verwijderen?", @@ -70,6 +74,16 @@ "Email Field" => "E-mailveld", "User Home Folder Naming Rule" => "Gebruikers Home map naamgevingsregel", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Laat leeg voor de gebruikersnaam (standaard). Of, specificeer een LDAP/AD attribuut.", +"Internal Username" => "Interne gebruikersnaam", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "Standaard wordt de interne gebruikersnaam aangemaakt op basis van de UUID attribuut. Het zorgt ervoor dat de gebruikersnaam uniek is en dat tekens niet hoeven te worden geconverteerd. De interne gebruikersnaam heeft als beperking dat alleen deze tekens zijn toegestaan​​: [a-zA-Z0-9_.@- ]. Andere tekens worden vervangen door hun ASCII vertaling of gewoonweg weggelaten. Bij identieke namen wordt een nummer toegevoegd of verhoogd. De interne gebruikersnaam wordt gebruikt om een ​​gebruiker binnen het systeem te herkennen. Het is ook de standaardnaam voor de standaardmap van de gebruiker in ownCloud. Het is ook een vertaling voor externe URL's, bijvoorbeeld voor alle * DAV diensten. Met deze instelling kan het standaardgedrag worden overschreven. Om een soortgelijk gedrag te bereiken als van voor ownCloud 5, voer het gebruikersweergavenaam attribuut in in het volgende veld. Laat het leeg voor standaard gedrag. Veranderingen worden alleen toegepast op nieuw in kaart gebracht (toegevoegde) LDAP-gebruikers.", +"Internal Username Attribute:" => "Interne gebruikersnaam attribuut:", +"Override UUID detection" => "Negeren UUID detectie", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "Standaard herkent ownCloud het UUID attribuut automatisch. Het UUID attribuut wordt gebruikt om LDAP-gebruikers en -groepen uniek te identificeren. Ook zal de interne gebruikersnaam worden aangemaakt op basis van het UUID, tenzij dit hierboven anders is aangegeven. U kunt de instelling overschrijven en zelf een waarde voor het attribuut opgeven. U moet ervoor zorgen dat het ingestelde attribuut kan worden opgehaald voor zowel gebruikers als groepen en dat het uniek is. Laat het leeg voor standaard gedrag. Veranderingen worden alleen doorgevoerd op nieuw in kaart gebrachte (toegevoegde) LDAP-gebruikers en-groepen.", +"UUID Attribute:" => "UUID Attribuut:", +"Username-LDAP User Mapping" => "Gebruikersnaam-LDAP gebruikers vertaling", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloud maakt gebruik van gebruikersnamen om (meta) data op te slaan en toe te wijzen. Om gebruikers uniek te identificeren, zal elke LDAP-gebruiker ook een interne gebruikersnaam krijgen. Dit vereist een mapping van de ownCloud gebruikersnaam naar een ​​LDAP-gebruiker. De gecreëerde gebruikersnaam is gekoppeld aan de UUID van de LDAP-gebruiker. Ook de 'DN' wordt gecached om het aantal LDAP transacties te verminderen, maar deze wordt niet gebruikt voor identificatie. Als de DN verandert, zullen de veranderingen worden gevonden door ownCloud. De interne ownCloud naam wordt overal in ownCloud gebruikt. Wissen van de koppeling zal overal overblijfsel laten staan. Het wissen van Mappings is niet configuratiegevoelig, maar het raakt wel alle LDAP instellingen! Zorg ervoor dat deze Mappings nooit in een productieomgeving plaatsvinden. Maak ze alleen leeg in een test-of ontwikkelomgeving.", +"Clear Username-LDAP User Mapping" => "Leegmaken Gebruikersnaam-LDAP gebruikers vertaling", +"Clear Groupname-LDAP Group Mapping" => "Leegmaken Groepsnaam-LDAP groep vertaling", "Test Configuration" => "Test configuratie", "Help" => "Help" ); diff --git a/apps/user_ldap/l10n/nn_NO.php b/apps/user_ldap/l10n/nn_NO.php index 54d1f158f6..4591002280 100644 --- a/apps/user_ldap/l10n/nn_NO.php +++ b/apps/user_ldap/l10n/nn_NO.php @@ -1,3 +1,6 @@ "Feil ved sletting", +"Error" => "Feil", +"Password" => "Passord", "Help" => "Hjelp" ); diff --git a/apps/user_ldap/l10n/oc.php b/apps/user_ldap/l10n/oc.php index a128638172..95ab51caad 100644 --- a/apps/user_ldap/l10n/oc.php +++ b/apps/user_ldap/l10n/oc.php @@ -1,4 +1,6 @@ "Fracàs d'escafatge", +"Error" => "Error", +"Password" => "Senhal", "Help" => "Ajuda" ); diff --git a/apps/user_ldap/l10n/pl.php b/apps/user_ldap/l10n/pl.php index 776aa445e4..7edfe0919e 100644 --- a/apps/user_ldap/l10n/pl.php +++ b/apps/user_ldap/l10n/pl.php @@ -1,12 +1,16 @@ "Nie udało się wyczyścić mapowania.", "Failed to delete the server configuration" => "Nie można usunąć konfiguracji serwera", "The configuration is valid and the connection could be established!" => "Konfiguracja jest prawidłowa i można ustanowić połączenie!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Konfiguracja jest prawidłowa, ale Bind nie. Sprawdź ustawienia serwera i poświadczenia.", "The configuration is invalid. Please look in the ownCloud log for further details." => "Konfiguracja jest nieprawidłowa. Proszę przejrzeć logi dziennika ownCloud ", -"Deletion failed" => "Skasowanie nie powiodło się", +"Deletion failed" => "Usunięcie nie powiodło się", "Take over settings from recent server configuration?" => "Przejmij ustawienia z ostatnich konfiguracji serwera?", "Keep settings?" => "Zachować ustawienia?", "Cannot add server configuration" => "Nie można dodać konfiguracji serwera", +"mappings cleared" => "Mapoanie wyczyszczone", +"Success" => "Sukces", +"Error" => "Błąd", "Connection test succeeded" => "Test połączenia udany", "Connection test failed" => "Test połączenia nie udany", "Do you really want to delete the current Server Configuration?" => "Czy chcesz usunąć bieżącą konfigurację serwera?", @@ -70,6 +74,13 @@ "Email Field" => "Pole email", "User Home Folder Naming Rule" => "Reguły nazewnictwa folderu domowego użytkownika", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Pozostaw puste dla user name (domyślnie). W przeciwnym razie podaj atrybut LDAP/AD.", +"Internal Username" => "Wewnętrzna nazwa użytkownika", +"Internal Username Attribute:" => "Wewnętrzny atrybut nazwy uzżytkownika:", +"Override UUID detection" => "Zastąp wykrywanie UUID", +"UUID Attribute:" => "Atrybuty UUID:", +"Username-LDAP User Mapping" => "Mapowanie użytkownika LDAP", +"Clear Username-LDAP User Mapping" => "Czyść Mapowanie użytkownika LDAP", +"Clear Groupname-LDAP Group Mapping" => "Czyść Mapowanie nazwy grupy LDAP", "Test Configuration" => "Konfiguracja testowa", "Help" => "Pomoc" ); diff --git a/apps/user_ldap/l10n/pt_BR.php b/apps/user_ldap/l10n/pt_BR.php index a728ea15fd..22247b8100 100644 --- a/apps/user_ldap/l10n/pt_BR.php +++ b/apps/user_ldap/l10n/pt_BR.php @@ -1,4 +1,5 @@ "Falha ao limpar os mapeamentos.", "Failed to delete the server configuration" => "Falha ao deletar a configuração do servidor", "The configuration is valid and the connection could be established!" => "A configuração é válida e a conexão foi estabelecida!", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "A configuração é válida, mas o Bind falhou. Confira as configurações do servidor e as credenciais.", @@ -7,6 +8,9 @@ "Take over settings from recent server configuration?" => "Tomar parámetros de recente configuração de servidor?", "Keep settings?" => "Manter ajustes?", "Cannot add server configuration" => "Impossível adicionar a configuração do servidor", +"mappings cleared" => "mapeamentos limpos", +"Success" => "Sucesso", +"Error" => "Erro", "Connection test succeeded" => "Teste de conexão bem sucedida", "Connection test failed" => "Teste de conexão falhou", "Do you really want to delete the current Server Configuration?" => "Você quer realmente deletar as atuais Configurações de Servidor?", @@ -70,6 +74,16 @@ "Email Field" => "Campo de Email", "User Home Folder Naming Rule" => "Regra para Nome da Pasta Pessoal do Usuário", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Deixe vazio para nome de usuário (padrão). Caso contrário, especifique um atributo LDAP/AD.", +"Internal Username" => "Nome de usuário interno", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "Por padrão, o nome de usuário interno será criado a partir do atributo UUID. Ele garante que o nome de usuário é única e personagens não precisam ser convertidos. O nome de usuário interno tem a restrição de que apenas estes caracteres são permitidos: [a-zA-Z0-9_ @ -.]. Outros caracteres são substituídas por seu correspondente ASCII ou simplesmente serão omitidos. Em colisões um número será adicionado/aumentado. O nome de utilizador interna é usada para identificar um utilizador internamente. É também o nome padrão para a pasta home do usuário em ownCloud. É também um porto de URLs remoto, por exemplo, para todos os serviços de *DAV. Com esta definição, o comportamento padrão pode ser anulado. Para conseguir um comportamento semelhante como antes ownCloud 5 entrar na tela atributo nome de usuário no campo seguinte. Deixe-o vazio para o comportamento padrão. As alterações terão efeito apenas no recém mapeados (adicionado) de usuários LDAP. ", +"Internal Username Attribute:" => "Atributo Interno de Nome de Usuário:", +"Override UUID detection" => "Substituir detecção UUID", +"By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups." => "Por padrão, ownCloud detecta automaticamente o atributo UUID. O atributo UUID é usado para identificar, sem dúvida, os usuários e grupos LDAP. Além disso, o nome de usuário interno será criado com base no UUID, se não especificada acima. Você pode substituir a configuração e passar um atributo de sua escolha. Você deve certificar-se de que o atributo de sua escolha pode ser obtida tanto para usuários e grupos e é único. Deixe-o vazio para o comportamento padrão. As alterações terão efeito apenas no recém mapeados (adicionado) de usuários e grupos LDAP.", +"UUID Attribute:" => "Atributo UUID:", +"Username-LDAP User Mapping" => "Usuário-LDAP Mapeamento de Usuário", +"ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage." => "ownCloud usa nomes de usuários para armazenar e atribuir (meta) dados. A fim de identificar com precisão e reconhecer usuários, cada usuário LDAP terá um nome de usuário interno. Isso requer um mapeamento de ownCloud do nome de usuário para usuário LDAP. O nome de usuário criado é mapeado para o UUID do usuário LDAP. Além disso, o DN está em cache, assim como para reduzir a interação LDAP, mas que não é utilizado para a identificação. Se a DN muda, as mudanças serão encontradas pelo ownCloud. O nome ownCloud interno é utilizado em todo ownCloud. Limpando os mapeamentos terá sobras em todos os lugares. Limpeza dos mapeamentos não são sensíveis a configuração, isso afeta todas as configurações LDAP! Nunca limpar os mapeamentos em um ambiente de produção. Somente limpe os mapeamentos em uma fase de testes ou experimental.", +"Clear Username-LDAP User Mapping" => "Limpar Mapeamento de Usuário Nome de Usuário-LDAP", +"Clear Groupname-LDAP Group Mapping" => "Limpar NomedoGrupo-LDAP Mapeamento do Grupo", "Test Configuration" => "Teste de Configuração", "Help" => "Ajuda" ); diff --git a/apps/user_ldap/l10n/pt_PT.php b/apps/user_ldap/l10n/pt_PT.php index 3092d06143..ed1e0f376d 100644 --- a/apps/user_ldap/l10n/pt_PT.php +++ b/apps/user_ldap/l10n/pt_PT.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "Assumir as configurações da configuração do servidor mais recente?", "Keep settings?" => "Manter as definições?", "Cannot add server configuration" => "Não foi possível adicionar as configurações do servidor.", +"Success" => "Sucesso", +"Error" => "Erro", "Connection test succeeded" => "Teste de conecção passado com sucesso.", "Connection test failed" => "Erro no teste de conecção.", "Do you really want to delete the current Server Configuration?" => "Deseja realmente apagar as configurações de servidor actuais?", @@ -22,7 +24,7 @@ "You can specify Base DN for users and groups in the Advanced tab" => "Pode especificar o ND Base para utilizadores e grupos no separador Avançado", "User DN" => "DN do utilizador", "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "O DN to cliente ", -"Password" => "Palavra-passe", +"Password" => "Password", "For anonymous access, leave DN and Password empty." => "Para acesso anónimo, deixe DN e a Palavra-passe vazios.", "User Login Filter" => "Filtro de login de utilizador", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Define o filtro a aplicar, para aquando de uma tentativa de login. %%uid substitui o nome de utilizador utilizado.", diff --git a/apps/user_ldap/l10n/ro.php b/apps/user_ldap/l10n/ro.php index 8f55a35b49..260ee61063 100644 --- a/apps/user_ldap/l10n/ro.php +++ b/apps/user_ldap/l10n/ro.php @@ -1,5 +1,7 @@ "Ștergerea a eșuat", +"Success" => "Succes", +"Error" => "Eroare", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Atentie: Apps user_ldap si user_webdavauth sunt incompatibile. Este posibil sa experimentati un comportament neasteptat. Vă rugăm să întrebați administratorul de sistem pentru a dezactiva una dintre ele.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Atenție Modulul PHP LDAP nu este instalat, infrastructura nu va funcționa. Contactează administratorul sistemului pentru al instala.", "Host" => "Gazdă", diff --git a/apps/user_ldap/l10n/ru.php b/apps/user_ldap/l10n/ru.php index 0746e1e892..eed6d373b9 100644 --- a/apps/user_ldap/l10n/ru.php +++ b/apps/user_ldap/l10n/ru.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "Принять настройки из последней конфигурации сервера?", "Keep settings?" => "Сохранить настройки?", "Cannot add server configuration" => "Не получилось добавить конфигурацию сервера", +"Success" => "Успешно", +"Error" => "Ошибка", "Connection test succeeded" => "Проверка соединения удалась", "Connection test failed" => "Проверка соединения не удалась", "Do you really want to delete the current Server Configuration?" => "Вы действительно хотите удалить существующую конфигурацию сервера?", diff --git a/apps/user_ldap/l10n/ru_RU.php b/apps/user_ldap/l10n/ru_RU.php index a4ed503b1d..7b6833ebf8 100644 --- a/apps/user_ldap/l10n/ru_RU.php +++ b/apps/user_ldap/l10n/ru_RU.php @@ -1,42 +1,4 @@ "Удаление не удалось", -"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Предупреждение: Приложения user_ldap и user_webdavauth несовместимы. Вы можете столкнуться с неожиданным поведением системы. Пожалуйста, обратитесь к системному администратору для отключения одного из них.", -"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Предупреждение: Модуль PHP LDAP не установлен, бэкэнд не будет работать. Пожалуйста, обратитесь к Вашему системному администратору, чтобы установить его.", -"Host" => "Хост", -"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Вы можете пропустить протокол, если Вам не требуется SSL. Затем начните с ldaps://", -"Base DN" => "База DN", -"One Base DN per line" => "Одно базовое DN на линию", -"You can specify Base DN for users and groups in the Advanced tab" => "Вы можете задать Base DN для пользователей и групп во вкладке «Дополнительно»", -"User DN" => "DN пользователя", -"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN клиентского пользователя, с которого должна осуществляться привязка, например, uid=agent,dc=example,dc=com. Для анонимного доступа оставьте поля DN и Пароль пустыми.", -"Password" => "Пароль", -"For anonymous access, leave DN and Password empty." => "Для анонимного доступа оставьте поля DN и пароль пустыми.", -"User Login Filter" => "Фильтр имен пользователей", -"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Задает фильтр, применяемый при загрузке пользователя. %%uid заменяет имя пользователя при входе.", -"use %%uid placeholder, e.g. \"uid=%%uid\"" => "используйте %%uid заполнитель, например, \"uid=%%uid\"", -"User List Filter" => "Фильтр списка пользователей", -"Defines the filter to apply, when retrieving users." => "Задает фильтр, применяемый при получении пользователей.", -"without any placeholder, e.g. \"objectClass=person\"." => "без каких-либо заполнителей, например, \"objectClass=person\".", -"Group Filter" => "Групповой фильтр", -"Defines the filter to apply, when retrieving groups." => "Задает фильтр, применяемый при получении групп.", -"without any placeholder, e.g. \"objectClass=posixGroup\"." => "без каких-либо заполнителей, например, \"objectClass=posixGroup\".", -"Port" => "Порт", -"Use TLS" => "Использовать TLS", -"Case insensitve LDAP server (Windows)" => "Нечувствительный к регистру LDAP-сервер (Windows)", -"Turn off SSL certificate validation." => "Выключить проверку сертификата SSL.", -"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Если соединение работает только с этой опцией, импортируйте SSL-сертификат LDAP сервера в ваш ownCloud сервер.", -"Not recommended, use for testing only." => "Не рекомендовано, используйте только для тестирования.", -"in seconds. A change empties the cache." => "в секундах. Изменение очищает кэш.", -"User Display Name Field" => "Поле, отображаемое как имя пользователя", -"The LDAP attribute to use to generate the user`s ownCloud name." => "Атрибут LDAP, используемый для создания имени пользователя в ownCloud.", -"Base User Tree" => "Базовое дерево пользователей", -"One User Base DN per line" => "Одно пользовательское базовое DN на линию", -"Group Display Name Field" => "Поле, отображаемое как имя группы", -"The LDAP attribute to use to generate the groups`s ownCloud name." => "Атрибут LDAP, используемый для создания группового имени в ownCloud.", -"Base Group Tree" => "Базовое дерево групп", -"One Group Base DN per line" => "Одно групповое базовое DN на линию", -"Group-Member association" => "Связь член-группа", -"in bytes" => "в байтах", -"Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Оставьте пустым под имя пользователя (по умолчанию). В противном случае задайте LDAP/AD атрибут.", -"Help" => "Помощь" +"Success" => "Успех", +"Error" => "Ошибка" ); diff --git a/apps/user_ldap/l10n/si_LK.php b/apps/user_ldap/l10n/si_LK.php index 50124e4d54..1d81b341b4 100644 --- a/apps/user_ldap/l10n/si_LK.php +++ b/apps/user_ldap/l10n/si_LK.php @@ -1,5 +1,7 @@ "මකාදැමීම අසාර්ථකයි", +"Success" => "සාර්ථකයි", +"Error" => "දෝෂයක්", "Host" => "සත්කාරකය", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL අවශ්‍යය වන විට පමණක් හැර, අන් අවස්ථාවන්හිදී ප්‍රොටොකෝලය අත් හැරිය හැක. භාවිතා කරන විට ldaps:// ලෙස ආරම්භ කරන්න", "Password" => "මුර පදය", diff --git a/apps/user_ldap/l10n/sk_SK.php b/apps/user_ldap/l10n/sk_SK.php index cb55762e64..e36a158936 100644 --- a/apps/user_ldap/l10n/sk_SK.php +++ b/apps/user_ldap/l10n/sk_SK.php @@ -1,4 +1,5 @@ "Nepodarilo sa vymazať mapovania.", "Failed to delete the server configuration" => "Zlyhalo zmazanie nastavenia servera.", "The configuration is valid and the connection could be established!" => "Nastavenie je v poriadku a pripojenie je stabilné.", "The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Nastavenie je v poriadku, ale pripojenie zlyhalo. Skontrolujte nastavenia servera a prihlasovacie údaje.", @@ -7,6 +8,9 @@ "Take over settings from recent server configuration?" => "Prebrať nastavenia z nedávneho nastavenia servera?", "Keep settings?" => "Ponechať nastavenia?", "Cannot add server configuration" => "Nemožno pridať nastavenie servera", +"mappings cleared" => "mapovanie vymazané", +"Success" => "Úspešné", +"Error" => "Chyba", "Connection test succeeded" => "Test pripojenia bol úspešný", "Connection test failed" => "Test pripojenia zlyhal", "Do you really want to delete the current Server Configuration?" => "Naozaj chcete zmazať súčasné nastavenie servera?", @@ -70,6 +74,11 @@ "Email Field" => "Pole email", "User Home Folder Naming Rule" => "Pravidlo pre nastavenie mena používateľského priečinka dát", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Nechajte prázdne pre používateľské meno (predvolené). Inak uveďte atribút LDAP/AD.", +"Internal Username" => "Interné používateľské meno", +"By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users." => "V predvolenom nastavení bude interné používateľské meno vytvorené z UUID atribútu. Zabezpečí sa to, že používateľské meno bude jedinečné a znaky nemusia byť prevedené. Interné meno má obmedzenie, iba tieto znaky sú povolené: [a-zA-Z0-9_ @ -.]. Ostatné znaky sú nahradené ich ASCII alebo jednoducho vynechané. Pri kolíziách bude číslo byť pridané / odobrané. Interné používateľské meno sa používa na identifikáciu používateľa interne. Je to tiež predvolený názov používateľského domovského priečinka v ownCloud. To je tiež port vzdialeného URL, napríklad pre všetky služby * DAV. S týmto nastavením sa dá prepísať predvolené správanie. Pre dosiahnutie podobného správania sa ako pred ownCloud 5 zadajte atribút zobrazenia používateľského mena v tomto poli. Ponechajte prázdne pre predvolené správanie. Zmeny budú mať vplyv iba na novo mapovaných (pridaných) LDAP používateľov.", +"Internal Username Attribute:" => "Atribút interného používateľského mena:", +"Override UUID detection" => "Prepísať UUID detekciu", +"UUID Attribute:" => "UUID atribút:", "Test Configuration" => "Test nastavenia", "Help" => "Pomoc" ); diff --git a/apps/user_ldap/l10n/sl.php b/apps/user_ldap/l10n/sl.php index 8ff1fd5344..1ade5d9b73 100644 --- a/apps/user_ldap/l10n/sl.php +++ b/apps/user_ldap/l10n/sl.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "Ali naj se prevzame nastavitve nedavne nastavitve strežnika?", "Keep settings?" => "Ali nas se nastavitve ohranijo?", "Cannot add server configuration" => "Ni mogoče dodati nastavitev strežnika", +"Success" => "Uspešno končano.", +"Error" => "Napaka", "Connection test succeeded" => "Preizkus povezave je uspešno končan.", "Connection test failed" => "Preizkus povezave je spodletel.", "Do you really want to delete the current Server Configuration?" => "Ali res želite izbrisati trenutne nastavitve strežnika?", diff --git a/apps/user_ldap/l10n/sq.php b/apps/user_ldap/l10n/sq.php index 24fd869057..12324b9f96 100644 --- a/apps/user_ldap/l10n/sq.php +++ b/apps/user_ldap/l10n/sq.php @@ -1,4 +1,5 @@ "Veprim i gabuar", "Password" => "Kodi", "Help" => "Ndihmë" ); diff --git a/apps/user_ldap/l10n/sr.php b/apps/user_ldap/l10n/sr.php index 52569a08ef..b94bc83e1e 100644 --- a/apps/user_ldap/l10n/sr.php +++ b/apps/user_ldap/l10n/sr.php @@ -1,5 +1,6 @@ "Брисање није успело", +"Error" => "Грешка", "Host" => "Домаћин", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Можете да изоставите протокол, осим ако захтевате SSL. У том случају почните са ldaps://.", "Base DN" => "База DN", diff --git a/apps/user_ldap/l10n/sr@latin.php b/apps/user_ldap/l10n/sr@latin.php index 9150331506..005a76d4bb 100644 --- a/apps/user_ldap/l10n/sr@latin.php +++ b/apps/user_ldap/l10n/sr@latin.php @@ -1,3 +1,4 @@ "Lozinka", "Help" => "Pomoć" ); diff --git a/apps/user_ldap/l10n/sv.php b/apps/user_ldap/l10n/sv.php index 1bb4d9dc0b..eb30bd22f0 100644 --- a/apps/user_ldap/l10n/sv.php +++ b/apps/user_ldap/l10n/sv.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "Ta över inställningar från tidigare serverkonfiguration?", "Keep settings?" => "Behåll inställningarna?", "Cannot add server configuration" => "Kunde inte lägga till serverinställning", +"Success" => "Lyckat", +"Error" => "Fel", "Connection test succeeded" => "Anslutningstestet lyckades", "Connection test failed" => "Anslutningstestet misslyckades", "Do you really want to delete the current Server Configuration?" => "Vill du verkligen radera den nuvarande serverinställningen?", diff --git a/apps/user_ldap/l10n/ta_LK.php b/apps/user_ldap/l10n/ta_LK.php index f6beb3c486..997f09ca87 100644 --- a/apps/user_ldap/l10n/ta_LK.php +++ b/apps/user_ldap/l10n/ta_LK.php @@ -1,5 +1,6 @@ "நீக்கம் தோல்வியடைந்தது", +"Error" => "வழு", "Host" => "ஓம்புனர்", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "நீங்கள் SSL சேவையை தவிர உடன்படு வரைமுறையை தவிர்க்க முடியும். பிறகு ldaps:.// உடன் ஆரம்பிக்கவும்", "Base DN" => "தள DN", diff --git a/apps/user_ldap/l10n/te.php b/apps/user_ldap/l10n/te.php index d9a3e713f0..3f047631cf 100644 --- a/apps/user_ldap/l10n/te.php +++ b/apps/user_ldap/l10n/te.php @@ -1,4 +1,5 @@ "పొరపాటు", "Password" => "సంకేతపదం", "Help" => "సహాయం" ); diff --git a/apps/user_ldap/l10n/th_TH.php b/apps/user_ldap/l10n/th_TH.php index 802badb2f0..ec279ba01e 100644 --- a/apps/user_ldap/l10n/th_TH.php +++ b/apps/user_ldap/l10n/th_TH.php @@ -6,6 +6,8 @@ "Deletion failed" => "การลบทิ้งล้มเหลว", "Keep settings?" => "รักษาการตั้งค่าไว้?", "Cannot add server configuration" => "ไม่สามารถเพิ่มค่ากำหนดเซิร์ฟเวอร์ได้", +"Success" => "เสร็จสิ้น", +"Error" => "ข้อผิดพลาด", "Connection test succeeded" => "ทดสอบการเชื่อมต่อสำเร็จ", "Connection test failed" => "ทดสอบการเชื่อมต่อล้มเหลว", "Do you really want to delete the current Server Configuration?" => "คุณแน่ใจแล้วหรือว่าต้องการลบการกำหนดค่าเซิร์ฟเวอร์ปัจจุบันทิ้งไป?", diff --git a/apps/user_ldap/l10n/tr.php b/apps/user_ldap/l10n/tr.php index 7bcabb0448..3835c72313 100644 --- a/apps/user_ldap/l10n/tr.php +++ b/apps/user_ldap/l10n/tr.php @@ -1,28 +1,66 @@ "Sunucu yapılandırmasını silme başarısız oldu", +"The configuration is valid and the connection could be established!" => "Yapılandırma geçerli ve bağlantı kuruldu!", +"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Yapılandırma geçerli fakat bağlanma(bind) başarısız. Lütfen Sunucu ayarları ve kimlik bilgilerini kontrol ediniz.", +"The configuration is invalid. Please look in the ownCloud log for further details." => "Yapılandırma geçersiz. Daha fazla detay için lütfen ownCloud günlüklerine bakınız.", "Deletion failed" => "Silme başarısız oldu", -"Keep settings?" => "Ayarları kalsınmı?", +"Take over settings from recent server configuration?" => "Ayarları son sunucu yapılandırmalarından devral?", +"Keep settings?" => "Ayarlar kalsın mı?", +"Cannot add server configuration" => "Sunucu yapılandırması eklenemedi", +"Error" => "Hata", "Connection test succeeded" => "Bağlantı testi başarılı oldu", "Connection test failed" => "Bağlantı testi başarısız oldu", +"Do you really want to delete the current Server Configuration?" => "Şu anki sunucu yapılandırmasını silmek istediğinizden emin misiniz?", "Confirm Deletion" => "Silmeyi onayla", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "Uyari Apps kullanici_Idap ve user_webdavauth uyunmayan. Bu belki sik degil. Lutfen sistem yonetici sormak on aktif yapmaya. ", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Ihbar Modulu PHP LDAP yuklemdi degil, backend calismacak. Lutfen sistem yonetici sormak yuklemek icin.", +"Server configuration" => "Sunucu uyunlama ", +"Add Server Configuration" => "Sunucu Uyunlama birlemek ", "Host" => "Sunucu", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol atlamak edesin, sadece SSL istiyorsaniz. O zaman, idapsile baslamak. ", "Base DN" => "Ana DN", +"One Base DN per line" => "Bir Tabani DN herbir dizi. ", +"You can specify Base DN for users and groups in the Advanced tab" => "Base DN kullanicileri ve kaynaklari icin tablosu Advanced tayin etmek ederiz. ", "User DN" => "Kullanıcı DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN musterinin, kimle baglamaya yapacagiz,meselâ uid=agent.dc mesela, dc=com Gecinme adisiz ici, DN ve Parola bos birakmak. ", "Password" => "Parola", "For anonymous access, leave DN and Password empty." => "Anonim erişim için DN ve Parola alanlarını boş bırakın.", "User Login Filter" => "Kullanıcı Oturum Filtresi", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Filter uyunlamak icin tayin ediyor, ne zaman girişmek isteminiz. % % uid adi kullanici girismeye karsi koymacak. ", "use %%uid placeholder, e.g. \"uid=%%uid\"" => "%%uid yer tutucusunu kullanın, örneğin \"uid=%%uid\"", "User List Filter" => "Kullanıcı Liste Filtresi", +"Defines the filter to apply, when retrieving users." => "Filter uyunmak icin tayin ediyor, ne zaman adi kullanici geri aliyor. ", "without any placeholder, e.g. \"objectClass=person\"." => "bir yer tutucusu olmadan, örneğin \"objectClass=person\"", "Group Filter" => "Grup Süzgeci", +"Defines the filter to apply, when retrieving groups." => "Filter uyunmak icin tayin ediyor, ne zaman grubalari tekrar aliyor. ", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "siz bir yer tutucu, mes. 'objectClass=posixGroup ('posixGrubu''. ", "Connection Settings" => "Bağlantı ayarları", +"When unchecked, this configuration will be skipped." => "Ne zaman iptal, bu uynnlama isletici ", "Port" => "Port", +"Backup (Replica) Host" => "Sigorta Kopya Cephe ", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Bir kopya cevre vermek, kopya sunucu onemli olmali. ", +"Backup (Replica) Port" => "Kopya Port ", "Disable Main Server" => "Ana sunucuyu devredışı birak", +"When switched on, ownCloud will only connect to the replica server." => "Ne zaman acik, ownCloud sadece sunuce replikayin baglamis.", "Use TLS" => "TLS kullan", +"Do not use it additionally for LDAPS connections, it will fail." => "Bu LDAPS baglama icin kullamaminiz, basamacak. ", +"Case insensitve LDAP server (Windows)" => "Dusme sunucu LDAP zor degil. (Windows)", "Turn off SSL certificate validation." => "SSL sertifika doğrulamasını kapat.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Bagladiginda, bunla secene sadece calisiyor, sunucu LDAP SSL sunucun ithal etemek, dneyme sizine sunucu ownClouden. ", "Not recommended, use for testing only." => "Önerilmez, sadece test için kullanın.", +"Cache Time-To-Live" => "Cache Time-To-Live ", "in seconds. A change empties the cache." => "saniye cinsinden. Bir değişiklik önbelleği temizleyecektir.", +"Directory Settings" => "Parametrar Listesin Adresinin ", +"User Display Name Field" => "Ekran Adi Kullanici, (Alan Adi Kullanici Ekrane)", +"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP kategori kullanmaya adi ownCloud kullanicin uremek icin. ", "Base User Tree" => "Temel Kullanıcı Ağacı", +"One User Base DN per line" => "Bir Temel Kullanici DN her dizgi ", +"User Search Attributes" => "Kategorii Arama Kullanici ", +"Group Display Name Field" => "Grub Ekrane Alani Adi", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP kullamayin grub adi ownCloud uremek icin. ", "Base Group Tree" => "Temel Grup Ağacı", +"One Group Base DN per line" => "Bir Grubu Tabani DN her dizgi. ", +"Group Search Attributes" => "Kategorii Arama Grubu", "Group-Member association" => "Grup-Üye işbirliği", "in bytes" => "byte cinsinden", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." => "Kullanıcı adı bölümünü boş bırakın (varsayılan). ", diff --git a/apps/user_ldap/l10n/ug.php b/apps/user_ldap/l10n/ug.php new file mode 100644 index 0000000000..8634cdbe1b --- /dev/null +++ b/apps/user_ldap/l10n/ug.php @@ -0,0 +1,14 @@ + "ئۆچۈرۈش مەغلۇپ بولدى", +"Error" => "خاتالىق", +"Host" => "باش ئاپپارات", +"Password" => "ئىم", +"User Login Filter" => "ئىشلەتكۈچى تىزىمغا كىرىش سۈزگۈچى", +"User List Filter" => "ئىشلەتكۈچى تىزىم سۈزگۈچى", +"Group Filter" => "گۇرۇپپا سۈزگۈچ", +"Connection Settings" => "باغلىنىش تەڭشىكى", +"Configuration Active" => "سەپلىمە ئاكتىپ", +"Port" => "ئېغىز", +"Use TLS" => "TLS ئىشلەت", +"Help" => "ياردەم" +); diff --git a/apps/user_ldap/l10n/uk.php b/apps/user_ldap/l10n/uk.php index 623d34c98e..f92c6d5894 100644 --- a/apps/user_ldap/l10n/uk.php +++ b/apps/user_ldap/l10n/uk.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "Застосувати налаштування з останньої конфігурації сервера ?", "Keep settings?" => "Зберегти налаштування ?", "Cannot add server configuration" => "Неможливо додати конфігурацію сервера", +"Success" => "Успіх", +"Error" => "Помилка", "Connection test succeeded" => "Перевірка з'єднання пройшла успішно", "Connection test failed" => "Перевірка з'єднання завершилась неуспішно", "Do you really want to delete the current Server Configuration?" => "Ви дійсно бажаєте видалити поточну конфігурацію сервера ?", diff --git a/apps/user_ldap/l10n/ur_PK.php b/apps/user_ldap/l10n/ur_PK.php index 4c606a1380..83570a596a 100644 --- a/apps/user_ldap/l10n/ur_PK.php +++ b/apps/user_ldap/l10n/ur_PK.php @@ -1,4 +1,5 @@ "ایرر", "Password" => "پاسورڈ", "Help" => "مدد" ); diff --git a/apps/user_ldap/l10n/vi.php b/apps/user_ldap/l10n/vi.php index 4bbb977f36..7e59822592 100644 --- a/apps/user_ldap/l10n/vi.php +++ b/apps/user_ldap/l10n/vi.php @@ -1,5 +1,7 @@ "Xóa thất bại", +"Success" => "Thành công", +"Error" => "Lỗi", "Host" => "Máy chủ", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "Bạn có thể bỏ qua các giao thức, ngoại trừ SSL. Sau đó bắt đầu với ldaps://", "Base DN" => "DN cơ bản", diff --git a/apps/user_ldap/l10n/zh_CN.GB2312.php b/apps/user_ldap/l10n/zh_CN.GB2312.php index f5bc41fd46..6c60ec64e2 100644 --- a/apps/user_ldap/l10n/zh_CN.GB2312.php +++ b/apps/user_ldap/l10n/zh_CN.GB2312.php @@ -1,5 +1,7 @@ "删除失败", +"Success" => "成功", +"Error" => "出错", "Host" => "主机", "You can omit the protocol, except you require SSL. Then start with ldaps://" => "您可以忽略协议,除非您需要 SSL。然后用 ldaps:// 开头", "Base DN" => "基本判别名", diff --git a/apps/user_ldap/l10n/zh_CN.php b/apps/user_ldap/l10n/zh_CN.php index 1911734805..7b8389227a 100644 --- a/apps/user_ldap/l10n/zh_CN.php +++ b/apps/user_ldap/l10n/zh_CN.php @@ -7,6 +7,8 @@ "Take over settings from recent server configuration?" => "从近期的服务器配置中导入设置?", "Keep settings?" => "保留设置吗?", "Cannot add server configuration" => "无法添加服务器配置", +"Success" => "成功", +"Error" => "错误", "Connection test succeeded" => "连接测试成功", "Connection test failed" => "连接测试失败", "Do you really want to delete the current Server Configuration?" => "您真的想要删除当前服务器配置吗?", diff --git a/apps/user_ldap/l10n/zh_HK.php b/apps/user_ldap/l10n/zh_HK.php index 190e4eba79..ba55c41479 100644 --- a/apps/user_ldap/l10n/zh_HK.php +++ b/apps/user_ldap/l10n/zh_HK.php @@ -1,4 +1,6 @@ "成功", +"Error" => "錯誤", "Password" => "密碼", "Port" => "連接埠", "Help" => "幫助" diff --git a/apps/user_ldap/l10n/zh_TW.php b/apps/user_ldap/l10n/zh_TW.php index 9a12bad074..d01e75356c 100644 --- a/apps/user_ldap/l10n/zh_TW.php +++ b/apps/user_ldap/l10n/zh_TW.php @@ -1,5 +1,7 @@ "移除失敗", +"Success" => "成功", +"Error" => "錯誤", "Host" => "主機", "Password" => "密碼", "Port" => "連接阜", diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 6d32e9b2ab..04f73cf01f 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -87,7 +87,7 @@ abstract class Access { for($i=0;$i<$result[$attr]['count'];$i++) { if($this->resemblesDN($attr)) { $values[] = $this->sanitizeDN($result[$attr][$i]); - } elseif(strtolower($attr) == 'objectguid' || strtolower($attr) == 'guid') { + } elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') { $values[] = $this->convertObjectGUID2Str($result[$attr][$i]); } else { $values[] = $result[$attr][$i]; @@ -317,7 +317,19 @@ abstract class Access { } $ldapname = $ldapname[0]; } - $intname = $isUser ? $this->sanitizeUsername($uuid) : $this->sanitizeUsername($ldapname); + + if($isUser) { + $usernameAttribute = $this->connection->ldapExpertUsernameAttr; + if(!emptY($usernameAttribute)) { + $username = $this->readAttribute($dn, $usernameAttribute); + $username = $username[0]; + } else { + $username = $uuid; + } + $intname = $this->sanitizeUsername($username); + } else { + $intname = $ldapname; + } //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check @@ -429,8 +441,8 @@ abstract class Access { //while loop is just a precaution. If a name is not generated within //20 attempts, something else is very wrong. Avoids infinite loop. while($attempts < 20){ - $altName = $name . '_' . uniqid(); - if(\OCP\User::userExists($altName)) { + $altName = $name . '_' . rand(1000,9999); + if(!\OCP\User::userExists($altName)) { return $altName; } $attempts++; @@ -462,7 +474,7 @@ abstract class Access { while($row = $res->fetchRow()) { $usedNames[] = $row['owncloud_name']; } - if(!($usedNames) || count($usedNames) == 0) { + if(!($usedNames) || count($usedNames) === 0) { $lastNo = 1; //will become name_2 } else { natsort($usedNames); @@ -550,7 +562,7 @@ abstract class Access { $sqlAdjustment = ''; $dbtype = \OCP\Config::getSystemValue('dbtype'); - if($dbtype == 'mysql') { + if($dbtype === 'mysql') { $sqlAdjustment = 'FROM DUAL'; } @@ -574,7 +586,7 @@ abstract class Access { $insRows = $res->numRows(); - if($insRows == 0) { + if($insRows === 0) { return false; } @@ -656,7 +668,7 @@ abstract class Access { $linkResources = array_pad(array(), count($base), $link_resource); $sr = ldap_search($linkResources, $base, $filter, $attr); $error = ldap_errno($link_resource); - if(!is_array($sr) || $error != 0) { + if(!is_array($sr) || $error !== 0) { \OCP\Util::writeLog('user_ldap', 'Error when searching: '.ldap_error($link_resource).' code '.ldap_errno($link_resource), \OCP\Util::ERROR); @@ -724,7 +736,7 @@ abstract class Access { foreach($attr as $key) { $key = mb_strtolower($key, 'UTF-8'); if(isset($item[$key])) { - if($key != 'dn') { + if($key !== 'dn') { $selection[$i][$key] = $this->resemblesDN($key) ? $this->sanitizeDN($item[$key][0]) : $item[$key][0]; @@ -816,7 +828,7 @@ abstract class Access { private function combineFilter($filters, $operator) { $combinedFilter = '('.$operator; foreach($filters as $filter) { - if($filter[0] != '(') { + if($filter[0] !== '(') { $filter = '('.$filter.')'; } $combinedFilter.=$filter; @@ -857,7 +869,7 @@ abstract class Access { private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) { $filter = array(); $search = empty($search) ? '*' : '*'.$search.'*'; - if(!is_array($searchAttributes) || count($searchAttributes) == 0) { + if(!is_array($searchAttributes) || count($searchAttributes) === 0) { if(empty($fallbackAttribute)) { return ''; } @@ -867,7 +879,7 @@ abstract class Access { $filter[] = $attribute . '=' . $search; } } - if(count($filter) == 1) { + if(count($filter) === 1) { return '('.$filter[0].')'; } return $this->combineFilterWithOr($filter); @@ -893,7 +905,13 @@ abstract class Access { * @returns true on success, false otherwise */ private function detectUuidAttribute($dn, $force = false) { - if(($this->connection->ldapUuidAttribute != 'auto') && !$force) { + if(($this->connection->ldapUuidAttribute !== 'auto') && !$force) { + return true; + } + + $fixedAttribute = $this->connection->ldapExpertUUIDAttr; + if(!empty($fixedAttribute)) { + $this->connection->ldapUuidAttribute = $fixedAttribute; return true; } @@ -1007,7 +1025,7 @@ abstract class Access { * @returns string containing the key or empty if none is cached */ private function getPagedResultCookie($base, $filter, $limit, $offset) { - if($offset == 0) { + if($offset === 0) { return ''; } $offset -= $limit; @@ -1031,7 +1049,7 @@ abstract class Access { */ private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) { if(!empty($cookie)) { - $cachekey = 'lc' . dechex(crc32($base)) . '-' . dechex(crc32($filter)) . '-' .$limit . '-' . $offset; + $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .$limit . '-' . $offset; $cookie = $this->connection->writeToCache($cachekey, $cookie); } } diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index 20784570e9..31150a5bec 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -65,6 +65,8 @@ class Connection { 'ldapAttributesForGroupSearch' => null, 'homeFolderNamingRule' => null, 'hasPagedResultSupport' => false, + 'ldapExpertUsernameAttr' => null, + 'ldapExpertUUIDAttr' => null, ); /** @@ -99,7 +101,7 @@ class Connection { public function __set($name, $value) { $changed = false; //only few options are writable - if($name == 'ldapUuidAttribute') { + if($name === 'ldapUuidAttribute') { \OCP\Util::writeLog('user_ldap', 'Set config ldapUuidAttribute to '.$value, \OCP\Util::DEBUG); $this->config[$name] = $value; if(!empty($this->configID)) { @@ -265,6 +267,10 @@ class Connection { = preg_split('/\r\n|\r|\n/', $this->$v('ldap_attributes_for_user_search')); $this->config['ldapAttributesForGroupSearch'] = preg_split('/\r\n|\r|\n/', $this->$v('ldap_attributes_for_group_search')); + $this->config['ldapExpertUsernameAttr'] + = $this->$v('ldap_expert_username_attr'); + $this->config['ldapExpertUUIDAttr'] + = $this->$v('ldap_expert_uuid_attr'); $this->configured = $this->validateConfiguration(); } @@ -290,7 +296,6 @@ class Connection { 'ldap_group_filter'=>'ldapGroupFilter', 'ldap_display_name'=>'ldapUserDisplayName', 'ldap_group_display_name'=>'ldapGroupDisplayName', - 'ldap_tls'=>'ldapTLS', 'ldap_nocase'=>'ldapNoCase', 'ldap_quota_def'=>'ldapQuotaDefault', @@ -302,7 +307,9 @@ class Connection { 'ldap_turn_off_cert_check' => 'turnOffCertCheck', 'ldap_configuration_active' => 'ldapConfigurationActive', 'ldap_attributes_for_user_search' => 'ldapAttributesForUserSearch', - 'ldap_attributes_for_group_search' => 'ldapAttributesForGroupSearch' + 'ldap_attributes_for_group_search' => 'ldapAttributesForGroupSearch', + 'ldap_expert_username_attr' => 'ldapExpertUsernameAttr', + 'ldap_expert_uuid_attr' => 'ldapExpertUUIDAttr', ); return $array; } @@ -321,9 +328,9 @@ class Connection { $params = $this->getConfigTranslationArray(); foreach($config as $parameter => $value) { - if(($parameter == 'homeFolderNamingRule' + if(($parameter === 'homeFolderNamingRule' || (isset($params[$parameter]) - && $params[$parameter] == 'homeFolderNamingRule')) + && $params[$parameter] === 'homeFolderNamingRule')) && !empty($value)) { $value = 'attr:'.$value; } @@ -389,7 +396,7 @@ class Connection { $trans = $this->getConfigTranslationArray(); $config = array(); foreach($trans as $dbKey => $classKey) { - if($classKey == 'homeFolderNamingRule') { + if($classKey === 'homeFolderNamingRule') { if(strpos($this->config[$classKey], 'attr:') === 0) { $config[$dbKey] = substr($this->config[$classKey], 5); } else { @@ -427,7 +434,9 @@ class Connection { 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO); } - if(!in_array($this->config['ldapUuidAttribute'], array('auto', 'entryuuid', 'nsuniqueid', 'objectguid')) + $uuidAttributes = array( + 'auto', 'entryuuid', 'nsuniqueid', 'objectguid', 'guid'); + if(!in_array($this->config['ldapUuidAttribute'], $uuidAttributes) && (!is_null($this->configID))) { \OCP\Config::setAppValue($this->configID, $this->configPrefix.'ldap_uuid_attribute', 'auto'); \OCP\Util::writeLog('user_ldap', @@ -440,7 +449,7 @@ class Connection { } foreach(array('ldapAttributesForUserSearch', 'ldapAttributesForGroupSearch') as $key) { if(is_array($this->config[$key]) - && count($this->config[$key]) == 1 + && count($this->config[$key]) === 1 && empty($this->config[$key][0])) { $this->config[$key] = array(); } @@ -503,6 +512,10 @@ class Connection { $configurationOK = false; } + if(!empty($this->config['ldapExpertUUIDAttr'])) { + $this->config['ldapUuidAttribute'] = $this->config['ldapExpertUUIDAttr']; + } + return $configurationOK; } @@ -541,6 +554,8 @@ class Connection { 'ldap_configuration_active' => 1, 'ldap_attributes_for_user_search' => '', 'ldap_attributes_for_group_search' => '', + 'ldap_expert_username_attr' => '', + 'ldap_expert_uuid_attr' => '', ); } @@ -586,14 +601,13 @@ class Connection { $error = null; } - $error = null; //if LDAP server is not reachable, try the Backup (Replica!) Server - if((!$bindStatus && ($error == -1)) + if((!$bindStatus && ($error !== 0)) || $this->config['ldapOverrideMainServer'] || $this->getFromCache('overrideMainServer')) { $this->doConnect($this->config['ldapBackupHost'], $this->config['ldapBackupPort']); $bindStatus = $this->bind(); - if($bindStatus && $error == -1) { + if(!$bindStatus && $error === -1) { //when bind to backup server succeeded and failed to main server, //skip contacting him until next cache refresh $this->writeToCache('overrideMainServer', true); @@ -607,6 +621,10 @@ class Connection { if(empty($host)) { return false; } + if(strpos($host, '://') !== false) { + //ldap_connect ignores port paramater when URLs are passed + $host .= ':' . $port; + } $this->ldapConnectionRes = ldap_connect($host, $port); if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { @@ -621,10 +639,17 @@ class Connection { * Binds to LDAP */ public function bind() { + static $getConnectionResourceAttempt = false; if(!$this->config['ldapConfigurationActive']) { return false; } + if($getConnectionResourceAttempt) { + $getConnectionResourceAttempt = false; + return false; + } + $getConnectionResourceAttempt = true; $cr = $this->getConnectionResource(); + $getConnectionResourceAttempt = false; if(!is_resource($cr)) { return false; } diff --git a/apps/user_ldap/lib/helper.php b/apps/user_ldap/lib/helper.php index 612a088269..10ed40ebd6 100644 --- a/apps/user_ldap/lib/helper.php +++ b/apps/user_ldap/lib/helper.php @@ -96,7 +96,38 @@ class Helper { return false; } - if($res->numRows() == 0) { + if($res->numRows() === 0) { + return false; + } + + return true; + } + + /** + * Truncate's the given mapping table + * + * @param string $mapping either 'user' or 'group' + * @return boolean true on success, false otherwise + */ + static public function clearMapping($mapping) { + if($mapping === 'user') { + $table = '`*PREFIX*ldap_user_mapping`'; + } else if ($mapping === 'group') { + $table = '`*PREFIX*ldap_group_mapping`'; + } else { + return false; + } + + if(strpos(\OCP\Config::getSystemValue('dbtype'), 'sqlite') !== false) { + $query = \OCP\DB::prepare('DELETE FROM '.$table); + } else { + $query = \OCP\DB::prepare('TRUNCATE '.$table); + } + + + $res = $query->execute(); + + if(\OCP\DB::isError($res)) { return false; } diff --git a/apps/user_ldap/lib/jobs.php b/apps/user_ldap/lib/jobs.php index 094d11db3d..60ecc0da33 100644 --- a/apps/user_ldap/lib/jobs.php +++ b/apps/user_ldap/lib/jobs.php @@ -23,20 +23,22 @@ namespace OCA\user_ldap\lib; -class Jobs { +class Jobs extends \OC\BackgroundJob\TimedJob { static private $groupsFromDB; static private $groupBE; static private $connector; + public function __construct(){ + $this->interval = self::getRefreshInterval(); + } + + public function run($argument){ + Jobs::updateGroups(); + } + static public function updateGroups() { \OCP\Util::writeLog('user_ldap', 'Run background job "updateGroups"', \OCP\Util::DEBUG); - $lastUpdate = \OCP\Config::getAppValue('user_ldap', 'bgjUpdateGroupsLastRun', 0); - if((time() - $lastUpdate) < self::getRefreshInterval()) { - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – last run too fresh, aborting.', \OCP\Util::DEBUG); - //komm runter Werner die Maurer geben ein aus - return; - } $knownGroups = array_keys(self::getKnownGroups()); $actualGroups = self::getGroupBE()->getGroups(); @@ -45,7 +47,6 @@ class Jobs { \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.', \OCP\Util::INFO); - \OCP\Config::setAppValue('user_ldap', 'bgjUpdateGroupsLastRun', time()); return; } @@ -53,8 +54,6 @@ class Jobs { self::handleCreatedGroups(array_diff($actualGroups, $knownGroups)); self::handleRemovedGroups(array_diff($knownGroups, $actualGroups)); - \OCP\Config::setAppValue('user_ldap', 'bgjUpdateGroupsLastRun', time()); - \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Finished.', \OCP\Util::DEBUG); } diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index 05497ae8a3..22e2dac6d2 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -4,7 +4,9 @@ * ownCloud - user_ldap * * @author Dominik Schmidt + * @author Arthur Schiwon * @copyright 2011 Dominik Schmidt dev@dominik-schmidt.de + * @copyright 2012-2013 Arthur Schiwon blizzz@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 diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index d3c2c29890..972970aa3e 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -3,6 +3,7 @@ '.$l->t('Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them.').'

'); @@ -14,7 +15,7 @@

-

+

t('Special Attributes'));?>

@@ -96,6 +97,17 @@
+
+

t('Internal Username'));?>

+

t('By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder in ownCloud. It is also a port of remote URLs, for instance for all *DAV services. With this setting, the default behaviour can be overriden. To achieve a similar behaviour as before ownCloud 5 enter the user display name attribute in the following field. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users.'));?>

+

+

t('Override UUID detection'));?>

+

t('By default, ownCloud autodetects the UUID attribute. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behaviour. Changes will have effect only on newly mapped (added) LDAP users and groups.'));?>

+

+

t('Username-LDAP User Mapping'));?>

+

t('ownCloud uses usernames to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from ownCloud username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found by ownCloud. The internal ownCloud name is used all over in ownCloud. Clearing the Mappings will have leftovers everywhere. Clearing the Mappings is not configuration sensitive, it affects all LDAP configurations! Do never clear the mappings in a production environment. Only clear mappings in a testing or experimental stage.'));?>

+


+
t('Help'));?> diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index 1277e07471..41e2926605 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -197,9 +197,9 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { //if attribute's value is an absolute path take this, otherwise append it to data dir //check for / at the beginning or pattern c:\ resp. c:/ if( - '/' == $path[0] + '/' === $path[0] || (3 < strlen($path) && ctype_alpha($path[0]) - && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2])) + && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2])) ) { $homedir = $path; } else { diff --git a/apps/user_ldap/user_proxy.php b/apps/user_ldap/user_proxy.php index 6a75bae381..73cc096318 100644 --- a/apps/user_ldap/user_proxy.php +++ b/apps/user_ldap/user_proxy.php @@ -76,8 +76,15 @@ class User_Proxy extends lib\Proxy implements \OCP\UserInterface { if(isset($this->backends[$prefix])) { $result = call_user_func_array(array($this->backends[$prefix], $method), $parameters); if(!$result) { - //not found here, reset cache to null - $this->writeToCache($cacheKey, null); + //not found here, reset cache to null if user vanished + //because sometimes methods return false with a reason + $userExists = call_user_func_array( + array($this->backends[$prefix], 'userExists'), + array($uid) + ); + if(!$userExists) { + $this->writeToCache($cacheKey, null); + } } return $result; } @@ -167,7 +174,7 @@ class User_Proxy extends lib\Proxy implements \OCP\UserInterface { foreach($this->backends as $backend) { $backendUsers = $backend->getDisplayNames($search, $limit, $offset); if (is_array($backendUsers)) { - $users = array_merge($users, $backendUsers); + $users = $users + $backendUsers; } } return $users; diff --git a/apps/user_webdavauth/l10n/bg_BG.php b/apps/user_webdavauth/l10n/bg_BG.php new file mode 100644 index 0000000000..a3bd703b25 --- /dev/null +++ b/apps/user_webdavauth/l10n/bg_BG.php @@ -0,0 +1,5 @@ + "WebDAV идентификация", +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud ще изпрати потребителските данни до този URL. " +); diff --git a/apps/user_webdavauth/l10n/nn_NO.php b/apps/user_webdavauth/l10n/nn_NO.php new file mode 100644 index 0000000000..772e084b63 --- /dev/null +++ b/apps/user_webdavauth/l10n/nn_NO.php @@ -0,0 +1,5 @@ + "WebDAV-autentisering", +"URL: http://" => "Nettadresse: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud sender brukarakkreditiv til denne nettadressa. Dette programtillegget kontrollerer svaret og tolkar HTTP-statuskodane 401 og 403 som ugyldige, og alle andre svar som gyldige." +); diff --git a/apps/user_webdavauth/l10n/tr.php b/apps/user_webdavauth/l10n/tr.php index 4a2f6d2403..c495a39dce 100644 --- a/apps/user_webdavauth/l10n/tr.php +++ b/apps/user_webdavauth/l10n/tr.php @@ -1,4 +1,5 @@ "WebDAV Kimlik doğrulaması", -"URL: http://" => "URL: http://" +"URL: http://" => "URL: http://", +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud deneyme kullanicin URLe gonderecek. Bu toplan cepaplama muayene edecek ve status kodeci HTTPden 401 ve 403 deneyi gecerli ve hepsi baska cevaplamari mantekli gibi yorumlacak. " ); diff --git a/apps/user_webdavauth/l10n/ug.php b/apps/user_webdavauth/l10n/ug.php new file mode 100644 index 0000000000..03ced5f4aa --- /dev/null +++ b/apps/user_webdavauth/l10n/ug.php @@ -0,0 +1,4 @@ + "WebDAV سالاھىيەت دەلىللەش", +"URL: http://" => "URL: http://" +); diff --git a/apps/user_webdavauth/l10n/zh_TW.php b/apps/user_webdavauth/l10n/zh_TW.php index 7a9d767eec..6f94b77ac5 100644 --- a/apps/user_webdavauth/l10n/zh_TW.php +++ b/apps/user_webdavauth/l10n/zh_TW.php @@ -1,5 +1,5 @@ "WebDAV 認證", "URL: http://" => "網址:http://", -"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud會將把用戶的證件發送到這個網址。這個插件會檢查回應,並把HTTP狀態代碼401和403視為無效證件和所有其他回應視為有效證件。" +"ownCloud will send the user credentials to this URL. This plugin checks the response and will interpret the HTTP statuscodes 401 and 403 as invalid credentials, and all other responses as valid credentials." => "ownCloud 會將把用戶的登入資訊發送到這個網址以嘗試登入,並檢查回應, HTTP 狀態碼401和403視為登入失敗,所有其他回應視為登入成功。" ); diff --git a/autotest.sh b/autotest.sh index fdf6d2fe09..4562b3ed08 100755 --- a/autotest.sh +++ b/autotest.sh @@ -54,6 +54,22 @@ cat > ./tests/autoconfig-pgsql.php < ./tests/autoconfig-oci.php < false, + 'dbtype' => 'oci', + 'dbtableprefix' => 'oc_', + 'adminlogin' => 'admin', + 'adminpass' => 'admin', + 'directory' => '$BASEDIR/$DATADIR', + 'dbuser' => 'oc_autotest', + 'dbname' => 'XE', + 'dbhost' => 'localhost', + 'dbpass' => 'owncloud', +); +DELIM + function execute_tests { echo "Setup environment for $1 testing ..." # back to root folder @@ -77,6 +93,30 @@ function execute_tests { if [ "$1" == "pgsql" ] ; then dropdb -U oc_autotest oc_autotest fi + if [ "$1" == "oci" ] ; then + echo "drop the database" + sqlplus -s -l / as sysdba < 60*60*24*15, /* Custom CSP policy, changing this will overwrite the standard policy */ -"custom_csp_policy" => "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src *; font-src 'self' data:", +"custom_csp_policy" => "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src *; font-src 'self' data:; media-src *", +/* Enable/disable X-Frame-Restriction */ +/* HIGH SECURITY RISK IF DISABLED*/ +"xframe_restriction" => true, + /* The directory where the user data is stored, default to data in the owncloud * directory. The sqlite database is also stored here, when sqlite is used. */ diff --git a/core/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/core/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png index 954e22dbd9..eed4abd192 100644 Binary files a/core/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png and b/core/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png differ diff --git a/core/css/images/ui-bg_diagonals-thick_20_666666_40x40.png b/core/css/images/ui-bg_diagonals-thick_20_666666_40x40.png index 64ece5707d..a618b06586 100644 Binary files a/core/css/images/ui-bg_diagonals-thick_20_666666_40x40.png and b/core/css/images/ui-bg_diagonals-thick_20_666666_40x40.png differ diff --git a/core/css/images/ui-bg_flat_100_ffffff_40x100.png b/core/css/images/ui-bg_flat_100_ffffff_40x100.png index ac8b229af9..6ebfa5026e 100644 Binary files a/core/css/images/ui-bg_flat_100_ffffff_40x100.png and b/core/css/images/ui-bg_flat_100_ffffff_40x100.png differ diff --git a/core/css/images/ui-bg_flat_10_000000_40x100.png b/core/css/images/ui-bg_flat_10_000000_40x100.png index abdc01082b..b10f59cd34 100644 Binary files a/core/css/images/ui-bg_flat_10_000000_40x100.png and b/core/css/images/ui-bg_flat_10_000000_40x100.png differ diff --git a/core/css/images/ui-bg_flat_35_1d2d44_40x100.png b/core/css/images/ui-bg_flat_35_1d2d44_40x100.png index 904ef14c37..2be93e582d 100644 Binary files a/core/css/images/ui-bg_flat_35_1d2d44_40x100.png and b/core/css/images/ui-bg_flat_35_1d2d44_40x100.png differ diff --git a/core/css/images/ui-icons_1d2d44_256x240.png b/core/css/images/ui-icons_1d2d44_256x240.png index 2a857e4da5..1b1474b1fd 100644 Binary files a/core/css/images/ui-icons_1d2d44_256x240.png and b/core/css/images/ui-icons_1d2d44_256x240.png differ diff --git a/core/css/images/ui-icons_222222_256x240.png b/core/css/images/ui-icons_222222_256x240.png index b273ff111d..82ef90aaba 100644 Binary files a/core/css/images/ui-icons_222222_256x240.png and b/core/css/images/ui-icons_222222_256x240.png differ diff --git a/core/css/images/ui-icons_ffd27a_256x240.png b/core/css/images/ui-icons_ffd27a_256x240.png index e117effa3d..a7ac4ec658 100644 Binary files a/core/css/images/ui-icons_ffd27a_256x240.png and b/core/css/images/ui-icons_ffd27a_256x240.png differ diff --git a/core/css/images/ui-icons_ffffff_256x240.png b/core/css/images/ui-icons_ffffff_256x240.png index 42f8f992c7..174be7c284 100644 Binary files a/core/css/images/ui-icons_ffffff_256x240.png and b/core/css/images/ui-icons_ffffff_256x240.png differ diff --git a/core/css/jquery.ocdialog.css b/core/css/jquery.ocdialog.css new file mode 100644 index 0000000000..c300b031af --- /dev/null +++ b/core/css/jquery.ocdialog.css @@ -0,0 +1,48 @@ +.oc-dialog { + background: white; + color: #333333; + border-radius: 3px; box-shadow: 0 0 7px #888888; + padding: 15px; + z-index: 1000; + font-size: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + min-width: 200px; +} +.oc-dialog-title { + background: white; + font-weight: bold; + font-size: 110%; + margin-bottom: 10px; +} +.oc-dialog-content { + z-index: 1000; + background: white; +} +.oc-dialog-separator { +} +.oc-dialog-buttonrow { + background: white; + float: right; + position: relative; + bottom: 0; + display: block; + margin-top: 10px; +} + +.oc-dialog-close { + position:absolute; + top:7px; right:7px; + height:20px; width:20px; + background:url('../img/actions/delete.svg') no-repeat center; +} + +.oc-dialog-dim { + background-color: #000; + opacity: .20;filter:Alpha(Opacity=20); + z-index: 999; + position: absolute; + top: 0; left: 0; + width: 100%; height: 100%; +} diff --git a/core/css/multiselect.css b/core/css/multiselect.css index 31c8ef88eb..def4e60d74 100644 --- a/core/css/multiselect.css +++ b/core/css/multiselect.css @@ -29,6 +29,12 @@ white-space:nowrap; } + ul.multiselectoptions>li>input[type="checkbox"] { + margin-top: 3px; + margin-right: 5px; + margin-left: 3px; + } + div.multiselect { display:inline-block; max-width:400px; @@ -75,4 +81,4 @@ padding-bottom:.2em; padding-top:.2em; margin:0; - } \ No newline at end of file + } diff --git a/core/css/styles.css b/core/css/styles.css index 043ca161c7..313f89195d 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -23,13 +23,13 @@ body { background:#fefefe; font:normal .8em/1.6em "Helvetica Neue",Helvetica,Ari #body-login #header { margin: -2em auto 0; text-align:center; height:10em; padding:1em 0 .5em; -moz-box-shadow:0 0 1em rgba(0, 0, 0, .5); -webkit-box-shadow:0 0 1em rgba(0, 0, 0, .5); box-shadow:0 0 1em rgba(0, 0, 0, .5); background:#1d2d44; /* Old browsers */ -background:-moz-linear-gradient(top, #35537a 0%, #1d2d42 100%); /* FF3.6+ */ -background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#35537a), color-stop(100%,#1d2d42)); /* Chrome,Safari4+ */ -background:-webkit-linear-gradient(top, #35537a 0%,#1d2d42 100%); /* Chrome10+,Safari5.1+ */ -background:-o-linear-gradient(top, #35537a 0%,#1d2d42 100%); /* Opera11.10+ */ -background:-ms-linear-gradient(top, #35537a 0%,#1d2d42 100%); /* IE10+ */ -background:linear-gradient(top, #35537a 0%,#1d2d42 100%); /* W3C */ -filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#35537a', endColorstr='#1d2d42',GradientType=0 ); /* IE6-9 */ } +background:-moz-linear-gradient(top, #35537a 0%, #1d2d44 100%); /* FF3.6+ */ +background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#35537a), color-stop(100%,#1d2d44)); /* Chrome,Safari4+ */ +background:-webkit-linear-gradient(top, #35537a 0%,#1d2d44 100%); /* Chrome10+,Safari5.1+ */ +background:-o-linear-gradient(top, #35537a 0%,#1d2d44 100%); /* Opera11.10+ */ +background:-ms-linear-gradient(top, #35537a 0%,#1d2d44 100%); /* IE10+ */ +background:linear-gradient(top, #35537a 0%,#1d2d44 100%); /* W3C */ +filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#35537a', endColorstr='#1d2d44',GradientType=0 ); /* IE6-9 */ } #owncloud { position:absolute; top:0; left:0; padding:6px; padding-bottom:0; } .header-right { float:right; vertical-align:middle; padding:0.5em; } @@ -37,7 +37,7 @@ filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#35537a', endC /* INPUTS */ -input[type="text"], input[type="password"], input[type="search"], input[type="number"], input[type="email"], +input[type="text"], input[type="password"], input[type="search"], input[type="number"], input[type="email"], input[type="url"], textarea, select, button, .button, #quota, div.jp-progress, .pager li a { @@ -48,10 +48,11 @@ button, .button, -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; } input[type="hidden"] { height:0; width:0; } -input[type="text"], input[type="password"], input[type="search"], input[type="number"], input[type="email"], textarea { +input[type="text"], input[type="password"], input[type="search"], input[type="number"], input[type="email"], input[type="url"], textarea { background:#f8f8f8; color:#555; cursor:text; + font-family: inherit; /* use default ownCloud font instead of default textarea monospace */ } -input[type="text"], input[type="password"], input[type="search"], input[type="number"], input[type="email"] { +input[type="text"], input[type="password"], input[type="search"], input[type="number"], input[type="email"], input[type="url"] { -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; } @@ -60,6 +61,7 @@ input[type="password"]:hover, input[type="password"]:focus, input[type="password input[type="number"]:hover, input[type="number"]:focus, input[type="number"]:active, .searchbox input[type="search"]:hover, .searchbox input[type="search"]:focus, .searchbox input[type="search"]:active, input[type="email"]:hover, input[type="email"]:focus, input[type="email"]:active, +input[type="url"]:hover, input[type="url"]:focus, input[type="url"]:active, textarea:hover, textarea:focus, textarea:active { background-color:#fff; color:#333; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; @@ -119,7 +121,7 @@ a.disabled, a.disabled:hover, a.disabled:focus { } .primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active { border:1px solid #1d2d44; - background:#1d2d42; color:#bbb; text-shadow:#000 0 -1px 0; + background:#1d2d44; color:#bbb; text-shadow:#000 0 -1px 0; -moz-box-shadow:0 1px 1px #fff,0 1px 1px 0 rgba(0,0,0,.2) inset; -webkit-box-shadow:0 1px 1px #fff,0 1px 1px 0 rgba(0,0,0,.2) inset; box-shadow:0 1px 1px #fff,0 1px 1px 0 rgba(0,0,0,.2) inset; } @@ -220,6 +222,7 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } #login #databaseField .infield { padding-left:0; } #login form input[type="checkbox"]+label { position:relative; margin:0; font-size:1em; text-shadow:#fff 0 1px 0; } #login form .errors { background:#fed7d7; border:1px solid #f00; list-style-indent:inside; margin:0 0 2em; padding:1em; } +#login .success { background:#d7fed7; border:1px solid #0f0; width: 35%; margin: 30px auto; padding:1em; text-align: center;} /* Show password toggle */ #show, #dbpassword { position:absolute; right:1em; top:.8em; float:right; } @@ -245,14 +248,24 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } } #login form #selectDbType label.ui-state-hover, #login form #selectDbType label.ui-state-active { color:#000; background-color:#e8e8e8; } -/* Warnings */ -fieldset.warning { - padding:8px; - color:#b94a48; background-color:#f2dede; border:1px solid #eed3d7; - border-radius:5px; +/* Warnings, for information */ +.warning { + display: block; + background-color: #f2dede; + color: #b94a48; + padding: 8px; + margin: 0 7px 5px; + border: 1px solid #eed3d7; + border-radius: 5px; } -fieldset.warning legend { color:#b94a48 !important; } -fieldset.warning a { color:#b94a48 !important; font-weight:bold; } +.warning legend, +.warning a { + color: #b94a48 !important; + font-weight: bold; +} +/* Errors, for grave states */ +li.update, li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ffe .8em .8em no-repeat; border:1px solid #ccc; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; cursor:default; } +.error { color:#FF3B3B; } /* Alternative Logins */ #alternative-logins legend { margin-bottom:10px; } @@ -262,23 +275,46 @@ fieldset.warning a { color:#b94a48 !important; font-weight:bold; } /* NAVIGATION ------------------------------------------------------------- */ #navigation { position:fixed; float:left; width:64px; padding-top:3.5em; z-index:75; height:100%; - background:#383c43 url('../img/noise.png') repeat; border-right:1px #333 solid; + background:#383c43 url('../img/noise.png') repeat; -moz-box-shadow:0 0 7px #000; -webkit-box-shadow:0 0 7px #000; box-shadow:0 0 7px #000; overflow:hidden; box-sizing:border-box; -moz-box-sizing:border-box; } -#navigation:hover { overflow-y:auto; } +#navigation:hover { overflow-y:auto; } /* show scrollbar only on hover */ #navigation a span { display:block; text-decoration:none; font-size:10px; text-align:center; color:#fff; text-shadow:#000 0 -1px 0; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter:alpha(opacity=50); opacity:.5; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; /* ellipsize long app names */ } - #navigation a:hover, #navigation a:focus { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; } - #navigation a.active .icon, #navigation a.active span { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } + + /* icon opacity and hover effect */ + #navigation a img, + #navigation a span { + /* 50% opacity when inactive */ + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + opacity: .5; + } + #navigation a:hover img, #navigation a:focus img, + #navigation a:hover span, #navigation a:focus span { + /* 80% opacity when hovered or focused */ + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; + filter: alpha(opacity=80); + opacity: .8; + } + #navigation a.active img, + #navigation a.active span { + /* full opacity for the active app */ + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + opacity: 1; + } + + /* positioning */ #navigation .icon { - display:block; width:32px; height:32px; margin:0 16px 0; padding:8px 0 4px; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter:alpha(opacity=50); opacity:.5; + display:block; + width:32px; height:32px; + margin:0 16px 0; padding:8px 0 4px; } #navigation li:first-child a { padding-top:16px; } @@ -309,8 +345,8 @@ fieldset.warning a { color:#b94a48 !important; font-weight:bold; } .center { text-align:center; } #notification-container { position: fixed; top: 0px; width: 100%; text-align: center; z-index: 101; line-height: 1.2;} -#notification { z-index:101; background-color:#fc4; border:0; padding:0 .7em .3em; display:none; position: relative; top:0; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; -moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; } -#notification span { cursor:pointer; font-weight:bold; margin-left:1em; } +#notification, #update-notification { z-index:101; background-color:#fc4; border:0; padding:0 .7em .3em; display:none; position: relative; top:0; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; -moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; } +#notification span, #update-notification span { cursor:pointer; font-weight:bold; margin-left:1em; } tr .action:not(.permanent), .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; } tr:hover .action, tr .action.permanent, .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter:alpha(opacity=50); opacity:.5; } @@ -333,29 +369,41 @@ div.jp-play-bar, div.jp-seek-bar { padding:0; } .pager { list-style:none; float:right; display:inline; margin:.7em 13em 0 0; } .pager li { display:inline-block; } -li.update, li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ffe .8em .8em no-repeat; border:1px solid #ccc; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; cursor:default; } -.error { color:#FF3B3B; } .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { overflow:hidden; text-overflow:ellipsis; } -.hint { background-image:url('../img/actions/info.png'); background-repeat:no-repeat; color:#777777; padding-left:25px; background-position:0 0.3em;} +.hint { background-image:url('../img/actions/info.png'); background-repeat:no-repeat; color:#777; padding-left:25px; background-position:0 0.3em;} .separator { display:inline; border-left:1px solid #d3d3d3; border-right:1px solid #fff; height:10px; width:0px; margin:4px; } a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;padding-top:0px;padding-bottom:2px; text-decoration:none; margin-top:5px } -.exception{color:#000000;} +.exception{color:#000;} .exception textarea{width:95%;height:200px;background:#ffe;border:0;} .ui-icon-circle-triangle-e{ background-image:url('../img/actions/play-next.svg'); } .ui-icon-circle-triangle-w{ background-image:url('../img/actions/play-previous.svg'); } -.ui-datepicker-prev,.ui-datepicker-next{ border:1px solid #ddd; background:#ffffff; } +.ui-datepicker-prev,.ui-datepicker-next{ border:1px solid #ddd; background:#fff; } /* ---- DIALOGS ---- */ -#dirup {width:4%;} -#dirtree {width:92%;} -#filelist {height:270px; overflow-y:auto; background-color:white; width:100%;} -.filepicker_element_selected { background-color:lightblue;} -.filepicker_loader {height:170px; width:100%; background-color:#333; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:alpha(opacity=30); opacity:.3; visibility:visible; position:absolute; top:0; left:0; text-align:center; padding-top:150px;} +#oc-dialog-filepicker-content .dirtree {width:92%; overflow:hidden; } +#oc-dialog-filepicker-content .dirtree .home { + background-image:url('../img/places/home.svg'); + background-repeat:no-repeat; + background-position: left center; +} +#oc-dialog-filepicker-content .dirtree span:not(:last-child) { cursor: pointer; } +#oc-dialog-filepicker-content .dirtree span:last-child { font-weight: bold; } +#oc-dialog-filepicker-content .dirtree span:not(:last-child)::after { content: '>'; padding: 3px;} +#oc-dialog-filepicker-content .filelist { + overflow-y:auto; + max-height: 300px; + background-color:white; + width:100%; +} +#oc-dialog-filepicker-content .filelist img { margin: 2px 1em 0 4px; } +#oc-dialog-filepicker-content .filelist .date { float:right;margin-right:1em; } +#oc-dialog-filepicker-content .filepicker_element_selected { background-color:lightblue;} .ui-dialog {position:fixed !important;} span.ui-icon {float: left; margin: 3px 7px 30px 0;} +.loading { background: url('../img/loading.gif') no-repeat center; cursor: wait; } /* ---- CATEGORIES ---- */ #categoryform .scrollarea { position:absolute; left:10px; top:10px; right:10px; bottom:50px; overflow:auto; border:1px solid #ddd; background:#f8f8f8; } @@ -367,7 +415,7 @@ span.ui-icon {float: left; margin: 3px 7px 30px 0;} #category_addinput { width:10em; } /* ---- APP SETTINGS ---- */ -.popup { background-color:white; border-radius:10px 10px 10px 10px; box-shadow:0 0 20px #888888; color:#333333; padding:10px; position:fixed !important; z-index:200; } +.popup { background-color:white; border-radius:10px 10px 10px 10px; box-shadow:0 0 20px #888; color:#333; padding:10px; position:fixed !important; z-index:200; } .popup.topright { top:7em; right:1em; } .popup.bottomleft { bottom:1em; left:33em; } .popup .close { position:absolute; top:0.2em; right:0.2em; height:20px; width:20px; background:url('../img/actions/delete.svg') no-repeat center; } @@ -414,7 +462,8 @@ div.crumb a{ padding: 0.9em 0 0.7em 0; } -moz-box-sizing: border-box; box-sizing: border-box; text-shadow: 0 1px 0 rgba(255,255,255,.9); } -#app-navigation .active { /* active navigation entry or folder */ +#app-navigation .active, +#app-navigation .active a { /* active navigation entry or folder */ background-color: #ddd; text-shadow: 0 1px 0 rgba(255,255,255,.7); } diff --git a/core/img/actions/add.svg b/core/img/actions/add.svg index 29994747c3..136d6c4b31 100644 --- a/core/img/actions/add.svg +++ b/core/img/actions/add.svg @@ -1,109 +1,10 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/core/img/actions/caret-dark.png b/core/img/actions/caret-dark.png index ce7e1e6980..8ac5fbbd19 100644 Binary files a/core/img/actions/caret-dark.png and b/core/img/actions/caret-dark.png differ diff --git a/core/img/actions/caret-dark.svg b/core/img/actions/caret-dark.svg index abb1dc192d..be45ad402b 100644 --- a/core/img/actions/caret-dark.svg +++ b/core/img/actions/caret-dark.svg @@ -1,102 +1,5 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - + + + diff --git a/core/img/actions/caret.png b/core/img/actions/caret.png index e0ae969a94..00baea9ece 100644 Binary files a/core/img/actions/caret.png and b/core/img/actions/caret.png differ diff --git a/core/img/actions/caret.svg b/core/img/actions/caret.svg index 7bb0c59cde..d1ae8d60a6 100644 --- a/core/img/actions/caret.svg +++ b/core/img/actions/caret.svg @@ -1,112 +1,11 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/core/img/actions/clock.png b/core/img/actions/clock.png index 671b3f4f0c..9c3a284b8b 100644 Binary files a/core/img/actions/clock.png and b/core/img/actions/clock.png differ diff --git a/core/img/actions/clock.svg b/core/img/actions/clock.svg old mode 100755 new mode 100644 index 1821f474df..f3fcb19031 --- a/core/img/actions/clock.svg +++ b/core/img/actions/clock.svg @@ -1,20 +1,21 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + diff --git a/core/img/actions/close.png b/core/img/actions/close.png index bc0c782882..0d8c89a56e 100644 Binary files a/core/img/actions/close.png and b/core/img/actions/close.png differ diff --git a/core/img/actions/close.svg b/core/img/actions/close.svg index 6a6d98e34a..ef564bfd48 100644 --- a/core/img/actions/close.svg +++ b/core/img/actions/close.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/delete-hover.png b/core/img/actions/delete-hover.png index 08b15510d9..048d91cee5 100644 Binary files a/core/img/actions/delete-hover.png and b/core/img/actions/delete-hover.png differ diff --git a/core/img/actions/delete-hover.svg b/core/img/actions/delete-hover.svg index 63cacd5e38..3e8d26c978 100644 --- a/core/img/actions/delete-hover.svg +++ b/core/img/actions/delete-hover.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/delete.svg b/core/img/actions/delete.svg index 86c8317d01..ef564bfd48 100644 --- a/core/img/actions/delete.svg +++ b/core/img/actions/delete.svg @@ -1,70 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/download.svg b/core/img/actions/download.svg index 107a46f07b..a469c3b8a0 100644 --- a/core/img/actions/download.svg +++ b/core/img/actions/download.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/history.png b/core/img/actions/history.png index 1d138b8cd5..3234880b25 100644 Binary files a/core/img/actions/history.png and b/core/img/actions/history.png differ diff --git a/core/img/actions/history.svg b/core/img/actions/history.svg index 9c2838d565..94512a2d43 100644 --- a/core/img/actions/history.svg +++ b/core/img/actions/history.svg @@ -1,240 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/info.svg b/core/img/actions/info.svg index 1e07aed852..55bdb17f2e 100644 --- a/core/img/actions/info.svg +++ b/core/img/actions/info.svg @@ -1,1758 +1,14 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/core/img/actions/lock.png b/core/img/actions/lock.png index 511bfa615b..dbcffa3990 100644 Binary files a/core/img/actions/lock.png and b/core/img/actions/lock.png differ diff --git a/core/img/actions/lock.svg b/core/img/actions/lock.svg old mode 100755 new mode 100644 index 8fb039b9e3..beef1d3ad3 --- a/core/img/actions/lock.svg +++ b/core/img/actions/lock.svg @@ -1,8 +1,5 @@ - - - - - - + + + + + diff --git a/core/img/actions/logout.png b/core/img/actions/logout.png index e2f4b7af12..e9c89a15a7 100644 Binary files a/core/img/actions/logout.png and b/core/img/actions/logout.png differ diff --git a/core/img/actions/logout.svg b/core/img/actions/logout.svg index e5edc24895..59543875d7 100644 --- a/core/img/actions/logout.svg +++ b/core/img/actions/logout.svg @@ -1,178 +1,5 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/core/img/actions/mail.svg b/core/img/actions/mail.svg index e82fa3b467..2c63daac03 100644 --- a/core/img/actions/mail.svg +++ b/core/img/actions/mail.svg @@ -1,58 +1,8 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - + + + + + + diff --git a/core/img/actions/password.png b/core/img/actions/password.png index 5167161dfa..edcafdd9bb 100644 Binary files a/core/img/actions/password.png and b/core/img/actions/password.png differ diff --git a/core/img/actions/password.svg b/core/img/actions/password.svg index ee6a9fe018..9ab5d4243d 100644 --- a/core/img/actions/password.svg +++ b/core/img/actions/password.svg @@ -1,2148 +1,5 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/core/img/actions/pause-big.svg b/core/img/actions/pause-big.svg index b521057a35..9c4944223f 100644 --- a/core/img/actions/pause-big.svg +++ b/core/img/actions/pause-big.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/pause.svg b/core/img/actions/pause.svg index ff3c69d6c7..d572ad6f5c 100644 --- a/core/img/actions/pause.svg +++ b/core/img/actions/pause.svg @@ -1,72 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/play-add.svg b/core/img/actions/play-add.svg index 25ff0b57ee..cdf4f6ea9f 100644 --- a/core/img/actions/play-add.svg +++ b/core/img/actions/play-add.svg @@ -1,83 +1,9 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - - - + + + + + + diff --git a/core/img/actions/play-big.svg b/core/img/actions/play-big.svg index 2ef6741532..884171ced8 100644 --- a/core/img/actions/play-big.svg +++ b/core/img/actions/play-big.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/play-next.svg b/core/img/actions/play-next.svg index 9a41e4bd9d..8b3d7d6eff 100644 --- a/core/img/actions/play-next.svg +++ b/core/img/actions/play-next.svg @@ -1,79 +1,7 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + diff --git a/core/img/actions/play-previous.svg b/core/img/actions/play-previous.svg index 31d45dedb4..6210b088cb 100644 --- a/core/img/actions/play-previous.svg +++ b/core/img/actions/play-previous.svg @@ -1,79 +1,7 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + diff --git a/core/img/actions/play.svg b/core/img/actions/play.svg index 7bb7b5c262..ae23e6a0d2 100644 --- a/core/img/actions/play.svg +++ b/core/img/actions/play.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/public.svg b/core/img/actions/public.svg index b47305fbd0..c70a762778 100644 --- a/core/img/actions/public.svg +++ b/core/img/actions/public.svg @@ -1,292 +1,4 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/core/img/actions/rename.svg b/core/img/actions/rename.svg index 44b464c850..d6779709d9 100644 --- a/core/img/actions/rename.svg +++ b/core/img/actions/rename.svg @@ -1,72 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/search.png b/core/img/actions/search.png index 98e1d73ee3..312e4f419e 100644 Binary files a/core/img/actions/search.png and b/core/img/actions/search.png differ diff --git a/core/img/actions/search.svg b/core/img/actions/search.svg index c8d9d848c4..4f27369dbb 100644 --- a/core/img/actions/search.svg +++ b/core/img/actions/search.svg @@ -1,1632 +1,14 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/core/img/actions/settings.png b/core/img/actions/settings.png index 8b3acb00a4..9ada308770 100644 Binary files a/core/img/actions/settings.png and b/core/img/actions/settings.png differ diff --git a/core/img/actions/settings.svg b/core/img/actions/settings.svg index da685e8be0..bd7ae3b3d7 100644 --- a/core/img/actions/settings.svg +++ b/core/img/actions/settings.svg @@ -1,270 +1,17 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/core/img/actions/share.svg b/core/img/actions/share.svg index a5f2f8cb4d..d67d35c6e5 100644 --- a/core/img/actions/share.svg +++ b/core/img/actions/share.svg @@ -1,70 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/shared.svg b/core/img/actions/shared.svg index 2302cc9891..3e63cc5468 100644 --- a/core/img/actions/shared.svg +++ b/core/img/actions/shared.svg @@ -1,1738 +1,5 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/core/img/actions/sound-off.svg b/core/img/actions/sound-off.svg index 053291311f..701d7a1a64 100644 --- a/core/img/actions/sound-off.svg +++ b/core/img/actions/sound-off.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/sound.svg b/core/img/actions/sound.svg index 6feea076a4..ecadf7dae9 100644 --- a/core/img/actions/sound.svg +++ b/core/img/actions/sound.svg @@ -1,78 +1,7 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + diff --git a/core/img/actions/toggle.png b/core/img/actions/toggle.png index 6ef3f2227b..d06e5cb32b 100644 Binary files a/core/img/actions/toggle.png and b/core/img/actions/toggle.png differ diff --git a/core/img/actions/toggle.svg b/core/img/actions/toggle.svg index 82a5171477..1b774a19b1 100644 --- a/core/img/actions/toggle.svg +++ b/core/img/actions/toggle.svg @@ -1,61 +1,5 @@ - - -image/svg+xml - - - - - \ No newline at end of file + + + + diff --git a/core/img/actions/triangle-n.png b/core/img/actions/triangle-n.png index 14825f7011..0ffcf6cbc4 100644 Binary files a/core/img/actions/triangle-n.png and b/core/img/actions/triangle-n.png differ diff --git a/core/img/actions/triangle-n.svg b/core/img/actions/triangle-n.svg index e8d70fa8ce..4f866978f4 100644 --- a/core/img/actions/triangle-n.svg +++ b/core/img/actions/triangle-n.svg @@ -1,88 +1,4 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - + + diff --git a/core/img/actions/triangle-s.png b/core/img/actions/triangle-s.png index f36faef2b8..0f533b142e 100644 Binary files a/core/img/actions/triangle-s.png and b/core/img/actions/triangle-s.png differ diff --git a/core/img/actions/triangle-s.svg b/core/img/actions/triangle-s.svg index 396c61e01e..b178b20a20 100644 --- a/core/img/actions/triangle-s.svg +++ b/core/img/actions/triangle-s.svg @@ -1,88 +1,4 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - + + diff --git a/core/img/actions/upload-white.svg b/core/img/actions/upload-white.svg index 32ecd8b82b..9c54cac5e1 100644 --- a/core/img/actions/upload-white.svg +++ b/core/img/actions/upload-white.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/upload.svg b/core/img/actions/upload.svg index 718da8f4a5..eae4515c72 100644 --- a/core/img/actions/upload.svg +++ b/core/img/actions/upload.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/user.svg b/core/img/actions/user.svg index 6d0dc714ce..aa71957370 100644 --- a/core/img/actions/user.svg +++ b/core/img/actions/user.svg @@ -1,1698 +1,5 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/core/img/actions/view-close.png b/core/img/actions/view-close.png index 80339d7822..330ae09ea7 100644 Binary files a/core/img/actions/view-close.png and b/core/img/actions/view-close.png differ diff --git a/core/img/actions/view-close.svg b/core/img/actions/view-close.svg index 45d6697608..1d5b1a9f49 100644 --- a/core/img/actions/view-close.svg +++ b/core/img/actions/view-close.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/view-next.png b/core/img/actions/view-next.png index b76bea0671..f9e6174ae3 100644 Binary files a/core/img/actions/view-next.png and b/core/img/actions/view-next.png differ diff --git a/core/img/actions/view-next.svg b/core/img/actions/view-next.svg index d5642f1a11..07c95b73ff 100644 --- a/core/img/actions/view-next.svg +++ b/core/img/actions/view-next.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/view-pause.png b/core/img/actions/view-pause.png index 64264ff928..94696bf686 100644 Binary files a/core/img/actions/view-pause.png and b/core/img/actions/view-pause.png differ diff --git a/core/img/actions/view-pause.svg b/core/img/actions/view-pause.svg index 0edc6f14e2..d901a4d789 100644 --- a/core/img/actions/view-pause.svg +++ b/core/img/actions/view-pause.svg @@ -1,72 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/view-play.png b/core/img/actions/view-play.png index 0080d45b5c..721787d9c4 100644 Binary files a/core/img/actions/view-play.png and b/core/img/actions/view-play.png differ diff --git a/core/img/actions/view-play.svg b/core/img/actions/view-play.svg index 0bdc63bf7e..d9fa355371 100644 --- a/core/img/actions/view-play.svg +++ b/core/img/actions/view-play.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/actions/view-previous.png b/core/img/actions/view-previous.png index 82943c23a5..97b41a195f 100644 Binary files a/core/img/actions/view-previous.png and b/core/img/actions/view-previous.png differ diff --git a/core/img/actions/view-previous.svg b/core/img/actions/view-previous.svg index df1f49511d..68a31c0443 100644 --- a/core/img/actions/view-previous.svg +++ b/core/img/actions/view-previous.svg @@ -1,73 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/appstore.png b/core/img/appstore.png index 009b2b51b9..234aa0bb6b 100644 Binary files a/core/img/appstore.png and b/core/img/appstore.png differ diff --git a/core/img/breadcrumb-start.svg b/core/img/breadcrumb-start.svg index 4197763dc6..7f36231cdf 100644 --- a/core/img/breadcrumb-start.svg +++ b/core/img/breadcrumb-start.svg @@ -1,71 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/breadcrumb.svg b/core/img/breadcrumb.svg index 9d522b42b7..05a216e50a 100644 --- a/core/img/breadcrumb.svg +++ b/core/img/breadcrumb.svg @@ -1,77 +1,6 @@ - - - - - - - - - - - image/svg+xml - - - - - - - - + + + + diff --git a/core/img/desktopapp.png b/core/img/desktopapp.png index 182ddd2cf1..25dae6f197 100644 Binary files a/core/img/desktopapp.png and b/core/img/desktopapp.png differ diff --git a/core/img/desktopapp.svg b/core/img/desktopapp.svg index 93d91e461a..a983e6f959 100644 --- a/core/img/desktopapp.svg +++ b/core/img/desktopapp.svg @@ -1,100 +1,5 @@ -image/svg+xml - - -Desktop app -Windows, OS X, Linux - \ No newline at end of file + +Desktop app +Windows, OS X, Linux + diff --git a/core/img/favicon-touch.svg b/core/img/favicon-touch.svg index 6d766d3ced..68f36a8a9a 100644 --- a/core/img/favicon-touch.svg +++ b/core/img/favicon-touch.svg @@ -1,787 +1,4 @@ - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + diff --git a/core/img/favicon.png b/core/img/favicon.png index 79b6795f6f..02936243cb 100644 Binary files a/core/img/favicon.png and b/core/img/favicon.png differ diff --git a/core/img/favicon.svg b/core/img/favicon.svg index f055c32efb..39cb174268 100644 --- a/core/img/favicon.svg +++ b/core/img/favicon.svg @@ -1,796 +1,4 @@ - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + diff --git a/core/img/filetypes/application-x-debian-package.png b/core/img/filetypes/application-x-debian-package.png index b3f6b7e5cf..1d6db5f933 100644 Binary files a/core/img/filetypes/application-x-debian-package.png and b/core/img/filetypes/application-x-debian-package.png differ diff --git a/core/img/googleplay.png b/core/img/googleplay.png index 2d9ad62960..1470518272 100644 Binary files a/core/img/googleplay.png and b/core/img/googleplay.png differ diff --git a/core/img/image-optimization.sh b/core/img/image-optimization.sh new file mode 100755 index 0000000000..0a96bf558d --- /dev/null +++ b/core/img/image-optimization.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +function recursive_optimize_images() { +cd $1; +optipng -o6 *.png; +jpegoptim --strip-all *.jpg; +for svg in `ls *.svg`; +do + mv $svg $svg.opttmp; + scour -i $svg.opttmp -o $svg --create-groups --enable-id-stripping --enable-comment-stripping --shorten-ids --remove-metadata; +done; +rm *.opttmp +for dir in `ls -d */`; +do + recursive_optimize_images $dir; + cd ..; +done; +} + +recursive_optimize_images ../../ diff --git a/core/img/logo-wide.png b/core/img/logo-wide.png index b0c90984e4..5b7d4c6f91 100644 Binary files a/core/img/logo-wide.png and b/core/img/logo-wide.png differ diff --git a/core/img/logo-wide.svg b/core/img/logo-wide.svg index cf8eace520..29c617d6f8 100644 --- a/core/img/logo-wide.svg +++ b/core/img/logo-wide.svg @@ -1,796 +1,3 @@ - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + diff --git a/core/img/logo.png b/core/img/logo.png index a84fe145bb..8d112d99be 100644 Binary files a/core/img/logo.png and b/core/img/logo.png differ diff --git a/core/img/logo.svg b/core/img/logo.svg index bd928cccfa..cfb20b60e4 100644 --- a/core/img/logo.svg +++ b/core/img/logo.svg @@ -1,759 +1,4 @@ - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + diff --git a/core/img/noise.png b/core/img/noise.png index 271dd5ebcf..6c06c8a4d6 100644 Binary files a/core/img/noise.png and b/core/img/noise.png differ diff --git a/core/img/places/calendar-dark.png b/core/img/places/calendar-dark.png index e372104a28..920dee610d 100644 Binary files a/core/img/places/calendar-dark.png and b/core/img/places/calendar-dark.png differ diff --git a/core/img/places/calendar-dark.svg b/core/img/places/calendar-dark.svg index 6f7cb8e74d..986be039ab 100644 --- a/core/img/places/calendar-dark.svg +++ b/core/img/places/calendar-dark.svg @@ -1,75 +1,7 @@ - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + diff --git a/core/img/places/contacts-dark.svg b/core/img/places/contacts-dark.svg index df364911c5..3fc10cfe08 100644 --- a/core/img/places/contacts-dark.svg +++ b/core/img/places/contacts-dark.svg @@ -1,73 +1,7 @@ - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + diff --git a/core/img/places/file.svg b/core/img/places/file.svg index 478714b75d..f93f3ef6fa 100644 --- a/core/img/places/file.svg +++ b/core/img/places/file.svg @@ -1,1841 +1,14 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/core/img/places/files.png b/core/img/places/files.png index 9c7ff2642f..52e0c6bf94 100644 Binary files a/core/img/places/files.png and b/core/img/places/files.png differ diff --git a/core/img/places/files.svg b/core/img/places/files.svg index 8ebf861f6d..d446ef655a 100644 --- a/core/img/places/files.svg +++ b/core/img/places/files.svg @@ -1,128 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + diff --git a/core/img/places/folder.svg b/core/img/places/folder.svg index c04b00fedc..676f10afe0 100644 --- a/core/img/places/folder.svg +++ b/core/img/places/folder.svg @@ -1,1830 +1,18 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/core/img/places/home.png b/core/img/places/home.png index 2945b84e86..e664719e2e 100644 Binary files a/core/img/places/home.png and b/core/img/places/home.png differ diff --git a/core/img/places/home.svg b/core/img/places/home.svg index a836a5999f..80b7dcc866 100644 --- a/core/img/places/home.svg +++ b/core/img/places/home.svg @@ -1,1819 +1,11 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/core/img/places/music.svg b/core/img/places/music.svg index e8f91f4616..f7eb391d98 100644 --- a/core/img/places/music.svg +++ b/core/img/places/music.svg @@ -1,73 +1,7 @@ - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + diff --git a/core/img/places/picture.png b/core/img/places/picture.png index a278240a6d..7b3af8c7f8 100644 Binary files a/core/img/places/picture.png and b/core/img/places/picture.png differ diff --git a/core/img/places/picture.svg b/core/img/places/picture.svg index aba68e6206..791cbb5909 100644 --- a/core/img/places/picture.svg +++ b/core/img/places/picture.svg @@ -1,75 +1,7 @@ - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + diff --git a/core/img/rating/s1.png b/core/img/rating/s1.png index 445d965ffe..015f948371 100644 Binary files a/core/img/rating/s1.png and b/core/img/rating/s1.png differ diff --git a/core/img/rating/s10.png b/core/img/rating/s10.png index b8d66c2a4c..b47b05e4f8 100644 Binary files a/core/img/rating/s10.png and b/core/img/rating/s10.png differ diff --git a/core/img/rating/s11.png b/core/img/rating/s11.png index aee9f92156..3dcb4bb483 100644 Binary files a/core/img/rating/s11.png and b/core/img/rating/s11.png differ diff --git a/core/img/rating/s2.png b/core/img/rating/s2.png index 4f860e74ca..94ac5bc956 100644 Binary files a/core/img/rating/s2.png and b/core/img/rating/s2.png differ diff --git a/core/img/rating/s3.png b/core/img/rating/s3.png index 26c9baff55..42a814ca08 100644 Binary files a/core/img/rating/s3.png and b/core/img/rating/s3.png differ diff --git a/core/img/rating/s4.png b/core/img/rating/s4.png index 47f1f694bf..5ce3888757 100644 Binary files a/core/img/rating/s4.png and b/core/img/rating/s4.png differ diff --git a/core/img/rating/s5.png b/core/img/rating/s5.png index aa225b6a9a..da4bbc5847 100644 Binary files a/core/img/rating/s5.png and b/core/img/rating/s5.png differ diff --git a/core/img/rating/s6.png b/core/img/rating/s6.png index fd4f42e22c..267c52ad3c 100644 Binary files a/core/img/rating/s6.png and b/core/img/rating/s6.png differ diff --git a/core/img/rating/s7.png b/core/img/rating/s7.png index 0d18a1dc02..3381d066d8 100644 Binary files a/core/img/rating/s7.png and b/core/img/rating/s7.png differ diff --git a/core/img/rating/s8.png b/core/img/rating/s8.png index 951c3fd3be..091dc5b21f 100644 Binary files a/core/img/rating/s8.png and b/core/img/rating/s8.png differ diff --git a/core/img/rating/s9.png b/core/img/rating/s9.png index b1a654c85d..dfe8356343 100644 Binary files a/core/img/rating/s9.png and b/core/img/rating/s9.png differ diff --git a/core/img/remoteStorage-big.png b/core/img/remoteStorage-big.png index f225423303..7e76e21209 100644 Binary files a/core/img/remoteStorage-big.png and b/core/img/remoteStorage-big.png differ diff --git a/core/js/compatibility.js b/core/js/compatibility.js index cc37949409..b690803ca7 100644 --- a/core/js/compatibility.js +++ b/core/js/compatibility.js @@ -133,4 +133,18 @@ if(!String.prototype.trim) { String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g,''); }; -} \ No newline at end of file +} + +// Older Firefoxes doesn't support outerHTML +// From http://stackoverflow.com/questions/1700870/how-do-i-do-outerhtml-in-firefox#answer-3819589 +function outerHTML(node){ + // In newer browsers use the internal property otherwise build a wrapper. + return node.outerHTML || ( + function(n){ + var div = document.createElement('div'), h; + div.appendChild( n.cloneNode(true) ); + h = div.innerHTML; + div = null; + return h; + })(node); +} diff --git a/core/js/config.php b/core/js/config.php index 0aaa448228..53a8fb9638 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -26,8 +26,8 @@ $array = array( "oc_debug" => (defined('DEBUG') && DEBUG) ? 'true' : 'false', "oc_webroot" => "\"".OC::$WEBROOT."\"", "oc_appswebroots" => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution - "oc_current_user" => "\"".OC_User::getUser(). "\"", - "oc_requesttoken" => "\"".OC_Util::callRegister(). "\"", + "oc_current_user" => "document.getElementsByTagName('head')[0].getAttribute('data-user')", + "oc_requesttoken" => "document.getElementsByTagName('head')[0].getAttribute('data-requesttoken')", "datepickerFormatDate" => json_encode($l->l('jsdate', 'jsdate')), "dayNames" => json_encode( array( diff --git a/core/js/jquery-1.10.0.min.js b/core/js/jquery-1.10.0.min.js new file mode 100644 index 0000000000..01c688164a --- /dev/null +++ b/core/js/jquery-1.10.0.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.0 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.0.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.0",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=lt(),k=lt(),E=lt(),S=!1,A=function(){return 0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=bt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+xt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return At(e.replace(z,"$1"),t,n,i)}function st(e){return K.test(e+"")}function lt(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[b]=!0,e}function ct(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function pt(e,t,n){e=e.split("|");var r,i=e.length,a=n?null:t;while(i--)(r=o.attrHandle[e[i]])&&r!==t||(o.attrHandle[e[i]]=a)}function ft(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function dt(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:t}function gt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function yt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function vt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),r.attributes=ct(function(e){return e.innerHTML="",pt("type|href|height|width",dt,"#"===e.firstChild.getAttribute("href")),pt(B,ft,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),r.input=ct(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ct(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=st(n.querySelectorAll))&&(ct(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ct(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=st(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ct(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=st(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},r.sortDetached=ct(function(e){return 1&e.compareDocumentPosition(n.createElement("div"))}),A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return gt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?gt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:ut,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=bt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?ut(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return at(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:vt(function(){return[0]}),last:vt(function(e,t){return[t-1]}),eq:vt(function(e,t,n){return[0>n?n+t:n]}),even:vt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:vt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:vt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:vt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=mt(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=yt(n);function bt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function wt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function Tt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Ct(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function Nt(e,t,n,r,i,o){return r&&!r[b]&&(r=Nt(r)),i&&!i[b]&&(i=Nt(i,o)),ut(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||St(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:Ct(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=Ct(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=Ct(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function kt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=wt(function(e){return e===t},s,!0),p=wt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[wt(Tt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return Nt(l>1&&Tt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),i>r&&kt(e=e.slice(r)),i>r&&xt(e))}f.push(n)}return Tt(f)}function Et(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=Ct(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?ut(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=bt(e)),n=t.length;while(n--)o=kt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Et(i,r))}return o};function St(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function At(e,t,n,i){var a,s,u,c,p,f=bt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&xt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}o.pseudos.nth=o.pseudos.eq;function jt(){}jt.prototype=o.filters=o.pseudos,o.setFilters=new jt,r.sortStable=b.split("").sort(A).join("")===b,p(),[0,0].sort(A),r.detectDuplicates=S,x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!u||(n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s; +if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=x(this),l=t,u=e.match(T)||[];while(o=u[a++])l=r?l:!s.hasClass(o),s[l?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle);u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}) +}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("