diff options
author | Michael Gapczynski <mtgap@owncloud.com> | 2013-05-27 11:35:57 -0400 |
---|---|---|
committer | Michael Gapczynski <mtgap@owncloud.com> | 2013-05-27 11:35:57 -0400 |
commit | 16925672e885071afab1fe158dc206ab8ed93baf (patch) | |
tree | 143f64c88d7d47cb0f5a3a7641228a7cae24821b /apps | |
parent | 0953b68556152187ed305323b64b186cc21c2ade (diff) | |
parent | 0c621ff6a93fe1b34c77257d83fd344489f59bab (diff) | |
download | nextcloud-server-16925672e885071afab1fe158dc206ab8ed93baf.tar.gz nextcloud-server-16925672e885071afab1fe158dc206ab8ed93baf.zip |
Merge branch 'master' into googledrive
Diffstat (limited to 'apps')
244 files changed, 8245 insertions, 3192 deletions
diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index 1cd2944483c..f568afad4da 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 9fd2ce3ad4b..f4551858283 100644 --- a/apps/files/ajax/rename.php +++ b/apps/files/ajax/rename.php @@ -1,26 +1,41 @@ <?php -// Init owncloud - +/** + * ownCloud - Core + * + * @author Morris Jobke + * @copyright 2013 Morris Jobke morris.jobke@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 <http://www.gnu.org/licenses/>. + * + */ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); -// Get data -$dir = stripslashes($_GET["dir"]); -$file = stripslashes($_GET["file"]); -$newname = stripslashes($_GET["newname"]); - -$l = OC_L10N::get('files'); +$files = new \OCA\Files\App( + \OC\Files\Filesystem::getView(), + \OC_L10n::get('files') +); +$result = $files->rename( + $_GET["dir"], + $_GET["file"], + $_GET["newname"] +); -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/css/files.css b/apps/files/css/files.css index ec323915b44..f788949b1b6 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/filelist.js b/apps/files/js/filelist.js index b1e9a885063..c24d1fd8244 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(); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index a15f0588f9f..a79d34c9b23 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -708,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; diff --git a/apps/files/l10n/ar.php b/apps/files/l10n/ar.php index bc01a340622..ca198b7efe9 100644 --- a/apps/files/l10n/ar.php +++ b/apps/files/l10n/ar.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "فشل في نقل الملف %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 ", @@ -45,6 +44,7 @@ "{count} folders" => "{count} مجلدات", "1 file" => "ملف واحد", "{count} files" => "{count} ملفات", +"Unable to rename file" => "فشل في اعادة تسمية الملف", "Upload" => "رفع", "File handling" => "التعامل مع الملف", "Maximum upload size" => "الحد الأقصى لحجم الملفات التي يمكن رفعها", diff --git a/apps/files/l10n/bn_BD.php b/apps/files/l10n/bn_BD.php index 42c78ab3470..83dd4dc36dc 100644 --- a/apps/files/l10n/bn_BD.php +++ b/apps/files/l10n/bn_BD.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%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 নির্দেশিত আয়তন অতিক্রম করছেঃ", @@ -40,6 +39,7 @@ "{count} folders" => "{count} টি ফোল্ডার", "1 file" => "১টি ফাইল", "{count} files" => "{count} টি ফাইল", +"Unable to rename file" => "ফাইলের নাম পরিবর্তন করা সম্ভব হলো না", "Upload" => "আপলোড", "File handling" => "ফাইল হ্যার্ডলিং", "Maximum upload size" => "আপলোডের সর্বোচ্চ আকার", diff --git a/apps/files/l10n/ca.php b/apps/files/l10n/ca.php index ff9572ad99e..c1c94b99003 100644 --- a/apps/files/l10n/ca.php +++ b/apps/files/l10n/ca.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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" => "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:", @@ -47,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", @@ -56,7 +57,7 @@ "0 is unlimited" => "0 és sense límit", "Maximum input size for ZIP files" => "Mida màxima d'entrada per fitxers ZIP", "Save" => "Desa", -"New" => "Nova", +"New" => "Nou", "Text file" => "Fitxer de text", "Folder" => "Carpeta", "From link" => "Des d'enllaç", diff --git a/apps/files/l10n/cs_CZ.php b/apps/files/l10n/cs_CZ.php index f28c6dad7e2..de6a1542421 100644 --- a/apps/files/l10n/cs_CZ.php +++ b/apps/files/l10n/cs_CZ.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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:", @@ -47,6 +46,7 @@ "{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í", diff --git a/apps/files/l10n/cy_GB.php b/apps/files/l10n/cy_GB.php index 6ec0e7f914f..ae339488910 100644 --- a/apps/files/l10n/cy_GB.php +++ b/apps/files/l10n/cy_GB.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "Methwyd symud %s - Mae ffeil gyda'r enw hwn eisoes yn bodoli", "Could not move %s" => "Methwyd symud %s", -"Unable to rename file" => "Methu ailenwi ffeil", "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:", @@ -47,6 +46,7 @@ "{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", diff --git a/apps/files/l10n/da.php b/apps/files/l10n/da.php index ff590aa9a3a..879fbc8451f 100644 --- a/apps/files/l10n/da.php +++ b/apps/files/l10n/da.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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 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", @@ -47,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", diff --git a/apps/files/l10n/de.php b/apps/files/l10n/de.php index 14d084bc211..bcc3a4c6c9d 100644 --- a/apps/files/l10n/de.php +++ b/apps/files/l10n/de.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "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", -"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 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", @@ -47,6 +46,7 @@ "{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", diff --git a/apps/files/l10n/de_DE.php b/apps/files/l10n/de_DE.php index 6f912976939..3c06c1ac83d 100644 --- a/apps/files/l10n/de_DE.php +++ b/apps/files/l10n/de_DE.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "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", -"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 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", @@ -47,6 +46,8 @@ "{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", diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php index d67a2fce36c..b273f6b522d 100644 --- a/apps/files/l10n/el.php +++ b/apps/files/l10n/el.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "Αδυναμία μετακίνησης του %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", @@ -47,6 +46,8 @@ "{count} folders" => "{count} φάκελοι", "1 file" => "1 αρχείο", "{count} files" => "{count} αρχεία", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Μη έγκυρο όνομα φακέλου. Η χρήση του 'Κοινόχρηστος' χρησιμοποιείται από το ownCloud", +"Unable to rename file" => "Αδυναμία μετονομασίας αρχείου", "Upload" => "Μεταφόρτωση", "File handling" => "Διαχείριση αρχείων", "Maximum upload size" => "Μέγιστο μέγεθος αποστολής", diff --git a/apps/files/l10n/eo.php b/apps/files/l10n/eo.php index 936c9aef19d..3eeb88754c7 100644 --- a/apps/files/l10n/eo.php +++ b/apps/files/l10n/eo.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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.", "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: ", @@ -42,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", diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php index dd756142e42..b11adfabeb3 100644 --- a/apps/files/l10n/es.php +++ b/apps/files/l10n/es.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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" => "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", @@ -47,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", diff --git a/apps/files/l10n/es_AR.php b/apps/files/l10n/es_AR.php index 3b6a1f431e1..0ae47302edf 100644 --- a/apps/files/l10n/es_AR.php +++ b/apps/files/l10n/es_AR.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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 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:", @@ -47,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", diff --git a/apps/files/l10n/et_EE.php b/apps/files/l10n/et_EE.php index 133f461a124..d3fab4b0bd1 100644 --- a/apps/files/l10n/et_EE.php +++ b/apps/files/l10n/et_EE.php @@ -1,10 +1,9 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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 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 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", @@ -25,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. ", +"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", @@ -47,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", @@ -68,7 +69,7 @@ "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 74c096e1965..a4afc2e8ca8 100644 --- a/apps/files/l10n/eu.php +++ b/apps/files/l10n/eu.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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 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:", @@ -47,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", diff --git a/apps/files/l10n/fa.php b/apps/files/l10n/fa.php index 10132fdf9e3..b97067ac193 100644 --- a/apps/files/l10n/fa.php +++ b/apps/files/l10n/fa.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%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 استفاده کرده است.", @@ -47,6 +46,7 @@ "{count} folders" => "{ شمار} پوشه ها", "1 file" => "1 پرونده", "{count} files" => "{ شمار } فایل ها", +"Unable to rename file" => "قادر به تغییر نام پرونده نیست.", "Upload" => "بارگزاری", "File handling" => "اداره پرونده ها", "Maximum upload size" => "حداکثر اندازه بارگزاری", diff --git a/apps/files/l10n/fi_FI.php b/apps/files/l10n/fi_FI.php index 08a07183238..3d0d7245781 100644 --- a/apps/files/l10n/fi_FI.php +++ b/apps/files/l10n/fi_FI.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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 upload_max_filesize directive in php.ini: " => "Lähetetyn tiedoston koko ylittää php.ini-tiedoston upload_max_filesize-säännön:", @@ -43,6 +42,7 @@ "{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 e4793ab5264..39c697396c9 100644 --- a/apps/files/l10n/fr.php +++ b/apps/files/l10n/fr.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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é 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:", @@ -47,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", diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php index 48145d44619..d22ed4b8721 100644 --- a/apps/files/l10n/gl.php +++ b/apps/files/l10n/gl.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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 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:", @@ -47,6 +46,8 @@ "{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", diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php index cd5154fcd85..76b8bd420da 100644 --- a/apps/files/l10n/hu_HU.php +++ b/apps/files/l10n/hu_HU.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%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.", @@ -47,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", diff --git a/apps/files/l10n/id.php b/apps/files/l10n/id.php index 7cba9ae66eb..58cc0ea7fd9 100644 --- a/apps/files/l10n/id.php +++ b/apps/files/l10n/id.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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.", "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", @@ -47,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", diff --git a/apps/files/l10n/is.php b/apps/files/l10n/is.php index f0a4aa81efa..aa10c838c1d 100644 --- a/apps/files/l10n/is.php +++ b/apps/files/l10n/is.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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:", @@ -40,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 77725b6770d..c588285aaca 100644 --- a/apps/files/l10n/it.php +++ b/apps/files/l10n/it.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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, 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:", @@ -47,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", diff --git a/apps/files/l10n/ja_JP.php b/apps/files/l10n/ja_JP.php index bff9fa5b519..55dcf3640e7 100644 --- a/apps/files/l10n/ja_JP.php +++ b/apps/files/l10n/ja_JP.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%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 に設定されたサイズを超えています:", @@ -47,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" => "最大アップロードサイズ", diff --git a/apps/files/l10n/ka_GE.php b/apps/files/l10n/ka_GE.php index d237a81856a..c50ca2594b6 100644 --- a/apps/files/l10n/ka_GE.php +++ b/apps/files/l10n/ka_GE.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%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 ფაილში", @@ -47,6 +46,7 @@ "{count} folders" => "{count} საქაღალდე", "1 file" => "1 ფაილი", "{count} files" => "{count} ფაილი", +"Unable to rename file" => "ფაილის სახელის გადარქმევა ვერ მოხერხდა", "Upload" => "ატვირთვა", "File handling" => "ფაილის დამუშავება", "Maximum upload size" => "მაქსიმუმ ატვირთის ზომა", diff --git a/apps/files/l10n/ko.php b/apps/files/l10n/ko.php index 46955bd675f..c78f58542e4 100644 --- a/apps/files/l10n/ko.php +++ b/apps/files/l10n/ko.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%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보다 큽니다:", @@ -47,6 +46,7 @@ "{count} folders" => "폴더 {count}개", "1 file" => "파일 1개", "{count} files" => "파일 {count}개", +"Unable to rename file" => "파일 이름바꾸기 할 수 없음", "Upload" => "업로드", "File handling" => "파일 처리", "Maximum upload size" => "최대 업로드 크기", diff --git a/apps/files/l10n/lv.php b/apps/files/l10n/lv.php index 1e7e8657074..f62bdd2d492 100644 --- a/apps/files/l10n/lv.php +++ b/apps/files/l10n/lv.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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" => "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ē:", @@ -46,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/nb_NO.php b/apps/files/l10n/nb_NO.php index 1ff21b1f0e2..d5710a4927a 100644 --- a/apps/files/l10n/nb_NO.php +++ b/apps/files/l10n/nb_NO.php @@ -1,11 +1,16 @@ <?php $TRANSLATIONS = array( +"Could not move %s - File with this name already exists" => "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" => "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", @@ -18,13 +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", @@ -33,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", @@ -46,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" => "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 aea25779dbc..bc4158df3b3 100644 --- a/apps/files/l10n/nl.php +++ b/apps/files/l10n/nl.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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" => "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:", @@ -47,6 +46,8 @@ "{count} folders" => "{count} mappen", "1 file" => "1 bestand", "{count} files" => "{count} bestanden", +"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", diff --git a/apps/files/l10n/nn_NO.php b/apps/files/l10n/nn_NO.php index 2042e7bf8ad..29593b6f2de 100644 --- a/apps/files/l10n/nn_NO.php +++ b/apps/files/l10n/nn_NO.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( -"Could not move %s - File with this name already exists" => "Klarte ikkje å flytta %s – det finst allereie ei fil med dette namnet", -"Could not move %s" => "Klarte ikkje å flytta %s", -"Unable to rename file" => "Klarte ikkje å endra filnamnet", +"Could not move %s - File with this name already exists" => "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: ", @@ -9,7 +8,7 @@ "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", +"Failed to write to disk" => "Klarte ikkje skriva til disk", "Not enough storage available" => "Ikkje nok lagringsplass tilgjengeleg", "Invalid directory." => "Ugyldig mappe.", "Files" => "Filer", @@ -33,11 +32,11 @@ "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", +"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 bli avbroten.", -"URL cannot be empty." => "URL-en kan ikkje vera tom.", +"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", @@ -47,12 +46,14 @@ "{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." => "Naudsynt for fleirfils- og mappenedlastingar.", -"Enable ZIP-download" => "Skru på ZIP-nedlasting", +"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", @@ -67,7 +68,7 @@ "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/pl.php b/apps/files/l10n/pl.php index ef0fd525778..4bdac055781 100644 --- a/apps/files/l10n/pl.php +++ b/apps/files/l10n/pl.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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" => "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: ", @@ -47,6 +46,8 @@ "{count} folders" => "Ilość folderów: {count}", "1 file" => "1 plik", "{count} files" => "Ilość plików: {count}", +"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", diff --git a/apps/files/l10n/pt_BR.php b/apps/files/l10n/pt_BR.php index f61084105de..0f349b69481 100644 --- a/apps/files/l10n/pt_BR.php +++ b/apps/files/l10n/pt_BR.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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" => "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: ", @@ -47,6 +46,8 @@ "{count} folders" => "{count} pastas", "1 file" => "1 arquivo", "{count} files" => "{count} arquivos", +"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", diff --git a/apps/files/l10n/pt_PT.php b/apps/files/l10n/pt_PT.php index a5de64cc1db..15d6fc80bd3 100644 --- a/apps/files/l10n/pt_PT.php +++ b/apps/files/l10n/pt_PT.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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" => "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", @@ -47,6 +46,7 @@ "{count} folders" => "{count} pastas", "1 file" => "1 ficheiro", "{count} files" => "{count} ficheiros", +"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", diff --git a/apps/files/l10n/ro.php b/apps/files/l10n/ro.php index b2b6ee4963f..8fdf62aeb32 100644 --- a/apps/files/l10n/ro.php +++ b/apps/files/l10n/ro.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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" => "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: ", @@ -47,6 +46,7 @@ "{count} folders" => "{count} foldare", "1 file" => "1 fisier", "{count} files" => "{count} fisiere", +"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", diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php index 54d6780c3d1..83412bf2be8 100644 --- a/apps/files/l10n/ru.php +++ b/apps/files/l10n/ru.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "Невозможно переместить %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:", @@ -47,6 +46,7 @@ "{count} folders" => "{count} папок", "1 file" => "1 файл", "{count} files" => "{count} файлов", +"Unable to rename file" => "Невозможно переименовать файл", "Upload" => "Загрузка", "File handling" => "Управление файлами", "Maximum upload size" => "Максимальный размер загружаемого файла", diff --git a/apps/files/l10n/ru_RU.php b/apps/files/l10n/ru_RU.php index 400a0dc8de7..e0bfab33215 100644 --- a/apps/files/l10n/ru_RU.php +++ b/apps/files/l10n/ru_RU.php @@ -1,71 +1,16 @@ <?php $TRANSLATIONS = array( -"Could not move %s - File with this name already exists" => "Неполучается перенести %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" => "Файлы", "Share" => "Сделать общим", -"Delete permanently" => "Удалить навсегда", "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/sk_SK.php b/apps/files/l10n/sk_SK.php index 86f01bfb0ed..ad33c9b4eee 100644 --- a/apps/files/l10n/sk_SK.php +++ b/apps/files/l10n/sk_SK.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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:", @@ -47,6 +46,8 @@ "{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", diff --git a/apps/files/l10n/sl.php b/apps/files/l10n/sl.php index 44c33d62fbe..6902d311ab7 100644 --- a/apps/files/l10n/sl.php +++ b/apps/files/l10n/sl.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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 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:", @@ -47,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", diff --git a/apps/files/l10n/sq.php b/apps/files/l10n/sq.php index fe3ae9e7a96..63c95f692e2 100644 --- a/apps/files/l10n/sq.php +++ b/apps/files/l10n/sq.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%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:", @@ -47,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 a10bd82b4cc..3be6dde91a7 100644 --- a/apps/files/l10n/sr.php +++ b/apps/files/l10n/sr.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "Не могу да преместим %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:", @@ -47,6 +46,7 @@ "{count} folders" => "{count} фасцикле/и", "1 file" => "1 датотека", "{count} files" => "{count} датотеке/а", +"Unable to rename file" => "Не могу да преименујем датотеку", "Upload" => "Отпреми", "File handling" => "Управљање датотекама", "Maximum upload size" => "Највећа величина датотеке", diff --git a/apps/files/l10n/sv.php b/apps/files/l10n/sv.php index c342db37538..82d169d569c 100644 --- a/apps/files/l10n/sv.php +++ b/apps/files/l10n/sv.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "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.", "The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Den uppladdade filen överskrider upload_max_filesize direktivet php.ini:", @@ -47,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/th_TH.php b/apps/files/l10n/th_TH.php index a707edb6283..06d26edfec8 100644 --- a/apps/files/l10n/th_TH.php +++ b/apps/files/l10n/th_TH.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "ไม่สามารถย้าย %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", @@ -46,6 +45,7 @@ "{count} folders" => "{count} โฟลเดอร์", "1 file" => "1 ไฟล์", "{count} files" => "{count} ไฟล์", +"Unable to rename file" => "ไม่สามารถเปลี่ยนชื่อไฟล์ได้", "Upload" => "อัพโหลด", "File handling" => "การจัดกาไฟล์", "Maximum upload size" => "ขนาดไฟล์สูงสุดที่อัพโหลดได้", diff --git a/apps/files/l10n/tr.php b/apps/files/l10n/tr.php index 1df062c994c..6a096d27039 100644 --- a/apps/files/l10n/tr.php +++ b/apps/files/l10n/tr.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%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" => "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ı.", @@ -47,6 +46,8 @@ "{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", diff --git a/apps/files/l10n/ug.php b/apps/files/l10n/ug.php index 1bcd78be5ae..fb8f187adef 100644 --- a/apps/files/l10n/ug.php +++ b/apps/files/l10n/ug.php @@ -1,6 +1,5 @@ <?php $TRANSLATIONS = array( "Could not move %s" => "%s يۆتكىيەلمەيدۇ", -"Unable to rename file" => "ھۆججەت ئاتىنى ئۆزگەرتكىلى بولمايدۇ", "No file was uploaded. Unknown error" => "ھېچقانداق ھۆججەت يۈكلەنمىدى. يوچۇن خاتالىق", "No file was uploaded" => "ھېچقانداق ھۆججەت يۈكلەنمىدى", "Missing a temporary folder" => "ۋاقىتلىق قىسقۇچ كەم.", @@ -29,6 +28,7 @@ "1 folder" => "1 قىسقۇچ", "1 file" => "1 ھۆججەت", "{count} files" => "{count} ھۆججەت", +"Unable to rename file" => "ھۆججەت ئاتىنى ئۆزگەرتكىلى بولمايدۇ", "Upload" => "يۈكلە", "Save" => "ساقلا", "New" => "يېڭى", diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php index 72915630cae..324b28936e7 100644 --- a/apps/files/l10n/uk.php +++ b/apps/files/l10n/uk.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "Не вдалося перемістити %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: ", @@ -47,6 +46,7 @@ "{count} folders" => "{count} папок", "1 file" => "1 файл", "{count} files" => "{count} файлів", +"Unable to rename file" => "Не вдалося перейменувати файл", "Upload" => "Вивантажити", "File handling" => "Робота з файлами", "Maximum upload size" => "Максимальний розмір відвантажень", diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php index 77df2b0db41..c8aa11295c8 100644 --- a/apps/files/l10n/vi.php +++ b/apps/files/l10n/vi.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "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: ", @@ -47,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 ", diff --git a/apps/files/l10n/zh_CN.php b/apps/files/l10n/zh_CN.php index 8d4d8b2c37c..c883670e848 100644 --- a/apps/files/l10n/zh_CN.php +++ b/apps/files/l10n/zh_CN.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "无法移动 %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所规定的值", @@ -47,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" => "最大上传大小", diff --git a/apps/files/l10n/zh_TW.php b/apps/files/l10n/zh_TW.php index 5cc7e358f02..600048a321c 100644 --- a/apps/files/l10n/zh_TW.php +++ b/apps/files/l10n/zh_TW.php @@ -1,7 +1,6 @@ <?php $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "無法移動 %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 參數的設定:", @@ -47,6 +46,7 @@ "{count} folders" => "{count} 個資料夾", "1 file" => "1 個檔案", "{count} files" => "{count} 個檔案", +"Unable to rename file" => "無法重新命名檔案", "Upload" => "上傳", "File handling" => "檔案處理", "Maximum upload size" => "最大上傳檔案大小", diff --git a/apps/files/lib/app.php b/apps/files/lib/app.php new file mode 100644 index 00000000000..c2a4b9c2675 --- /dev/null +++ b/apps/files/lib/app.php @@ -0,0 +1,79 @@ +<?php + +/** + * ownCloud - Core + * + * @author Morris Jobke + * @copyright 2013 Morris Jobke morris.jobke@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 <http://www.gnu.org/licenses/>. + * + */ + + +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/part.breadcrumb.php b/apps/files/templates/part.breadcrumb.php index 7ea1755d1d7..9886b42e424 100644 --- a/apps/files/templates/part.breadcrumb.php +++ b/apps/files/templates/part.breadcrumb.php @@ -1,5 +1,5 @@ <?php if(count($_["breadcrumb"])):?> - <div class="crumb"> + <div class="crumb" data-dir=''> <a href="<?php print_unescaped($_['baseURL']); ?>"> <img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" /> </a> diff --git a/apps/files/tests/ajax_rename.php b/apps/files/tests/ajax_rename.php new file mode 100644 index 00000000000..23e5761ddda --- /dev/null +++ b/apps/files/tests/ajax_rename.php @@ -0,0 +1,117 @@ +<?php + +/** + * ownCloud - Core + * + * @author Morris Jobke + * @copyright 2013 Morris Jobke morris.jobke@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 <http://www.gnu.org/licenses/>. + * + */ + +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 00000000000..4ccacb963e3 --- /dev/null +++ b/apps/files_encryption/3rdparty/Crypt_Blowfish/Blowfish.php @@ -0,0 +1,317 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * Crypt_Blowfish allows for encryption and decryption on the fly using + * the Blowfish algorithm. Crypt_Blowfish does not require the mcrypt + * PHP extension, it uses only PHP. + * Crypt_Blowfish support encryption/decryption with or without a secret key. + * + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Encryption + * @package Crypt_Blowfish + * @author Matthew Fonda <mfonda@php.net> + * @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 <mfonda@php.net> + * @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 00000000000..2ff8ac788a6 --- /dev/null +++ b/apps/files_encryption/3rdparty/Crypt_Blowfish/Blowfish/DefaultKey.php @@ -0,0 +1,327 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * Crypt_Blowfish allows for encryption and decryption on the fly using + * the Blowfish algorithm. Crypt_Blowfish does not require the mcrypt + * PHP extension, it uses only PHP. + * Crypt_Blowfish support encryption/decryption with or without a secret key. + * + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Encryption + * @package Crypt_Blowfish + * @author Matthew Fonda <mfonda@php.net> + * @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 <mfonda@php.net> + * @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 00000000000..6d7953b5639 --- /dev/null +++ b/apps/files_encryption/ajax/adminrecovery.php @@ -0,0 +1,43 @@ +<?php + +/** + * Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com> + * 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']); + $action = "enable"; + +// Disable recoveryAdmin +} elseif ( + isset($_POST['adminEnableRecovery']) + && 0 == $_POST['adminEnableRecovery'] +) { + $return = \OCA\Encryption\Helper::adminDisableRecovery($_POST['recoveryPassword']); + $action = "disable"; +} + +// Return success or failure +if ($return) { + \OCP\JSON::success(array("data" => array( "message" => $l->t('Recovery key successfully ' . $action.'d')))); +} else { + \OCP\JSON::error(array("data" => array( "message" => $l->t('Could not '.$action.' 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 00000000000..d990796a4fb --- /dev/null +++ b/apps/files_encryption/ajax/changeRecoveryPassword.php @@ -0,0 +1,52 @@ +<?php + +/** + * Copyright (c) 2013, Bjoern Schiessle <schiessle@owncloud.com> + * 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 00000000000..1f42b376e42 --- /dev/null +++ b/apps/files_encryption/ajax/userrecovery.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com> + * 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 bf16fec3aea..7d01696e08a 100644 --- a/apps/files_encryption/appinfo/app.php +++ b/apps/files_encryption/appinfo/app.php @@ -8,42 +8,44 @@ 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() ); -// 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' ); -$session = new OCA\Encryption\Session(); +// check if we are logged in +if (OCP\User::isLoggedIn()) { + $view = new OC_FilesystemView('/'); + $session = new \OCA\Encryption\Session($view); -if ( - ! $session->getPrivateKey( \OCP\USER::getUser() ) - && OCP\User::isLoggedIn() - && OCA\Encryption\Crypt::mode() == 'server' -) { + // 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(); + // 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::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 d294c35d63d..4587930da0a 100644 --- a/apps/files_encryption/appinfo/database.xml +++ b/apps/files_encryption/appinfo/database.xml @@ -18,6 +18,21 @@ <type>text</type> <notnull>true</notnull> <length>64</length> + <comments>What client-side / server-side configuration is used</comments> + </field> + <field> + <name>recovery_enabled</name> + <type>integer</type> + <notnull>true</notnull> + <default>0</default> + <comments>Whether encryption key recovery is enabled</comments> + </field> + <field> + <name>migration_status</name> + <type>integer</type> + <notnull>true</notnull> + <default>0</default> + <comments>Whether encryption migration has been performed</comments> </field> </declaration> </table> diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml index 39ea155488f..ea8f6cf6f31 100644 --- a/apps/files_encryption/appinfo/info.xml +++ b/apps/files_encryption/appinfo/info.xml @@ -2,9 +2,9 @@ <info> <id>files_encryption</id> <name>Encryption</name> - <description>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.</description> + <description>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.</description> <licence>AGPL</licence> - <author>Sam Tuke</author> + <author>Sam Tuke, Bjoern Schiessle, Florin Peter</author> <require>4</require> <shipped>true</shipped> <types> diff --git a/apps/files_encryption/appinfo/spec.txt b/apps/files_encryption/appinfo/spec.txt index 2d22dffe08d..ddd3983a9eb 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 1d71ef97443..bd73f47072b 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 00000000000..4ee0acc9768 --- /dev/null +++ b/apps/files_encryption/css/settings-personal.css @@ -0,0 +1,10 @@ +/* Copyright (c) 2013, Sam Tuke, <samtuke@owncloud.com> + 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 2731d5a92f7..2066300a163 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
@@ -40,152 +41,471 @@ class Hooks { // 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' . '/' );
+ // NOTE: disabled because this give errors on webdav!
+ //\OC\Files\Filesystem::init( $params['uid'], '/' . 'files' . '/' );
$view = new \OC_FilesystemView( '/' );
$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'] );
- }
-
- \OC_FileProxy::$enabled = false;
-
+ // setup user, if user not ready force relogin
+ if(Helper::setupUser($util, $params['password']) === false) {
+ return false;
+ }
+
$encryptedKey = Keymanager::getPrivateKey( $view, $params['uid'] );
- \OC_FileProxy::$enabled = true;
-
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] );
-
- $session = new Session();
+
+ $session = new Session( $view );
$session->setPrivateKey( $privateKey, $params['uid'] );
- $view1 = new \OC_FilesystemView( '/' . $params['uid'] );
+ // Check if first-run file migration has already been performed
+ $migrationCompleted = $util->getMigrationStatus();
- // 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' )
- ) {
+ // If migration not yet done
+ if ( ! $migrationCompleted ) {
- $plainLegacyKey = Crypt::legacyDecrypt( $encLegacyKey, $params['password'] );
+ $userView = new \OC_FilesystemView( '/' . $params['uid'] );
- $session->setLegacyKey( $plainLegacyKey );
-
- }
-
- $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'] )
- ) {
+ // 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::legacyDecrypt( $encLegacyKey, $params['password'] );
+
+ $session->setLegacyKey( $plainLegacyKey );
+
+ }
+
+ $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( '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
+ ) {
+
+ \OC_Log::write(
+ 'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
+ , \OC_Log::INFO
+ );
- \OC_Log::write(
- 'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" started at login'
- , \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 (Crypt::mode() == 'server') {
+
+ if ($params['uid'] == \OCP\User::getUser()) {
+
+ $view = new \OC_FilesystemView('/');
+
+ $session = new 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
- if ( isset( $params['properties']['key'] ) ) {
- $view = new \OC_FilesystemView( '/' );
- $userId = \OCP\User::getUser();
+ } else { // admin changed the password for a different user, create new keys and reencrypt file keys
- 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!" );
+ $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 check if files can be encrypted to every user.
+ */
/**
- * @brief
+ * @param $params
*/
- public static function postShared( $params ) {
+ 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
*/
- public static function preUnshare( $params ) {
-
- // Delete existing catfile
-
- // Generate new catfile and env keys
-
- // Save env keys to user folders
+ 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 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();
+
+ // 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 preUnshareAll( $params ) {
-
- trigger_error( "preUnshareAll" );
-
+ 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'] );
+ }
+
+ // 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 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 00000000000..7c1866445ee --- /dev/null +++ b/apps/files_encryption/js/settings-admin.js @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>, Robin Appelman + * <icewind1991@gmail.com> + * 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 00000000000..312b672ad46 --- /dev/null +++ b/apps/files_encryption/js/settings-personal.js @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com> + * 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 0be857bb73e..00000000000 --- a/apps/files_encryption/js/settings.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com> - * 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 c8a475afd67..1adc158c6b8 100644 --- a/apps/files_encryption/l10n/ar.php +++ b/apps/files_encryption/l10n/ar.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "التشفير", -"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 07a97f5f8a6..f21f7641c1a 100644 --- a/apps/files_encryption/l10n/bg_BG.php +++ b/apps/files_encryption/l10n/bg_BG.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Криптиране", -"None" => "Няма" +"Saving..." => "Записване...", +"Encryption" => "Криптиране" ); diff --git a/apps/files_encryption/l10n/bn_BD.php b/apps/files_encryption/l10n/bn_BD.php index 43767d56518..068de46e7a1 100644 --- a/apps/files_encryption/l10n/bn_BD.php +++ b/apps/files_encryption/l10n/bn_BD.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "সংকেতায়ন", -"None" => "কোনটিই নয়" +"Saving..." => "সংরক্ষণ করা হচ্ছে..", +"Encryption" => "সংকেতায়ন" ); diff --git a/apps/files_encryption/l10n/ca.php b/apps/files_encryption/l10n/ca.php index 2d59a306d33..46e91d1f070 100644 --- a/apps/files_encryption/l10n/ca.php +++ b/apps/files_encryption/l10n/ca.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Xifrat", -"File encryption is enabled." => "El xifrat de fitxers està activat.", -"The following file types will not be encrypted:" => "Els tipus de fitxers següents no es xifraran:", -"Exclude the following file types from encryption:" => "Exclou els tipus de fitxers següents del xifratge:", -"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 d225688a079..f9b2dd06b6f 100644 --- a/apps/files_encryption/l10n/cs_CZ.php +++ b/apps/files_encryption/l10n/cs_CZ.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Š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 index 523b5dd73df..6e18a7913c8 100644 --- a/apps/files_encryption/l10n/cy_GB.php +++ b/apps/files_encryption/l10n/cy_GB.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Amgryptiad", -"File encryption is enabled." => "Galluogwyd amgryptio ffeiliau.", -"The following file types will not be encrypted:" => "Ni fydd ffeiliau o'r math yma'n cael eu hamgryptio:", -"Exclude the following file types from encryption:" => "Eithrio'r mathau canlynol o ffeiliau rhag cael eu hamgryptio:", -"None" => "Dim" +"Saving..." => "Yn cadw...", +"Encryption" => "Amgryptiad" ); diff --git a/apps/files_encryption/l10n/da.php b/apps/files_encryption/l10n/da.php index b085381ea7b..1cd43390aa3 100644 --- a/apps/files_encryption/l10n/da.php +++ b/apps/files_encryption/l10n/da.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 bcf0ca5ad63..2ab77f480cf 100644 --- a/apps/files_encryption/l10n/de.php +++ b/apps/files_encryption/l10n/de.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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" => "Nichts" +"Saving..." => "Speichern...", +"Encryption" => "Verschlüsselung" ); diff --git a/apps/files_encryption/l10n/de_DE.php b/apps/files_encryption/l10n/de_DE.php index 71fd7d96711..5ab283ccab3 100644 --- a/apps/files_encryption/l10n/de_DE.php +++ b/apps/files_encryption/l10n/de_DE.php @@ -1,7 +1,9 @@ <?php $TRANSLATIONS = array( +"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" => "Nichts" +"Enabled" => "Aktiviert", +"Disabled" => "Deaktiviert", +"Change Password" => "Passwort ändern" ); diff --git a/apps/files_encryption/l10n/el.php b/apps/files_encryption/l10n/el.php index 82a4c92ec28..7067799cd2e 100644 --- a/apps/files_encryption/l10n/el.php +++ b/apps/files_encryption/l10n/el.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Κρυπτογράφηση", -"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 50847062c3b..ea405fda1ab 100644 --- a/apps/files_encryption/l10n/eo.php +++ b/apps/files_encryption/l10n/eo.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Ĉifrado", -"None" => "Nenio" +"Saving..." => "Konservante...", +"Encryption" => "Ĉifrado" ); diff --git a/apps/files_encryption/l10n/es.php b/apps/files_encryption/l10n/es.php index 4ea87b92e7c..7311a78f09b 100644 --- a/apps/files_encryption/l10n/es.php +++ b/apps/files_encryption/l10n/es.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"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" +"Saving..." => "Guardando...", +"Encryption" => "Cifrado" ); diff --git a/apps/files_encryption/l10n/es_AR.php b/apps/files_encryption/l10n/es_AR.php index af522879e16..857186a55fa 100644 --- a/apps/files_encryption/l10n/es_AR.php +++ b/apps/files_encryption/l10n/es_AR.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 0d189ac062e..c8d5361c08a 100644 --- a/apps/files_encryption/l10n/et_EE.php +++ b/apps/files_encryption/l10n/et_EE.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"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" +"Saving..." => "Salvestamine...", +"Encryption" => "Krüpteerimine" ); diff --git a/apps/files_encryption/l10n/eu.php b/apps/files_encryption/l10n/eu.php index 7e3b7611ff2..253953e5c52 100644 --- a/apps/files_encryption/l10n/eu.php +++ b/apps/files_encryption/l10n/eu.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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" => "Ezer" +"Saving..." => "Gordetzen...", +"Encryption" => "Enkriptazioa" ); diff --git a/apps/files_encryption/l10n/fa.php b/apps/files_encryption/l10n/fa.php index 7acf196b791..af2e36b2a83 100644 --- a/apps/files_encryption/l10n/fa.php +++ b/apps/files_encryption/l10n/fa.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "رمزگذاری", -"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 6352d396b3c..a00cc8ab96e 100644 --- a/apps/files_encryption/l10n/fi_FI.php +++ b/apps/files_encryption/l10n/fi_FI.php @@ -1,7 +1,9 @@ <?php $TRANSLATIONS = array( +"Password successfully changed." => "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 88f1e4a393f..98fb70691df 100644 --- a/apps/files_encryption/l10n/fr.php +++ b/apps/files_encryption/l10n/fr.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 3210f715453..a3384174d78 100644 --- a/apps/files_encryption/l10n/gl.php +++ b/apps/files_encryption/l10n/gl.php @@ -1,7 +1,14 @@ <?php $TRANSLATIONS = array( +"Recovery key successfully " => "O contrasinal foi recuperado satisfactoriamente", +"Could not " => "Non foi posíbel", +"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:", +"Change Password" => "Cambiar o contrasinal" ); diff --git a/apps/files_encryption/l10n/he.php b/apps/files_encryption/l10n/he.php index cbb74bfee9a..7a80cfa2f9f 100644 --- a/apps/files_encryption/l10n/he.php +++ b/apps/files_encryption/l10n/he.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "הצפנה", -"None" => "כלום" +"Saving..." => "שמירה…", +"Encryption" => "הצפנה" ); diff --git a/apps/files_encryption/l10n/hr.php b/apps/files_encryption/l10n/hr.php new file mode 100644 index 00000000000..9b9284ddc5e --- /dev/null +++ b/apps/files_encryption/l10n/hr.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Saving..." => "Spremanje..." +); diff --git a/apps/files_encryption/l10n/hu_HU.php b/apps/files_encryption/l10n/hu_HU.php index 4043da108c0..bf95c31f2c5 100644 --- a/apps/files_encryption/l10n/hu_HU.php +++ b/apps/files_encryption/l10n/hu_HU.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 6044348e72e..ad827b53791 100644 --- a/apps/files_encryption/l10n/id.php +++ b/apps/files_encryption/l10n/id.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 bd964185c45..0f98c6bd3bf 100644 --- a/apps/files_encryption/l10n/is.php +++ b/apps/files_encryption/l10n/is.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 c7171345269..8f7a4023b6f 100644 --- a/apps/files_encryption/l10n/it.php +++ b/apps/files_encryption/l10n/it.php @@ -1,7 +1,20 @@ <?php $TRANSLATIONS = array( +"Recovery key successfully " => "Chiave ripristinata correttamente", +"Could not " => "Impossibile", +"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" => "Nessuno" +"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 35fba615aec..99fd3ec3aba 100644 --- a/apps/files_encryption/l10n/ja_JP.php +++ b/apps/files_encryption/l10n/ja_JP.php @@ -1,7 +1,19 @@ <?php $TRANSLATIONS = array( +"Recovery key successfully " => "鍵を復旧することができました。", +"Could not " => "できませんでした。", +"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" => "なし" +"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 0362c676f00..55a59f44341 100644 --- a/apps/files_encryption/l10n/ka_GE.php +++ b/apps/files_encryption/l10n/ka_GE.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "ენკრიპცია", -"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 bd1580578c4..cf8149da3ab 100644 --- a/apps/files_encryption/l10n/ko.php +++ b/apps/files_encryption/l10n/ko.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "암호화", -"None" => "없음" +"Saving..." => "저장 중...", +"Encryption" => "암호화" ); diff --git a/apps/files_encryption/l10n/ku_IQ.php b/apps/files_encryption/l10n/ku_IQ.php index 02c030014fa..61b720372ec 100644 --- a/apps/files_encryption/l10n/ku_IQ.php +++ b/apps/files_encryption/l10n/ku_IQ.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "نهێنیکردن", -"None" => "هیچ" +"Saving..." => "پاشکهوتدهکات...", +"Encryption" => "نهێنیکردن" ); diff --git a/apps/files_encryption/l10n/lb.php b/apps/files_encryption/l10n/lb.php new file mode 100644 index 00000000000..77bad681732 --- /dev/null +++ b/apps/files_encryption/l10n/lb.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Saving..." => "Speicheren..." +); diff --git a/apps/files_encryption/l10n/lt_LT.php b/apps/files_encryption/l10n/lt_LT.php index 67769c8f365..6bc80ff44ab 100644 --- a/apps/files_encryption/l10n/lt_LT.php +++ b/apps/files_encryption/l10n/lt_LT.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Šifravimas", -"None" => "Nieko" +"Saving..." => "Saugoma...", +"Encryption" => "Šifravimas" ); diff --git a/apps/files_encryption/l10n/lv.php b/apps/files_encryption/l10n/lv.php index fc31ccdb92d..04922854ceb 100644 --- a/apps/files_encryption/l10n/lv.php +++ b/apps/files_encryption/l10n/lv.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Š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 513606fadc3..a7216f205ad 100644 --- a/apps/files_encryption/l10n/mk.php +++ b/apps/files_encryption/l10n/mk.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Енкрипција", -"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 00000000000..bb963cb72d2 --- /dev/null +++ b/apps/files_encryption/l10n/ms_MY.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Saving..." => "Simpan..." +); diff --git a/apps/files_encryption/l10n/nb_NO.php b/apps/files_encryption/l10n/nb_NO.php index a5e16a03421..d4e2b1ffb50 100644 --- a/apps/files_encryption/l10n/nb_NO.php +++ b/apps/files_encryption/l10n/nb_NO.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 b1cba96aad7..e2f22b4d92f 100644 --- a/apps/files_encryption/l10n/nl.php +++ b/apps/files_encryption/l10n/nl.php @@ -1,7 +1,20 @@ <?php $TRANSLATIONS = array( +"Recovery key successfully " => "Sleutelherstel succesvol", +"Could not " => "Kon niet", +"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 00000000000..97b3a27a9d0 --- /dev/null +++ b/apps/files_encryption/l10n/nn_NO.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Saving..." => "Lagrar …" +); diff --git a/apps/files_encryption/l10n/oc.php b/apps/files_encryption/l10n/oc.php new file mode 100644 index 00000000000..0a34c4cda12 --- /dev/null +++ b/apps/files_encryption/l10n/oc.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Saving..." => "Enregistra..." +); diff --git a/apps/files_encryption/l10n/pl.php b/apps/files_encryption/l10n/pl.php index 836f5453596..313d27b70c9 100644 --- a/apps/files_encryption/l10n/pl.php +++ b/apps/files_encryption/l10n/pl.php @@ -1,7 +1,20 @@ <?php $TRANSLATIONS = array( +"Recovery key successfully " => "Odzyskanie klucza udane", +"Could not " => "Nie można", +"Password successfully changed." => "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" => "Nic" +"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 b41c6ed3153..73d7b57b878 100644 --- a/apps/files_encryption/l10n/pt_BR.php +++ b/apps/files_encryption/l10n/pt_BR.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"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" => "Nada" +"Saving..." => "Salvando...", +"Encryption" => "Criptografia" ); diff --git a/apps/files_encryption/l10n/pt_PT.php b/apps/files_encryption/l10n/pt_PT.php index 1c46011fc10..be75c0b768a 100644 --- a/apps/files_encryption/l10n/pt_PT.php +++ b/apps/files_encryption/l10n/pt_PT.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"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" +"Saving..." => "A guardar...", +"Encryption" => "Encriptação" ); diff --git a/apps/files_encryption/l10n/ro.php b/apps/files_encryption/l10n/ro.php index a5a6fb3cb78..9e04b627c42 100644 --- a/apps/files_encryption/l10n/ro.php +++ b/apps/files_encryption/l10n/ro.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Î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 f07dec621d7..fac785730a8 100644 --- a/apps/files_encryption/l10n/ru.php +++ b/apps/files_encryption/l10n/ru.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Шифрование", -"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/ru_RU.php b/apps/files_encryption/l10n/ru_RU.php index 7222235485c..1351f63f89b 100644 --- a/apps/files_encryption/l10n/ru_RU.php +++ b/apps/files_encryption/l10n/ru_RU.php @@ -1,4 +1,3 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Шифрование", -"None" => "Ни один" +"Saving..." => "Сохранение" ); diff --git a/apps/files_encryption/l10n/si_LK.php b/apps/files_encryption/l10n/si_LK.php index d9cec4b7220..6c678bb3a4f 100644 --- a/apps/files_encryption/l10n/si_LK.php +++ b/apps/files_encryption/l10n/si_LK.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "ගුප්ත කේතනය", -"None" => "කිසිවක් නැත" +"Saving..." => "සුරැකෙමින් පවතී...", +"Encryption" => "ගුප්ත කේතනය" ); diff --git a/apps/files_encryption/l10n/sk_SK.php b/apps/files_encryption/l10n/sk_SK.php index aaea9da21b4..fd77bb7e91d 100644 --- a/apps/files_encryption/l10n/sk_SK.php +++ b/apps/files_encryption/l10n/sk_SK.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"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" => "Žiadny" +"Saving..." => "Ukladám...", +"Encryption" => "Šifrovanie" ); diff --git a/apps/files_encryption/l10n/sl.php b/apps/files_encryption/l10n/sl.php index 4754e21214e..a420fe161df 100644 --- a/apps/files_encryption/l10n/sl.php +++ b/apps/files_encryption/l10n/sl.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Š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 91f7fc62a90..a36e37c1790 100644 --- a/apps/files_encryption/l10n/sr.php +++ b/apps/files_encryption/l10n/sr.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Шифровање", -"None" => "Ништа" +"Saving..." => "Чување у току...", +"Encryption" => "Шифровање" ); diff --git a/apps/files_encryption/l10n/sv.php b/apps/files_encryption/l10n/sv.php index e214a937a1d..966963b5549 100644 --- a/apps/files_encryption/l10n/sv.php +++ b/apps/files_encryption/l10n/sv.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 152e631d0fc..63fe9ecde86 100644 --- a/apps/files_encryption/l10n/ta_LK.php +++ b/apps/files_encryption/l10n/ta_LK.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "மறைக்குறியீடு", -"None" => "ஒன்றுமில்லை" +"Saving..." => "சேமிக்கப்படுகிறது...", +"Encryption" => "மறைக்குறியீடு" ); diff --git a/apps/files_encryption/l10n/th_TH.php b/apps/files_encryption/l10n/th_TH.php index 30c0324a988..6cab4370ccf 100644 --- a/apps/files_encryption/l10n/th_TH.php +++ b/apps/files_encryption/l10n/th_TH.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "การเข้ารหัส", -"None" => "ไม่มี" +"Saving..." => "กำลังบันทึกข้อมูล...", +"Encryption" => "การเข้ารหัส" ); diff --git a/apps/files_encryption/l10n/tr.php b/apps/files_encryption/l10n/tr.php index 6b42c757e65..917ff0a0eae 100644 --- a/apps/files_encryption/l10n/tr.php +++ b/apps/files_encryption/l10n/tr.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Ş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 index 34eeb373b3e..954d95b4132 100644 --- a/apps/files_encryption/l10n/ug.php +++ b/apps/files_encryption/l10n/ug.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "شىفىرلاش", -"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/uk.php b/apps/files_encryption/l10n/uk.php index d4957141191..1c176a39142 100644 --- a/apps/files_encryption/l10n/uk.php +++ b/apps/files_encryption/l10n/uk.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "Шифрування", -"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 40d4b1d0fec..0af5bdc9a67 100644 --- a/apps/files_encryption/l10n/vi.php +++ b/apps/files_encryption/l10n/vi.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "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 gì cả" +"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 12d903e6567..3c405a81ace 100644 --- a/apps/files_encryption/l10n/zh_CN.GB2312.php +++ b/apps/files_encryption/l10n/zh_CN.GB2312.php @@ -1,4 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "加密", -"None" => "无" +"Saving..." => "保存中...", +"Encryption" => "加密" ); diff --git a/apps/files_encryption/l10n/zh_CN.php b/apps/files_encryption/l10n/zh_CN.php index 13fa95203e4..e565fce801c 100644 --- a/apps/files_encryption/l10n/zh_CN.php +++ b/apps/files_encryption/l10n/zh_CN.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "加密", -"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 0c0b709fdc1..0a38a2ddf85 100644 --- a/apps/files_encryption/l10n/zh_HK.php +++ b/apps/files_encryption/l10n/zh_HK.php @@ -1,6 +1,3 @@ <?php $TRANSLATIONS = array( -"Encryption" => "加密", -"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 95e61b45dc2..2bfadce8553 100644 --- a/apps/files_encryption/l10n/zh_TW.php +++ b/apps/files_encryption/l10n/zh_TW.php @@ -1,7 +1,4 @@ <?php $TRANSLATIONS = array( -"Encryption" => "加密", -"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/lib/crypt.php b/apps/files_encryption/lib/crypt.php index 437a18669e5..f5b7a8a0a40 100755 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -25,23 +25,19 @@ 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
*/
-class Crypt {
+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 ) {
@@ -56,7 +52,7 @@ 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 );
@@ -66,14 +62,14 @@ class Crypt { $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
@@ -90,7 +86,7 @@ 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 ) {
@@ -111,10 +107,11 @@ 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 ) {
@@ -133,7 +130,7 @@ class Crypt { // Fetch identifier from start of metadata
$identifier = substr( $meta, 0, 6 );
- if ( $identifier == '00iv00') {
+ if ( $identifier == '00iv00' ) {
return true;
@@ -155,7 +152,7 @@ class Crypt { // 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'];
@@ -164,9 +161,10 @@ class Crypt { /**
* @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 ) {
@@ -179,7 +177,7 @@ class Crypt { if (
isset( $metadata['encrypted'] )
and $metadata['encrypted'] === true
- and ! self::isCatfile( $data )
+ and !self::isCatfileContent( $data )
) {
return true;
@@ -194,7 +192,10 @@ 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 = '' ) {
@@ -214,7 +215,11 @@ 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 ) {
@@ -222,7 +227,6 @@ class Crypt { return $plainContent;
-
} else {
throw new \Exception( 'Encryption library: Decryption (symmetric) of content failed' );
@@ -237,7 +241,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,7 +254,7 @@ 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 );
@@ -272,8 +276,10 @@ 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.
*/
@@ -309,10 +315,14 @@ 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
*/
@@ -334,6 +344,8 @@ class Crypt { return $plainContent;
+ } else {
+ return false;
}
}
@@ -350,11 +362,11 @@ class Crypt { $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 +380,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 ) {
+ // 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, $shareKeys, $publicKeys ) ) {
+
+ $i = 0;
- if( openssl_seal( $plainContent, $sealed, $envKeys, $publicKeys ) ) {
+ // 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,13 +427,17 @@ 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 ) {
@@ -410,7 +445,7 @@ class Crypt { }
- if ( openssl_open( $encryptedContent, $plainContent, $envKey, $privateKey ) ) {
+ if ( openssl_open( $encryptedContent, $plainContent, $shareKey, $privateKey ) ) {
return $plainContent;
@@ -425,8 +460,8 @@ class Crypt { }
/**
- * @brief Asymmetrically encrypt a string using a public key
- * @returns encrypted file
+ * @brief Asymetrically encrypt a string using a public key
+ * @return string encrypted file
*/
public static function keyEncrypt( $plainContent, $publicKey ) {
@@ -438,110 +473,17 @@ class Crypt { /**
* @brief Asymetrically decrypt a file using a private key
- * @returns decrypted file
+ * @return string decrypted file
*/
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 );
-
- }
-
- 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 );
+ $result = @openssl_private_decrypt( $encryptedContent, $plainContent, $privatekey );
+ if ( $result ) {
+ return $plainContent;
}
- return $decrypted;
+ return $result;
}
@@ -586,7 +528,7 @@ class Crypt { 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()' );
}
@@ -621,6 +563,10 @@ class Crypt { }
+ /**
+ * @param $passphrase
+ * @return mixed
+ */
public static function legacyCreateKey( $passphrase ) {
// Generate a random integer
@@ -635,9 +581,11 @@ 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
+ * @return
+ * @internal param \OCA\Encryption\the $key encryption key (optional)
+ * @returns string encrypted content
*
* This function encrypts an content
*/
@@ -651,9 +599,11 @@ class Crypt { /**
* @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
+ * @internal param \OCA\Encryption\the $key encryption key (optional)
+ * @return string cleartext content
*
* This function decrypts an content
*/
@@ -663,33 +613,49 @@ class Crypt { $decrypted = $bf->decrypt( $content );
- $trimmed = rtrim( $decrypted, "\0" );
-
- return $trimmed;
+ return rtrim( $decrypted, "\0" );;
}
- public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKey, $newPassphrase ) {
-
- $decrypted = self::legacyDecrypt( $legacyEncryptedContent, $legacyPassphrase );
-
- $recrypted = self::keyEncryptKeyfile( $decrypted, $publicKey );
-
- return $recrypted;
-
+ /**
+ * @param $data
+ * @param string $key
+ * @param int $maxLength
+ * @return string
+ */
+ private static function legacyBlockDecrypt( $data, $key = '', $maxLength = 0 ) {
+ $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" );
+ }
}
/**
- * @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 $legacyEncryptedContent
+ * @param $legacyPassphrase
+ * @param $publicKeys
+ * @param $newPassphrase
+ * @param $path
+ * @return array
*/
- public static function legacyRecrypt( $legacyContent, $legacyPassphrase, $newPassphrase ) {
+ public static function legacyKeyRecryptKeyfile( $legacyEncryptedContent, $legacyPassphrase, $publicKeys, $newPassphrase, $path ) {
+
+ $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 );
- // TODO: write me
+ 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 00000000000..7a2d19eed57 --- /dev/null +++ b/apps/files_encryption/lib/helper.php @@ -0,0 +1,176 @@ +<?php + +/** + * ownCloud + * + * @author Florin Peter + * @copyright 2013 Florin Peter <owncloud@florin-peter.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Encryption; + + /** + * @brief Class to manage registration of hooks an various helper methods + */ +/** + * Class Helper + * @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() ) { + + \OC_Log::write( 'Encryption library', 'User account "' . $util->getUserId() . '" is not ready for encryption; configuration started', \OC_Log::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; + } +}
\ No newline at end of file diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 95587797154..aaa2e4ba1b5 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -27,20 +27,28 @@ namespace OCA\Encryption; * @brief Class to manage storage and retrieval of encryption keys * @note Where a method requires a view object, it's root must be '/' */ -class Keymanager { - +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'; - + + $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; } @@ -51,101 +59,150 @@ class Keymanager { * @return string public key or false */ public static function getPublicKey( \OC_FilesystemView $view, $userId ) { - - return $view->file_get_contents( '/public-keys/' . '/' . $userId . '.public.key' ); - + + $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 ) + , '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 + * @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, $userId, $filePath ) { - - $path = ltrim( $path, '/' ); - - $filepath = '/' . $userId . '/files/' . $filePath; - - // Check if sharing is enabled - if ( OC_App::isEnabled( 'files_sharing' ) ) { - - - - } 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' ); - + public static function getPublicKeys( \OC_FilesystemView $view, array $userIds ) { + + $keys = array(); + + foreach ( $userIds as $userId ) { + + $keys[$userId] = self::getPublicKey( $view, $userId ); + } - - return $keylist; - + + 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 ) ) { - - - + + $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 ) ) { + + $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 ) ) { + + return true; + + } else { + + return false; + + } + + } + /** * @brief retrieve keyfile for an encrypted file * @param \OC_FilesystemView $view @@ -157,27 +214,50 @@ class Keymanager { * 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 ) ) { - - return $view->file_get_contents( $catfilePath ); - + + // try reusing key file if part file + if ( self::isPartialFilePath( $filePath ) ) { + + $result = self::getFileKey( $view, $userId, self::fixPartialFilePath( $filePath ) ); + + if ( $result ) { + + return $result; + + } + + } + + $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 { - - return false; - + + $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 @@ -185,139 +265,299 @@ class Keymanager { * /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 ); - - } else { - + $keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed; + + $result = false; + + if ( $view->is_dir( $keyPath ) ) { + + $result = $view->unlink( $keyPath ); + + } else if ( $view->file_exists( $keyPath . '.key' ) ) { + + $result = $view->unlink( $keyPath . '.key' ); + + } + + if ( !$result ) { + \OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR ); - - return false; - + } - + + 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 ) { - + $user = \OCP\User::getUser(); - + $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 ); + + $result = $view->file_put_contents( $user . '.private.key', $key ); + + \OC_FileProxy::$enabled = $proxyStatus; + + return $result; } - + /** - * @brief store private keys from the user + * @brief store share key * - * @param string privatekey - * @param string publickey + * @param \OC_FilesystemView $view + * @param string $path relative path of the file, including filename + * @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 setUserKeys($privatekey, $publickey) { - - return ( self::setPrivateKey( $privatekey ) && self::setPublicKey( $publickey ) ); - + 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'; + + } + + $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 store public key of the user - * - * @param string key - * @return bool true/false + * @brief store multiple share keys for a single file + * @param \OC_FilesystemView $view + * @param $path + * @param array $shareKeys + * @return bool + */ + 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 setPublicKey( $key ) { - - $view = new \OC_FilesystemView( '/public-keys' ); - + 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; - - if ( !$view->file_exists( '' ) ) - $view->mkdir( '' ); - - return $view->file_put_contents( \OCP\User::getUser() . '.public.key', $key ); - + //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 store file encryption key + * @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 ) { + \OC_Log::write( 'Encryption library', 'Keyfile or shareKey could not be deleted for file "' . $filePath . '"', \OC_Log::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' ) ) { + \OC_Log::write( 'Encryption library', 'Could not delete shareKey; does not exist: "' . $shareKeyPath . '.' . $userId . '.shareKey"', \OC_Log::ERROR ); + } + + } + } + + \OC_FileProxy::$enabled = $proxyStatus; + } + + /** + * @brief recursively delete share keys from given users * - * @param string $path relative path of the file, including filename - * @param string $key - * @param null $view - * @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 + * @param string $dir directory + * @param array $userIds user ids for which the share keys should be deleted */ - 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 ); - + 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 ) ) { + \OC_Log::write( 'Encryption library', 'Could not delete shareKey; does not exist: "' . $ma . '"', \OC_Log::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'] ) + if ( + isset( $path_parts['dirname'] ) + && !$view->file_exists( $basePath . '/' . $path_parts['dirname'] ) ) { - - $view->mkdir( $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; - - } - /** - * @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 - */ - public function getLegacyKey() { - - $user = \OCP\User::getUser(); - $view = new \OC_FilesystemView( '/' . $user ); - return $view->file_get_contents( 'encryption.key' ); - } - }
\ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 55cddf2bec8..eaaeae9b619 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -1,41 +1,46 @@ <?php /** -* 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 <http://www.gnu.org/licenses/>. -* -*/ + * 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 <http://www.gnu.org/licenses/>. + * + */ /** -* @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 extends \OC_FileProxy { +/** + * 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 @@ -44,346 +49,417 @@ 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' + + if ( + \OCP\Config::getAppValue( 'files_encryption', 'enable_encryption', 'true' ) == 'true' + && Crypt::mode() == 'server' ) { - + self::$enableEncryption = true; - + } else { - + self::$enableEncryption = false; - + } - + } - + 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 ); - + + $extension = substr( $path, strrpos( $path, '.' ) + 1 ); + if ( array_search( $extension, self::$blackList ) === false ) { - + return true; - + } - + return false; } - + + /** + * @param $path + * @param $data + * @return bool + */ public function preFile_put_contents( $path, &$data ) { - + if ( self::shouldEncrypt( $path ) ) { - - if ( !is_resource( $data ) ) { //stream put contents should have been converted to fopen - + + // Stream put contents should have been converted to fopen + if ( !is_resource( $data ) ) { + $userId = \OCP\USER::getUser(); - - $rootView = new \OC_FilesystemView( '/' ); - + $view = new \OC_FilesystemView( '/' ); + $util = new Util( $view, $userId ); + $session = new Session( $view ); + $privateKey = $session->getPrivateKey(); + $filePath = $util->stripUserFilesPath( $path ); // Set the filesize for userland, before encrypting $size = strlen( $data ); - + // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; \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( '/' ); - + + // Check if there is an existing key we can reuse + if ( $encKeyfile = Keymanager::getFileKey( $view, $userId, $filePath ) ) { + + // Fetch shareKey + $shareKey = Keymanager::getShareKey( $view, $userId, $filePath ); + + // Decrypt the keyfile + $plainKey = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey ); + + } else { + + // Make a new key + $plainKey = Crypt::generateKey(); + + } + + // Encrypt data + $encData = Crypt::symmetricEncryptFileContent( $data, $plainKey ); + + $sharingEnabled = \OCP\Share::isEnabled(); + + // if file exists try to get sharing users + if ( $view->file_exists( $path ) ) { + $uniqueUserIds = $util->getSharingUsersArray( $sharingEnabled, $filePath, $userId ); + } else { + $uniqueUserIds[] = $userId; + } + + // Fetch public keys for all users who will share the file + $publicKeys = Keymanager::getPublicKeys( $view, $uniqueUserIds ); + + // Encrypt plain keyfile to multiple sharefiles + $multiEncrypted = Crypt::multiKeyEncrypt( $plainKey, $publicKeys ); + + // Save sharekeys to user folders + Keymanager::setShareKeys( $view, $filePath, $multiEncrypted['keys'] ); + + // Set encrypted keyfile as common varname + $encKey = $multiEncrypted['data']; + // Save keyfile for newly encrypted file in parallel directory tree - Keymanager::setFileKey( $view, $filePath, $userId, $encrypted['key'] ); - + Keymanager::setFileKey( $view, $filePath, $userId, $encKey ); + + // Replace plain content with encrypted content by reference + $data = $encData; + // Update the file cache with file info - \OC\Files\Filesystem::putFileInfo( $path, array( 'encrypted'=>true, 'size' => $size ), '' ); - + \OC\Files\Filesystem::putFileInfo( $filePath, array( 'encrypted' => true, 'size' => strlen( $data ), 'unencrypted_size' => $size ), '' ); + // Re-enable proxy - our work is done - \OC_FileProxy::$enabled = true; - + \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 + + $userId = \OCP\USER::getUser(); + $view = new \OC_FilesystemView( '/' ); + $util = new Util( $view, $userId ); + + $relPath = $util->stripUserFilesPath( $path ); // Disable encryption proxy to prevent recursive calls + $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - + + // init session + $session = new Session( $view ); + // 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] ) ); - + + $privateKey = $session->getPrivateKey( $userId ); + + // Get the encrypted keyfile + $encKeyfile = Keymanager::getFileKey( $view, $userId, $relPath ); + + // Attempt to fetch the user's shareKey + $shareKey = Keymanager::getShareKey( $view, $userId, $relPath ); + + // Decrypt keyfile with shareKey + $plainKeyfile = Crypt::multiKeyDecrypt( $encKeyfile, $shareKey, $privateKey ); + + $plainData = Crypt::symmetricDecryptFileContent( $data, $plainKeyfile ); + } elseif ( - Crypt::mode() == 'server' - && isset( $_SESSION['legacyenckey'] ) - && Crypt::isEncryptedMeta( $path ) + Crypt::mode() == 'server' + && isset( $_SESSION['legacyenckey'] ) + && Crypt::isEncryptedMeta( $path ) ) { - - $decrypted = Crypt::legacyDecrypt( $data, $_SESSION['legacyenckey'] ); - + $plainData = Crypt::legacyDecrypt( $data, $session->getLegacyKey() ); } - - \OC_FileProxy::$enabled = true; - - if ( ! isset( $decrypted ) ) { - - $decrypted = $data; - + + \OC_FileProxy::$enabled = $proxyStatus; + + if ( !isset( $plainData ) ) { + + $plainData = $data; + } - - return $decrypted; - + + return $plainData; + } - + /** * @brief When a file is deleted, remove its keyfile also */ 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 ); + // 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; - + $relPath = $util->stripUserFilesPath( $path ); + + list( $owner, $ownerPath ) = $util->getUidAndFilename( $relPath ); + + // Delete keyfile & shareKey so it isn't orphaned + if ( !Keymanager::deleteFileKey( $view, $owner, $ownerPath ) ) { + \OC_Log::write( 'Encryption library', 'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OC_Log::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 ){ - + + /** + * @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 ) ); - + $path_f = implode( '/', array_slice( $path_split, 3 ) ); + + // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted + if ( count($path_split) >= 2 && $path_split[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()); - + + $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 ); - + // 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' + + } 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://' . $path_f, $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; - } - public function postStat( $path, $data ) { - - if ( Crypt::isCatfile( $path ) ) { - - $cached = \OC\Files\Filesystem::getFileInfo( $path, '' ); - - $data['size'] = $cached['size']; - + /** + * @param $path + * @param $data + * @return array + */ + public function postGetFileInfo( $path, $data ) { + + // 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; } + /** + * @param $path + * @param $size + * @return bool + */ public function postFileSize( $path, $size ) { - - if ( Crypt::isCatfile( $path ) ) { - - $cached = \OC\Files\Filesystem::getFileInfo( $path, '' ); - - return $cached['size']; - - } else { - + + $view = new \OC_FilesystemView( '/' ); + + // if path is a folder do nothing + if ( $view->is_dir( $path ) ) { + return $size; + } + + // Reformat path for use with OC_FSV + $path_split = explode( '/', $path ); + $path_f = implode( '/', array_slice( $path_split, 3 ) ); + + // if path is empty we cannot resolve anything + if ( empty( $path_f ) ) { 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( $path_f ) ) { + $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 Session( $view ); + $userId = \OCP\User::getUser(); + $util = new Util( $view, $userId ); + + // Reformat path for use with OC_FSV + $path_split = explode( '/', $path ); + $path_f = implode( '/', array_slice( $path_split, 3 ) ); + + // only if file is on 'files' folder fix file size and sharing + if ( count($path_split) >= 2 && $path_split[2] == 'files' && $util->fixFileSize( $path ) ) { + + // get sharing app state + $sharingEnabled = \OCP\Share::isEnabled(); + + // get users + $usersSharing = $util->getSharingUsersArray( $sharingEnabled, $path_f ); + + // update sharing-keys + $util->setSharedFileKeyfiles( $session, $usersSharing, $path_f ); + } + + \OC_FileProxy::$enabled = $proxyStatus; } } diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php index 769a40b359f..2ddad0a15da 100644 --- a/apps/files_encryption/lib/session.php +++ b/apps/files_encryption/lib/session.php @@ -26,78 +26,146 @@ namespace OCA\Encryption; * Class for handling encryption related session data */ -class Session { +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 ( \OCP\USER::getUser() === false || + ( isset( $_GET['service'] ) && $_GET['service'] == 'files' && + isset( $_GET['t'] ) ) + ) { + // 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->setPrivateKey( $privateKey ); + + \OC_FileProxy::$enabled = $proxyStatus; + } + } /** * @brief Sets user private key to session + * @param string $privateKey * @return bool - * */ public function setPrivateKey( $privateKey ) { - + $_SESSION['privateKey'] = $privateKey; - + return true; - + } - + /** * @brief Gets user private key from session * @returns string $privateKey The user's plaintext private key * */ public function getPrivateKey() { - - if ( + + if ( isset( $_SESSION['privateKey'] ) && !empty( $_SESSION['privateKey'] ) ) { - + return $_SESSION['privateKey']; - + } else { - + return false; - + } - + } - + /** * @brief Sets user legacy key to session + * @param $legacyKey * @return bool - * */ public function setLegacyKey( $legacyKey ) { - - if ( $_SESSION['legacyKey'] = $legacyKey ) { - - return true; - - } - + + $_SESSION['legacyKey'] = $legacyKey; + + return true; } - + /** * @brief Gets user legacy key from session * @returns string $legacyKey The user's plaintext legacy key * */ public function getLegacyKey() { - - if ( + + if ( isset( $_SESSION['legacyKey'] ) && !empty( $_SESSION['legacyKey'] ) ) { - + return $_SESSION['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 65d7d57a05a..fa9df02f085 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 <samtuke@owncloud.com>, 2011 Robin Appelman + * @copyright 2012 Sam Tuke <samtuke@owncloud.com>, 2011 Robin Appelman * <icewind1991@gmail.com> * * This library is free software; you can redistribute it and/or @@ -32,27 +32,29 @@ 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 { +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,117 +62,99 @@ 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 '/' + /** + * @param $path + * @param $mode + * @param $options + * @param $opened_path + * @return bool + */ 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 . '/' ); + 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 )] ) - ) { - - // Is this just for unit testing purposes? - - $this->handle = self::$sourceStreams[basename( $path )]['stream']; + $this->userId = $util->getUserId(); - $this->path = self::$sourceStreams[basename( $path )]['path']; + // Strip identifier text from path, this gives us the path relative to data/<user>/files + $this->relPath = \OC\Files\Filesystem::normalizePath( str_replace( 'crypt://', '', $path ) ); - $this->size = self::$sourceStreams[basename( $path )]['size']; + // rawPath is relative to the data directory + $this->rawPath = $util->getUserFilesDir() . $this->relPath; - } else { + // 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+' - ) { + if ( + $mode == 'w' + or $mode == 'w+' + or $mode == 'wb' + or $mode == 'wb+' + ) { - $this->size = 0; + // We're writing a new file so start write counter with 0 bytes + $this->size = 0; + $this->unencryptedSize = 0; - } else { - - - - $this->size = self::$view->filesize( $this->path_f, $mode ); - - //$this->size = filesize( $path ); - - } + } else { - // Disable fileproxies so we can open the source file without recursive encryption - \OC_FileProxy::$enabled = false; + $this->size = $this->rootView->filesize( $this->rawPath, $mode ); + } - //$this->handle = fopen( $path, $mode ); - - $this->handle = self::$view->fopen( $this->path_f, $mode ); - - \OC_FileProxy::$enabled = true; + $this->handle = $this->rootView->fopen( $this->rawPath, $mode ); - if ( !is_resource( $this->handle ) ) { + \OC_FileProxy::$enabled = $proxyStatus; - \OCP\Util::writeLog( 'files_encryption', 'failed to open '.$path, \OCP\Util::ERROR ); + if ( !is_resource( $this->handle ) ) { - } + \OCP\Util::writeLog( 'files_encryption', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR ); - } - - if ( is_resource( $this->handle ) ) { + } else { $this->meta = stream_get_meta_data( $this->handle ); } + return is_resource( $this->handle ); } - + + /** + * @param $offset + * @param int $whence + */ public function stream_seek( $offset, $whence = SEEK_SET ) { - + $this->flush(); - + fseek( $this->handle, $offset, $whence ); - - } - - public function stream_tell() { - return ftell($this->handle); + } - + + /** + * @param $count + * @return bool|string + * @throws \Exception + */ public function stream_read( $count ) { - + $this->writeCache = ''; 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 ); @@ -179,107 +163,89 @@ class Stream { } -// $pos = ftell( $this->handle ); -// // Get the data from the file handle $data = fread( $this->handle, 8192 ); - + + $result = ''; + if ( strlen( $data ) ) { - - $this->getKey(); - - $result = Crypt::symmetricDecryptFileContent( $data, $this->keyfile ); - - } else { - $result = ''; + 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' ); -// $length = $this->size - $pos; -// -// if ( $length < 8192 ) { -// -// $result = substr( $result, 0, $length ); -// -// } + } + + // Decrypt data + $result = Crypt::symmetricDecryptFileContent( $data, $this->plainKey ); + + } 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 ) { - + // Encrypt data to 'catfile', which includes IV if ( $encrypted = Crypt::symmetricEncryptFileContent( $plainData, $key ) ) { - - return $encrypted; - + + 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(); - + + // 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 Session( $this->rootView ); + $privateKey = $session->getPrivateKey( $this->userId ); - - $this->keyfile = Crypt::keyDecrypt( $this->encKeyfile, $privateKey ); - + + $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 @@ -290,98 +256,54 @@ class Stream { * @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 ) { - + // 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 + + // 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 - + // 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 ) { - + // 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 ); -// - } -// $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 ) { - + // 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 ) { + // Set writeCache to contents of $data // The writeCache will be carried over to the // next write round, and added to the start of @@ -394,98 +316,164 @@ 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 ); - + + $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 ); - // 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 ); } - + } $this->size = max( $this->size, $pointer + $length ); - + $this->unencryptedSize += $length; + + \OC_FileProxy::$enabled = $proxyStatus; + return $length; } + /** + * @param $option + * @param $arg1 + * @param $arg2 + */ public function stream_set_option( $option, $arg1, $arg2 ) { - switch($option) { + $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); + return fstat( $this->handle ); } - + + /** + * @param $mode + */ public function stream_lock( $mode ) { - flock( $this->handle, $mode ); + return flock( $this->handle, $mode ); } - + + /** + * @return bool + */ public function stream_flush() { - - return fflush( $this->handle ); + + 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); + return feof( $this->handle ); } private function flush() { - + if ( $this->writeCache ) { - + // Set keyfile property for file in question $this->getKey(); - - $encrypted = $this->preWriteEncrypt( $this->writeCache, $this->keyfile ); - + + $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; + + // 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; - \OC\Files\Filesystem::putFileInfo( $this->path, array( 'encrypted' => true, 'size' => $this->size ), '' ); + // 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 ); diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 52bc74db27a..2980aa94e0c 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 <samtuke@owncloud.com>, + * Frank Karlitschek <frank@owncloud.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -21,17 +21,29 @@ * */ -// 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; @@ -43,56 +55,49 @@ namespace OCA\Encryption; * unused, likely to become obsolete shortly */ -class Util { - - +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 +108,303 @@ 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 ) { - + $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 - - } - + $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 ( $this->userId === false || + ( isset( $_GET['service'] ) && $_GET['service'] == 'files' && + isset( $_GET['t'] ) ) + ) { + $this->userId = $this->publicShareKeyId; + + // only handle for files_sharing app + if ( $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 { + $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 + } + } + + /** + * @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 ) + + 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; - + } - + } - - /** - * @brief Sets up user folders and keys for serverside encryption - * @param $passphrase passphrase to encrypt server-stored private key with - */ + + /** + * @brief Sets up user folders and keys for serverside encryption + * @param string $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 ); - - } - - // Create mirrored share env keys directory - if( !$this->view->file_exists( $this->shareKeysPath ) ) { - - $this->view->mkdir( $this->shareKeysPath ); - - } - + + // 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'] ); - + // Encrypt private key with user pwd as passphrase $encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $passphrase ); - + // Save private key $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 ) ) { + \OC_Log::write( 'Encryption library', 'public key exists but private key is missing for "' . $this->userId . '"', \OC_Log::FATAL ); + return false; + } else if ( !$this->view->file_exists( $this->publicKeyPath ) && $this->view->file_exists( $this->privateKeyPath ) ) { + \OC_Log::write( 'Encryption library', 'private key exists but public key is missing for "' . $this->userId . '"', \OC_Log::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(); + + while ( $row = $result->fetchRow() ) { + + $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 * @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 ) ) ) { - + if ( - $file != "." - && $file != ".." + $file != "." + && $file != ".." ) { - + $filePath = $directory . '/' . $this->view->getRelativePath( '/' . $file ); $relPath = $this->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 + 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 ); - + // If the file is encrypted // NOTE: If the userId is // empty or not set, file will @@ -270,207 +412,1049 @@ 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 ) ) { - + + // If the file uses old + // encryption system + } elseif ( Crypt::isLegacyEncryptedContent( $this->tail( $filePath, 3 ), $relPath ) ) { + $found['legacy'][] = array( 'name' => $file, 'path' => $filePath ); - - // If the file is not encrypted + + // 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 ) ) { - + return false; - + } else { - + return $found; - + } - + } - + \OC_FileProxy::$enabled = true; - + return false; } - - /** - * @brief Check if a given path identifies an encrypted file - * @return true / false - */ + + /** + * @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; + + $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 $text; + } + + /** + * @brief Check if a given path identifies an encrypted file + * @param $path + * @return boolean + */ public function isEncryptedPath( $path ) { - - // Disable encryption proxy so data retreived is in its + + // Disable encryption proxy so data retrieved is in its // original form + $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - - $data = $this->view->file_get_contents( $path ); - - \OC_FileProxy::$enabled = true; - - return Crypt::isCatfile( $data ); - + + // we only need 24 byte from the last chunk + $data = ''; + $handle = $this->view->fopen( $path, 'r' ); + if ( !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; + + // Reformat path for use with OC_FSV + $pathSplit = explode( '/', $path ); + $pathRelative = implode( '/', array_slice( $pathSplit, 3 ) ); + + if ( $pathSplit[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://' . $pathRelative, "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 $path absolute path + * @return 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 + * @note e.g. turns '/admin/files/test.txt' into 'test.txt' */ public function stripUserFilesPath( $path ) { - + $trimmed = ltrim( $path, '/' ); $split = explode( '/', $trimmed ); $sliced = array_slice( $split, 2 ); $relPath = implode( '/', $sliced ); - + return $relPath; - + + } + + /** + * @param $path + * @return bool + */ + public function isSharedPath( $path ) { + + $trimmed = ltrim( $path, '/' ); + $split = explode( '/', $trimmed ); + + if ( $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'] ); - + + //relative to data/<user>/file + $relPath = $plainFile['path']; + + //relative to /data + $rawPath = $this->userId . '/files/' . $plainFile['path']; + + // Open plain file handle for binary reading + $plainHandle1 = $this->view->fopen( $rawPath, 'rb' ); + + // 2nd handle for moving plain file - view->rename() doesn't work, this is a workaround + $plainHandle2 = $this->view->fopen( $rawPath . '.plaintmp', 'wb' ); + + // Move plain file to a temporary location + stream_copy_to_stream( $plainHandle1, $plainHandle2 ); + + // Close access to original file + // $this->view->fclose( $plainHandle1 ); // not implemented in view{} + // Delete original plain file so we can rename enc file later + $this->view->unlink( $rawPath ); + + // Open enc file handle for binary writing, with same filename as original plain file + $encHandle = fopen( 'crypt://' . $relPath, 'wb' ); + + // Save data from plain stream to new encrypted file via enc stream + // NOTE: Stream{} will be invoked for handling + // the encryption, and should handle all keys + // and their generation etc. automatically + stream_copy_to_stream( $plainHandle2, $encHandle ); + + // get file size + $size = $this->view->filesize( $rawPath . '.plaintmp' ); + + // Delete temporary plain copy of file + $this->view->unlink( $rawPath . '.plaintmp' ); + // Add the file to the cache - \OC\Files\Filesystem::putFileInfo( $plainFile['path'], array( 'encrypted'=>true, 'size' => $size ), '' ); - + \OC\Files\Filesystem::putFileInfo( $plainFile['path'], 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 ) { - + // Fetch data from file $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, $newPassphrase, $legacyFile['path'] ); + + $rawPath = $legacyFile['path']; + $relPath = $this->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'] ); - + $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 ) { - + 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 ) { + + $query = \OC_DB::prepare( 'SELECT `path`' + . ' FROM `*PREFIX*filecache`' + . ' WHERE `fileid` = ?' ); + + $result = $query->execute( array( $fileId ) ); + + $row = $result->fetchRow(); + + return substr( $row['path'], 5 ); + + } + + /** + * @brief Filter an array of UIDs to return only ones ready for sharing + * @param array $unfilteredUsers users to be checked for sharing readiness + * @return 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 + \OC_Log::write( 'Encryption library', '"' . $user . '" is not setup for encryption', \OC_Log::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 + * @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'] ) ) { + + \OC_Log::write( 'Encryption library', 'Sharing to these user(s) failed as they are unready for encryption:"' . print_r( $filteredUids['unready'], 1 ), \OC_Log::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'] ) + ) { + + \OC_Log::write( 'Encryption library', 'Keyfiles could not be saved for users sharing ' . $filePath, \OC_Log::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, true, 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(); + + $row = $result->fetchRow(); + if($row) { + $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 + * @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( $this->userFilesDir . $dir ); + + // handling for re shared folders + $path_split = explode( '/', $dir ); + + foreach ( $content as $c ) { + + $sharedPart = $path_split[sizeof( $path_split ) - 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 ) { + + $query = \OC_DB::prepare( 'SELECT `file_target`, `item_type`' + . ' FROM `*PREFIX*share`' + . ' WHERE `id` = ?' ); + + $result = $query->execute( array( $id ) ); + + $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 ) { + + $query = \OC_DB::prepare( 'SELECT `parent`' + . ' FROM `*PREFIX*share`' + . ' WHERE `id` = ?' ); + + $result = $query->execute( array( $id ) ); + + $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 = \OC_DB::prepare( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1 ); + $source = $query->execute( array( $id ) )->fetchRow(); + + $fileOwner = false; + + if ( isset( $source['parent'] ) ) { + + $parent = $source['parent']; + + while ( isset( $parent ) ) { + + $query = \OC_DB::prepare( 'SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1 ); + $item = $query->execute( array( $parent ) )->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 Session( new \OC_FilesystemView( '/' ) ); + $sharingEnabled = \OCP\Share::isEnabled(); + $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 { + $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, true, 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 ) { + $filePath = substr( $item['path'], 25 ); + if ( $item['type'] == 'dir' ) { + $this->recoverAllFiles( $filePath . '/', $privateKey ); + } else { + $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 ); } } diff --git a/apps/files_encryption/settings-admin.php b/apps/files_encryption/settings-admin.php new file mode 100644 index 00000000000..6cc5b997fdb --- /dev/null +++ b/apps/files_encryption/settings-admin.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright (c) 2011 Robin Appelman <icewind@owncloud.com> + * 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 af0273cfdc4..57f7f584523 100644 --- a/apps/files_encryption/settings-personal.php +++ b/apps/files_encryption/settings-personal.php @@ -6,12 +6,23 @@ * See the COPYING-README file.
*/
+// Add CSS stylesheet
+\OC_Util::addStyle( 'files_encryption', 'settings-personal' );
+
$tmpl = new OCP\Template( 'files_encryption', 'settings-personal');
-$blackList = explode( ',', \OCP\Config::getAppValue( 'files_encryption', 'type_blacklist', 'jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg' ) );
+$user = \OCP\USER::getUser();
+$view = new \OC_FilesystemView( '/' );
+$util = new \OCA\Encryption\Util( $view, $user );
-$tmpl->assign( 'blacklist', $blackList );
+$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 d1260f44e9f..00000000000 --- a/apps/files_encryption/settings.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Robin Appelman <icewind@owncloud.com> - * 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 00000000000..18fea1845f4 --- /dev/null +++ b/apps/files_encryption/templates/settings-admin.php @@ -0,0 +1,56 @@ +<form id="encryption"> + <fieldset class="personalblock"> + + <p> + <strong><?php p($l->t( 'Encryption' )); ?></strong> + <br /> + </p> + <p> + <?php p($l->t( "Enable encryption passwords recovery key (allow sharing to recovery key):" )); ?> + <br /> + <br /> + <input type="password" name="recoveryPassword" id="recoveryPassword" /> + <label for="recoveryPassword"><?php p($l->t( "Recovery account password" )); ?></label> + <br /> + <input + type='radio' + name='adminEnableRecovery' + value='1' + <?php echo ( $_["recoveryEnabled"] == 1 ? 'checked="checked"' : 'disabled' ); ?> /> + <?php p($l->t( "Enabled" )); ?> + <br /> + + <input + type='radio' + name='adminEnableRecovery' + value='0' + <?php echo ( $_["recoveryEnabled"] == 0 ? 'checked="checked"' : 'disabled' ); ?> /> + <?php p($l->t( "Disabled" )); ?> + </p> + <br /><br /> + <p> + <strong><?php p($l->t( "Change encryption passwords recovery key:" )); ?></strong> + <br /><br /> + <input + type="password" + name="changeRecoveryPassword" + id="oldRecoveryPassword" + <?php echo ( $_["recoveryEnabled"] == 0 ? 'disabled' : '' ); ?> /> + <label for="oldRecoveryPassword"><?php p($l->t( "Old Recovery account password" )); ?></label> + <br /> + <input + type="password" + name="changeRecoveryPassword" + id="newRecoveryPassword" + <?php echo ( $_["recoveryEnabled"] == 0 ? 'disabled' : '' ); ?> /> + <label for="newRecoveryPassword"><?php p($l->t( "New Recovery account password" )); ?></label> + <br /> + <button + type="button" + name="submitChangeRecoveryKey" + disabled><?php p($l->t( "Change Password" )); ?> + </button> + <span class="msg"></span> + </p> + </fieldset> +</form> diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php index 5f0accaed5f..04d6e79179e 100644 --- a/apps/files_encryption/templates/settings-personal.php +++ b/apps/files_encryption/templates/settings-personal.php @@ -1,22 +1,33 @@ <form id="encryption">
<fieldset class="personalblock">
<legend>
- <?php p($l->t( 'Encryption' )); ?>
+ <?php p( $l->t( 'Encryption' ) ); ?>
</legend>
- <p>
- <?php p($l->t( 'File encryption is enabled.' )); ?>
- </p>
- <?php if ( ! empty( $_["blacklist"] ) ): ?>
- <p>
- <?php p($l->t( 'The following file types will not be encrypted:' )); ?>
- </p>
- <ul>
- <?php foreach( $_["blacklist"] as $type ): ?>
- <li>
- <?php p($type); ?>
- </li>
- <?php endforeach; ?>
- </ul>
+
+ <?php if ( $_["recoveryEnabled"] ): ?>
+ <p>
+ <label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery by sharing all files with your administrator:" ) ); ?></label>
+ <br />
+ <em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" ) ); ?></em>
+ <br />
+ <input
+ type='radio'
+ name='userEnableRecovery'
+ value='1'
+ <?php echo ( $_["recoveryEnabledForUser"] == 1 ? 'checked="checked"' : '' ); ?> />
+ <?php p( $l->t( "Enabled" ) ); ?>
+ <br />
+
+ <input
+ type='radio'
+ name='userEnableRecovery'
+ value='0'
+ <?php echo ( $_["recoveryEnabledForUser"] == 0 ? 'checked="checked"' : '' ); ?> />
+ <?php p( $l->t( "Disabled" ) ); ?>
+ <div id="recoveryEnabledSuccess"><?php p( $l->t( 'File recovery settings updated' ) ); ?></div>
+ <div id="recoveryEnabledError"><?php p( $l->t( 'Could not update file recovery' ) ); ?></div>
+ </p>
<?php endif; ?>
+ <br />
</fieldset>
</form>
diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php deleted file mode 100644 index b873d7f5aaf..00000000000 --- a/apps/files_encryption/templates/settings.php +++ /dev/null @@ -1,20 +0,0 @@ -<form id="encryption"> - <fieldset class="personalblock"> - - <p> - <strong><?php p($l->t( 'Encryption' )); ?></strong> - - <?php p($l->t( "Exclude the following file types from encryption:" )); ?> - <br /> - - <select - id='encryption_blacklist' - title="<?php p($l->t( 'None' ))?>" - multiple="multiple"> - <?php foreach($_["blacklist"] as $type): ?> - <option selected="selected" value="<?php p($type); ?>"> <?php p($type); ?> </option> - <?php endforeach;?> - </select> - </p> - </fieldset> -</form> diff --git a/apps/files_encryption/test/crypt.php b/apps/files_encryption/test/crypt.php deleted file mode 100755 index aa87ec32821..00000000000 --- a/apps/files_encryption/test/crypt.php +++ /dev/null @@ -1,667 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>, and - * Robin Appelman <icewind@owncloud.com> - * 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 bf453fe3163..00000000000 --- a/apps/files_encryption/test/keymanager.php +++ /dev/null @@ -1,130 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com> - * 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 Binary files differdeleted file mode 100644 index cb5bf50550d..00000000000 --- a/apps/files_encryption/test/legacy-encrypted-text.txt +++ /dev/null diff --git a/apps/files_encryption/test/stream.php b/apps/files_encryption/test/stream.php deleted file mode 100644 index ba82ac80eab..00000000000 --- a/apps/files_encryption/test/stream.php +++ /dev/null @@ -1,226 +0,0 @@ -// <?php -// /** -// * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> -// * This file is licensed under the Affero General Public License version 3 or -// * later. -// * See the COPYING-README file. -// */ -// -// namespace 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 1cdeff8008d..00000000000 --- a/apps/files_encryption/test/util.php +++ /dev/null @@ -1,225 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com> - * 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 Binary files differindex 79bc99479da..79bc99479da 100644 --- a/apps/files_encryption/test/binary +++ b/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 00000000000..621941c52a1 --- /dev/null +++ b/apps/files_encryption/tests/crypt.php @@ -0,0 +1,833 @@ +<?php +/** + * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>, and + * Robin Appelman <icewind@owncloud.com> + * 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'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Crypt + */ +class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase +{ + + public $userId; + public $pass; + public $stateFilesTrashbin; + public $dataLong; + public $dataUrl; + public $dataShort; + /** + * @var OC_FilesystemView + */ + public $view; + public $legacyEncryptedData; + public $genPrivateKey; + public $genPublicKey; + + function setUp() + { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + // 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('/'); + + \OC_User::setUserId('admin'); + $this->userId = 'admin'; + $this->pass = 'admin'; + + $userHome = \OC_User::getHome($this->userId); + $this->dataDir = str_replace('/' . $this->userId, '', $userHome); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerUserHooks(); + + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // 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'); + + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_Util::setupFS($this->userId); + \OC_User::setUserId($this->userId); + + $params['uid'] = $this->userId; + $params['password'] = $this->pass; + OCA\Encryption\Hooks::login($params); + + } + + function tearDown() + { + \OC_FileProxy::clearProxies(); + + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } else { + OC_App::disable('files_trashbin'); + } + } + + 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 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 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::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); + + $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::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, array($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 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); + } + + 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 00000000000..4495cee78e2 --- /dev/null +++ b/apps/files_encryption/tests/encryption.key @@ -0,0 +1 @@ +E_cP6HVs
\ No newline at end of file diff --git a/apps/files_encryption/tests/keymanager.php b/apps/files_encryption/tests/keymanager.php new file mode 100644 index 00000000000..b1bae673e82 --- /dev/null +++ b/apps/files_encryption/tests/keymanager.php @@ -0,0 +1,245 @@ +<?php +/** + * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com> + * 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; + + function setUp() + { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + \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'; + + $userHome = \OC_User::getHome($this->userId); + $this->dataDir = str_replace('/' . $this->userId, '', $userHome); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // 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'); + + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_Util::setupFS($this->userId); + \OC_User::setUserId($this->userId); + + $params['uid'] = $this->userId; + $params['password'] = $this->pass; + OCA\Encryption\Hooks::login($params); + } + + function tearDown() + { + + \OC_FileProxy::$enabled = true; + \OC_FileProxy::clearProxies(); + + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } else { + OC_App::disable('files_trashbin'); + } + } + + 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 00000000000..d38cb7d1b0d --- /dev/null +++ b/apps/files_encryption/tests/legacy-encrypted-text.txt @@ -0,0 +1 @@ +
ߕt.dS@t9QJ
\ No newline at end of file diff --git a/apps/files_encryption/test/proxy.php b/apps/files_encryption/tests/proxy.php index 709730f7609..5a2d851ff7c 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 00000000000..1d0cbfbc1de --- /dev/null +++ b/apps/files_encryption/tests/share.php @@ -0,0 +1,790 @@ +<?php +/** + * ownCloud + * + * @author Florin Peter + * @copyright 2013 Florin Peter <owncloud@florin-peter.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +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'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Share + */ +class Test_Encryption_Share extends \PHPUnit_Framework_TestCase +{ + + public $stateFilesTrashbin; + public $filename; + public $dataShort; + /** + * @var OC_FilesystemView + */ + public $view; + public $folder1; + public $subfolder; + public $subsubfolder; + + function setUp() + { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + $this->dataShort = 'hats'; + $this->view = new \OC_FilesystemView('/'); + + $userHome = \OC_User::getHome('admin'); + $this->dataDir = str_replace('/admin', '', $userHome); + + $this->folder1 = '/folder1'; + $this->subfolder = '/subfolder1'; + $this->subsubfolder = '/subsubfolder1'; + + $this->filename = 'share-tmp.test'; + + // 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(); + + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // 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 users + $this->loginHelper('user1', true); + $this->loginHelper('user2', true); + $this->loginHelper('user3', true); + + // create group and assign users + \OC_Group::createGroup('group1'); + \OC_Group::addToGroup('user2', 'group1'); + \OC_Group::addToGroup('user3', 'group1'); + } + + function tearDown() + { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } else { + OC_App::disable('files_trashbin'); + } + + // clean group + \OC_Group::deleteGroup('group1'); + + // cleanup users + \OC_User::deleteUser('user1'); + \OC_User::deleteUser('user2'); + \OC_User::deleteUser('user3'); + + \OC_FileProxy::clearProxies(); + } + + /** + * @param bool $withTeardown + */ + function testShareFile($withTeardown = true) + { + // login as admin + $this->loginHelper('admin'); + + // 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('/admin/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, 'user1', OCP\PERMISSION_ALL); + + // login as admin + $this->loginHelper('admin'); + + // check if share key for user1 exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey')); + + // login as user1 + $this->loginHelper('user1'); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents('/user1/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 + $this->loginHelper('admin'); + + // unshare the file + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey')); + + // cleanup + $this->view->unlink('/admin/files/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey')); + } + } + + /** + * @param bool $withTeardown + */ + function testReShareFile($withTeardown = true) + { + $this->testShareFile(false); + + // login as user1 + $this->loginHelper('user1'); + + // get the file info + $fileInfo = $this->view->getFileInfo('/user1/files/Shared/' . $this->filename); + + // share the file with user2 + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2', OCP\PERMISSION_ALL); + + // login as admin + $this->loginHelper('admin'); + + // check if share key for user2 exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey')); + + // login as user2 + $this->loginHelper('user2'); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared/' . $this->filename); + + // check if data is the same as previously written + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // cleanup + if ($withTeardown) { + + // login as user1 + $this->loginHelper('user1'); + + // unshare the file with user2 + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2'); + + // login as admin + $this->loginHelper('admin'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey')); + + // unshare the file with user1 + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user1.shareKey')); + + // cleanup + $this->view->unlink('/admin/files/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey')); + } + } + + /** + * @param bool $withTeardown + * @return array + */ + function testShareFolder($withTeardown = true) + { + // login as admin + $this->loginHelper('admin'); + + // create folder structure + $this->view->mkdir('/admin/files' . $this->folder1); + $this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder); + $this->view->mkdir('/admin/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('/admin/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, 'user1', OCP\PERMISSION_ALL); + + // login as admin + $this->loginHelper('admin'); + + // check if share key for user1 exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey')); + + // login as user1 + $this->loginHelper('user1'); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents('/user1/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 + $this->loginHelper('admin'); + + // unshare the folder with user1 + \OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey')); + + // cleanup + $this->view->unlink('/admin/files' . $this->folder1); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey')); + } + + return $fileInfo; + } + + /** + * @param bool $withTeardown + */ + function testReShareFolder($withTeardown = true) + { + $fileInfoFolder1 = $this->testShareFolder(false); + + // login as user1 + $this->loginHelper('user1'); + + // 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('/user1/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, 'user2', OCP\PERMISSION_ALL); + + // login as admin + $this->loginHelper('admin'); + + // check if share key for user2 exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user2.shareKey')); + + // login as user2 + $this->loginHelper('user2'); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents('/user2/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('/user2/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, 'user3', OCP\PERMISSION_ALL); + + // login as admin + $this->loginHelper('admin'); + + // check if share key for user3 exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user3.shareKey')); + + // login as user3 + $this->loginHelper('user3'); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents('/user3/files/Shared/' . $this->filename); + + // check if data is the same + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // cleanup + if ($withTeardown) { + + // login as user2 + $this->loginHelper('user2'); + + // unshare the file with user3 + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user3'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user3.shareKey')); + + // login as user1 + $this->loginHelper('user1'); + + // unshare the folder with user2 + \OCP\Share::unshare('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user2'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user2.shareKey')); + + // login as admin + $this->loginHelper('admin'); + + // unshare the folder1 with user1 + \OCP\Share::unshare('folder', $fileInfoFolder1['fileid'], \OCP\Share::SHARE_TYPE_USER, 'user1'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey')); + + // cleanup + $this->view->unlink('/admin/files' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey')); + } + } + + function testPublicShareFile() + { + // login as admin + $this->loginHelper('admin'); + + // 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('/admin/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 + $this->loginHelper('admin'); + + $publicShareKeyId = \OC_Appconfig::getValue('files_encryption', 'publicShareKeyId'); + + // check if share key for public exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey')); + + // some hacking to simulate public link + $GLOBALS['app'] = 'files_sharing'; + $GLOBALS['fileOwner'] = 'admin'; + \OC_User::setUserId(''); + + // 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 + $this->loginHelper('admin'); + + // 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('/admin/files_encryption/share-keys/' . $this->filename . '.' . $publicShareKeyId . '.shareKey')); + + // cleanup + $this->view->unlink('/admin/files/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey')); + } + + function testShareFileWithGroup() + { + // login as admin + $this->loginHelper('admin'); + + // 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('/admin/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, 'group1', OCP\PERMISSION_ALL); + + // login as admin + $this->loginHelper('admin'); + + // check if share key for user2 and user3 exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey')); + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user3.shareKey')); + + // login as user1 + $this->loginHelper('user2'); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents('/user2/files/Shared/' . $this->filename); + + // check if data is the same as we previously written + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // login as admin + $this->loginHelper('admin'); + + // unshare the file + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, 'group1'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey')); + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user3.shareKey')); + + // cleanup + $this->view->unlink('/admin/files/' . $this->filename); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey')); + + } + + function testRecoveryFile() + { + \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 + $this->loginHelper('admin'); + + $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), 'admin'); + + // 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('/admin/files' . $this->folder1); + $this->view->mkdir('/admin/files' . $this->folder1 . $this->subfolder); + $this->view->mkdir('/admin/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('/admin/files_encryption/share-keys/' . $this->filename . '.admin.shareKey')); + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.admin.shareKey')); + $this->assertTrue($this->view->file_exists('/admin/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('/admin/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertFalse($this->view->file_exists('/admin/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('/admin/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + + // cleanup + $this->view->unlink('/admin/files/' . $this->filename); + $this->view->unlink('/admin/files/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename); + + // check if share key for recovery not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertFalse($this->view->file_exists('/admin/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 + $this->loginHelper('admin'); + + \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 + $this->loginHelper('user1'); + + $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), 'user1'); + + // enable recovery for admin + $this->assertTrue($util->setRecoveryForUser(1)); + + // create folder structure + $this->view->mkdir('/user1/files' . $this->folder1); + $this->view->mkdir('/user1/files' . $this->folder1 . $this->subfolder); + $this->view->mkdir('/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 user and recovery exists + $this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.user1.shareKey')); + $this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey')); + $this->assertTrue($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + + // login as admin + $this->loginHelper('admin'); + + // change password + \OC_User::setPassword('user1', 'test', 'test123'); + + // login as user1 + $this->loginHelper('user1', 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('/user1/files' . $this->folder1); + $this->view->unlink('/user1/files' . $this->filename); + + // check if share key for user and recovery exists + $this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.user1.shareKey')); + $this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->filename . '.' . $recoveryKeyId . '.shareKey')); + $this->assertFalse($this->view->file_exists('/user1/files_encryption/share-keys/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.user1.shareKey')); + $this->assertFalse($this->view->file_exists('/user1/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 + $this->loginHelper('admin'); + + // 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('/admin/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/user2.public.key', '/public-keys/user2.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, 'group1', OCP\PERMISSION_ALL); + + // login as admin + $this->loginHelper('admin'); + + // check if share key for user1 not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.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/user2.public.key_backup', '/public-keys/user2.public.key'); + + // remove share file + $this->view->unlink('/admin/files_encryption/share-keys/' . $this->filename . '.user2.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, 'group1'); + + // check if share key not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $this->filename . '.user2.shareKey')); + + // cleanup + $this->view->unlink('/admin/files/' . $this->filename); + } + + + + /** + * @param $user + * @param bool $create + * @param bool $password + */ + 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/stream.php b/apps/files_encryption/tests/stream.php new file mode 100644 index 00000000000..3765d986e12 --- /dev/null +++ b/apps/files_encryption/tests/stream.php @@ -0,0 +1,182 @@ +<?php +/** + * ownCloud + * + * @author Florin Peter + * @copyright 2013 Florin Peter <owncloud@florin-peter.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +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_Stream + * @brief this class provide basic stream tests + */ +class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase +{ + + public $userId; + public $pass; + /** + * @var \OC_FilesystemView + */ + public $view; + public $dataShort; + public $stateFilesTrashbin; + + function setUp() + { + // reset backend + \OC_User::useBackend('database'); + + // set user id + \OC_User::setUserId('admin'); + $this->userId = 'admin'; + $this->pass = 'admin'; + + // init filesystem view + $this->view = new \OC_FilesystemView('/'); + + // init short data + $this->dataShort = 'hats'; + + // init filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // register encryption file proxy + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // 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'); + + // init filesystem for user + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_Util::setupFS($this->userId); + \OC_User::setUserId($this->userId); + + // login user + $params['uid'] = $this->userId; + $params['password'] = $this->pass; + OCA\Encryption\Hooks::login($params); + } + + function tearDown() + { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } else { + OC_App::disable('files_trashbin'); + } + + // clear all proxies + \OC_FileProxy::clearProxies(); + } + + 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 00000000000..cc8709b6f24 --- /dev/null +++ b/apps/files_encryption/tests/trashbin.php @@ -0,0 +1,270 @@ +<?php +/** + * ownCloud + * + * @author Florin Peter + * @copyright 2013 Florin Peter <owncloud@florin-peter.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +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'); + +use OCA\Encryption; + +/** + * Class Test_Encryption_Trashbin + * @brief this class provide basic trashbin app tests + */ +class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase +{ + + public $userId; + public $pass; + /** + * @var \OC_FilesystemView + */ + public $view; + public $dataShort; + public $stateFilesTrashbin; + public $folder1; + public $subfolder; + public $subsubfolder; + + function setUp() + { + // reset backend + \OC_User::useBackend('database'); + + // set user id + \OC_User::setUserId('admin'); + $this->userId = 'admin'; + $this->pass = 'admin'; + + // init filesystem view + $this->view = new \OC_FilesystemView('/'); + + // init short data + $this->dataShort = 'hats'; + + $this->folder1 = '/folder1'; + $this->subfolder = '/subfolder1'; + $this->subsubfolder = '/subsubfolder1'; + + \OC_Hook::clear('OC_Filesystem'); + \OC_Hook::clear('OC_User'); + + // init filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // register encryption file proxy + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // trashbin hooks + \OCA\Files_Trashbin\Trashbin::registerHooks(); + + // remember files_trashbin state + $this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin'); + + // we don't want to tests with app files_trashbin enabled + \OC_App::enable('files_trashbin'); + + // init filesystem for user + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_Util::setupFS($this->userId); + \OC_User::setUserId($this->userId); + + // login user + $params['uid'] = $this->userId; + $params['password'] = $this->pass; + OCA\Encryption\Hooks::login($params); + } + + function tearDown() + { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } else { + OC_App::disable('files_trashbin'); + } + + // clear all proxies + \OC_FileProxy::clearProxies(); + } + + /** + * @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('/admin/files_encryption/keyfiles/' . $filename . '.key')); + + // check if share key for admin exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $filename . '.admin.shareKey')); + + // delete file + \OC\FIles\Filesystem::unlink($filename); + + // check if file not exists + $this->assertFalse($this->view->file_exists('/admin/files/' . $filename)); + + // check if key for admin not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/keyfiles/' . $filename . '.key')); + + // check if share key for admin not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $filename . '.admin.shareKey')); + + // get files + $trashFiles = $this->view->getDirectoryContent('/admin/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('/admin/files_trashbin/keyfiles/' . $filename . '.key.' . $trashFileSuffix)); + + // check if share key for admin not exists + $this->assertTrue($this->view->file_exists('/admin/files_trashbin/share-keys/' . $filename . '.admin.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('/admin/files/' . $fileNameWithoutSuffix)); + + // check if key for admin exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/keyfiles/' . $fileNameWithoutSuffix . '.key')); + + // check if share key for admin exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $fileNameWithoutSuffix . '.admin.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('/admin/files_encryption/keyfiles/' . $filename . '.key')); + + // check if share key for admin exists + $this->assertTrue($this->view->file_exists('/admin/files_encryption/share-keys/' . $filename . '.admin.shareKey')); + + // delete file + \OC\FIles\Filesystem::unlink($filename); + + // check if file not exists + $this->assertFalse($this->view->file_exists('/admin/files/' . $filename)); + + // check if key for admin not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/keyfiles/' . $filename . '.key')); + + // check if share key for admin not exists + $this->assertFalse($this->view->file_exists('/admin/files_encryption/share-keys/' . $filename . '.admin.shareKey')); + + // get files + $trashFiles = $this->view->getDirectoryContent('/admin/files_trashbin/files/'); + + $trashFileSuffix = null; + // find created file with timestamp + foreach($trashFiles as $file) { + if(strncmp($file['name'], $filename, strlen($filename)) == 0) { + $path_parts = pathinfo($file['name']); + $trashFileSuffix = $path_parts['extension']; + break; + } + } + + // check if we found the file we created + $this->assertNotNull($trashFileSuffix); + + // check if key for admin exists + $this->assertTrue($this->view->file_exists('/admin/files_trashbin/keyfiles/' . $filename . '.key.' . $trashFileSuffix)); + + // check if share key for admin exists + $this->assertTrue($this->view->file_exists('/admin/files_trashbin/share-keys/' . $filename . '.admin.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('/admin/files_trashbin/files/' . $filename . '.' . $trashFileSuffix)); + + // check if key for admin not exists + $this->assertFalse($this->view->file_exists('/admin/files_trashbin/keyfiles/' . $filename . '.key.' . $trashFileSuffix)); + + // check if share key for admin not exists + $this->assertFalse($this->view->file_exists('/admin/files_trashbin/share-keys/' . $filename . '.admin.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 00000000000..a2be8a40417 --- /dev/null +++ b/apps/files_encryption/tests/util.php @@ -0,0 +1,277 @@ +<?php +/** + * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com> + * 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 +{ + + 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 $lagacyKey; + + function setUp() + { + // reset backend + \OC_User::useBackend('database'); + + \OC_User::setUserId('admin'); + $this->userId = 'admin'; + $this->pass = 'admin'; + + // 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->lagacyKey = '62829813025828180801'; + + $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('/'); + + $userHome = \OC_User::getHome($this->userId); + $this->dataDir = str_replace('/' . $this->userId, '', $userHome); + + // Filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_Util::setupFS($this->userId); + \OC_User::setUserId($this->userId); + + $params['uid'] = $this->userId; + $params['password'] = $this->pass; + OCA\Encryption\Hooks::login($params); + + $this->util = new Encryption\Util($this->view, $this->userId); + } + + function tearDown() + { + + \OC_FileProxy::clearProxies(); + } + + /** + * @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 testIsLagacyUser() + { + $userView = new \OC_FilesystemView( '/' . $this->userId ); + + // 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'] = $this->userId; + $params['password'] = $this->pass; + + $util = new Encryption\Util($this->view, $this->userId); + $util->setMigrationStatus(0); + + $this->assertTrue(OCA\Encryption\Hooks::login($params)); + + $this->assertEquals($this->lagacyKey, $_SESSION['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('admin'); + + $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('admin', $fileOwnerUid); + + $this->assertEquals($file, $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 testEncryptLagacyFiles() + { + $userView = new \OC_FilesystemView( '/' . $this->userId); + $view = new \OC_FilesystemView( '/' . $this->userId . '/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'] = $this->userId; + $params['password'] = $this->pass; + + $util = new Encryption\Util($this->view, $this->userId); + $util->setMigrationStatus(0); + + $this->assertTrue(OCA\Encryption\Hooks::login($params)); + + $this->assertEquals($this->lagacyKey, $_SESSION['legacyKey']); + + $files = $util->findEncFiles('/' . $this->userId . '/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); + } +}
\ No newline at end of file diff --git a/apps/files_encryption/tests/webdav.php b/apps/files_encryption/tests/webdav.php new file mode 100755 index 00000000000..4b453d0c9d1 --- /dev/null +++ b/apps/files_encryption/tests/webdav.php @@ -0,0 +1,251 @@ +<?php +/** + * ownCloud + * + * @author Florin Peter + * @copyright 2013 Florin Peter <owncloud@florin-peter.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +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_Webdav + * @brief this class provide basic webdav tests for PUT,GET and DELETE + */ +class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase +{ + + public $userId; + public $pass; + /** + * @var \OC_FilesystemView + */ + public $view; + public $dataShort; + public $stateFilesTrashbin; + + function setUp() + { + // reset backend + \OC_User::useBackend('database'); + + // set user id + \OC_User::setUserId('admin'); + $this->userId = 'admin'; + $this->pass = 'admin'; + + // init filesystem view + $this->view = new \OC_FilesystemView('/'); + + // init short data + $this->dataShort = 'hats'; + + // init filesystem related hooks + \OCA\Encryption\Helper::registerFilesystemHooks(); + + // register encryption file proxy + \OC_FileProxy::register(new OCA\Encryption\Proxy()); + + // 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'); + + // init filesystem for user + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_Util::setupFS($this->userId); + \OC_User::setUserId($this->userId); + + // login user + $params['uid'] = $this->userId; + $params['password'] = $this->pass; + OCA\Encryption\Hooks::login($params); + } + + function tearDown() + { + // reset app files_trashbin + if ($this->stateFilesTrashbin) { + OC_App::enable('files_trashbin'); + } else { + OC_App::disable('files_trashbin'); + } + + // clear all proxies + \OC_FileProxy::clearProxies(); + } + + /** + * @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 YWRtaW46YWRtaW4='; + $_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 YWRtaW46YWRtaW4='; + $_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 YWRtaW46YWRtaW4='; + $_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/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 Binary files differindex ff982acf423..ff982acf423 100644 --- a/apps/files_encryption/test/zeros +++ b/apps/files_encryption/tests/zeros diff --git a/apps/files_external/l10n/nb_NO.php b/apps/files_external/l10n/nb_NO.php index 961ef2b1046..ea8648303d1 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", +"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Advarsel:</b> \"smbclient\" er ikke installert. Kan ikke montere CIFS/SMB mapper. Ta kontakt med din systemadministrator for å installere det.", +"<b>Warning:</b> 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." => "<b>Advarsel:</b> 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.", +"<b>Warning:</b> 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." => "<b>Advarsel:</b> 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/pl.php b/apps/files_external/l10n/pl.php index cd1b1fe84a1..e03ded1e70a 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", "<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Ostrzeżenie:</b> \"smbclient\" nie jest zainstalowany. Zamontowanie katalogów CIFS/SMB nie jest możliwe. Skontaktuj sie z administratorem w celu zainstalowania.", "<b>Warning:</b> 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." => "<b>Ostrzeżenie:</b> Wsparcie dla FTP w PHP nie jest zainstalowane lub włączone. Skontaktuj sie z administratorem w celu zainstalowania lub włączenia go.", +"<b>Warning:</b> 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." => "<b>Ostrzeżenie:</b> 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/ru_RU.php b/apps/files_external/l10n/ru_RU.php index 406e284b278..a43417dfbc1 100644 --- a/apps/files_external/l10n/ru_RU.php +++ b/apps/files_external/l10n/ru_RU.php @@ -1,23 +1,4 @@ <?php $TRANSLATIONS = array( -"Access granted" => "Доступ разрешен", -"Error configuring Dropbox storage" => "Ошибка при конфигурировании хранилища Dropbox", -"Grant access" => "Предоставить доступ", -"Please provide a valid Dropbox app key and secret." => "Пожалуйста представьте допустимый ключ приложения Dropbox и пароль.", -"Error configuring Google Drive storage" => "Ошибка настройки хранилища Google Drive", -"<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Предупреждение:</b> \"smbclient\" не установлен. Подключение общих папок CIFS/SMB невозможно. Пожалуйста, обратитесь к системному администратору, чтобы установить его.", -"<b>Warning:</b> 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." => "<b>Предупреждение:</b> Поддержка 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_sharing/l10n/nn_NO.php b/apps/files_sharing/l10n/nn_NO.php index abd1ee394bc..aeba545dabc 100644 --- a/apps/files_sharing/l10n/nn_NO.php +++ b/apps/files_sharing/l10n/nn_NO.php @@ -1,6 +1,9 @@ <?php $TRANSLATIONS = array( "Password" => "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/ru_RU.php b/apps/files_sharing/l10n/ru_RU.php index 36e4b2fd0e1..2cadd163462 100644 --- a/apps/files_sharing/l10n/ru_RU.php +++ b/apps/files_sharing/l10n/ru_RU.php @@ -1,9 +1,3 @@ <?php $TRANSLATIONS = array( -"Password" => "Пароль", -"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/public.php b/apps/files_sharing/public.php index 2b283375a67..59598e35fa2 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -1,5 +1,4 @@ <?php -$RUNTIME_NOSETUPFS = true; // Load other apps for file previews OC_App::loadApps(); @@ -46,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']); } diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php index e83d3b8fbbd..3b1e0ac30cc 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/l10n/nb_NO.php b/apps/files_trashbin/l10n/nb_NO.php index e1dce4b3995..43ad0180494 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 8166a024e58..454ea2b0575 100644 --- a/apps/files_trashbin/l10n/nn_NO.php +++ b/apps/files_trashbin/l10n/nn_NO.php @@ -1,10 +1,18 @@ <?php $TRANSLATIONS = array( +"Couldn't delete %s permanently" => "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", +"Deleted" => "Sletta", "1 folder" => "1 mappe", "{count} folders" => "{count} mapper", "1 file" => "1 fil", "{count} files" => "{count} filer", -"Delete" => "Slett" +"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/ru_RU.php b/apps/files_trashbin/l10n/ru_RU.php index 178eb531077..8636e417ecb 100644 --- a/apps/files_trashbin/l10n/ru_RU.php +++ b/apps/files_trashbin/l10n/ru_RU.php @@ -1,18 +1,5 @@ <?php $TRANSLATIONS = array( -"Couldn't delete %s permanently" => "%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/lib/trash.php b/apps/files_trashbin/lib/trash.php index 88c71a75ab0..2d1830a38f1 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 * @@ -62,8 +73,12 @@ class Trashbin { if ( $trashbinSize === false || $trashbinSize < 0 ) { $trashbinSize = self::calculateSize(new \OC\Files\View('/'. $user.'/files_trashbin')); } - + + // 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; @@ -110,13 +125,17 @@ class Trashbin { \OC_FileProxy::$enabled = false; $user = \OCP\User::getUser(); - if ($view->is_dir('files_versions/' . $file_path)) { - $size += self::calculateSize(new \OC\Files\View('/' . $user . '/files_versions/' . $file_path)); - $view->rename('files_versions/' . $file_path, 'files_trashbin/versions/' . $filename . '.d' . $timestamp); - } else if ($versions = \OCA\Files_Versions\Storage::getVersions($user, $file_path)) { + $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 += $view->filesize('files_versions' . $v['path'] . '.v' . $v['version']); - $view->rename('files_versions' . $v['path'] . '.v' . $v['version'], 'files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp); + $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); } } @@ -143,35 +162,38 @@ class Trashbin { 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('files_encryption/keyfiles/' . $file_path); + $keyfile = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/keyfiles/' . $ownerPath); - if ($view->is_dir($keyfile) || $view->file_exists($keyfile . '.key')) { - $user = \OCP\User::getUser(); + if ($rootView->is_dir($keyfile) || $rootView->file_exists($keyfile . '.key')) { // move keyfiles - if ($view->is_dir($keyfile)) { - $size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile)); - $view->rename($keyfile, 'files_trashbin/keyfiles/' . $filename . '.d' . $timestamp); + 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 += $view->filesize($keyfile . '.key'); - $view->rename($keyfile . '.key', 'files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp); + $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('files_encryption/share-keys/' . $file_path); + $sharekeys = \OC\Files\Filesystem::normalizePath($owner.'/files_encryption/share-keys/' . $ownerPath); - if ($view->is_dir($sharekeys)) { - $size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $sharekeys)); - $view->rename($sharekeys, 'files_trashbin/share-keys/' . $filename . '.d' . $timestamp); + 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 = $view->getLocalFile($sharekeys); + $localShareKeysPath = $rootView->getLocalFile($sharekeys); // handle share-keys $matches = glob(preg_quote($localShareKeysPath).'*.shareKey'); @@ -186,10 +208,10 @@ class Trashbin { if($pathinfo['basename'] == $ownerShareKey) { // calculate size - $size += $view->filesize($sharekeys. '.' . $user. '.shareKey'); + $size += $rootView->filesize($sharekeys. '.' . $user. '.shareKey'); // move file - $view->rename($sharekeys. '.' . $user. '.shareKey', 'files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp); + $rootView->rename($sharekeys. '.' . $user. '.shareKey', $user.'/files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp); } else { // calculate size @@ -266,7 +288,10 @@ class Trashbin { // handle the restore result if( $restoreResult ) { - $view->touch($target.$ext, $mtime); + $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))); @@ -318,6 +343,12 @@ class Trashbin { \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 { @@ -326,15 +357,15 @@ class Trashbin { if ($view->is_dir('/files_trashbin/versions/'.$file)) { $size += 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)); + $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); - $view->rename('files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v); + $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); - $view->rename('files_trashbin/versions/' . $versionedFile . '.v' . $v, 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v); + $rootView->rename($user.'/files_trashbin/versions/' . $versionedFile . '.v' . $v, $owner.'/files_versions/' . $ownerPath . '.v' . $v); } } } @@ -353,7 +384,7 @@ class Trashbin { * @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 $location location of file * @param $timestamp deleteion time * * @return size of restored encrypted file @@ -363,20 +394,25 @@ class Trashbin { $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('files_trashbin/keyfiles/' . $source_location . '/' . $filename); - $sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename); + $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('files_trashbin/keyfiles/' . $filename); - $sharekey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $filename); + $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('files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key'); + $keyfile = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key'); } if ($timestamp) { @@ -387,35 +423,36 @@ class Trashbin { $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - if ($view->file_exists($keyfile)) { + if ($rootView->file_exists($keyfile)) { // handle directory - if ($view->is_dir($keyfile)) { + if ($rootView->is_dir($keyfile)) { // handle keyfiles - $size += self::calculateSize(new \OC\Files\View('/' . $user . '/' . $keyfile)); - $view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext); + $size += self::calculateSize(new \OC\Files\View($keyfile)); + $rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath); // handle share-keys if ($timestamp) { $sharekey .= '.d' . $timestamp; } - $view->rename($sharekey, 'files_encryption/share-keys/' . $location . '/' . $filename . $ext); + $size += self::calculateSize(new \OC\Files\View($sharekey)); + $rootView->rename($sharekey, $owner.'/files_encryption/share-keys/' . $ownerPath); } else { // handle keyfiles - $size += $view->filesize($keyfile); - $view->rename($keyfile, 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext . '.key'); + $size += $rootView->filesize($keyfile); + $rootView->rename($keyfile, $owner.'/files_encryption/keyfiles/' . $ownerPath . '.key'); // handle share-keys - $ownerShareKey = \OC\Files\Filesystem::normalizePath('files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey'); + $ownerShareKey = \OC\Files\Filesystem::normalizePath($user.'/files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user. '.shareKey'); if ($timestamp) { $ownerShareKey .= '.d' . $timestamp; } - $size += $view->filesize($ownerShareKey); + $size += $rootView->filesize($ownerShareKey); // move only owners key - $view->rename($ownerShareKey, 'files_encryption/share-keys/' . $location . '/' . $filename . $ext . '.' . $user. '.shareKey'); + $rootView->rename($ownerShareKey, $owner.'/files_encryption/share-keys/' . $ownerPath . '.' . $user. '.shareKey'); // try to re-share if file is shared $filesystemView = new \OC_FilesystemView('/'); @@ -423,7 +460,7 @@ class Trashbin { $util = new \OCA\Encryption\Util($filesystemView, $user); // fix the file size - $absolutePath = \OC\Files\Filesystem::normalizePath('/' . $user . '/files/'. $location. '/' .$filename); + $absolutePath = \OC\Files\Filesystem::normalizePath('/' . $owner . '/files/'. $ownerPath); $util->fixFileSize($absolutePath); // get current sharing state @@ -472,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); @@ -488,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; } @@ -776,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/l10n/nn_NO.php b/apps/files_versions/l10n/nn_NO.php new file mode 100644 index 00000000000..940cc2371a1 --- /dev/null +++ b/apps/files_versions/l10n/nn_NO.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"Could not revert: %s" => "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/templates/history.php b/apps/files_versions/templates/history.php index f7284439041..3a6d5f0c9e7 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('<strong>File: '.OC_Util::sanitizeHTML($_['path'])).'</strong><br>'; - print_unescaped('<strong>'.OC_Util::sanitizeHTML($_['message']) ).'</strong><br>'; + if( isset($_['path'] ) ) print_unescaped('<strong>File: '.OC_Util::sanitizeHTML($_['path']).'</strong><br>'); + print_unescaped('<strong>'.OC_Util::sanitizeHTML($_['message']) .'</strong><br>'); }else{ if( isset( $_['outcome_stat'] ) ) { - print_unescaped( '<div id="feedback-messages" class="'.OC_Util::sanitizeHTML($_['outcome_stat']).'"><h3>'.OC_Util::sanitizeHTML($_['outcome_msg']) ).'</h3></div><br>'; + print_unescaped( '<div id="feedback-messages" class="'.OC_Util::sanitizeHTML($_['outcome_stat']).'"><h3>'.OC_Util::sanitizeHTML($_['outcome_msg']).'</h3></div><br>'); } - print_unescaped( '<strong>Versions of '.OC_Util::sanitizeHTML($_['path']) ).'</strong><br>'; + print_unescaped( '<strong>Versions of '.OC_Util::sanitizeHTML($_['path']).'</strong><br>'); print_unescaped('<p><em>'.OC_Util::sanitizeHTML($l->t('Revert a file to a previous version by clicking on its revert button')).'</em></p><br />'); 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 00000000000..5dab39839b6 --- /dev/null +++ b/apps/user_ldap/ajax/clearMappings.php @@ -0,0 +1,35 @@ +<?php + +/** + * ownCloud - user_ldap + * + * @author Arthur Schiwon + * @copyright 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 + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +// 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/css/settings.css b/apps/user_ldap/css/settings.css index 84ada0832ab..185952e14bb 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/js/settings.js b/apps/user_ldap/js/settings.js index 9279dc0203b..f47d49cf222 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -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') + ); + } + } + ); } } @@ -166,6 +186,16 @@ $(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') { diff --git a/apps/user_ldap/l10n/ar.php b/apps/user_ldap/l10n/ar.php index 4d7b7ac4ade..5f8b6b81455 100644 --- a/apps/user_ldap/l10n/ar.php +++ b/apps/user_ldap/l10n/ar.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "فشل الحذف", +"Error" => "خطأ", "Password" => "كلمة المرور", "Help" => "المساعدة" ); diff --git a/apps/user_ldap/l10n/bg_BG.php b/apps/user_ldap/l10n/bg_BG.php index c064534a6b8..0330046d80e 100644 --- a/apps/user_ldap/l10n/bg_BG.php +++ b/apps/user_ldap/l10n/bg_BG.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Error" => "Грешка", "Password" => "Парола", "Help" => "Помощ" ); diff --git a/apps/user_ldap/l10n/bn_BD.php b/apps/user_ldap/l10n/bn_BD.php index 69dfc896179..4cee35777df 100644 --- a/apps/user_ldap/l10n/bn_BD.php +++ b/apps/user_ldap/l10n/bn_BD.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"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/ca.php b/apps/user_ldap/l10n/ca.php index 8f2799b6e68..7f0849b2382 100644 --- a/apps/user_ldap/l10n/ca.php +++ b/apps/user_ldap/l10n/ca.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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?", @@ -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 c5d77026b9e..dd7373eb690 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 index 335e2109c2d..abe2336b2ba 100644 --- a/apps/user_ldap/l10n/cy_GB.php +++ b/apps/user_ldap/l10n/cy_GB.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 9329c4e8a24..0a77f466479 100644 --- a/apps/user_ldap/l10n/da.php +++ b/apps/user_ldap/l10n/da.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 27f5adb8b6c..f0010818421 100644 --- a/apps/user_ldap/l10n/de.php +++ b/apps/user_ldap/l10n/de.php @@ -7,6 +7,8 @@ "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" => "Erfolgreich", +"Error" => "Fehler", "Connection test succeeded" => "Verbindungstest erfolgreich", "Connection test failed" => "Verbindungstest fehlgeschlagen", "Do you really want to delete the current Server Configuration?" => "Möchtest Du die aktuelle Serverkonfiguration wirklich löschen?", diff --git a/apps/user_ldap/l10n/de_DE.php b/apps/user_ldap/l10n/de_DE.php index 488d8aad7c8..e22c5b5bdd9 100644 --- a/apps/user_ldap/l10n/de_DE.php +++ b/apps/user_ldap/l10n/de_DE.php @@ -7,6 +7,8 @@ "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 aktuelle Serverkonfiguration wirklich löschen?", @@ -70,6 +72,8 @@ "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.", "Test Configuration" => "Testkonfiguration", "Help" => "Hilfe" ); diff --git a/apps/user_ldap/l10n/el.php b/apps/user_ldap/l10n/el.php index e5fe6b6da7e..acecf27125f 100644 --- a/apps/user_ldap/l10n/el.php +++ b/apps/user_ldap/l10n/el.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/eo.php b/apps/user_ldap/l10n/eo.php index 3ffcbddb3e3..c9a9ba130c6 100644 --- a/apps/user_ldap/l10n/eo.php +++ b/apps/user_ldap/l10n/eo.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 098e16a5d13..31d43288e5b 100644 --- a/apps/user_ldap/l10n/es.php +++ b/apps/user_ldap/l10n/es.php @@ -1,17 +1,21 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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", -"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Advertencia:</b> 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.", +"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Advertencia:</b> 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.", "<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Advertencia:</b> 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 c8aec0cd41b..98fb32b1d26 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 9a65455ed23..39346def039 100644 --- a/apps/user_ldap/l10n/et_EE.php +++ b/apps/user_ldap/l10n/et_EE.php @@ -1,14 +1,18 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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", "<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Hoiatus:</b> 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.", @@ -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 5e9fd014c64..42f184e5390 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 89fc40af4f1..bef13457adb 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?" => "آیا واقعا می خواهید پیکربندی کنونی سرور را حذف کنید؟", diff --git a/apps/user_ldap/l10n/fi_FI.php b/apps/user_ldap/l10n/fi_FI.php index 38ecb5d82a8..38a8b99cf7e 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 ea07bd4a11c..11f8fbaaf44 100644 --- a/apps/user_ldap/l10n/fr.php +++ b/apps/user_ldap/l10n/fr.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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 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.", @@ -6,9 +7,12 @@ "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", "<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Avertissement:</b> 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 215d518e7a5..3f44ccd9bd1 100644 --- a/apps/user_ldap/l10n/gl.php +++ b/apps/user_ldap/l10n/gl.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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.", @@ -7,6 +8,9 @@ "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 97259a0ddd5..0d60768dcfc 100644 --- a/apps/user_ldap/l10n/he.php +++ b/apps/user_ldap/l10n/he.php @@ -2,6 +2,7 @@ "Deletion failed" => "מחיקה נכשלה", "Keep settings?" => "האם לשמור את ההגדרות?", "Cannot add server configuration" => "לא ניתן להוסיף את הגדרות השרת", +"Error" => "שגיאה", "Connection test succeeded" => "בדיקת החיבור עברה בהצלחה", "Connection test failed" => "בדיקת החיבור נכשלה", "Do you really want to delete the current Server Configuration?" => "האם אכן למחוק את הגדרות השרת הנוכחיות?", diff --git a/apps/user_ldap/l10n/hr.php b/apps/user_ldap/l10n/hr.php index 005a76d4bbc..cc8918301f5 100644 --- a/apps/user_ldap/l10n/hr.php +++ b/apps/user_ldap/l10n/hr.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Error" => "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 a82a64ab32f..a06d0bd5355 100644 --- a/apps/user_ldap/l10n/hu_HU.php +++ b/apps/user_ldap/l10n/hu_HU.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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,7 @@ "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", "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 38374abda7f..624fd4fa0eb 100644 --- a/apps/user_ldap/l10n/ia.php +++ b/apps/user_ldap/l10n/ia.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Error" => "Error", "Password" => "Contrasigno", "Help" => "Adjuta" ); diff --git a/apps/user_ldap/l10n/id.php b/apps/user_ldap/l10n/id.php index 5f76d6b99fb..c04d09fc671 100644 --- a/apps/user_ldap/l10n/id.php +++ b/apps/user_ldap/l10n/id.php @@ -7,6 +7,8 @@ "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?", diff --git a/apps/user_ldap/l10n/is.php b/apps/user_ldap/l10n/is.php index 29bc7692795..dadac9eedaa 100644 --- a/apps/user_ldap/l10n/is.php +++ b/apps/user_ldap/l10n/is.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Error" => "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 a2790fd1dec..48bcbdf589a 100644 --- a/apps/user_ldap/l10n/it.php +++ b/apps/user_ldap/l10n/it.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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 8239ecf3cc9..53fa9ae697d 100644 --- a/apps/user_ldap/l10n/ja_JP.php +++ b/apps/user_ldap/l10n/ja_JP.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "マッピングのクリアに失敗しました。", "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の属性を指定してください。", +"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 b3f6058a0ca..8057f7c8455 100644 --- a/apps/user_ldap/l10n/ka_GE.php +++ b/apps/user_ldap/l10n/ka_GE.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/ko.php b/apps/user_ldap/l10n/ko.php index 8aa9fe74b3d..b8196e09d09 100644 --- a/apps/user_ldap/l10n/ko.php +++ b/apps/user_ldap/l10n/ko.php @@ -1,6 +1,7 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "삭제 실패", "Keep settings?" => "설정을 유지합니까?", +"Error" => "오류", "Connection test succeeded" => "연결 시험 성공", "Connection test failed" => "연결 시험 실패", "<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>경고:</b> user_ldap 앱과 user_webdavauth 앱은 호환되지 않습니다. 오동작을 일으킬 수 있으므로, 시스템 관리자에게 요청하여 둘 중 하나만 사용하도록 하십시오.", diff --git a/apps/user_ldap/l10n/ku_IQ.php b/apps/user_ldap/l10n/ku_IQ.php index f8f893834b1..00602ae5d79 100644 --- a/apps/user_ldap/l10n/ku_IQ.php +++ b/apps/user_ldap/l10n/ku_IQ.php @@ -1,4 +1,6 @@ <?php $TRANSLATIONS = array( +"Success" => "سهرکهوتن", +"Error" => "ههڵه", "Password" => "وشەی تێپەربو", "Help" => "یارمەتی" ); diff --git a/apps/user_ldap/l10n/lb.php b/apps/user_ldap/l10n/lb.php index 39ed627ce2c..cf58c9ec5be 100644 --- a/apps/user_ldap/l10n/lb.php +++ b/apps/user_ldap/l10n/lb.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 aa21dd2d3c1..6f396847b8e 100644 --- a/apps/user_ldap/l10n/lt_LT.php +++ b/apps/user_ldap/l10n/lt_LT.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 50126664e5b..73ffedcb134 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 7d34ff49492..6a060aca415 100644 --- a/apps/user_ldap/l10n/mk.php +++ b/apps/user_ldap/l10n/mk.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "Бришењето е неуспешно", +"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 88ed18346ca..b3004028c58 100644 --- a/apps/user_ldap/l10n/ms_MY.php +++ b/apps/user_ldap/l10n/ms_MY.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 c4700245f24..f8cdf694ff6 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 7973c66cd10..c935d387ccc 100644 --- a/apps/user_ldap/l10n/nl.php +++ b/apps/user_ldap/l10n/nl.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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,13 @@ "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", +"Internal Username Attribute:" => "Interne gebruikersnaam attribuut:", +"Override UUID detection" => "Negeren UUID detectie", +"UUID Attribute:" => "UUID Attribuut:", +"Username-LDAP User Mapping" => "Gebruikersnaam-LDAP gebruikers vertaling", +"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 9f84258c56d..45910022803 100644 --- a/apps/user_ldap/l10n/nn_NO.php +++ b/apps/user_ldap/l10n/nn_NO.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 49b6c5970cc..95ab51caadd 100644 --- a/apps/user_ldap/l10n/oc.php +++ b/apps/user_ldap/l10n/oc.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 a5b620e48ba..29c814b5fb2 100644 --- a/apps/user_ldap/l10n/pl.php +++ b/apps/user_ldap/l10n/pl.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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.", @@ -7,6 +8,9 @@ "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,11 @@ "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", "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 a728ea15fde..22247b81005 100644 --- a/apps/user_ldap/l10n/pt_BR.php +++ b/apps/user_ldap/l10n/pt_BR.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Failed to clear the mappings." => "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 02b03d5a752..ed1e0f376db 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?", diff --git a/apps/user_ldap/l10n/ro.php b/apps/user_ldap/l10n/ro.php index 8f55a35b491..260ee610639 100644 --- a/apps/user_ldap/l10n/ro.php +++ b/apps/user_ldap/l10n/ro.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "Ștergerea a eșuat", +"Success" => "Succes", +"Error" => "Eroare", "<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Atentie:</b> 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.", "<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Atenție</b> 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 0746e1e8929..eed6d373b9f 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 a4ed503b1d1..7b6833ebf8c 100644 --- a/apps/user_ldap/l10n/ru_RU.php +++ b/apps/user_ldap/l10n/ru_RU.php @@ -1,42 +1,4 @@ <?php $TRANSLATIONS = array( -"Deletion failed" => "Удаление не удалось", -"<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Предупреждение:</b> Приложения user_ldap и user_webdavauth несовместимы. Вы можете столкнуться с неожиданным поведением системы. Пожалуйста, обратитесь к системному администратору для отключения одного из них.", -"<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Предупреждение:</b> Модуль 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 50124e4d54f..1d81b341b46 100644 --- a/apps/user_ldap/l10n/si_LK.php +++ b/apps/user_ldap/l10n/si_LK.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "මකාදැමීම අසාර්ථකයි", +"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 cb55762e64f..b31fe377563 100644 --- a/apps/user_ldap/l10n/sk_SK.php +++ b/apps/user_ldap/l10n/sk_SK.php @@ -7,6 +7,8 @@ "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", +"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?", diff --git a/apps/user_ldap/l10n/sl.php b/apps/user_ldap/l10n/sl.php index 8ff1fd53440..1ade5d9b733 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 24fd869057d..12324b9f966 100644 --- a/apps/user_ldap/l10n/sq.php +++ b/apps/user_ldap/l10n/sq.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Error" => "Veprim i gabuar", "Password" => "Kodi", "Help" => "Ndihmë" ); diff --git a/apps/user_ldap/l10n/sr.php b/apps/user_ldap/l10n/sr.php index 52569a08ef8..b94bc83e1e4 100644 --- a/apps/user_ldap/l10n/sr.php +++ b/apps/user_ldap/l10n/sr.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "Брисање није успело", +"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/sv.php b/apps/user_ldap/l10n/sv.php index 1bb4d9dc0b1..eb30bd22f01 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 f6beb3c4863..997f09ca877 100644 --- a/apps/user_ldap/l10n/ta_LK.php +++ b/apps/user_ldap/l10n/ta_LK.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "நீக்கம் தோல்வியடைந்தது", +"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 d9a3e713f03..3f047631cf7 100644 --- a/apps/user_ldap/l10n/te.php +++ b/apps/user_ldap/l10n/te.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Error" => "పొరపాటు", "Password" => "సంకేతపదం", "Help" => "సహాయం" ); diff --git a/apps/user_ldap/l10n/th_TH.php b/apps/user_ldap/l10n/th_TH.php index 802badb2f03..ec279ba01e5 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 6f75f4371db..3835c72313a 100644 --- a/apps/user_ldap/l10n/tr.php +++ b/apps/user_ldap/l10n/tr.php @@ -1,15 +1,16 @@ <?php $TRANSLATIONS = array( -"Failed to delete the server configuration" => "Sunucu uyunlama basarmadi ", -"The configuration is valid and the connection could be established!" => "Uyunlama mantikli ve baglama yerlestirmek edebilmi.", -"The configuration is valid, but the Bind failed. Please check the server settings and credentials." => "Uyunlama gecerli, fakat Baglama yapamadi. Lutfen kontrol yapmak, eger bu iyi yerlertirdi. ", -"The configuration is invalid. Please look in the ownCloud log for further details." => "Uyunma mantikli degil. Lutfen log daha kontrol yapmak. ", +"Failed to delete the server configuration" => "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", -"Take over settings from recent server configuration?" => "Parametri sonadan uyunlama cikarmak mi?", -"Keep settings?" => "Ayarları kalsınmı?", -"Cannot add server configuration" => "Sunucu uyunlama birlemek edemen. ", +"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?" => "Hakikatten, Sonuncu Funksyon durmak istiyor mi?", +"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", "<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them." => "<b>Uyari </b> Apps kullanici_Idap ve user_webdavauth uyunmayan. Bu belki sik degil. Lutfen sistem yonetici sormak on aktif yapmaya. ", "<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "<b>Ihbar <b> Modulu PHP LDAP yuklemdi degil, backend calismacak. Lutfen sistem yonetici sormak yuklemek icin.", diff --git a/apps/user_ldap/l10n/ug.php b/apps/user_ldap/l10n/ug.php index 05a7a3f9a06..8634cdbe1be 100644 --- a/apps/user_ldap/l10n/ug.php +++ b/apps/user_ldap/l10n/ug.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "ئۆچۈرۈش مەغلۇپ بولدى", +"Error" => "خاتالىق", "Host" => "باش ئاپپارات", "Password" => "ئىم", "User Login Filter" => "ئىشلەتكۈچى تىزىمغا كىرىش سۈزگۈچى", diff --git a/apps/user_ldap/l10n/uk.php b/apps/user_ldap/l10n/uk.php index 623d34c98e6..f92c6d5894e 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 4c606a13808..83570a596a2 100644 --- a/apps/user_ldap/l10n/ur_PK.php +++ b/apps/user_ldap/l10n/ur_PK.php @@ -1,4 +1,5 @@ <?php $TRANSLATIONS = array( +"Error" => "ایرر", "Password" => "پاسورڈ", "Help" => "مدد" ); diff --git a/apps/user_ldap/l10n/vi.php b/apps/user_ldap/l10n/vi.php index 4bbb977f363..7e598225926 100644 --- a/apps/user_ldap/l10n/vi.php +++ b/apps/user_ldap/l10n/vi.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "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 f5bc41fd46b..6c60ec64e27 100644 --- a/apps/user_ldap/l10n/zh_CN.GB2312.php +++ b/apps/user_ldap/l10n/zh_CN.GB2312.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "删除失败", +"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 1911734805f..7b8389227ae 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 190e4eba798..ba55c414790 100644 --- a/apps/user_ldap/l10n/zh_HK.php +++ b/apps/user_ldap/l10n/zh_HK.php @@ -1,4 +1,6 @@ <?php $TRANSLATIONS = array( +"Success" => "成功", +"Error" => "錯誤", "Password" => "密碼", "Port" => "連接埠", "Help" => "幫助" diff --git a/apps/user_ldap/l10n/zh_TW.php b/apps/user_ldap/l10n/zh_TW.php index 9a12bad0747..d01e75356c0 100644 --- a/apps/user_ldap/l10n/zh_TW.php +++ b/apps/user_ldap/l10n/zh_TW.php @@ -1,5 +1,7 @@ <?php $TRANSLATIONS = array( "Deletion failed" => "移除失敗", +"Success" => "成功", +"Error" => "錯誤", "Host" => "主機", "Password" => "密碼", "Port" => "連接阜", diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index ad355ce5e24..a7611eb3e84 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -317,7 +317,19 @@ abstract class Access { } $ldapname = $ldapname[0]; } - $intname = $isUser ? $this->sanitizeUsername($uuid) : $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 @@ -897,6 +909,12 @@ abstract class Access { return true; } + $fixedAttribute = $this->connection->ldapExpertUUIDAttr; + if(!empty($fixedAttribute)) { + $this->connection->ldapUuidAttribute = $fixedAttribute; + return true; + } + //for now, supported (known) attributes are entryUUID, nsuniqueid, objectGUID $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid'); diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index 88ff318586a..ba4de135341 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, ); /** @@ -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; } @@ -505,6 +512,10 @@ class Connection { $configurationOK = false; } + if(!empty($this->config['ldapExpertUUIDAttr'])) { + $this->config['ldapUuidAttribute'] = $this->config['ldapExpertUUIDAttr']; + } + return $configurationOK; } @@ -543,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' => '', ); } diff --git a/apps/user_ldap/lib/helper.php b/apps/user_ldap/lib/helper.php index 8bebd84c12e..07d13a806a6 100644 --- a/apps/user_ldap/lib/helper.php +++ b/apps/user_ldap/lib/helper.php @@ -102,4 +102,29 @@ class Helper { 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; + } + + $query = \OCP\DB::prepare('TRUNCATE '.$table); + $res = $query->execute(); + + if(\OCP\DB::isError($res)) { + return false; + } + + return true; + } } diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index 05497ae8a33..22e2dac6d26 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 f0ee8c6b08a..972970aa3ef 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -3,6 +3,7 @@ <ul> <li><a href="#ldapSettings-1">LDAP Basic</a></li> <li><a href="#ldapSettings-2">Advanced</a></li> + <li><a href="#ldapSettings-3">Expert</a></li> </ul> <?php if(OCP\App::isEnabled('user_webdavauth')) { print_unescaped('<p class="ldapwarning">'.$l->t('<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them.').'</p>'); @@ -96,6 +97,17 @@ </div> </div> </fieldset> + <fieldset id="ldapSettings-3"> + <p><strong><?php p($l->t('Internal Username'));?></strong></p> + <p class="ldapIndent"><?php p($l->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.'));?></p> + <p class="ldapIndent"><label for="ldap_expert_username_attr"><?php p($l->t('Internal Username Attribute:'));?></label><input type="text" id="ldap_expert_username_attr" name="ldap_expert_username_attr" data-default="<?php p($_['ldap_expert_username_attr_default']); ?>" /></p> + <p><strong><?php p($l->t('Override UUID detection'));?></strong></p> + <p class="ldapIndent"><?php p($l->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.'));?></p> + <p class="ldapIndent"><label for="ldap_expert_uuid_attr"><?php p($l->t('UUID Attribute:'));?></label><input type="text" id="ldap_expert_uuid_attr" name="ldap_expert_uuid_attr" data-default="<?php p($_['ldap_expert_uuid_attr_default']); ?>" /></p> + <p><strong><?php p($l->t('Username-LDAP User Mapping'));?></strong></p> + <p class="ldapIndent"><?php p($l->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.'));?></p> + <p class="ldapIndent"><button id="ldap_action_clear_user_mappings" name="ldap_action_clear_user_mappings"><?php p($l->t('Clear Username-LDAP User Mapping'));?></button><br/><button id="ldap_action_clear_group_mappings" name="ldap_action_clear_group_mappings"><?php p($l->t('Clear Groupname-LDAP Group Mapping'));?></button></p> + </fieldset> <input id="ldap_submit" type="submit" value="Save" /> <button id="ldap_action_test_connection" name="ldap_action_test_connection"><?php p($l->t('Test Configuration'));?></button> <a href="http://doc.owncloud.org/server/5.0/admin_manual/auth_ldap.html" target="_blank"><img src="<?php print_unescaped(OCP\Util::imagePath('', 'actions/info.png')); ?>" style="height:1.75ex" /> <?php p($l->t('Help'));?></a> </div> diff --git a/apps/user_ldap/user_proxy.php b/apps/user_ldap/user_proxy.php index 7e5b9045df3..73cc0963182 100644 --- a/apps/user_ldap/user_proxy.php +++ b/apps/user_ldap/user_proxy.php @@ -174,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; |