diff options
110 files changed, 2456 insertions, 1387 deletions
diff --git a/.jshintrc b/.jshintrc index f40dd22b5fd..90cec5c5961 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,5 +1,5 @@ { - "camelCase": true, + "camelcase": true, "eqeqeq": true, "immed": true, "latedef": false, diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 00000000000..4473cf9056d --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,23 @@ +filter: + excluded_paths: + - '3rdparty/*' + - 'apps/*/3rdparty/*' + - 'l10n/*' + - 'core/l10n/*' + - 'apps/*/l10n/*' + - 'lib/l10n/*' + - 'core/js/tests/lib/*.js' + - 'core/js/jquery-1.10.0.min.js' + - 'core/js/jquery-migrate-1.2.1.min.js' + - 'core/js/jquery-showpassword.js' + - 'core/js/jquery-tipsy.js' + - 'core/js/jquery.infieldlabel.js' + - 'core/js/jquery-ui-1.10.0.custom.js' + - 'core/js/jquery.inview.js' + - 'core/js/jquery.placeholder.js' + + +imports: + - javascript + - php + diff --git a/3rdparty b/3rdparty -Subproject 478de4b756f3729f762d838b29f69f2a40e5f4f +Subproject 184f0a59f87c590ee7e89ced401205a87f21395 diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php index 69f859daa97..99f49188384 100644 --- a/apps/files/ajax/delete.php +++ b/apps/files/ajax/delete.php @@ -9,7 +9,7 @@ OCP\JSON::callCheck(); // Get data $dir = stripslashes($_POST["dir"]); $files = isset($_POST["file"]) ? $_POST["file"] : $_POST["files"]; -$allFiles = isset($_POST["allfiles"]) ? $_POST["allfiles"] : $_POST["allfiles"]; +$allFiles = isset($_POST["allfiles"]) ? $_POST["allfiles"] : false; if ($allFiles === 'true') { $allFiles = true; } diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php index 93063e52eb0..04a260265c2 100644 --- a/apps/files/ajax/move.php +++ b/apps/files/ajax/move.php @@ -18,7 +18,7 @@ if(\OC\Files\Filesystem::file_exists($target . '/' . $file)) { exit; } -if ($dir != '' || $file != 'Shared') { +if ($target != '' || strtolower($file) != 'shared') { $targetFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file); $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) { diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index 40da32b223a..89c21a172fc 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -1,12 +1,12 @@ <?php // only need filesystem apps -$RUNTIME_APPTYPES=array('filesystem'); +$RUNTIME_APPTYPES = array('filesystem'); OCP\JSON::checkLoggedIn(); // Load the files -$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; +$dir = isset($_GET['dir']) ? $_GET['dir'] : ''; $mimetypes = isset($_GET['mimetypes']) ? json_decode($_GET['mimetypes'], true) : ''; // Clean up duplicates from array and deal with non-array requests @@ -18,43 +18,40 @@ if (is_array($mimetypes)) { // make filelist $files = array(); +/** + * @var \OCP\Files\FileInfo[] $files + */ // If a type other than directory is requested first load them. -if($mimetypes && !in_array('httpd/unix-directory', $mimetypes)) { - foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, 'httpd/unix-directory' ) as $file ) { - $file['directory'] = $dir; - $file['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file['mimetype']); - $file["date"] = OCP\Util::formatDate($file["mtime"]); - $file['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file); - $files[] = $file; - } +if ($mimetypes && !in_array('httpd/unix-directory', $mimetypes)) { + $files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir, 'httpd/unix-directory')); } if (is_array($mimetypes) && count($mimetypes)) { foreach ($mimetypes as $mimetype) { - foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, $mimetype ) as $file ) { - $file['directory'] = $dir; - $file['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file['mimetype']); - $file["date"] = OCP\Util::formatDate($file["mtime"]); - $file['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file); - $files[] = $file; - } + $files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir, $mimetype)); } } else { - foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $file ) { - $file['directory'] = $dir; - $file['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file['mimetype']); - $file["date"] = OCP\Util::formatDate($file["mtime"]); - $file['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file); - $files[] = $file; - } + $files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir)); +} + +$result = array(); +foreach ($files as $file) { + $fileData = array(); + $fileData['directory'] = $dir; + $fileData['name'] = $file->getName(); + $fileData['type'] = $file->getType(); + $fileData['path'] = $file['path']; + $fileData['id'] = $file->getId(); + $fileData['size'] = $file->getSize(); + $fileData['mtime'] = $file->getMtime(); + $fileData['mimetype'] = $file->getMimetype(); + $fileData['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file->getMimetype()); + $fileData["date"] = OCP\Util::formatDate($file->getMtime()); + $fileData['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file); + $result[] = $fileData; } // Sort by name -usort($files, function ($a, $b) { - if ($a['name'] === $b['name']) { - return 0; - } - return ($a['name'] < $b['name']) ? -1 : 1; -}); +usort($result, array('\OCA\Files\Helper', 'fileCmp')); -OC_JSON::success(array('data' => $files)); +OC_JSON::success(array('data' => $result)); diff --git a/apps/files/js/admin.js b/apps/files/js/admin.js index f735079fcbe..842b73c0cae 100644 --- a/apps/files/js/admin.js +++ b/apps/files/js/admin.js @@ -8,19 +8,22 @@ * */ -function switchPublicFolder() -{ +function switchPublicFolder() { var publicEnable = $('#publicEnable').is(':checked'); - var sharingaimGroup = $('input:radio[name=sharingaim]'); //find all radiobuttons of that group + // find all radiobuttons of that group + var sharingaimGroup = $('input:radio[name=sharingaim]'); $.each(sharingaimGroup, function(index, sharingaimItem) { - sharingaimItem.disabled = !publicEnable; //set all buttons to the correct state + // set all buttons to the correct state + sharingaimItem.disabled = !publicEnable; }); } -$(document).ready(function(){ - switchPublicFolder(); // Execute the function after loading DOM tree - $('#publicEnable').click(function(){ - switchPublicFolder(); // To get rid of onClick() +$(document).ready(function() { + // Execute the function after loading DOM tree + switchPublicFolder(); + $('#publicEnable').click(function() { + // To get rid of onClick() + switchPublicFolder(); }); $('#allowZipDownload').bind('change', function() { diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index f962a7044a8..ea4061c9b0b 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -82,7 +82,7 @@ OC.Upload = { */ isProcessing:function() { var count = 0; - + jQuery.each(this._uploads,function(i, data) { if (data.state() === 'pending') { count++; @@ -177,549 +177,556 @@ OC.Upload = { checkExistingFiles: function (selection, callbacks) { // TODO check filelist before uploading and show dialog on conflicts, use callbacks callbacks.onNoConflicts(selection); - } -}; + }, -$(document).ready(function() { + init: function() { + if ( $('#file_upload_start').exists() && $('#file_upload_start').is(':visible')) { - if ( $('#file_upload_start').exists() ) { - - var file_upload_param = { - dropZone: $('#content'), // restrict dropZone to content div - autoUpload: false, - sequentialUploads: true, - //singleFileUploads is on by default, so the data.files array will always have length 1 - /** - * on first add of every selection - * - check all files of originalFiles array with files in dir - * - on conflict show dialog - * - skip all -> remember as single skip action for all conflicting files - * - replace all -> remember as single replace action for all conflicting files - * - choose -> show choose dialog - * - mark files to keep - * - when only existing -> remember as single skip action - * - when only new -> remember as single replace action - * - when both -> remember as single autorename action - * - start uploading selection - * @param {object} e - * @param {object} data - * @returns {boolean} - */ - add: function(e, data) { - OC.Upload.log('add', e, data); - var that = $(this); - - // we need to collect all data upload objects before starting the upload so we can check their existence - // and set individual conflict actions. unfortunately there is only one variable that we can use to identify - // the selection a data upload is part of, so we have to collect them in data.originalFiles - // turning singleFileUploads off is not an option because we want to gracefully handle server errors like - // already exists - - // create a container where we can store the data objects - if ( ! data.originalFiles.selection ) { - // initialize selection and remember number of files to upload - data.originalFiles.selection = { - uploads: [], - filesToUpload: data.originalFiles.length, - totalBytes: 0 - }; - } - var selection = data.originalFiles.selection; - - // add uploads - if ( selection.uploads.length < selection.filesToUpload ) { - // remember upload - selection.uploads.push(data); - } - - //examine file - var file = data.files[0]; - try { - // FIXME: not so elegant... need to refactor that method to return a value - Files.isFileNameValid(file.name); - } - catch (errorMessage) { - data.textStatus = 'invalidcharacters'; - data.errorThrown = errorMessage; - } - - if (file.type === '' && file.size === 4096) { - data.textStatus = 'dirorzero'; - data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', - {filename: file.name} - ); - } - - // add size - selection.totalBytes += file.size; - - // check PHP upload limit - if (selection.totalBytes > $('#upload_limit').val()) { - data.textStatus = 'sizeexceedlimit'; - data.errorThrown = t('files', 'Total file size {size1} exceeds upload limit {size2}', { - 'size1': humanFileSize(selection.totalBytes), - 'size2': humanFileSize($('#upload_limit').val()) - }); - } + var file_upload_param = { + dropZone: $('#content'), // restrict dropZone to content div + autoUpload: false, + sequentialUploads: true, + //singleFileUploads is on by default, so the data.files array will always have length 1 + /** + * on first add of every selection + * - check all files of originalFiles array with files in dir + * - on conflict show dialog + * - skip all -> remember as single skip action for all conflicting files + * - replace all -> remember as single replace action for all conflicting files + * - choose -> show choose dialog + * - mark files to keep + * - when only existing -> remember as single skip action + * - when only new -> remember as single replace action + * - when both -> remember as single autorename action + * - start uploading selection + * @param {object} e + * @param {object} data + * @returns {boolean} + */ + add: function(e, data) { + OC.Upload.log('add', e, data); + var that = $(this); + var freeSpace; - // check free space - if (selection.totalBytes > $('#free_space').val()) { - data.textStatus = 'notenoughspace'; - data.errorThrown = t('files', 'Not enough free space, you are uploading {size1} but only {size2} is left', { - 'size1': humanFileSize(selection.totalBytes), - 'size2': humanFileSize($('#free_space').val()) - }); - } - - // end upload for whole selection on error - if (data.errorThrown) { - // trigger fileupload fail - var fu = that.data('blueimp-fileupload') || that.data('fileupload'); - fu._trigger('fail', e, data); - return false; //don't upload anything - } + // we need to collect all data upload objects before starting the upload so we can check their existence + // and set individual conflict actions. unfortunately there is only one variable that we can use to identify + // the selection a data upload is part of, so we have to collect them in data.originalFiles + // turning singleFileUploads off is not an option because we want to gracefully handle server errors like + // already exists - // check existing files when all is collected - if ( selection.uploads.length >= selection.filesToUpload ) { - - //remove our selection hack: - delete data.originalFiles.selection; - - var callbacks = { - - onNoConflicts: function (selection) { - $.each(selection.uploads, function(i, upload) { - upload.submit(); - }); - }, - onSkipConflicts: function (selection) { - //TODO mark conflicting files as toskip - }, - onReplaceConflicts: function (selection) { - //TODO mark conflicting files as toreplace - }, - onChooseConflicts: function (selection) { - //TODO mark conflicting files as chosen - }, - onCancel: function (selection) { - $.each(selection.uploads, function(i, upload) { - upload.abort(); - }); - } - }; + // create a container where we can store the data objects + if ( ! data.originalFiles.selection ) { + // initialize selection and remember number of files to upload + data.originalFiles.selection = { + uploads: [], + filesToUpload: data.originalFiles.length, + totalBytes: 0 + }; + } + var selection = data.originalFiles.selection; - OC.Upload.checkExistingFiles(selection, callbacks); - - } + // add uploads + if ( selection.uploads.length < selection.filesToUpload ) { + // remember upload + selection.uploads.push(data); + } - return true; // continue adding files - }, - /** - * called after the first add, does NOT have the data param - * @param {object} e - */ - start: function(e) { - OC.Upload.log('start', e, null); - }, - submit: function(e, data) { - OC.Upload.rememberUpload(data); - if ( ! data.formData ) { - // noone set update parameters, we set the minimum - data.formData = { - requesttoken: oc_requesttoken, - dir: $('#dir').val() - }; - } - }, - fail: function(e, data) { - OC.Upload.log('fail', e, data); - if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { - if (data.textStatus === 'abort') { - OC.Notification.show(t('files', 'Upload cancelled.')); - } else { - // HTTP connection problem - OC.Notification.show(data.errorThrown); - if (data.result) { - var result = JSON.parse(data.result); - if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') { - // abort upload of next files if any - OC.Upload.cancelUploads(); + //examine file + var file = data.files[0]; + try { + // FIXME: not so elegant... need to refactor that method to return a value + Files.isFileNameValid(file.name, FileList.getCurrentDirectory()); + } + catch (errorMessage) { + data.textStatus = 'invalidcharacters'; + data.errorThrown = errorMessage; + } + + if (file.type === '' && file.size === 4096) { + data.textStatus = 'dirorzero'; + data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', + {filename: file.name} + ); + } + + // add size + selection.totalBytes += file.size; + + // check PHP upload limit + if (selection.totalBytes > $('#upload_limit').val()) { + data.textStatus = 'sizeexceedlimit'; + data.errorThrown = t('files', 'Total file size {size1} exceeds upload limit {size2}', { + 'size1': humanFileSize(selection.totalBytes), + 'size2': humanFileSize($('#upload_limit').val()) + }); + } + + // check free space + freeSpace = $('#free_space').val(); + if (freeSpace >= 0 && selection.totalBytes > freeSpace) { + data.textStatus = 'notenoughspace'; + data.errorThrown = t('files', 'Not enough free space, you are uploading {size1} but only {size2} is left', { + 'size1': humanFileSize(selection.totalBytes), + 'size2': humanFileSize($('#free_space').val()) + }); + } + + // end upload for whole selection on error + if (data.errorThrown) { + // trigger fileupload fail + var fu = that.data('blueimp-fileupload') || that.data('fileupload'); + fu._trigger('fail', e, data); + return false; //don't upload anything + } + + // check existing files when all is collected + if ( selection.uploads.length >= selection.filesToUpload ) { + + //remove our selection hack: + delete data.originalFiles.selection; + + var callbacks = { + + onNoConflicts: function (selection) { + $.each(selection.uploads, function(i, upload) { + upload.submit(); + }); + }, + onSkipConflicts: function (selection) { + //TODO mark conflicting files as toskip + }, + onReplaceConflicts: function (selection) { + //TODO mark conflicting files as toreplace + }, + onChooseConflicts: function (selection) { + //TODO mark conflicting files as chosen + }, + onCancel: function (selection) { + $.each(selection.uploads, function(i, upload) { + upload.abort(); + }); + } + }; + + OC.Upload.checkExistingFiles(selection, callbacks); + + } + + return true; // continue adding files + }, + /** + * called after the first add, does NOT have the data param + * @param {object} e + */ + start: function(e) { + OC.Upload.log('start', e, null); + }, + submit: function(e, data) { + OC.Upload.rememberUpload(data); + if ( ! data.formData ) { + // noone set update parameters, we set the minimum + data.formData = { + requesttoken: oc_requesttoken, + dir: $('#dir').val() + }; + } + }, + fail: function(e, data) { + OC.Upload.log('fail', e, data); + if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { + if (data.textStatus === 'abort') { + OC.Notification.show(t('files', 'Upload cancelled.')); + } else { + // HTTP connection problem + OC.Notification.show(data.errorThrown); + if (data.result) { + var result = JSON.parse(data.result); + if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') { + // abort upload of next files if any + OC.Upload.cancelUploads(); + } } } + //hide notification after 10 sec + setTimeout(function() { + OC.Notification.hide(); + }, 10000); } - //hide notification after 10 sec - setTimeout(function() { - OC.Notification.hide(); - }, 10000); - } - OC.Upload.deleteUpload(data); - }, - /** - * called for every successful upload - * @param {object} e - * @param {object} data - */ - done:function(e, data) { - OC.Upload.log('done', e, data); - // handle different responses (json or body from iframe for ie) - var response; - if (typeof data.result === 'string') { - response = data.result; - } else { - //fetch response from iframe - response = data.result[0].body.innerText; - } - var result=$.parseJSON(response); - - delete data.jqXHR; - - if (result.status === 'error' && result.data && result.data.message){ - data.textStatus = 'servererror'; - data.errorThrown = result.data.message; - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - } else if (typeof result[0] === 'undefined') { - data.textStatus = 'servererror'; - data.errorThrown = t('files', 'Could not get result from server.'); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - } else if (result[0].status === 'existserror') { - //show "file already exists" dialog - var original = result[0]; - var replacement = data.files[0]; - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); - } else if (result[0].status !== 'success') { - //delete data.jqXHR; - data.textStatus = 'servererror'; - data.errorThrown = result[0].data.message; // error message has been translated on server - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); + OC.Upload.deleteUpload(data); + }, + /** + * called for every successful upload + * @param {object} e + * @param {object} data + */ + done:function(e, data) { + OC.Upload.log('done', e, data); + // handle different responses (json or body from iframe for ie) + var response; + if (typeof data.result === 'string') { + response = data.result; + } else { + //fetch response from iframe + response = data.result[0].body.innerText; + } + var result=$.parseJSON(response); + + delete data.jqXHR; + + if (result.status === 'error' && result.data && result.data.message){ + data.textStatus = 'servererror'; + data.errorThrown = result.data.message; + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); + } else if (typeof result[0] === 'undefined') { + data.textStatus = 'servererror'; + data.errorThrown = t('files', 'Could not get result from server.'); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); + } else if (result[0].status === 'existserror') { + //show "file already exists" dialog + var original = result[0]; + var replacement = data.files[0]; + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); + } else if (result[0].status !== 'success') { + //delete data.jqXHR; + data.textStatus = 'servererror'; + data.errorThrown = result[0].data.message; // error message has been translated on server + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); + } + }, + /** + * called after last upload + * @param {object} e + * @param {object} data + */ + stop: function(e, data) { + OC.Upload.log('stop', e, data); } - }, - /** - * called after last upload - * @param {object} e - * @param {object} data - */ - stop: function(e, data) { - OC.Upload.log('stop', e, data); - } - }; + }; - // initialize jquery fileupload (blueimp) - var fileupload = $('#file_upload_start').fileupload(file_upload_param); - window.file_upload_param = fileupload; + // initialize jquery fileupload (blueimp) + var fileupload = $('#file_upload_start').fileupload(file_upload_param); + window.file_upload_param = fileupload; - if (supportAjaxUploadWithProgress()) { + if (supportAjaxUploadWithProgress()) { + + // add progress handlers + fileupload.on('fileuploadadd', function(e, data) { + OC.Upload.log('progress handle fileuploadadd', e, data); + //show cancel button + //if (data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie? + // $('#uploadprogresswrapper input.stop').show(); + //} + }); + // add progress handlers + fileupload.on('fileuploadstart', function(e, data) { + OC.Upload.log('progress handle fileuploadstart', e, data); + $('#uploadprogresswrapper input.stop').show(); + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); + }); + fileupload.on('fileuploadprogress', function(e, data) { + OC.Upload.log('progress handle fileuploadprogress', e, data); + //TODO progressbar in row + }); + fileupload.on('fileuploadprogressall', function(e, data) { + OC.Upload.log('progress handle fileuploadprogressall', e, data); + var progress = (data.loaded / data.total) * 100; + $('#uploadprogressbar').progressbar('value', progress); + }); + fileupload.on('fileuploadstop', function(e, data) { + OC.Upload.log('progress handle fileuploadstop', e, data); - // add progress handlers - fileupload.on('fileuploadadd', function(e, data) { - OC.Upload.log('progress handle fileuploadadd', e, data); - //show cancel button - //if (data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie? - // $('#uploadprogresswrapper input.stop').show(); - //} - }); - // add progress handlers - fileupload.on('fileuploadstart', function(e, data) { - OC.Upload.log('progress handle fileuploadstart', e, data); - $('#uploadprogresswrapper input.stop').show(); - $('#uploadprogressbar').progressbar({value:0}); - $('#uploadprogressbar').fadeIn(); - }); - fileupload.on('fileuploadprogress', function(e, data) { - OC.Upload.log('progress handle fileuploadprogress', e, data); - //TODO progressbar in row - }); - fileupload.on('fileuploadprogressall', function(e, data) { - OC.Upload.log('progress handle fileuploadprogressall', e, data); - var progress = (data.loaded / data.total) * 100; - $('#uploadprogressbar').progressbar('value', progress); - }); - fileupload.on('fileuploadstop', function(e, data) { - OC.Upload.log('progress handle fileuploadstop', e, data); - - $('#uploadprogresswrapper input.stop').fadeOut(); - $('#uploadprogressbar').fadeOut(); - Files.updateStorageStatistics(); - }); - fileupload.on('fileuploadfail', function(e, data) { - OC.Upload.log('progress handle fileuploadfail', e, data); - //if user pressed cancel hide upload progress bar and cancel button - if (data.errorThrown === 'abort') { $('#uploadprogresswrapper input.stop').fadeOut(); $('#uploadprogressbar').fadeOut(); - } - }); - - } else { - console.log('skipping file progress because your browser is broken'); - } - } + Files.updateStorageStatistics(); + }); + fileupload.on('fileuploadfail', function(e, data) { + OC.Upload.log('progress handle fileuploadfail', e, data); + //if user pressed cancel hide upload progress bar and cancel button + if (data.errorThrown === 'abort') { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } + }); - $.assocArraySize = function(obj) { - // http://stackoverflow.com/a/6700/11236 - var size = 0, key; - for (key in obj) { - if (obj.hasOwnProperty(key)) { - size++; + } else { + console.log('skipping file progress because your browser is broken'); } } - return size; - }; - // warn user not to leave the page while upload is in progress - $(window).on('beforeunload', function(e) { - if (OC.Upload.isProcessing()) { - return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.'); - } - }); + $.assocArraySize = function(obj) { + // http://stackoverflow.com/a/6700/11236 + var size = 0, key; + for (key in obj) { + if (obj.hasOwnProperty(key)) { + size++; + } + } + return size; + }; - //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) - if (navigator.userAgent.search(/konqueror/i) === -1) { - $('#file_upload_start').attr('multiple', 'multiple'); - } + // warn user not to leave the page while upload is in progress + $(window).on('beforeunload', function(e) { + if (OC.Upload.isProcessing()) { + return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.'); + } + }); - //if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder - var crumb=$('div.crumb').first(); - while($('div.controls').height() > 40 && crumb.next('div.crumb').length > 0) { - crumb.children('a').text('...'); - crumb = crumb.next('div.crumb'); - } - //if that isn't enough, start removing items from the breacrumb except for the current folder and it's parent - var crumb = $('div.crumb').first(); - var next = crumb.next('div.crumb'); - while($('div.controls').height()>40 && next.next('div.crumb').length > 0) { - crumb.remove(); - crumb = next; - next = crumb.next('div.crumb'); - } - //still not enough, start shorting down the current folder name - var crumb=$('div.crumb>a').last(); - while($('div.controls').height() > 40 && crumb.text().length > 6) { - var text=crumb.text(); - text = text.substr(0,text.length-6)+'...'; - crumb.text(text); - } + //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) + if (navigator.userAgent.search(/konqueror/i) === -1) { + $('#file_upload_start').attr('multiple', 'multiple'); + } - $(document).click(function(ev) { - // do not close when clicking in the dropdown - if ($(ev.target).closest('#new').length){ - return; + //if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder + var crumb=$('div.crumb').first(); + while($('div.controls').height() > 40 && crumb.next('div.crumb').length > 0) { + crumb.children('a').text('...'); + crumb = crumb.next('div.crumb'); } - $('#new>ul').hide(); - $('#new').removeClass('active'); - if ($('#new .error').length > 0) { - $('#new .error').tipsy('hide'); + //if that isn't enough, start removing items from the breacrumb except for the current folder and it's parent + var crumb = $('div.crumb').first(); + var next = crumb.next('div.crumb'); + while($('div.controls').height()>40 && next.next('div.crumb').length > 0) { + crumb.remove(); + crumb = next; + next = crumb.next('div.crumb'); } - $('#new li').each(function(i,element) { - if ($(element).children('p').length === 0) { - $(element).children('form').remove(); - $(element).append('<p>'+$(element).data('text')+'</p>'); - } - }); - }); - $('#new').click(function(event) { - event.stopPropagation(); - }); - $('#new>a').click(function() { - $('#new>ul').toggle(); - $('#new').toggleClass('active'); - }); - $('#new li').click(function() { - if ($(this).children('p').length === 0) { - return; + //still not enough, start shorting down the current folder name + var crumb=$('div.crumb>a').last(); + while($('div.controls').height() > 40 && crumb.text().length > 6) { + var text=crumb.text(); + text = text.substr(0,text.length-6)+'...'; + crumb.text(text); } - - $('#new .error').tipsy('hide'); - $('#new li').each(function(i,element) { - if ($(element).children('p').length === 0) { - $(element).children('form').remove(); - $(element).append('<p>'+$(element).data('text')+'</p>'); + $(document).click(function(ev) { + // do not close when clicking in the dropdown + if ($(ev.target).closest('#new').length){ + return; + } + $('#new>ul').hide(); + $('#new').removeClass('active'); + if ($('#new .error').length > 0) { + $('#new .error').tipsy('hide'); } + $('#new li').each(function(i,element) { + if ($(element).children('p').length === 0) { + $(element).children('form').remove(); + $(element).append('<p>'+$(element).data('text')+'</p>'); + } + }); }); - - var type=$(this).data('type'); - var text=$(this).children('p').text(); - $(this).data('text',text); - $(this).children('p').remove(); - - // add input field - var form = $('<form></form>'); - var input = $('<input type="text">'); - var newName = $(this).attr('data-newname') || ''; - if (newName) { - input.val(newName); - } - form.append(input); - $(this).append(form); - var lastPos; - var checkInput = function () { - var filename = input.val(); - if (type === 'web' && filename.length === 0) { - throw t('files', 'URL cannot be empty'); - } else if (type !== 'web' && !Files.isFileNameValid(filename)) { - // Files.isFileNameValid(filename) throws an exception itself - } else if ($('#dir').val() === '/' && filename === 'Shared') { - throw t('files', 'In the home folder \'Shared\' is a reserved filename'); - } else if (FileList.inList(filename)) { - throw t('files', '{new_name} already exists', {new_name: filename}); - } else { - return true; + $('#new').click(function(event) { + event.stopPropagation(); + }); + $('#new>a').click(function() { + $('#new>ul').toggle(); + $('#new').toggleClass('active'); + }); + $('#new li').click(function() { + if ($(this).children('p').length === 0) { + return; } - }; - // verify filename on typing - input.keyup(function(event) { - try { - checkInput(); - input.tipsy('hide'); - input.removeClass('error'); - } catch (error) { - input.attr('title', error); - input.tipsy({gravity: 'w', trigger: 'manual'}); - input.tipsy('show'); - input.addClass('error'); - } - }); + $('#new .error').tipsy('hide'); - input.focus(); - // pre select name up to the extension - lastPos = newName.lastIndexOf('.'); - if (lastPos === -1) { - lastPos = newName.length; - } - input.selectRange(0, lastPos); - form.submit(function(event) { - event.stopPropagation(); - event.preventDefault(); - try { - checkInput(); - var newname = input.val(); - if (FileList.lastAction) { - FileList.lastAction(); + $('#new li').each(function(i,element) { + if ($(element).children('p').length === 0) { + $(element).children('form').remove(); + $(element).append('<p>'+$(element).data('text')+'</p>'); } - var name = getUniqueName(newname); - if (newname !== name) { - FileList.checkName(name, newname, true); - var hidden = true; + }); + + var type=$(this).data('type'); + var text=$(this).children('p').text(); + $(this).data('text',text); + $(this).children('p').remove(); + + // add input field + var form = $('<form></form>'); + var input = $('<input type="text">'); + var newName = $(this).attr('data-newname') || ''; + if (newName) { + input.val(newName); + } + form.append(input); + $(this).append(form); + var lastPos; + var checkInput = function () { + var filename = input.val(); + if (type === 'web' && filename.length === 0) { + throw t('files', 'URL cannot be empty'); + } else if (type !== 'web' && !Files.isFileNameValid(filename)) { + // Files.isFileNameValid(filename) throws an exception itself + } else if (FileList.getCurrentDirectory() === '/' && filename.toLowerCase() === 'shared') { + throw t('files', 'In the home folder \'Shared\' is a reserved filename'); + } else if (FileList.inList(filename)) { + throw t('files', '{new_name} already exists', {new_name: filename}); } else { - var hidden = false; + return true; + } + }; + + // verify filename on typing + input.keyup(function(event) { + try { + checkInput(); + input.tipsy('hide'); + input.removeClass('error'); + } catch (error) { + input.attr('title', error); + input.tipsy({gravity: 'w', trigger: 'manual'}); + input.tipsy('show'); + input.addClass('error'); } - switch(type) { - case 'file': - $.post( - OC.filePath('files', 'ajax', 'newfile.php'), - {dir:$('#dir').val(), filename:name}, - function(result) { - if (result.status === 'success') { - var date = new Date(); - // TODO: ideally addFile should be able to receive - // all attributes and set them automatically, - // and also auto-load the preview - var tr = FileList.addFile(name, 0, date, false, hidden); - tr.attr('data-size', result.data.size); - tr.attr('data-mime', result.data.mime); - tr.attr('data-id', result.data.id); - tr.attr('data-etag', result.data.etag); - tr.find('.filesize').text(humanFileSize(result.data.size)); - var path = getPathForPreview(name); - Files.lazyLoadPreview(path, result.data.mime, function(previewpath) { - tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); - }, null, null, result.data.etag); - FileActions.display(tr.find('td.filename'), true); - } else { - OC.dialogs.alert(result.data.message, t('core', 'Could not create file')); + }); + + input.focus(); + // pre select name up to the extension + lastPos = newName.lastIndexOf('.'); + if (lastPos === -1) { + lastPos = newName.length; + } + input.selectRange(0, lastPos); + form.submit(function(event) { + event.stopPropagation(); + event.preventDefault(); + try { + checkInput(); + var newname = input.val(); + if (FileList.lastAction) { + FileList.lastAction(); + } + var name = getUniqueName(newname); + if (newname !== name) { + FileList.checkName(name, newname, true); + var hidden = true; + } else { + var hidden = false; + } + switch(type) { + case 'file': + $.post( + OC.filePath('files', 'ajax', 'newfile.php'), + {dir:$('#dir').val(), filename:name}, + function(result) { + if (result.status === 'success') { + var date = new Date(); + // TODO: ideally addFile should be able to receive + // all attributes and set them automatically, + // and also auto-load the preview + var tr = FileList.addFile(name, 0, date, false, hidden); + tr.attr('data-size', result.data.size); + tr.attr('data-mime', result.data.mime); + tr.attr('data-id', result.data.id); + tr.attr('data-etag', result.data.etag); + tr.find('.filesize').text(humanFileSize(result.data.size)); + var path = getPathForPreview(name); + Files.lazyLoadPreview(path, result.data.mime, function(previewpath) { + tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); + }, null, null, result.data.etag); + FileActions.display(tr.find('td.filename'), true); + } else { + OC.dialogs.alert(result.data.message, t('core', 'Could not create file')); + } } - } - ); - break; - case 'folder': - $.post( - OC.filePath('files','ajax','newfolder.php'), - {dir:$('#dir').val(), foldername:name}, - function(result) { - if (result.status === 'success') { - var date=new Date(); - FileList.addDir(name, 0, date, hidden); - var tr = FileList.findFileEl(name); - tr.attr('data-id', result.data.id); - } else { - OC.dialogs.alert(result.data.message, t('core', 'Could not create folder')); + ); + break; + case 'folder': + $.post( + OC.filePath('files','ajax','newfolder.php'), + {dir:$('#dir').val(), foldername:name}, + function(result) { + if (result.status === 'success') { + var date=new Date(); + FileList.addDir(name, 0, date, hidden); + var tr = FileList.findFileEl(name); + tr.attr('data-id', result.data.id); + } else { + OC.dialogs.alert(result.data.message, t('core', 'Could not create folder')); + } } + ); + break; + case 'web': + if (name.substr(0,8) !== 'https://' && name.substr(0,7) !== 'http://') { + name = 'http://' + name; } - ); - break; - case 'web': - if (name.substr(0,8) !== 'https://' && name.substr(0,7) !== 'http://') { - name = 'http://' + name; - } - var localName=name; - if (localName.substr(localName.length-1,1)==='/') {//strip / - localName=localName.substr(0,localName.length-1); - } - if (localName.indexOf('/')) {//use last part of url - localName=localName.split('/').pop(); - } else { //or the domain - localName=(localName.match(/:\/\/(.[^\/]+)/)[1]).replace('www.',''); - } - localName = getUniqueName(localName); - //IE < 10 does not fire the necessary events for the progress bar. - if ($('html.lte9').length === 0) { - $('#uploadprogressbar').progressbar({value:0}); - $('#uploadprogressbar').fadeIn(); - } - - var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName}); - eventSource.listen('progress',function(progress) { + var localName=name; + if (localName.substr(localName.length-1,1)==='/') {//strip / + localName=localName.substr(0,localName.length-1); + } + if (localName.indexOf('/')) {//use last part of url + localName=localName.split('/').pop(); + } else { //or the domain + localName=(localName.match(/:\/\/(.[^\/]+)/)[1]).replace('www.',''); + } + localName = getUniqueName(localName); //IE < 10 does not fire the necessary events for the progress bar. if ($('html.lte9').length === 0) { - $('#uploadprogressbar').progressbar('value',progress); + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); } - }); - eventSource.listen('success',function(data) { - var mime = data.mime; - var size = data.size; - var id = data.id; - $('#uploadprogressbar').fadeOut(); - var date = new Date(); - FileList.addFile(localName, size, date, false, hidden); - var tr = FileList.findFileEl(localName); - tr.data('mime', mime).data('id', id); - tr.attr('data-id', id); - var path = $('#dir').val()+'/'+localName; - Files.lazyLoadPreview(path, mime, function(previewpath) { - tr.find('td.filename').attr('style', 'background-image:url('+previewpath+')'); - }, null, null, data.etag); - FileActions.display(tr.find('td.filename'), true); - }); - eventSource.listen('error',function(error) { - $('#uploadprogressbar').fadeOut(); - var message = (error && error.message) || t('core', 'Error fetching URL'); - OC.Notification.show(message); - //hide notification after 10 sec - setTimeout(function() { - OC.Notification.hide(); - }, 10000); - }); - break; + + var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName}); + eventSource.listen('progress',function(progress) { + //IE < 10 does not fire the necessary events for the progress bar. + if ($('html.lte9').length === 0) { + $('#uploadprogressbar').progressbar('value',progress); + } + }); + eventSource.listen('success',function(data) { + var mime = data.mime; + var size = data.size; + var id = data.id; + $('#uploadprogressbar').fadeOut(); + var date = new Date(); + FileList.addFile(localName, size, date, false, hidden); + var tr = FileList.findFileEl(localName); + tr.data('mime', mime).data('id', id); + tr.attr('data-id', id); + var path = $('#dir').val()+'/'+localName; + Files.lazyLoadPreview(path, mime, function(previewpath) { + tr.find('td.filename').attr('style', 'background-image:url('+previewpath+')'); + }, null, null, data.etag); + FileActions.display(tr.find('td.filename'), true); + }); + eventSource.listen('error',function(error) { + $('#uploadprogressbar').fadeOut(); + var message = (error && error.message) || t('core', 'Error fetching URL'); + OC.Notification.show(message); + //hide notification after 10 sec + setTimeout(function() { + OC.Notification.hide(); + }, 10000); + }); + break; + } + var li=form.parent(); + form.remove(); + /* workaround for IE 9&10 click event trap, 2 lines: */ + $('input').first().focus(); + $('#content').focus(); + li.append('<p>'+li.data('text')+'</p>'); + $('#new>a').click(); + } catch (error) { + input.attr('title', error); + input.tipsy({gravity: 'w', trigger: 'manual'}); + input.tipsy('show'); + input.addClass('error'); } - var li=form.parent(); - form.remove(); - /* workaround for IE 9&10 click event trap, 2 lines: */ - $('input').first().focus(); - $('#content').focus(); - li.append('<p>'+li.data('text')+'</p>'); - $('#new>a').click(); - } catch (error) { - input.attr('title', error); - input.tipsy({gravity: 'w', trigger: 'manual'}); - input.tipsy('show'); - input.addClass('error'); - } + }); }); - }); - window.file_upload_param = file_upload_param; + window.file_upload_param = file_upload_param; + return file_upload_param; + } +}; + +$(document).ready(function() { + OC.Upload.init(); }); + diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 550c10dba3e..503bf681139 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -137,7 +137,9 @@ window.FileList={ var download_url = null; if (!param.download_url) { - download_url = OC.Router.generate('download', { file: $('#dir').val()+'/'+name }); + download_url = OC.generateUrl( + 'apps/files/download{file}', + { file: $('#dir').val()+'/'+name }); } else { download_url = param.download_url; } @@ -419,15 +421,12 @@ window.FileList={ len = input.val().length; } input.selectRange(0, len); - var checkInput = function () { var filename = input.val(); if (filename !== oldname) { - if (!Files.isFileNameValid(filename)) { - // Files.isFileNameValid(filename) throws an exception itself - } else if($('#dir').val() === '/' && filename === 'Shared') { - throw t('files','In the home folder \'Shared\' is a reserved filename'); - } else if (FileList.inList(filename)) { + // Files.isFileNameValid(filename) throws an exception itself + Files.isFileNameValid(filename, FileList.getCurrentDirectory()); + if (FileList.inList(filename)) { throw t('files', '{new_name} already exists', {new_name: filename}); } } @@ -1156,9 +1155,9 @@ $(document).ready(function() { // need to initially switch the dir to the one from the hash (IE8) FileList.changeDirectory(parseCurrentDirFromUrl(), false, true); } - } - FileList.setCurrentDir(parseCurrentDirFromUrl(), false); + FileList.setCurrentDir(parseCurrentDirFromUrl(), false); + } FileList.createFileSummary(); }); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index f4546120702..1186a72a44f 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -87,9 +87,12 @@ var Files = { * Throws a string exception with an error message if * the file name is not valid */ - isFileNameValid: function (name) { + isFileNameValid: function (name, root) { var trimmedName = name.trim(); - if (trimmedName === '.' || trimmedName === '..') { + if (trimmedName === '.' + || trimmedName === '..' + || (root === '/' && trimmedName.toLowerCase() === 'shared')) + { throw t('files', '"{name}" is an invalid file name.', {name: name}); } else if (trimmedName.length === 0) { throw t('files', 'File name cannot be empty.'); @@ -780,9 +783,9 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { if ( $('#isPublic').length ) { urlSpec.t = $('#dirToken').val(); - previewURL = OC.Router.generate('core_ajax_public_preview', urlSpec); + previewURL = OC.generateUrl('/publicpreview.png?') + $.param(urlSpec); } else { - previewURL = OC.Router.generate('core_ajax_preview', urlSpec); + previewURL = OC.generateUrl('/core/preview.png?') + $.param(urlSpec); } previewURL = previewURL.replace('(', '%28'); previewURL = previewURL.replace(')', '%29'); diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index f4fb96a7a7c..8a7a1e370eb 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -17,7 +17,13 @@ $totalsize = 0; ?> data-mime="<?php p($file['mimetype'])?>" data-size="<?php p($file['size']);?>" data-etag="<?php p($file['etag']);?>" - data-permissions="<?php p($file['permissions']); ?>"> + data-permissions="<?php p($file['permissions']); ?>" + + <?php if(isset($file['displayname_owner'])): ?> + data-share-owner="<?php p($file['displayname_owner']) ?>" + <?php endif; ?> + > + <?php if(isset($file['isPreviewAvailable']) and $file['isPreviewAvailable']): ?> <td class="filename svg preview-icon" <?php else: ?> @@ -34,17 +40,15 @@ $totalsize = 0; ?> <span class="nametext"> <?php print_unescaped(htmlspecialchars($file['name']));?> </span> + <span class="uploadtext" currentUploads="0"> + </span> + </a> <?php else: ?> <a class="name" href="<?php p(rtrim($_['downloadURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>"> <label class="filetext" title="" for="select-<?php p($file['fileid']); ?>"></label> <span class="nametext"><?php print_unescaped(htmlspecialchars($file['basename']));?><span class='extension'><?php p($file['extension']);?></span></span> </a> <?php endif; ?> - <?php if($file['type'] == 'dir'):?> - <span class="uploadtext" currentUploads="0"> - </span> - <?php endif;?> - </a> </td> <td class="filesize" style="color:rgb(<?php p($simple_size_color.','.$simple_size_color.','.$simple_size_color) ?>)"> diff --git a/apps/files/tests/js/fileUploadSpec.js b/apps/files/tests/js/fileUploadSpec.js new file mode 100644 index 00000000000..2b4341ef1c3 --- /dev/null +++ b/apps/files/tests/js/fileUploadSpec.js @@ -0,0 +1,127 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry <pvince81@owncloud.com> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/* global OC */ +describe('OC.Upload tests', function() { + var $dummyUploader; + var testFile; + + beforeEach(function() { + testFile = { + name: 'test.txt', + size: 5000, // 5 KB + type: 'text/plain', + lastModifiedDate: new Date() + }; + // need a dummy button because file-upload checks on it + $('#testArea').append( + '<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' + + '<input type="hidden" id="upload_limit" name="upload_limit" value="10000000">' + // 10 MB + '<input type="hidden" id="free_space" name="free_space" value="50000000">' // 50 MB + ); + $dummyUploader = $('#file_upload_start'); + }); + afterEach(function() { + delete window.file_upload_param; + $dummyUploader = undefined; + }); + describe('Adding files for upload', function() { + var params; + var failStub; + + beforeEach(function() { + params = OC.Upload.init(); + failStub = sinon.stub(); + $dummyUploader.on('fileuploadfail', failStub); + }); + afterEach(function() { + params = undefined; + failStub = undefined; + }); + + /** + * Add file for upload + * @param file file data + */ + function addFile(file) { + return params.add.call( + $dummyUploader[0], + {}, + { + originalFiles: {}, + files: [file] + }); + } + + it('adds file when size is below limits', function() { + var result = addFile(testFile); + expect(result).toEqual(true); + }); + it('adds file when free space is unknown', function() { + var result; + $('#free_space').val(-2); + + result = addFile(testFile); + + expect(result).toEqual(true); + expect(failStub.notCalled).toEqual(true); + }); + it('does not add file if it exceeds upload limit', function() { + var result; + $('#upload_limit').val(1000); + + result = addFile(testFile); + + expect(result).toEqual(false); + expect(failStub.calledOnce).toEqual(true); + expect(failStub.getCall(0).args[1].textStatus).toEqual('sizeexceedlimit'); + expect(failStub.getCall(0).args[1].errorThrown).toEqual( + 'Total file size 5 kB exceeds upload limit 1000 B' + ); + }); + it('does not add file if it exceeds free space', function() { + var result; + $('#free_space').val(1000); + + result = addFile(testFile); + + expect(result).toEqual(false); + expect(failStub.calledOnce).toEqual(true); + expect(failStub.getCall(0).args[1].textStatus).toEqual('notenoughspace'); + expect(failStub.getCall(0).args[1].errorThrown).toEqual( + 'Not enough free space, you are uploading 5 kB but only 1000 B is left' + ); + }); + it('does not add file if it has invalid characters', function() { + var result; + testFile.name = 'stars*stars.txt'; + + result = addFile(testFile); + + expect(result).toEqual(false); + expect(failStub.calledOnce).toEqual(true); + expect(failStub.getCall(0).args[1].textStatus).toEqual('invalidcharacters'); + expect(failStub.getCall(0).args[1].errorThrown.substr(0, 12)).toEqual( + 'Invalid name' + ); + }); + }); +}); diff --git a/apps/files/tests/js/filesSpec.js b/apps/files/tests/js/filesSpec.js index 018c8ef0f3c..95bf87e03ec 100644 --- a/apps/files/tests/js/filesSpec.js +++ b/apps/files/tests/js/filesSpec.js @@ -48,6 +48,41 @@ describe('Files tests', function() { expect(error).toEqual(false); } }); + it('Validates correct file names do not create Shared folder in root', function() { + // create shared file in subfolder + var error = false; + try { + expect(Files.isFileNameValid('shared', '/foo')).toEqual(true); + expect(Files.isFileNameValid('Shared', '/foo')).toEqual(true); + } + catch (e) { + error = e; + } + expect(error).toEqual(false); + + // create shared file in root + var threwException = false; + try { + Files.isFileNameValid('Shared', '/'); + console.error('Invalid file name not detected'); + } + catch (e) { + threwException = true; + } + expect(threwException).toEqual(true); + + // create shared file in root + var threwException = false; + try { + Files.isFileNameValid('shared', '/'); + console.error('Invalid file name not detected'); + } + catch (e) { + threwException = true; + } + expect(threwException).toEqual(true); + + }); it('Detects invalid file names', function() { var fileNames = [ '', diff --git a/apps/files_encryption/js/settings-admin.js b/apps/files_encryption/js/settings-admin.js index c2140a6f1eb..785d02002fa 100644 --- a/apps/files_encryption/js/settings-admin.js +++ b/apps/files_encryption/js/settings-admin.js @@ -7,28 +7,6 @@ * 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(); diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 9d456f6c517..b7e1599c1fe 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -38,6 +38,7 @@ class Proxy extends \OC_FileProxy { private static $blackList = null; //mimetypes blacklisted from encryption private static $unencryptedSizes = array(); // remember unencrypted size + private static $fopenMode = array(); // remember the fopen mode /** * Check if a file requires encryption @@ -146,7 +147,7 @@ class Proxy extends \OC_FileProxy { if ( isset(self::$unencryptedSizes[$normalizedPath]) ) { $view = new \OC_FilesystemView('/'); $view->putFileInfo($normalizedPath, - array('encrypted' => true, 'encrypted_size' => self::$unencryptedSizes[$normalizedPath])); + array('encrypted' => true, 'unencrypted_size' => self::$unencryptedSizes[$normalizedPath])); unset(self::$unencryptedSizes[$normalizedPath]); } @@ -214,6 +215,16 @@ class Proxy extends \OC_FileProxy { } /** + * @brief remember initial fopen mode because sometimes it gets changed during the request + * @param string $path path + * @param string $mode type of access + */ + public function preFopen($path, $mode) { + self::$fopenMode[$path] = $mode; + } + + + /** * @param $path * @param $result * @return resource @@ -240,7 +251,15 @@ class Proxy extends \OC_FileProxy { $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - $meta = stream_get_meta_data($result); + // if we remember the mode from the pre proxy we re-use it + // oterwise we fall back to stream_get_meta_data() + if (isset(self::$fopenMode[$path])) { + $mode = self::$fopenMode[$path]; + unset(self::$fopenMode[$path]); + } else { + $meta = stream_get_meta_data($result); + $mode = $meta['mode']; + } $view = new \OC_FilesystemView(''); @@ -258,14 +277,15 @@ class Proxy extends \OC_FileProxy { // Open the file using the crypto stream wrapper // protocol and let it do the decryption work instead - $result = fopen('crypt://' . $path, $meta['mode']); + $result = fopen('crypt://' . $path, $mode); } elseif ( - self::shouldEncrypt($path) - and $meta['mode'] !== 'r' - and $meta['mode'] !== 'rb' + self::shouldEncrypt($path) + and $mode !== 'r' + and $mode !== 'rb' + ) { - $result = fopen('crypt://' . $path, $meta['mode']); + $result = fopen('crypt://' . $path, $mode); } // Re-enable the proxy @@ -320,6 +340,13 @@ class Proxy extends \OC_FileProxy { // if path is a folder do nothing if ($view->is_dir($path)) { + $proxyState = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + $fileInfo = $view->getFileInfo($path); + \OC_FileProxy::$enabled = $proxyState; + if ($fileInfo['unencrypted_size'] > 0) { + return $fileInfo['unencrypted_size']; + } return $size; } diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php index aa58e33e9d2..3daaa06425f 100644 --- a/apps/files_encryption/lib/session.php +++ b/apps/files_encryption/lib/session.php @@ -134,6 +134,14 @@ class Session { } + /** + * @brief remove encryption keys and init status from session + */ + public function closeSession() { + \OC::$session->remove('encryptionInitialized'); + \OC::$session->remove('privateKey'); + } + /** * @brief Gets status if we already tried to initialize the encryption app diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 88eacc6f136..58ac03373a7 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -167,6 +167,9 @@ class Stream { } else { $this->meta = stream_get_meta_data($this->handle); + // sometimes fopen changes the mode, e.g. for a url "r" convert to "r+" + // but we need to remember the original access type + $this->meta['mode'] = $mode; } diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index ec06bd52f5e..3db5a423478 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -135,7 +135,6 @@ class Util { // Set directories to check / create $setUpDirs = array( $this->userDir, - $this->userFilesDir, $this->publicKeyDir, $this->encryptionDir, $this->keyfilesPath, @@ -1772,4 +1771,12 @@ class Util { return $session; } + /* + * @brief remove encryption related keys from the session + */ + public function closeEncryptionSession() { + $session = new \OCA\Encryption\Session($this->view); + $session->closeSession(); + } + } diff --git a/apps/files_encryption/tests/proxy.php b/apps/files_encryption/tests/proxy.php index 51cc0b795e3..647ee955eb1 100644 --- a/apps/files_encryption/tests/proxy.php +++ b/apps/files_encryption/tests/proxy.php @@ -112,4 +112,24 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase { } + function testPostFileSizeWithDirectory() { + + $this->view->file_put_contents($this->filename, $this->data); + + \OC_FileProxy::$enabled = false; + + // get root size, must match the file's unencrypted size + $unencryptedSize = $this->view->filesize(''); + + \OC_FileProxy::$enabled = true; + + $encryptedSize = $this->view->filesize(''); + + $this->assertTrue($encryptedSize !== $unencryptedSize); + + // cleanup + $this->view->unlink($this->filename); + + } + } diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php index f70e30c4d73..203ba55dbfd 100755 --- a/apps/files_encryption/tests/util.php +++ b/apps/files_encryption/tests/util.php @@ -344,7 +344,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { // check if mtime and etags unchanged $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']); - $this->assertEquals($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); + $this->assertSame($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); $this->view->unlink($this->userId . '/files/' . $filename); } @@ -373,7 +373,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { // check if mtime and etags unchanged $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']); - $this->assertEquals($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); + $this->assertSame($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); // file should no longer be encrypted $this->assertEquals(0, $fileInfoUnencrypted['encrypted']); diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 895f97bd2c3..cd2a3103eb7 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -264,7 +264,7 @@ $(document).ready(function() { OC.MountConfig.saveStorage($(this).parent().parent()); }); - $('#sslCertificate').on('click', 'td.remove>img', function() { + $('#sslCertificate').on('click', 'td.remove>img', function() { var $tr = $(this).parent().parent(); var row = this.parentNode.parentNode; $.post(OC.filePath('files_external', 'ajax', 'removeRootCertificate.php'), {cert: row.id}); @@ -302,13 +302,23 @@ $(document).ready(function() { }); $('#allowUserMounting').bind('change', function() { + OC.msg.startSaving('#userMountingMsg'); if (this.checked) { OC.AppConfig.setValue('files_external', 'allow_user_mounting', 'yes'); + $('#userMountingBackups').removeClass('hidden'); } else { OC.AppConfig.setValue('files_external', 'allow_user_mounting', 'no'); + $('#userMountingBackups').addClass('hidden'); } + OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('settings', 'Saved')}}); }); + $('input[name="allowUserMountingBackends\\[\\]"]').bind('change', function() { + OC.msg.startSaving('#userMountingMsg'); + var user_mounting_backends = $('input[name="allowUserMountingBackends\\[\\]"]:checked').map(function(){return $(this).val();}).get(); + OC.AppConfig.setValue('files_external', 'user_mounting_backends', user_mounting_backends.join()); + OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('settings', 'Saved')}}); + }); }); })(); diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 9a8b95c14c9..2767076eefa 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -45,7 +45,7 @@ class OC_Mount_Config { 'datadir' => 'Location')); $backends['\OC\Files\Storage\AmazonS3']=array( - 'backend' => 'Amazon S3', + 'backend' => 'Amazon S3 and compliant', 'configuration' => array( 'key' => 'Access Key', 'secret' => '*Secret Key', @@ -156,6 +156,35 @@ class OC_Mount_Config { } /** + * Get details on each of the external storage backends, used for the mount config UI + * Some backends are not available as a personal backend, f.e. Local and such that have + * been disabled by the admin. + * + * If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded + * If the configuration parameter should be secret, add a '*' to the beginning of the value + * If the configuration parameter is a boolean, add a '!' to the beginning of the value + * If the configuration parameter is optional, add a '&' to the beginning of the value + * If the configuration parameter is hidden, add a '#' to the beginning of the value + * @return array + */ + public static function getPersonalBackends() { + + $backends = self::getBackends(); + + // Remove local storage and other disabled storages + unset($backends['\OC\Files\Storage\Local']); + + $allowed_backends = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', '')); + foreach ($backends as $backend => $null) { + if (!in_array($backend, $allowed_backends)) { + unset($backends[$backend]); + } + } + + return $backends; + } + + /** * Get the system mount points * The returned array is not in the same format as getUserMountPoints() * @return array @@ -287,11 +316,12 @@ class OC_Mount_Config { if (!isset($backends[$class])) { // invalid backend return false; - } + } if ($isPersonal) { // Verify that the mount point applies for the current user - // Prevent non-admin users from mounting local storage - if ($applicable !== OCP\User::getUser() || strtolower($class) === '\oc\files\storage\local') { + // Prevent non-admin users from mounting local storage and other disabled backends + $allowed_backends = self::getPersonalBackends(); + if ($applicable != OCP\User::getUser() || !in_array($class, $allowed_backends)) { return false; } $mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/'); @@ -359,7 +389,8 @@ class OC_Mount_Config { $jsonFile = OC_User::getHome(OCP\User::getUser()).'/mount.json'; } else { $phpFile = OC::$SERVERROOT.'/config/mount.php'; - $jsonFile = \OC_Config::getValue("mount_file", \OC::$SERVERROOT . "/data/mount.json"); + $datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data/'); + $jsonFile = \OC_Config::getValue('mount_file', $datadir . '/mount.json'); } if (is_file($jsonFile)) { $mountPoints = json_decode(file_get_contents($jsonFile), true); @@ -385,7 +416,8 @@ class OC_Mount_Config { if ($isPersonal) { $file = OC_User::getHome(OCP\User::getUser()).'/mount.json'; } else { - $file = \OC_Config::getValue("mount_file", \OC::$SERVERROOT . "/data/mount.json"); + $datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data/'); + $file = \OC_Config::getValue('mount_file', $datadir . '/mount.json'); } $content = json_encode($data); @file_put_contents($file, $content); @@ -444,7 +476,7 @@ class OC_Mount_Config { */ public static function checksmbclient() { if(function_exists('shell_exec')) { - $output=shell_exec('which smbclient 2> /dev/null'); + $output=shell_exec('command -v smbclient 2> /dev/null'); return !empty($output); }else{ return false; diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 9afe73aebd7..279ae716935 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -8,7 +8,7 @@ namespace OC\Files\Storage; -class DAV extends \OC\Files\Storage\Common{ +class DAV extends \OC\Files\Storage\Common { private $password; private $user; private $host; @@ -21,7 +21,7 @@ class DAV extends \OC\Files\Storage\Common{ */ private $client; - private static $tempFiles=array(); + private static $tempFiles = array(); public function __construct($params) { if (isset($params['host']) && isset($params['user']) && isset($params['password'])) { @@ -29,9 +29,9 @@ class DAV extends \OC\Files\Storage\Common{ //remove leading http[s], will be generated in createBaseUri() if (substr($host, 0, 8) == "https://") $host = substr($host, 8); else if (substr($host, 0, 7) == "http://") $host = substr($host, 7); - $this->host=$host; - $this->user=$params['user']; - $this->password=$params['password']; + $this->host = $host; + $this->user = $params['user']; + $this->password = $params['password']; if (isset($params['secure'])) { if (is_string($params['secure'])) { $this->secure = ($params['secure'] === 'true'); @@ -42,25 +42,25 @@ class DAV extends \OC\Files\Storage\Common{ $this->secure = false; } if ($this->secure === true) { - $certPath=\OC_User::getHome(\OC_User::getUser()) . '/files_external/rootcerts.crt'; + $certPath = \OC_User::getHome(\OC_User::getUser()) . '/files_external/rootcerts.crt'; if (file_exists($certPath)) { - $this->certPath=$certPath; + $this->certPath = $certPath; } } - $this->root=isset($params['root'])?$params['root']:'/'; - if ( ! $this->root || $this->root[0]!='/') { - $this->root='/'.$this->root; + $this->root = isset($params['root']) ? $params['root'] : '/'; + if (!$this->root || $this->root[0] != '/') { + $this->root = '/' . $this->root; } - if (substr($this->root, -1, 1)!='/') { - $this->root.='/'; + if (substr($this->root, -1, 1) != '/') { + $this->root .= '/'; } } else { throw new \Exception(); } } - private function init(){ - if($this->ready) { + private function init() { + if ($this->ready) { return; } $this->ready = true; @@ -78,28 +78,28 @@ class DAV extends \OC\Files\Storage\Common{ } } - public function getId(){ + public function getId() { return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root; } protected function createBaseUri() { - $baseUri='http'; + $baseUri = 'http'; if ($this->secure) { - $baseUri.='s'; + $baseUri .= 's'; } - $baseUri.='://'.$this->host.$this->root; + $baseUri .= '://' . $this->host . $this->root; return $baseUri; } public function mkdir($path) { $this->init(); - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); return $this->simpleResponse('MKCOL', $path, null, 201); } public function rmdir($path) { $this->init(); - $path=$this->cleanPath($path) . '/'; + $path = $this->cleanPath($path) . '/'; // FIXME: some WebDAV impl return 403 when trying to DELETE // a non-empty folder return $this->simpleResponse('DELETE', $path, null, 204); @@ -107,35 +107,35 @@ class DAV extends \OC\Files\Storage\Common{ public function opendir($path) { $this->init(); - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); try { - $response=$this->client->propfind($this->encodePath($path), array(), 1); - $id=md5('webdav'.$this->root.$path); + $response = $this->client->propfind($this->encodePath($path), array(), 1); + $id = md5('webdav' . $this->root . $path); $content = array(); - $files=array_keys($response); - array_shift($files);//the first entry is the current directory + $files = array_keys($response); + array_shift($files); //the first entry is the current directory foreach ($files as $file) { $file = urldecode(basename($file)); - $content[]=$file; + $content[] = $file; } \OC\Files\Stream\Dir::register($id, $content); - return opendir('fakedir://'.$id); - } catch(\Exception $e) { + return opendir('fakedir://' . $id); + } catch (\Exception $e) { return false; } } public function filetype($path) { $this->init(); - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); try { - $response=$this->client->propfind($this->encodePath($path), array('{DAV:}resourcetype')); + $response = $this->client->propfind($this->encodePath($path), array('{DAV:}resourcetype')); $responseType = array(); if (isset($response["{DAV:}resourcetype"])) { - $responseType=$response["{DAV:}resourcetype"]->resourceType; + $responseType = $response["{DAV:}resourcetype"]->resourceType; } - return (count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file'; - } catch(\Exception $e) { + return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; + } catch (\Exception $e) { error_log($e->getMessage()); \OCP\Util::writeLog("webdav client", \OCP\Util::sanitizeHTML($e->getMessage()), \OCP\Util::ERROR); return false; @@ -144,11 +144,11 @@ class DAV extends \OC\Files\Storage\Common{ public function file_exists($path) { $this->init(); - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); try { $this->client->propfind($this->encodePath($path), array('{DAV:}resourcetype')); - return true;//no 404 exception - } catch(\Exception $e) { + return true; //no 404 exception + } catch (\Exception $e) { return false; } } @@ -160,34 +160,34 @@ class DAV extends \OC\Files\Storage\Common{ public function fopen($path, $mode) { $this->init(); - $path=$this->cleanPath($path); - switch($mode) { + $path = $this->cleanPath($path); + switch ($mode) { case 'r': case 'rb': - if ( ! $this->file_exists($path)) { + if (!$this->file_exists($path)) { return false; } //straight up curl instead of sabredav here, sabredav put's the entire get result in memory $curl = curl_init(); $fp = fopen('php://temp', 'r+'); - curl_setopt($curl, CURLOPT_USERPWD, $this->user.':'.$this->password); - curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$this->encodePath($path)); + curl_setopt($curl, CURLOPT_USERPWD, $this->user . ':' . $this->password); + curl_setopt($curl, CURLOPT_URL, $this->createBaseUri() . $this->encodePath($path)); curl_setopt($curl, CURLOPT_FILE, $fp); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); if ($this->secure === true) { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); - if($this->certPath){ + if ($this->certPath) { curl_setopt($curl, CURLOPT_CAINFO, $this->certPath); } } - - curl_exec ($curl); + + curl_exec($curl); $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($statusCode !== 200) { \OCP\Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, \OCP\Util::ERROR); } - curl_close ($curl); + curl_close($curl); rewind($fp); return $fp; case 'w': @@ -203,18 +203,19 @@ class DAV extends \OC\Files\Storage\Common{ case 'c': case 'c+': //emulate these - if (strrpos($path, '.')!==false) { - $ext=substr($path, strrpos($path, '.')); + if (strrpos($path, '.') !== false) { + $ext = substr($path, strrpos($path, '.')); } else { - $ext=''; + $ext = ''; } - $tmpFile = \OCP\Files::tmpFile($ext); - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); - if($this->file_exists($path)) { - $this->getFile($path, $tmpFile); + if ($this->file_exists($path)) { + $tmpFile = $this->getCachedFile($path); + } else { + $tmpFile = \OCP\Files::tmpFile($ext); } - self::$tempFiles[$tmpFile]=$path; - return fopen('close://'.$tmpFile, $mode); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); + self::$tempFiles[$tmpFile] = $path; + return fopen('close://' . $tmpFile, $mode); } } @@ -227,32 +228,31 @@ class DAV extends \OC\Files\Storage\Common{ public function free_space($path) { $this->init(); - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); try { - $response=$this->client->propfind($this->encodePath($path), array('{DAV:}quota-available-bytes')); + $response = $this->client->propfind($this->encodePath($path), array('{DAV:}quota-available-bytes')); if (isset($response['{DAV:}quota-available-bytes'])) { return (int)$response['{DAV:}quota-available-bytes']; } else { return \OC\Files\SPACE_UNKNOWN; } - } catch(\Exception $e) { + } catch (\Exception $e) { return \OC\Files\SPACE_UNKNOWN; } } - public function touch($path, $mtime=null) { + public function touch($path, $mtime = null) { $this->init(); if (is_null($mtime)) { - $mtime=time(); + $mtime = time(); } - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); // if file exists, update the mtime, else create a new empty file if ($this->file_exists($path)) { try { $this->client->proppatch($this->encodePath($path), array('{DAV:}lastmodified' => $mtime)); - } - catch (\Sabre_DAV_Exception_NotImplemented $e) { + } catch (\Sabre_DAV_Exception_NotImplemented $e) { return false; } } else { @@ -261,23 +261,13 @@ class DAV extends \OC\Files\Storage\Common{ return true; } - /** - * @param string $path - * @param string $target - */ - public function getFile($path, $target) { - $this->init(); - $source=$this->fopen($path, 'r'); - file_put_contents($target, $source); - } - - public function uploadFile($path, $target) { + protected function uploadFile($path, $target) { $this->init(); - $source=fopen($path, 'r'); + $source = fopen($path, 'r'); $curl = curl_init(); - curl_setopt($curl, CURLOPT_USERPWD, $this->user.':'.$this->password); - curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().str_replace(' ', '%20', $target)); + curl_setopt($curl, CURLOPT_USERPWD, $this->user . ':' . $this->password); + curl_setopt($curl, CURLOPT_URL, $this->createBaseUri() . str_replace(' ', '%20', $target)); curl_setopt($curl, CURLOPT_BINARYTRANSFER, true); curl_setopt($curl, CURLOPT_INFILE, $source); // file pointer curl_setopt($curl, CURLOPT_INFILESIZE, filesize($path)); @@ -285,26 +275,29 @@ class DAV extends \OC\Files\Storage\Common{ if ($this->secure === true) { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); - if($this->certPath){ + if ($this->certPath) { curl_setopt($curl, CURLOPT_CAINFO, $this->certPath); } } - curl_exec ($curl); + curl_exec($curl); $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($statusCode !== 200) { \OCP\Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, \OCP\Util::ERROR); } - curl_close ($curl); + curl_close($curl); + $this->removeCachedFile($target); } public function rename($path1, $path2) { $this->init(); $path1 = $this->encodePath($this->cleanPath($path1)); - $path2 = $this->createBaseUri().$this->encodePath($this->cleanPath($path2)); + $path2 = $this->createBaseUri() . $this->encodePath($this->cleanPath($path2)); try { - $this->client->request('MOVE', $path1, null, array('Destination'=>$path2)); + $this->client->request('MOVE', $path1, null, array('Destination' => $path2)); + $this->removeCachedFile($path1); + $this->removeCachedFile($path2); return true; - } catch(\Exception $e) { + } catch (\Exception $e) { return false; } } @@ -312,47 +305,48 @@ class DAV extends \OC\Files\Storage\Common{ public function copy($path1, $path2) { $this->init(); $path1 = $this->encodePath($this->cleanPath($path1)); - $path2 = $this->createBaseUri().$this->encodePath($this->cleanPath($path2)); + $path2 = $this->createBaseUri() . $this->encodePath($this->cleanPath($path2)); try { - $this->client->request('COPY', $path1, null, array('Destination'=>$path2)); + $this->client->request('COPY', $path1, null, array('Destination' => $path2)); + $this->removeCachedFile($path2); return true; - } catch(\Exception $e) { + } catch (\Exception $e) { return false; } } public function stat($path) { $this->init(); - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); try { $response = $this->client->propfind($this->encodePath($path), array('{DAV:}getlastmodified', '{DAV:}getcontentlength')); return array( - 'mtime'=>strtotime($response['{DAV:}getlastmodified']), - 'size'=>(int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0, + 'mtime' => strtotime($response['{DAV:}getlastmodified']), + 'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0, ); - } catch(\Exception $e) { + } catch (\Exception $e) { return array(); } } public function getMimeType($path) { $this->init(); - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); try { - $response=$this->client->propfind($this->encodePath($path), array('{DAV:}getcontenttype', '{DAV:}resourcetype')); + $response = $this->client->propfind($this->encodePath($path), array('{DAV:}getcontenttype', '{DAV:}resourcetype')); $responseType = array(); if (isset($response["{DAV:}resourcetype"])) { - $responseType=$response["{DAV:}resourcetype"]->resourceType; + $responseType = $response["{DAV:}resourcetype"]->resourceType; } - $type=(count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file'; - if ($type=='dir') { + $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; + if ($type == 'dir') { return 'httpd/unix-directory'; } elseif (isset($response['{DAV:}getcontenttype'])) { return $response['{DAV:}getcontenttype']; } else { return false; } - } catch(\Exception $e) { + } catch (\Exception $e) { return false; } } @@ -368,6 +362,7 @@ class DAV extends \OC\Files\Storage\Common{ /** * URL encodes the given path but keeps the slashes + * * @param string $path to encode * @return string encoded path */ @@ -382,11 +377,11 @@ class DAV extends \OC\Files\Storage\Common{ * @param integer $expected */ private function simpleResponse($method, $path, $body, $expected) { - $path=$this->cleanPath($path); + $path = $this->cleanPath($path); try { - $response=$this->client->request($method, $this->encodePath($path), $body); - return $response['statusCode']==$expected; - } catch(\Exception $e) { + $response = $this->client->request($method, $this->encodePath($path), $body); + return $response['statusCode'] == $expected; + } catch (\Exception $e) { return false; } } diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index 90f5e159535..90d7afed28b 100755 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -22,9 +22,8 @@ OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); -$backends = OC_Mount_Config::getBackends(); -// Remove local storage -unset($backends['\OC\Files\Storage\Local']); +$backends = OC_Mount_Config::getPersonalBackends(); + $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('isAdminPage', false); $tmpl->assign('mounts', OC_Mount_Config::getPersonalMountPoints()); diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 31183409e39..5b62b542200 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -26,10 +26,26 @@ OCP\Util::addScript('files_external', 'settings'); OCP\Util::addscript('3rdparty', 'chosen/chosen.jquery.min'); OCP\Util::addStyle('files_external', 'settings'); OCP\Util::addStyle('3rdparty', 'chosen/chosen'); + +$backends = OC_Mount_Config::getBackends(); +$personal_backends = array(); +$enabled_backends = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', '')); +foreach ($backends as $class => $backend) +{ + if ($class != '\OC\Files\Storage\Local') + { + $personal_backends[$class] = array( + 'backend' => $backend['backend'], + 'enabled' => in_array($class, $enabled_backends), + ); + } +} + $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('isAdminPage', true); $tmpl->assign('mounts', OC_Mount_Config::getSystemMountPoints()); -$tmpl->assign('backends', OC_Mount_Config::getBackends()); +$tmpl->assign('backends', $backends); +$tmpl->assign('personal_backends', $personal_backends); $tmpl->assign('groups', OC_Group::getGroups()); $tmpl->assign('users', OCP\User::getUsers()); $tmpl->assign('userDisplayNames', OC_User::getDisplayNames()); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index 3ca16c3c7a8..de44d3c8644 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -122,12 +122,18 @@ <?php if ($_['isAdminPage']): ?> <br /> - <input type="checkbox" - name="allowUserMounting" - id="allowUserMounting" - value="1" <?php if ($_['allowUserMounting'] == 'yes') print_unescaped(' checked="checked"'); ?> /> - <label for="allowUserMounting"><?php p($l->t('Enable User External Storage')); ?></label><br/> - <em><?php p($l->t('Allow users to mount their own external storage')); ?></em> + <input type="checkbox" name="allowUserMounting" id="allowUserMounting" + value="1" <?php if ($_['allowUserMounting'] == 'yes') print_unescaped(' checked="checked"'); ?> /> + <label for="allowUserMounting"><?php p($l->t('Enable User External Storage')); ?></label> <span id="userMountingMsg" class="msg"></span> + + <p id="userMountingBackups"<?php if ($_['allowUserMounting'] != 'yes'): ?> class="hidden"<?php endif; ?>> + <?php p($l->t('Allow users to mount the following external storage')); ?><br /> + <?php $i = 0; foreach ($_['personal_backends'] as $class => $backend): ?> + <input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($class); ?>" <?php if ($backend['enabled']) print_unescaped(' checked="checked"'); ?> /> + <label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend['backend']); ?></label> <br /> + <?php $i++; ?> + <?php endforeach; ?> + </p> <?php endif; ?> </fieldset> </form> diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php index 3469829b6f7..9417a6eeb89 100644 --- a/apps/files_sharing/appinfo/routes.php +++ b/apps/files_sharing/appinfo/routes.php @@ -1,4 +1,5 @@ <?php +/** @var $this OC_Router */ $this->create('core_ajax_public_preview', '/publicpreview.png')->action( function() { require_once __DIR__ . '/../ajax/publicpreview.php'; diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 36de452a55e..9f0ed12f935 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -5,6 +5,14 @@ $(document).ready(function() { if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) { $('#fileList').on('fileActionsReady',function(){ + + var allShared = $('#fileList').find('[data-share-owner]').find('[data-Action="Share"]'); + allShared.addClass('permanent'); + allShared.find('span').text(function(){ + $owner = $(this).closest('tr').attr('data-share-owner'); + return ' ' + t('files_sharing', 'Shared by {owner}', {owner: $owner}); + }); + if (!sharesLoaded){ OC.Share.loadIcons('file'); // assume that we got all shares, so switching directories @@ -17,16 +25,15 @@ $(document).ready(function() { }); FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) { - if ($('#dir').val() == '/') { - var item = $('#dir').val() + filename; - } else { - var item = $('#dir').val() + '/' + filename; + var dir = $('#dir').val(); + var item = dir + '/' + filename; + if (dir == '/') { + item = dir + filename; } var tr = FileList.findFileEl(filename); + var itemType = 'file'; if ($(tr).data('type') == 'dir') { - var itemType = 'folder'; - } else { - var itemType = 'file'; + itemType = 'folder'; } var possiblePermissions = $(tr).data('permissions'); var appendTo = $(tr).find('td.filename'); diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 19a2d22b068..0ba58aa896a 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -172,12 +172,12 @@ class Api { // workaround because folders are named 'dir' in this context $itemType = $file['type'] === 'file' ? 'file' : 'folder'; $share = \OCP\Share::getItemShared($itemType, $file['fileid']); - $receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $file['fileid']); - if ($receivedFrom) { - $share['received_from'] = $receivedFrom['uid_owner']; - $share['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']); - } - if ($share) { + if($share) { + $receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $file['fileid']); + if ($receivedFrom) { + $share['received_from'] = $receivedFrom['uid_owner']; + $share['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']); + } $result = array_merge($result, $share); } } diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 4b0da0b002d..10f2182655f 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -137,9 +137,12 @@ class Shared_Cache extends Cache { } else { $cache = $this->getSourceCache($folder); if ($cache) { + $parent = $this->storage->getFile($folder); $sourceFolderContent = $cache->getFolderContents($this->files[$folder]); foreach ($sourceFolderContent as $key => $c) { $sourceFolderContent[$key]['usersPath'] = 'files/Shared/' . $folder . '/' . $c['name']; + $sourceFolderContent[$key]['uid_owner'] = $parent['uid_owner']; + $sourceFolderContent[$key]['displayname_owner'] = $parent['uid_owner']; } return $sourceFolderContent; diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index ec0f368386f..5e00050fe1e 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -94,6 +94,9 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $file['mtime'] = $item['mtime']; $file['encrypted'] = $item['encrypted']; $file['etag'] = $item['etag']; + $file['uid_owner'] = $item['uid_owner']; + $file['displayname_owner'] = $item['displayname_owner']; + $storage = \OC\Files\Filesystem::getStorage('/'); $cache = $storage->getCache(); if ($item['encrypted'] or ($item['unencrypted_size'] > 0 and $cache->getMimetype($item['mimetype']) === 'httpd/unix-directory')) { diff --git a/apps/files_sharing/templates/authenticate.php b/apps/files_sharing/templates/authenticate.php index 928be93fc96..19b1fb27630 100644 --- a/apps/files_sharing/templates/authenticate.php +++ b/apps/files_sharing/templates/authenticate.php @@ -8,7 +8,10 @@ <?php endif; ?> <p class="infield"> <label for="password" class="infield"><?php p($l->t('Password')); ?></label> - <input type="password" name="password" id="password" placeholder="" value="" autofocus /> + <input type="password" name="password" id="password" + placeholder="" value="" + autocomplete="off" autocapitalize="off" autocorrect="off" + autofocus /> <input type="submit" value="" class="svg icon icon-confirm" /> </p> </fieldset> diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php index 1278e0c4d1f..0d3d9b98b2e 100644 --- a/apps/files_sharing/tests/api.php +++ b/apps/files_sharing/tests/api.php @@ -33,13 +33,16 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { parent::setUp(); $this->folder = '/folder_share_api_test'; + $this->subfolder = '/subfolder_share_api_test'; $this->filename = 'share-api-test.txt'; // save file with content $this->view->file_put_contents($this->filename, $this->data); $this->view->mkdir($this->folder); + $this->view->mkdir($this->folder . '/' . $this->subfolder); $this->view->file_put_contents($this->folder.'/'.$this->filename, $this->data); + $this->view->file_put_contents($this->folder.'/' . $this->subfolder . '/' .$this->filename, $this->data); } function tearDown() { @@ -287,6 +290,71 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { } /** + * @brief share a folder, than reshare a file within the shared folder and check if we construct the correct path + * @medium + */ + function testGetShareFromFolderReshares() { + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + $fileInfo1 = $this->view->getFileInfo($this->folder); + $fileInfo2 = $this->view->getFileInfo($this->folder.'/'.$this->filename); + $fileInfo3 = $this->view->getFileInfo($this->folder.'/' . $this->subfolder . '/' .$this->filename); + + // share root folder to user2 + $result = \OCP\Share::shareItem('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31); + + // share was successful? + $this->assertTrue($result); + + // login as user2 + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + // share file in root folder + $result = \OCP\Share::shareItem('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1); + // share was successful? + $this->assertTrue(is_string($result)); + + // share file in subfolder + $result = \OCP\Share::shareItem('file', $fileInfo3['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1); + // share was successful? + $this->assertTrue(is_string($result)); + + $testValues=array( + array('query' => 'Shared/' . $this->folder, + 'expectedResult' => '/Shared' . $this->folder . '/' . $this->filename), + array('query' => 'Shared/' . $this->folder . $this->subfolder, + 'expectedResult' => '/Shared' . $this->folder . $this->subfolder . '/' . $this->filename), + ); + foreach ($testValues as $value) { + + $_GET['path'] = $value['query']; + $_GET['subfiles'] = 'true'; + + $result = Share\Api::getAllShares(array()); + + $this->assertTrue($result->succeeded()); + + // test should return one share within $this->folder + $data = $result->getData(); + + $this->assertEquals($value['expectedResult'], $data[0]['path']); + } + + // cleanup + + \OCP\Share::unshare('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); + \OCP\Share::unshare('file', $fileInfo3['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + \OCP\Share::unshare('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2); + + } + + /** * @medium */ function testGetShareFromUnknownId() { diff --git a/apps/files_sharing/tests/base.php b/apps/files_sharing/tests/base.php index 3e283271f5d..d44972d01f1 100644 --- a/apps/files_sharing/tests/base.php +++ b/apps/files_sharing/tests/base.php @@ -43,6 +43,7 @@ abstract class Test_Files_Sharing_Base extends \PHPUnit_Framework_TestCase { */ public $view; public $folder; + public $subfolder; public static function setUpBeforeClass() { // reset backend diff --git a/apps/user_ldap/lib/helper.php b/apps/user_ldap/lib/helper.php index 7de7fe8667f..b5955cb2abb 100644 --- a/apps/user_ldap/lib/helper.php +++ b/apps/user_ldap/lib/helper.php @@ -120,7 +120,7 @@ class Helper { $saveOtherConfigurations = ''; if(empty($prefix)) { - $saveOtherConfigurations = 'AND `Configkey` NOT LIKE \'s%\''; + $saveOtherConfigurations = 'AND `configkey` NOT LIKE \'s%\''; } $query = \OCP\DB::prepare(' diff --git a/apps/user_ldap/user_proxy.php b/apps/user_ldap/user_proxy.php index 5ad127197f3..2cb3dfb2c60 100644 --- a/apps/user_ldap/user_proxy.php +++ b/apps/user_ldap/user_proxy.php @@ -54,11 +54,15 @@ class User_Proxy extends lib\Proxy implements \OCP\UserInterface { protected function walkBackends($uid, $method, $parameters) { $cacheKey = $this->getUserCacheKey($uid); foreach($this->backends as $configPrefix => $backend) { -// print("walkBackend '$configPrefix'<br/>"); - if($result = call_user_func_array(array($backend, $method), $parameters)) { + $instance = $backend; + if(!method_exists($instance, $method) + && method_exists($this->getAccess($configPrefix), $method)) { + $instance = $this->getAccess($configPrefix); + } + if($result = call_user_func_array(array($instance, $method), $parameters)) { $this->writeToCache($cacheKey, $configPrefix); return $result; - } + } } return false; } @@ -77,7 +81,12 @@ class User_Proxy extends lib\Proxy implements \OCP\UserInterface { //in case the uid has been found in the past, try this stored connection first if(!is_null($prefix)) { if(isset($this->backends[$prefix])) { - $result = call_user_func_array(array($this->backends[$prefix], $method), $parameters); + $instance = $this->backends[$prefix]; + if(!method_exists($instance, $method) + && method_exists($this->getAccess($prefix), $method)) { + $instance = $this->getAccess($prefix); + } + $result = call_user_func_array(array($instance, $method), $parameters); if($result === $passOnWhen) { //not found here, reset cache to null if user vanished //because sometimes methods return false with a reason diff --git a/config/config.sample.php b/config/config.sample.php index 9c5eca8a5ec..987a866e49b 100755 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -178,6 +178,9 @@ $CONFIG = array( /* Enable or disable the logging of IP addresses in case of webform auth failures */ "log_authfailip" => false, +/* Whether http-basic username must equal username to login */ +"basic_auth" => true, + /* * Configure the size in bytes log rotation should happen, 0 or false disables the rotation. * This rotates the current owncloud logfile to a new name, this way the total log usage diff --git a/core/css/apps.css b/core/css/apps.css index f68f53d6999..58f2759f69a 100644 --- a/core/css/apps.css +++ b/core/css/apps.css @@ -187,6 +187,9 @@ } #app-settings.open #app-settings-content { display: block; + /* restrict height of settings and make scrollable */ + max-height: 300px; + overflow-y: scroll; } .settings-button { diff --git a/core/css/icons.css b/core/css/icons.css index 814749c5af8..cdfdd8e43ef 100644 --- a/core/css/icons.css +++ b/core/css/icons.css @@ -1,4 +1,4 @@ -.icon { +[class^="icon-"], [class*=" icon-"] { background-repeat: no-repeat; background-position: center; } @@ -24,7 +24,7 @@ .icon-noise { background-image: url('../img/noise.png'); - background-repeat: no-repeat; + background-repeat: repeat; } @@ -66,7 +66,8 @@ .icon-delete { background-image: url('../img/actions/delete.svg'); } -.icon-delete-hover { +.icon-delete:hover, +.icon-delete:focus { background-image: url('../img/actions/delete-hover.svg'); } @@ -155,11 +156,15 @@ background-image: url('../img/actions/sound-off.svg'); } -.icon-star { +.icon-star, +.icon-starred:hover, +.icon-starred:focus { background-image: url('../img/actions/star.svg'); } -.icon-starred { +.icon-starred, +.icon-star:hover, +.icon-star:focus { background-image: url('../img/actions/starred.svg'); } diff --git a/core/css/styles.css b/core/css/styles.css index 341a507ce37..69cf6df07d0 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -175,6 +175,7 @@ button, .button, border: 1px solid rgba(190,190,190,.9); cursor: pointer; border-radius: 3px; + outline: none; } input[type="submit"]:hover, input[type="submit"]:focus, input[type="button"]:hover, input[type="button"]:focus, @@ -204,18 +205,18 @@ textarea:disabled { /* Primary action button, use sparingly */ .primary, input[type="submit"].primary, input[type="button"].primary, button.primary, .button.primary { border: 1px solid #1d2d44; - background: #35537a; + background-color: #35537a; color: #ddd; } .primary:hover, input[type="submit"].primary:hover, input[type="button"].primary:hover, button.primary:hover, .button.primary:hover, .primary:focus, input[type="submit"].primary:focus, input[type="button"].primary:focus, button.primary:focus, .button.primary:focus { border: 1px solid #1d2d44; - background: #304d76; + background-color: #304d76; color: #fff; } .primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active { border: 1px solid #1d2d44; - background: #1d2d44; + background-color: #1d2d44; color: #bbb; } @@ -233,7 +234,7 @@ textarea:disabled { } input[type="submit"].enabled { - background: #66f866; + background-color: #66f866; border: 1px solid #5e5; } @@ -405,11 +406,9 @@ input[name="adminpass-clone"] { padding-left:1.8em; width:11.7em !important; } /* General new input field look */ #body-login input[type="text"], #body-login input[type="password"], -#body-login input[type="email"] { - border: 1px solid #323233; - border-radius: 5px; -} -#body-login input[type='submit'] { +#body-login input[type="email"], +#body-login input[type="submit"] { + border: none; border-radius: 5px; } diff --git a/core/img/logo-mail.gif b/core/img/logo-mail.gif Binary files differindex 6a1caaa9188..f1dd108450e 100644 --- a/core/img/logo-mail.gif +++ b/core/img/logo-mail.gif diff --git a/core/js/config.php b/core/js/config.php index c606ef35056..7e23f3e2e41 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -61,8 +61,10 @@ $array = array( "firstDay" => json_encode($l->l('firstday', 'firstday')) , "oc_config" => json_encode( array( - 'session_lifetime' => \OCP\Config::getSystemValue('session_lifetime', ini_get('session.gc_maxlifetime')), - 'session_keepalive' => \OCP\Config::getSystemValue('session_keepalive', true) + 'session_lifetime' => \OCP\Config::getSystemValue('session_lifetime', ini_get('session.gc_maxlifetime')), + 'session_keepalive' => \OCP\Config::getSystemValue('session_keepalive', true), + 'version' => implode('.', OC_Util::getVersion()), + 'versionstring' => OC_Util::getVersionString(), ) ), "oc_defaults" => json_encode( diff --git a/core/js/core.json b/core/js/core.json index 4beab7cf796..665e2485a90 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -17,7 +17,6 @@ "eventsource.js", "config.js", "multiselect.js", - "router.js", "oc-requesttoken.js" ] } diff --git a/core/js/jquery.avatar.js b/core/js/jquery.avatar.js index 02a40c088b4..381c42d9dbb 100644 --- a/core/js/jquery.avatar.js +++ b/core/js/jquery.avatar.js @@ -75,31 +75,32 @@ var $div = this; - OC.Router.registerLoadedCallback(function() { - var url = OC.Router.generate('core_avatar_get', {user: user, size: size})+'?requesttoken='+oc_requesttoken; - $.get(url, function(result) { - if (typeof(result) === 'object') { - if (!hidedefault) { - if (result.data && result.data.displayname) { - $div.imageplaceholder(user, result.data.displayname); - } else { - $div.imageplaceholder(user); - } + var url = OC.generateUrl( + '/avatar/{user}/{size}?requesttoken={requesttoken}', + {user: user, size: size, requesttoken: oc_requesttoken}); + + $.get(url, function(result) { + if (typeof(result) === 'object') { + if (!hidedefault) { + if (result.data && result.data.displayname) { + $div.imageplaceholder(user, result.data.displayname); } else { - $div.hide(); + $div.imageplaceholder(user); } } else { - $div.show(); - if (ie8fix === true) { - $div.html('<img src="'+url+'#'+Math.floor(Math.random()*1000)+'">'); - } else { - $div.html('<img src="'+url+'">'); - } + $div.hide(); } - if(typeof callback === 'function') { - callback(); + } else { + $div.show(); + if (ie8fix === true) { + $div.html('<img src="'+url+'#'+Math.floor(Math.random()*1000)+'">'); + } else { + $div.html('<img src="'+url+'">'); } - }); + } + if(typeof callback === 'function') { + callback(); + } }); }; }(jQuery)); diff --git a/core/js/js.js b/core/js/js.js index ec890be4541..77aadd23e03 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -194,6 +194,30 @@ var OC={ linkToRemoteBase:function(service) { return OC.webroot + '/remote.php/' + service; }, + + /** + * Generates the absolute url for the given relative url, which can contain parameters. + * + * @returns {string} + * @param {string} url + * @param params + */ + generateUrl: function(url, params) { + var _build = function (text, vars) { + return text.replace(/{([^{}]*)}/g, + function (a, b) { + var r = vars[b]; + return typeof r === 'string' || typeof r === 'number' ? r : a; + } + ); + }; + if (url.charAt(0) !== '/') { + url = '/' + url; + + } + return OC.webroot + '/index.php' + _build(url, params); + }, + /** * @brief Creates an absolute url for remote use * @param string $service id @@ -467,6 +491,34 @@ OC.search.lastResults={}; OC.addStyle.loaded=[]; OC.addScript.loaded=[]; +OC.msg={ + startSaving:function(selector){ + OC.msg.startAction(selector, t('core', 'Saving...')); + }, + finishedSaving:function(selector, data){ + OC.msg.finishedAction(selector, data); + }, + startAction:function(selector, message){ + $(selector) + .html( message ) + .removeClass('success') + .removeClass('error') + .stop(true, true) + .show(); + }, + finishedAction: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'); + } + } +}; + OC.Notification={ queuedNotifications: [], getDefaultNotificationFunction: null, @@ -763,12 +815,10 @@ function initCore() { if (interval < 60) { interval = 60; } - OC.Router.registerLoadedCallback(function(){ - var url = OC.Router.generate('heartbeat'); - setInterval(function(){ - $.post(url); - }, interval * 1000); - }); + var url = OC.generateUrl('/heartbeat'); + setInterval(function(){ + $.post(url); + }, interval * 1000); } // session heartbeat (defaults to enabled) diff --git a/core/js/router.js b/core/js/router.js deleted file mode 100644 index e6ef54a1864..00000000000 --- a/core/js/router.js +++ /dev/null @@ -1,81 +0,0 @@ -OC.router_base_url = OC.webroot + '/index.php'; -OC.Router = { - // register your ajax requests to load after the loading of the routes - // has finished. otherwise you face problems with race conditions - registerLoadedCallback: function(callback){ - if (!this.routes_request){ - return; - } - this.routes_request.done(callback); - }, - routes_request: !window.TESTING && $.ajax(OC.router_base_url + '/core/routes.json', { - dataType: 'json', - success: function(jsondata) { - if (jsondata.status === 'success') { - OC.Router.routes = jsondata.data; - } - } - }), - generate:function(name, opt_params) { - if (!('routes' in this)) { - if(this.routes_request.state() != 'resolved') { - console.warn('To avoid race conditions, please register a callback');// wait - } - } - if (!(name in this.routes)) { - throw new Error('The route "' + name + '" does not exist.'); - } - var route = this.routes[name]; - var params = opt_params || {}; - var unusedParams = $.extend(true, {}, params); - var url = ''; - var optional = true; - $(route.tokens).each(function(i, token) { - if ('text' === token[0]) { - url = token[1] + url; - optional = false; - - return; - } - - if ('variable' === token[0]) { - if (false === optional || !(token[3] in route.defaults) - || ((token[3] in params) && params[token[3]] != route.defaults[token[3]])) { - var value; - if (token[3] in params) { - value = params[token[3]]; - delete unusedParams[token[3]]; - } else if (token[3] in route.defaults) { - value = route.defaults[token[3]]; - } else if (optional) { - return; - } else { - throw new Error('The route "' + name + '" requires the parameter "' + token[3] + '".'); - } - - var empty = true === value || false === value || '' === value; - - if (!empty || !optional) { - url = token[1] + encodeURIComponent(value).replace(/%2F/g, '/') + url; - } - - optional = false; - } - - return; - } - - throw new Error('The token type "' + token[0] + '" is not supported.'); - }); - if (url === '') { - url = '/'; - } - - unusedParams = $.param(unusedParams); - if (unusedParams.length > 0) { - url += '?'+unusedParams; - } - - return OC.router_base_url + url; - } -} diff --git a/core/js/setup.js b/core/js/setup.js index 279b5fbebb9..96719540f96 100644 --- a/core/js/setup.js +++ b/core/js/setup.js @@ -36,6 +36,7 @@ $(document).ready(function() { $('#showAdvanced').click(function() { $('#datadirContent').slideToggle(250); + $('#databaseBackend').slideToggle(250); $('#databaseField').slideToggle(250); }); $("form").submit(function(){ @@ -73,6 +74,7 @@ $(document).ready(function() { if (currentDbType === 'sqlite' || (dbtypes.sqlite && currentDbType === undefined)){ $('#datadirContent').hide(250); + $('#databaseBackend').hide(250); $('#databaseField').hide(250); } diff --git a/core/js/share.js b/core/js/share.js index 0939259b7da..129e50b22d5 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -584,8 +584,8 @@ $(document).ready(function() { $(checkboxes).filter('input[name="edit"]').attr('checked', false); // Check Edit if Create, Update, or Delete is checked } else if (($(this).attr('name') == 'create' - || $(this).attr('name') == 'update' - || $(this).attr('name') == 'delete')) + || $(this).attr('name') == 'update' + || $(this).attr('name') == 'delete')) { $(checkboxes).filter('input[name="edit"]').attr('checked', true); } diff --git a/core/js/tags.js b/core/js/tags.js index bc6d7b4e071..bc2b42bf5ff 100644 --- a/core/js/tags.js +++ b/core/js/tags.js @@ -69,7 +69,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_ids_for_tag', {type: type}); + url = OC.generateUrl('/tags/{type}/ids', {type: type}); $.getJSON(url, {tag: tag}, function(response) { if(response.status === 'success') { defer.resolve(response.ids); @@ -90,7 +90,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_favorites', {type: type}); + url = OC.generateUrl('/tags/{type}/favorites', {type: type}); $.getJSON(url, function(response) { if(response.status === 'success') { defer.resolve(response.ids); @@ -111,7 +111,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_tags', {type: type}); + url = OC.generateUrl('/tags/{type}', {type: type}); $.getJSON(url, function(response) { if(response.status === 'success') { defer.resolve(response.tags); @@ -133,7 +133,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_tag', {type: type, id: id}); + url = OC.generateUrl('/tags/{type}/tag/{id}/', {type: type, id: id}); $.post(url, {tag: tag}, function(response) { if(response.status === 'success') { defer.resolve(response); @@ -157,7 +157,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_untag', {type: type, id: id}); + url = OC.generateUrl('/tags/{type}/untag/{id}/', {type: type, id: id}); $.post(url, {tag: tag}, function(response) { if(response.status === 'success') { defer.resolve(response); @@ -181,7 +181,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_favorite', {type: type, id: id}); + url = OC.generateUrl('/tags/{type}/favorite/{id}/', {type: type, id: id}); $.post(url, function(response) { if(response.status === 'success') { defer.resolve(response); @@ -205,7 +205,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_unfavorite', {type: type, id: id}); + url = OC.generateUrl('/tags/{type}/unfavorite/{id}/', {type: type, id: id}); $.post(url, function(response) { if(response.status === 'success') { defer.resolve(); @@ -229,7 +229,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_add', {type: type}); + url = OC.generateUrl('/tags/{type}/add', {type: type}); $.post(url,{tag:tag}, function(response) { if(typeof cb == 'function') { cb(response); @@ -256,7 +256,7 @@ OC.Tags= { type = type ? type : this.type; var defer = $.Deferred(), self = this, - url = OC.Router.generate('core_tags_delete', {type: type}); + url = OC.generateUrl('/tags/{type}/delete', {type: type}); if(!tags || !tags.length) { throw new Error(t('core', 'No tags selected for deletion.')); } diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js index b1193240580..d86cd81cda8 100644 --- a/core/js/tests/specHelper.js +++ b/core/js/tests/specHelper.js @@ -68,9 +68,14 @@ window.oc_defaults = {}; // global setup for all tests (function setupTests() { var fakeServer = null, + $testArea = null, routesRequestStub; beforeEach(function() { + // test area for elements that need absolute selector access or measure widths/heights + // which wouldn't work for detached or hidden elements + $testArea = $('<div id="testArea" style="position: absolute; width: 1280px; height: 800px; top: -3000px; left: -3000px;"></div>'); + $('body').append($testArea); // enforce fake XHR, tests should not depend on the server and // must use fake responses for expected calls fakeServer = sinon.fakeServer.create(); @@ -86,21 +91,14 @@ window.oc_defaults = {}; // make it globally available, so that other tests can define // custom responses window.fakeServer = fakeServer; - - OC.Router.routes = []; - OC.Router.routes_request = { - state: sinon.stub().returns('resolved'), - done: sinon.stub() - }; }); afterEach(function() { - OC.Router.routes_request.state.reset(); - OC.Router.routes_request.done.reset(); - // uncomment this to log requests // console.log(window.fakeServer.requests); fakeServer.restore(); + + $testArea.remove(); }); })(); diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index 478505e9287..069546387c7 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -179,7 +179,7 @@ describe('Core base tests', function() { }); it('Encodes special characters', function() { expect(OC.buildQueryString({ - unicode: '汉å—', + unicode: '汉å—' })).toEqual('unicode=%E6%B1%89%E5%AD%97'); expect(OC.buildQueryString({ b: 'spaace value', @@ -199,22 +199,20 @@ describe('Core base tests', function() { 'booleantrue': true })).toEqual('booleanfalse=false&booleantrue=true'); expect(OC.buildQueryString({ - 'number': 123, + 'number': 123 })).toEqual('number=123'); }); }); describe('Session heartbeat', function() { var clock, oldConfig, - loadedStub, routeStub, counter; beforeEach(function() { clock = sinon.useFakeTimers(); oldConfig = window.oc_config; - loadedStub = sinon.stub(OC.Router, 'registerLoadedCallback'); - routeStub = sinon.stub(OC.Router, 'generate').returns('/heartbeat'); + routeStub = sinon.stub(OC, 'generateUrl').returns('/heartbeat'); counter = 0; fakeServer.autoRespond = true; @@ -227,7 +225,6 @@ describe('Core base tests', function() { afterEach(function() { clock.restore(); window.oc_config = oldConfig; - loadedStub.restore(); routeStub.restore(); }); it('sends heartbeat half the session lifetime when heartbeat enabled', function() { @@ -236,9 +233,7 @@ describe('Core base tests', function() { session_lifetime: 300 }; window.initCore(); - expect(loadedStub.calledOnce).toEqual(true); - loadedStub.yield(); - expect(routeStub.calledWith('heartbeat')).toEqual(true); + expect(routeStub.calledWith('/heartbeat')).toEqual(true); expect(counter).toEqual(0); @@ -264,7 +259,6 @@ describe('Core base tests', function() { session_lifetime: 300 }; window.initCore(); - expect(loadedStub.notCalled).toEqual(true); expect(routeStub.notCalled).toEqual(true); expect(counter).toEqual(0); @@ -276,5 +270,14 @@ describe('Core base tests', function() { }); }); + describe('Generate Url', function() { + it('returns absolute urls', function() { + expect(OC.generateUrl('heartbeat')).toEqual(OC.webroot + '/index.php/heartbeat'); + expect(OC.generateUrl('/heartbeat')).toEqual(OC.webroot + '/index.php/heartbeat'); + }); + it('substitutes parameters', function() { + expect(OC.generateUrl('apps/files/download{file}', {file: '/Welcome.txt'})).toEqual(OC.webroot + '/index.php/apps/files/download/Welcome.txt'); + }); + }); }); diff --git a/core/lostpassword/controller.php b/core/lostpassword/controller.php index fd20c6ba249..c858696885b 100644 --- a/core/lostpassword/controller.php +++ b/core/lostpassword/controller.php @@ -69,7 +69,7 @@ class Controller { $defaults = new \OC_Defaults(); \OC_Mail::send($email, $_POST['user'], $l->t('%s password reset', array($defaults->getName())), $msg, $from, $defaults->getName()); } catch (Exception $e) { - \OC_Template::printErrorPage( 'A problem occurs during sending the e-mail please contact your administrator.'); + \OC_Template::printErrorPage( $l->t('A problem has occurred whilst sending the email, please contact your administrator.') ); } self::displayLostPasswordPage(false, true); } else { diff --git a/core/routes.php b/core/routes.php index aea788bdc6b..76cf03c3673 100644 --- a/core/routes.php +++ b/core/routes.php @@ -65,8 +65,6 @@ $this->create('core_tags_delete', '/tags/{type}/delete') $this->create('js_config', '/core/js/oc.js') ->actionInclude('core/js/config.php'); // Routing -$this->create('core_ajax_routes', '/core/routes.json') - ->action('OC_Router', 'JSRoutes'); $this->create('core_ajax_preview', '/core/preview.png') ->actionInclude('core/ajax/preview.php'); $this->create('core_lostpassword_index', '/lostpassword/') diff --git a/core/templates/installation.php b/core/templates/installation.php index d3adb34f412..e2d296a713f 100644 --- a/core/templates/installation.php +++ b/core/templates/installation.php @@ -67,7 +67,7 @@ <?php if(!$_['directoryIsSet'] OR !$_['dbIsSet'] OR count($_['errors']) > 0): ?> <fieldset id="advancedHeader"> - <legend><a id="showAdvanced"><?php p($l->t( 'Advanced' )); ?> <img class="svg" src="<?php print_unescaped(image_path('', 'actions/caret.svg')); ?>" /></a></legend> + <legend><a id="showAdvanced"><?php p($l->t( 'Storage & database' )); ?> <img class="svg" src="<?php print_unescaped(image_path('', 'actions/caret.svg')); ?>" /></a></legend> </fieldset> <?php endif; ?> @@ -84,7 +84,7 @@ <?php endif; ?> <?php if(!$_['dbIsSet'] OR count($_['errors']) > 0): ?> - <fieldset id='databaseField'> + <fieldset id='databaseBackend'> <?php if($_['hasMySQL'] or $_['hasPostgreSQL'] or $_['hasOracle'] or $_['hasMSSQL']) $hasOtherDB = true; else $hasOtherDB =false; //other than SQLite ?> <legend><?php p($l->t( 'Configure the database' )); ?></legend> @@ -100,8 +100,10 @@ <?php endif; ?> <?php endforeach; ?> </div> + </fieldset> <?php if($hasOtherDB): ?> + <fieldset id='databaseField'> <div id="use_other_db"> <p class="infield grouptop"> <label for="dbuser" class="infield"><?php p($l->t( 'Database user' )); ?></label> @@ -141,8 +143,8 @@ autocomplete="off" autocapitalize="off" autocorrect="off" /> </p> </div> + </fieldset> <?php endif; ?> - </fieldset> <?php endif; ?> <div class="buttons"><input type="submit" class="primary" value="<?php p($l->t( 'Finish setup' )); ?>" data-finishing="<?php p($l->t( 'Finishing …' )); ?>" /></div> diff --git a/core/templates/mail.php b/core/templates/mail.php index b8b0a2bfe96..ae46eaca788 100644 --- a/core/templates/mail.php +++ b/core/templates/mail.php @@ -2,15 +2,15 @@ <tr><td> <table cellspacing="0" cellpadding="0" border="0" width="600px"> <tr> -<td bgcolor="#1d2d44" width="20px"> </td> -<td bgcolor="#1d2d44"> -<img src="<?php print_unescaped(OC_Helper::makeURLAbsolute(image_path('', 'logo-mail.gif'))); ?>" alt="<?php p($theme->getName()); ?>"/> +<td bgcolor="<?php p($theme->getMailHeaderColor());?>" width="20px"> </td> +<td bgcolor="<?php p($theme->getMailHeaderColor());?>"> +<img src="<?php p(OC_Helper::makeURLAbsolute(image_path('', 'logo-mail.gif'))); ?>" alt="<?php p($theme->getName()); ?>"/> </td> </tr> -<tr><td bgcolor="#f8f8f8" colspan="2"> </td></tr> +<tr><td colspan="2"> </td></tr> <tr> -<td bgcolor="#f8f8f8" width="20px"> </td> -<td bgcolor="#f8f8f8" style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;"> +<td width="20px"> </td> +<td style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;"> <?php print_unescaped($l->t('Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href="%s">View it!</a><br><br>', array($_['user_displayname'], $_['filename'], $_['link']))); if ( isset($_['expiration']) ) { @@ -21,17 +21,17 @@ p($l->t('Cheers!')); ?> </td> </tr> -<tr><td bgcolor="#f8f8f8" colspan="2"> </td></tr> +<tr><td colspan="2"> </td></tr> <tr> -<td bgcolor="#f8f8f8" width="20px"> </td> -<td bgcolor="#f8f8f8" style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">--<br> +<td width="20px"> </td> +<td style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">--<br> <?php p($theme->getName()); ?> - <?php p($theme->getSlogan()); ?> -<br><a href="<?php print_unescaped($theme->getBaseUrl()); ?>"><?php print_unescaped($theme->getBaseUrl());?></a> +<br><a href="<?php p($theme->getBaseUrl()); ?>"><?php p($theme->getBaseUrl());?></a> </td> </tr> <tr> -<td bgcolor="#f8f8f8" colspan="2"> </td> +<td colspan="2"> </td> </tr> </table> </td></tr> diff --git a/lib/base.php b/lib/base.php index 525d290931f..86ee5349828 100644 --- a/lib/base.php +++ b/lib/base.php @@ -316,7 +316,6 @@ class OC { OC_Util::addScript("config"); //OC_Util::addScript( "multiselect" ); OC_Util::addScript('search', 'result'); - OC_Util::addScript('router'); OC_Util::addScript("oc-requesttoken"); // avatars @@ -554,7 +553,8 @@ class OC { OC_User::useBackend(new OC_User_Database()); OC_Group::useBackend(new OC_Group_Database()); - if (isset($_SERVER['PHP_AUTH_USER']) && self::$session->exists('loginname') + $basic_auth = OC_Config::getValue('basic_auth', true); + if ($basic_auth && isset($_SERVER['PHP_AUTH_USER']) && self::$session->exists('loginname') && $_SERVER['PHP_AUTH_USER'] !== self::$session->get('loginname')) { $sessionUser = self::$session->get('loginname'); $serverUser = $_SERVER['PHP_AUTH_USER']; @@ -693,6 +693,22 @@ class OC { exit(); } + $host = OC_Request::insecureServerHost(); + // if the host passed in headers isn't trusted + if (!OC::$CLI + // overwritehost is always trusted + && OC_Request::getOverwriteHost() === null + && !OC_Request::isTrustedDomain($host)) { + + header('HTTP/1.1 400 Bad Request'); + header('Status: 400 Bad Request'); + OC_Template::printErrorPage( + 'You are accessing the server from an untrusted domain.', + 'Please contact your administrator' + ); + return; + } + $request = OC_Request::getPathInfo(); if (substr($request, -3) !== '.js') { // we need these files during the upgrade self::checkMaintenanceMode(); @@ -752,7 +768,8 @@ class OC { OC_Preferences::deleteKey(OC_User::getUser(), 'login_token', $_COOKIE['oc_token']); } OC_User::logout(); - header("Location: " . OC::$WEBROOT . '/'); + // redirect to webroot and add slash if webroot is empty + header("Location: " . OC::$WEBROOT.(empty(OC::$WEBROOT) ? '/' : '')); } else { if (is_null($file)) { $param['file'] = 'index.php'; diff --git a/lib/private/api.php b/lib/private/api.php index 3f96196e6df..1537cc11dd0 100644 --- a/lib/private/api.php +++ b/lib/private/api.php @@ -270,6 +270,18 @@ class OC_API { * @return string|false (username, or false on failure) */ private static function loginUser(){ + + // reuse existing login + $loggedIn = OC_User::isLoggedIn(); + $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false; + if ($loggedIn === true && $ocsApiRequest) { + + // initialize the user's filesystem + \OC_Util::setUpFS(\OC_User::getUser()); + + return OC_User::getUser(); + } + // basic auth $authUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : ''; $authPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; @@ -283,17 +295,6 @@ class OC_API { return $authUser; } - // reuse existing login - $loggedIn = OC_User::isLoggedIn(); - $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false; - if ($loggedIn === true && $ocsApiRequest) { - - // initialize the user's filesystem - \OC_Util::setUpFS(\OC_User::getUser()); - - return OC_User::getUser(); - } - return false; } diff --git a/lib/private/app.php b/lib/private/app.php index 048d4d4aeb1..58bf67c1d47 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -219,6 +219,8 @@ class OC_App{ $appdata=OC_OCSClient::getApplication($app); $download=OC_OCSClient::getApplicationDownload($app, 1); if(isset($download['downloadlink']) and $download['downloadlink']!='') { + // Replace spaces in download link without encoding entire URL + $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']); $info = array('source'=>'http', 'href'=>$download['downloadlink'], 'appdata'=>$appdata); $app=OC_Installer::installApp($info); } diff --git a/lib/private/config.php b/lib/private/config.php index 3649da84973..56f47256134 100644 --- a/lib/private/config.php +++ b/lib/private/config.php @@ -77,7 +77,7 @@ class Config { /** * @brief Gets a value from config.php * @param string $key key - * @param string|null $default = null default value + * @param array|bool|string|null $default = null default value * @return string the value or $default * * This function gets the value from config.php. If it does not exist, diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php index 02d1a9f4ba2..3ed9e94d69b 100644 --- a/lib/private/connector/sabre/directory.php +++ b/lib/private/connector/sabre/directory.php @@ -50,7 +50,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function createFile($name, $data = null) { - if ($name === 'Shared' && empty($this->path)) { + if (strtolower($name) === 'shared' && empty($this->path)) { throw new \Sabre_DAV_Exception_Forbidden(); } @@ -86,7 +86,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function createDirectory($name) { - if ($name === 'Shared' && empty($this->path)) { + if (strtolower($name) === 'shared' && empty($this->path)) { throw new \Sabre_DAV_Exception_Forbidden(); } diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php index d2fa425b22c..accf020daa2 100644 --- a/lib/private/connector/sabre/objecttree.php +++ b/lib/private/connector/sabre/objecttree.php @@ -94,6 +94,9 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { } if ($sourceDir !== $destinationDir) { // for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir + if (ltrim($destinationDir, '/') === '' && strtolower($sourceNode->getName()) === 'shared') { + throw new \Sabre_DAV_Exception_Forbidden(); + } if (!$fs->isUpdatable($sourceDir)) { throw new \Sabre_DAV_Exception_Forbidden(); } diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index c050d47b499..aaf2ea543b9 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -82,6 +82,9 @@ class MDB2SchemaManager { $platform = $this->conn->getDatabasePlatform(); foreach($schemaDiff->changedTables as $tableDiff) { $tableDiff->name = $platform->quoteIdentifier($tableDiff->name); + foreach($tableDiff->changedColumns as $column) { + $column->oldColumnName = $platform->quoteIdentifier($column->oldColumnName); + } } if ($generateSql) { diff --git a/lib/private/db/mdb2schemareader.php b/lib/private/db/mdb2schemareader.php index f9a76786c3e..1c16d03eab2 100644 --- a/lib/private/db/mdb2schemareader.php +++ b/lib/private/db/mdb2schemareader.php @@ -41,7 +41,9 @@ class MDB2SchemaReader { */ public function loadSchemaFromFile($file) { $schema = new \Doctrine\DBAL\Schema\Schema(); + $loadEntities = libxml_disable_entity_loader(false); $xml = simplexml_load_file($file); + libxml_disable_entity_loader($loadEntities); foreach ($xml->children() as $child) { /** * @var \SimpleXMLElement $child diff --git a/lib/private/defaults.php b/lib/private/defaults.php index 0b97497baa1..79be211b82f 100644 --- a/lib/private/defaults.php +++ b/lib/private/defaults.php @@ -21,6 +21,7 @@ class OC_Defaults { private $defaultDocBaseUrl; private $defaultSlogan; private $defaultLogoClaim; + private $defaultMailHeaderColor; function __construct() { $this->l = OC_L10N::get('core'); @@ -33,6 +34,7 @@ class OC_Defaults { $this->defaultDocBaseUrl = "http://doc.owncloud.org"; $this->defaultSlogan = $this->l->t("web services under your control"); $this->defaultLogoClaim = ""; + $this->defaultMailHeaderColor = "#1d2d44"; /* header color of mail notifications */ if (class_exists("OC_Theme")) { $this->theme = new OC_Theme(); @@ -174,4 +176,23 @@ class OC_Defaults { return $footer; } + public function buildDocLinkToKey($key) { + if ($this->themeExist('buildDocLinkToKey')) { + return $this->theme->buildDocLinkToKey($key); + } + return $this->getDocBaseUrl() . '/server/6.0/go.php?to=' . $key; + } + + /** + * Returns mail header color + * @return mail header color + */ + public function getMailHeaderColor() { + if ($this->themeExist('getMailHeaderColor')) { + return $this->theme->getMailHeaderColor(); + } else { + return $this->defaultMailHeaderColor; + } + } + } diff --git a/lib/private/files.php b/lib/private/files.php index 656d6f044ca..7e7a27f48dc 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -21,22 +21,39 @@ * */ +// TODO: get rid of this using proper composer packages +require_once 'mcnetic/phpzipstreamer/ZipStreamer.php'; + +class GET_TYPE { + const FILE = 1; + const ZIP_FILES = 2; + const ZIP_DIR = 3; +} + /** - * Class for fileserver access + * Class for file server access * */ class OC_Files { - static $tmpFiles = array(); - - static public function getFileInfo($path, $includeMountPoints = true){ - return \OC\Files\Filesystem::getFileInfo($path, $includeMountPoints); - } /** - * @param string $path + * @param string $filename + * @param string $name + * @param bool $zip */ - static public function getDirectoryContent($path){ - return \OC\Files\Filesystem::getDirectoryContent($path); + private static function sendHeaders($filename, $name, $zip = false) { + OC_Response::setContentDispositionHeader($name, 'attachment'); + header('Content-Transfer-Encoding: binary'); + OC_Response::disableCaching(); + if ($zip) { + header('Content-Type: application/zip'); + } else { + $filesize = \OC\Files\Filesystem::filesize($filename); + header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); + if ($filesize > -1) { + header("Content-Length: ".$filesize); + } + } } /** @@ -54,97 +71,50 @@ class OC_Files { $xsendfile = true; } - if (is_array($files) && count($files) == 1) { + if (is_array($files) && count($files) === 1) { $files = $files[0]; } if (is_array($files)) { - self::validateZipDownload($dir, $files); - $executionTime = intval(ini_get('max_execution_time')); - set_time_limit(0); - $zip = new ZipArchive(); - $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { - $l = OC_L10N::get('lib'); - throw new Exception($l->t('cannot open "%s"', array($filename))); - } - foreach ($files as $file) { - $file = $dir . '/' . $file; - if (\OC\Files\Filesystem::is_file($file)) { - $tmpFile = \OC\Files\Filesystem::toTmpFile($file); - self::$tmpFiles[] = $tmpFile; - $zip->addFile($tmpFile, basename($file)); - } elseif (\OC\Files\Filesystem::is_dir($file)) { - self::zipAddDir($file, $zip); - } - } - $zip->close(); - if ($xsendfile) { - $filename = OC_Helper::moveToNoClean($filename); - } + $get_type = GET_TYPE::ZIP_FILES; $basename = basename($dir); if ($basename) { $name = $basename . '.zip'; } else { $name = 'download.zip'; } - - set_time_limit($executionTime); - } elseif (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { - self::validateZipDownload($dir, $files); - $executionTime = intval(ini_get('max_execution_time')); - set_time_limit(0); - $zip = new ZipArchive(); - $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { - $l = OC_L10N::get('lib'); - throw new Exception($l->t('cannot open "%s"', array($filename))); - } - $file = $dir . '/' . $files; - self::zipAddDir($file, $zip); - $zip->close(); - if ($xsendfile) { - $filename = OC_Helper::moveToNoClean($filename); - } - // downloading root ? - if ($files === '') { - $name = 'download.zip'; + + $filename = $dir . '/' . $name; + } else { + $filename = $dir . '/' . $files; + if (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { + $get_type = GET_TYPE::ZIP_DIR; + // downloading root ? + if ($files === '') { + $name = 'download.zip'; + } else { + $name = $files . '.zip'; + } + } else { - $name = $files . '.zip'; + $get_type = GET_TYPE::FILE; + $name = $files; } - set_time_limit($executionTime); - } else { + } + + if ($get_type === GET_TYPE::FILE) { $zip = false; - $filename = $dir . '/' . $files; - $name = $files; if ($xsendfile && OC_App::isEnabled('files_encryption')) { $xsendfile = false; } + } else { + self::validateZipDownload($dir, $files); + $zip = new ZipStreamer(false); } OC_Util::obEnd(); if ($zip or \OC\Files\Filesystem::isReadable($filename)) { - OC_Response::setContentDispositionHeader($name, 'attachment'); - header('Content-Transfer-Encoding: binary'); - OC_Response::disableCaching(); - if ($zip) { - ini_set('zlib.output_compression', 'off'); - header('Content-Type: application/zip'); - header('Content-Length: ' . filesize($filename)); - self::addSendfileHeader($filename); - }else{ - $filesize = \OC\Files\Filesystem::filesize($filename); - header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); - if ($filesize > -1) { - header("Content-Length: ".$filesize); - } - if ($xsendfile) { - list($storage) = \OC\Files\Filesystem::resolvePath(\OC\Files\Filesystem::getView()->getAbsolutePath($filename)); - if ($storage->isLocal()) { - self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); - } - } - } - } elseif ($zip or !\OC\Files\Filesystem::file_exists($filename)) { + self::sendHeaders($filename, $name, $zip); + } elseif (!\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OC_Template('', '404', 'guest'); $tmpl->assign('file', $name); @@ -157,23 +127,36 @@ class OC_Files { return ; } if ($zip) { - $handle = fopen($filename, 'r'); - if ($handle) { - $chunkSize = 8 * 1024; // 1 MB chunks - while (!feof($handle)) { - echo fread($handle, $chunkSize); - flush(); + $executionTime = intval(ini_get('max_execution_time')); + set_time_limit(0); + if ($get_type === GET_TYPE::ZIP_FILES) { + foreach ($files as $file) { + $file = $dir . '/' . $file; + if (\OC\Files\Filesystem::is_file($file)) { + $fh = \OC\Files\Filesystem::fopen($file, 'r'); + $zip->addFileFromStream($fh, basename($file)); + fclose($fh); + } elseif (\OC\Files\Filesystem::is_dir($file)) { + self::zipAddDir($file, $zip); + } } + } elseif ($get_type === GET_TYPE::ZIP_DIR) { + $file = $dir . '/' . $files; + self::zipAddDir($file, $zip); } - if (!$xsendfile) { - unlink($filename); - } - }else{ - \OC\Files\Filesystem::readfile($filename); - } - foreach (self::$tmpFiles as $tmpFile) { - if (file_exists($tmpFile) and is_file($tmpFile)) { - unlink($tmpFile); + $zip->finalize(); + set_time_limit($executionTime); + } else { + if ($xsendfile) { + /** @var $storage \OC\Files\Storage\Storage */ + list($storage) = \OC\Files\Filesystem::resolvePath($filename); + if ($storage->isLocal()) { + self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); + } else { + \OC\Files\Filesystem::readfile($filename); + } + } else { + \OC\Files\Filesystem::readfile($filename); } } } @@ -186,10 +169,10 @@ class OC_Files { header("X-Sendfile: " . $filename); } if (isset($_SERVER['MOD_X_SENDFILE2_ENABLED'])) { - if (isset($_SERVER['HTTP_RANGE']) && + if (isset($_SERVER['HTTP_RANGE']) && preg_match("/^bytes=([0-9]+)-([0-9]*)$/", $_SERVER['HTTP_RANGE'], $range)) { $filelength = filesize($filename); - if ($range[2] == "") { + if ($range[2] === "") { $range[2] = $filelength - 1; } header("Content-Range: bytes $range[1]-$range[2]/" . $filelength); @@ -199,7 +182,7 @@ class OC_Files { header("X-Sendfile: " . $filename); } } - + if (isset($_SERVER['MOD_X_ACCEL_REDIRECT_ENABLED'])) { header("X-Accel-Redirect: " . $filename); } @@ -207,22 +190,27 @@ class OC_Files { /** * @param string $dir - * @param ZipArchive $zip + * @param ZipStreamer $zip + * @param string $internalDir */ public static function zipAddDir($dir, $zip, $internalDir='') { $dirname=basename($dir); - $zip->addEmptyDir($internalDir.$dirname); + $rootDir = $internalDir.$dirname; + if (!empty($rootDir)) { + $zip->addEmptyDir($rootDir); + } $internalDir.=$dirname.='/'; // prevent absolute dirs $internalDir = ltrim($internalDir, '/'); - $files=OC_Files::getDirectoryContent($dir); + + $files=\OC\Files\Filesystem::getDirectoryContent($dir); foreach($files as $file) { $filename=$file['name']; $file=$dir.'/'.$filename; if(\OC\Files\Filesystem::is_file($file)) { - $tmpFile=\OC\Files\Filesystem::toTmpFile($file); - OC_Files::$tmpFiles[]=$tmpFile; - $zip->addFile($tmpFile, $internalDir.$filename); + $fh = \OC\Files\Filesystem::fopen($file, 'r'); + $zip->addFileFromStream($fh, $internalDir.$filename); + fclose($fh); }elseif(\OC\Files\Filesystem::is_dir($file)) { self::zipAddDir($file, $zip, $internalDir); } @@ -232,8 +220,8 @@ class OC_Files { /** * checks if the selected files are within the size constraint. If not, outputs an error page. * - * @param string $dir - * @param files $files + * @param string $dir + * @param array | string $files */ static function validateZipDownload($dir, $files) { if (!OC_Config::getValue('allowZipDownload', true)) { @@ -280,8 +268,8 @@ class OC_Files { /** * set the maximum upload size limit for apache hosts using .htaccess * - * @param int size filesisze in bytes - * @return false on failure, size on success + * @param int $size file size in bytes + * @return bool false on failure, size on success */ static function setUploadLimit($size) { //don't allow user to break his config -- upper boundary @@ -297,11 +285,12 @@ class OC_Files { } //don't allow user to break his config -- broken or malicious size input - if (intval($size) == 0) { + if (intval($size) === 0) { return false; } - $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess'); //supress errors in case we don't have permissions for + //suppress errors in case we don't have permissions for + $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess'); if (!$htaccess) { return false; } @@ -319,7 +308,7 @@ class OC_Files { if ($content !== null) { $htaccess = $content; } - if ($hasReplaced == 0) { + if ($hasReplaced === 0) { $htaccess .= "\n" . $setting; } } diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 4cab4619149..9b18257088c 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -166,6 +166,16 @@ class Cache { */ public function getFolderContents($folder) { $fileId = $this->getId($folder); + return $this->getFolderContentsById($fileId); + } + + /** + * get the metadata of all files stored in $folder + * + * @param int $fileId the file id of the folder + * @return array + */ + public function getFolderContentsById($fileId) { if ($fileId > -1) { $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `unencrypted_size`, `etag` diff --git a/lib/private/files/cache/homecache.php b/lib/private/files/cache/homecache.php index a35e4d5e1b9..82f31d0867d 100644 --- a/lib/private/files/cache/homecache.php +++ b/lib/private/files/cache/homecache.php @@ -24,15 +24,20 @@ class HomeCache extends Cache { $entry = $this->get($path); if ($entry && $entry['mimetype'] === 'httpd/unix-directory') { $id = $entry['fileid']; - $sql = 'SELECT SUM(`size`) FROM `*PREFIX*filecache` ' . + $sql = 'SELECT SUM(`size`) AS f1, ' . + 'SUM(`unencrypted_size`) AS f2 FROM `*PREFIX*filecache` ' . 'WHERE `parent` = ? AND `storage` = ? AND `size` >= 0'; $result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId())); if ($row = $result->fetchRow()) { - list($sum) = array_values($row); + list($sum, $unencryptedSum) = array_values($row); $totalSize = (int)$sum; + $unencryptedSize = (int)$unencryptedSum; if ($entry['size'] !== $totalSize) { $this->update($id, array('size' => $totalSize)); } + if ($entry['unencrypted_size'] !== $unencryptedSize) { + $this->update($id, array('unencrypted_size' => $unencryptedSize)); + } } } return $totalSize; diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 2dbdd80a26b..d6940f50bf1 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -53,7 +53,13 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { } public function offsetGet($offset) { - return $this->data[$offset]; + if ($offset === 'type') { + return $this->getType(); + } elseif (isset($this->data[$offset])) { + return $this->data[$offset]; + } else { + return null; + } } /** @@ -144,10 +150,14 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return \OCP\Files\FileInfo::TYPE_FILE | \OCP\Files\FileInfo::TYPE_FOLDER */ public function getType() { - return $this->data['type']; + if (isset($this->data['type'])) { + return $this->data['type']; + } else { + return $this->getMimetype() === 'httpd/unix-directory' ? self::TYPE_FOLDER : self::TYPE_FILE; + } } - public function getData(){ + public function getData() { return $this->data; } diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php index 952f9f9febf..6478854eae8 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -320,7 +320,8 @@ class Filesystem { else { self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user); } - $mount_file = \OC_Config::getValue("mount_file", \OC::$SERVERROOT . "/data/mount.json"); + $datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data"); + $mount_file = \OC_Config::getValue("mount_file", $datadir . "/mount.json"); //move config file to it's new position if (is_file(\OC::$SERVERROOT . '/config/mount.json')) { @@ -760,7 +761,7 @@ class Filesystem { * * @param string $directory path under datadirectory * @param string $mimetype_filter limit returned content to this mimetype or mimepart - * @return array + * @return \OC\Files\FileInfo[] */ public static function getDirectoryContent($directory, $mimetype_filter = '') { return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 9e826dd6192..3c078d7b1b4 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -27,6 +27,11 @@ abstract class Common implements \OC\Files\Storage\Storage { protected $watcher; protected $storageCache; + /** + * @var string[] + */ + protected $cachedFiles = array(); + public function __construct($parameters) { } @@ -122,11 +127,13 @@ abstract class Common implements \OC\Files\Storage\Storage { public function file_put_contents($path, $data) { $handle = $this->fopen($path, "w"); + $this->removeCachedFile($path); return fwrite($handle, $data); } public function rename($path1, $path2) { if ($this->copy($path1, $path2)) { + $this->removeCachedFile($path1); return $this->unlink($path1); } else { return false; @@ -137,6 +144,7 @@ abstract class Common implements \OC\Files\Storage\Storage { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); list($count, $result) = \OC_Helper::streamCopy($source, $target); + $this->removeCachedFile($path2); return $result; } @@ -162,13 +170,14 @@ abstract class Common implements \OC\Files\Storage\Storage { } public function getLocalFile($path) { - return $this->toTmpFile($path); + return $this->getCachedFile($path); } /** * @param string $path + * @return string */ - private function toTmpFile($path) { //no longer in the storage api, still useful here + protected function toTmpFile($path) { //no longer in the storage api, still useful here $source = $this->fopen($path, 'r'); if (!$source) { return false; @@ -352,4 +361,15 @@ abstract class Common implements \OC\Files\Storage\Storage { // default, which is not local return false; } + + protected function getCachedFile($path) { + if (!isset($this->cachedFiles[$path])) { + $this->cachedFiles[$path] = $this->toTmpFile($path); + } + return $this->cachedFiles[$path]; + } + + protected function removeCachedFile($path) { + unset($this->cachedFiles[$path]); + } } diff --git a/lib/private/files/storage/wrapper/quota.php b/lib/private/files/storage/wrapper/quota.php index 26c952e694a..ea612735477 100644 --- a/lib/private/files/storage/wrapper/quota.php +++ b/lib/private/files/storage/wrapper/quota.php @@ -36,6 +36,11 @@ class Quota extends Wrapper { $cache = $this->getCache(); $data = $cache->get($path); if (is_array($data) and isset($data['size'])) { + if (isset($data['unencrypted_size']) + && $data['unencrypted_size'] > 0 + ) { + return $data['unencrypted_size']; + } return $data['size']; } else { return \OC\Files\SPACE_NOT_COMPUTED; diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 6f235be8e34..2dbbf5b88c9 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -882,12 +882,13 @@ class View { $watcher->checkUpdate($internalPath); } + $folderId = $cache->getId($internalPath); $files = array(); - $contents = $cache->getFolderContents($internalPath); //TODO: mimetype_filter + $contents = $cache->getFolderContents($internalPath, $folderId); //TODO: mimetype_filter foreach ($contents as $content) { $files[] = new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content); } - $permissions = $permissionsCache->getDirectoryPermissions($cache->getId($internalPath), $user); + $permissions = $permissionsCache->getDirectoryPermissions($folderId, $user); $ids = array(); foreach ($files as $i => $file) { diff --git a/lib/private/helper.php b/lib/private/helper.php index 1aab2f296e1..b9956d5ec1c 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -64,7 +64,7 @@ class OC_Helper { */ public static function linkToDocs($key) { $theme = new OC_Defaults(); - return $theme->getDocBaseUrl() . '/server/6.0/go.php?to=' . $key; + return $theme->buildDocLinkToKey($key); } /** @@ -839,7 +839,7 @@ class OC_Helper { * @return int number of bytes representing */ public static function maxUploadFilesize($dir, $freeSpace = null) { - if (is_null($freeSpace)){ + if (is_null($freeSpace) || $freeSpace < 0){ $freeSpace = self::freeSpace($dir); } return min($freeSpace, self::uploadLimit()); diff --git a/lib/private/image.php b/lib/private/image.php index 17caaa012f5..a4a23f0f097 100644 --- a/lib/private/image.php +++ b/lib/private/image.php @@ -41,8 +41,7 @@ class OC_Image { // exif_imagetype throws "read error!" if file is less than 12 byte if (filesize($filePath) > 11) { $imageType = exif_imagetype($filePath); - } - else { + } else { $imageType = false; } return $imageType ? image_type_to_mime_type($imageType) : ''; @@ -50,7 +49,7 @@ class OC_Image { /** * @brief Constructor. - * @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function. + * @param string|resource $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function. * @returns bool False on error */ public function __construct($imageRef = null) { @@ -115,13 +114,11 @@ class OC_Image { case 3: case 4: // Not tested return $this->width(); - break; case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->height(); - break; } return $this->width(); } @@ -140,13 +137,11 @@ class OC_Image { case 3: case 4: // Not tested return $this->height(); - break; case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->width(); - break; } return $this->height(); } @@ -197,7 +192,6 @@ class OC_Image { return false; } - $retVal = false; switch($this->imageType) { case IMAGETYPE_GIF: $retVal = imagegif($this->resource, $filePath); @@ -264,8 +258,8 @@ class OC_Image { } /** - * @returns Returns a base64 encoded string suitable for embedding in a VCard. - */ + * @return string - base64 encoded, which is suitable for embedding in a VCard. + */ function __toString() { return base64_encode($this->data()); } @@ -307,47 +301,37 @@ class OC_Image { $o = $this->getOrientation(); OC_Log::write('core', 'OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG); $rotate = 0; - $flip = false; switch($o) { case -1: return false; //Nothing to fix - break; case 1: $rotate = 0; - $flip = false; break; case 2: // Not tested $rotate = 0; - $flip = true; break; case 3: $rotate = 180; - $flip = false; break; case 4: // Not tested $rotate = 180; - $flip = true; break; case 5: // Not tested $rotate = 90; - $flip = true; break; case 6: //$rotate = 90; $rotate = 270; - $flip = false; break; case 7: // Not tested $rotate = 270; - $flip = true; break; case 8: $rotate = 90; - $flip = false; break; } if($rotate) { - $res = imagerotate($this->resource, $rotate, -1); + $res = imagerotate($this->resource, $rotate, 0); if($res) { if(imagealphablending($res, true)) { if(imagesavealpha($res, true)) { @@ -367,6 +351,7 @@ class OC_Image { return false; } } + return false; } /** @@ -382,10 +367,10 @@ class OC_Image { } elseif(in_array(get_resource_type($imageRef), array('file', 'stream'))) { return $this->loadFromFileHandle($imageRef); } - } elseif($this->loadFromFile($imageRef) !== false) { - return $this->resource; } elseif($this->loadFromBase64($imageRef) !== false) { return $this->resource; + } elseif($this->loadFromFile($imageRef) !== false) { + return $this->resource; } elseif($this->loadFromData($imageRef) !== false) { return $this->resource; } else { @@ -416,7 +401,6 @@ class OC_Image { public function loadFromFile($imagePath=false) { // exif_imagetype throws "read error!" if file is less than 12 byte if(!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) { - // Debug output disabled because this method is tried before loadFromBase64? OC_Log::write('core', 'OC_Image->loadFromFile, couldn\'t load: ' . (string) urlencode($imagePath), OC_Log::DEBUG); return false; } @@ -599,9 +583,9 @@ class OC_Image { $meta['imagesize'] = $meta['filesize'] - $meta['offset']; // in rare cases filesize is equal to offset so we need to read physical size if ($meta['imagesize'] < 1) { - $meta['imagesize'] = @filesize($filename) - $meta['offset']; + $meta['imagesize'] = @filesize($fileName) - $meta['offset']; if ($meta['imagesize'] < 1) { - trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING); + trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $fileName . '!', E_USER_WARNING); return false; } } @@ -947,7 +931,7 @@ if ( ! function_exists( 'imagebmp') ) { $index = imagecolorat($im, $i, $j); if ($index !== $lastIndex || $sameNum > 255) { if ($sameNum != 0) { - $bmpData .= chr($same_num) . chr($lastIndex); + $bmpData .= chr($sameNum) . chr($lastIndex); } $lastIndex = $index; $sameNum = 1; diff --git a/lib/private/installer.php b/lib/private/installer.php index 11633a4d4a1..64e8e3a5e7a 100644 --- a/lib/private/installer.php +++ b/lib/private/installer.php @@ -464,7 +464,7 @@ class OC_Installer{ // is the code checker enabled? if(OC_Config::getValue('appcodechecker', true)) { // check if grep is installed - $grep = exec('which grep'); + $grep = exec('command -v grep'); if($grep=='') { OC_Log::write('core', 'grep not installed. So checking the code of the app "'.$appname.'" was not possible', diff --git a/lib/private/l10n.php b/lib/private/l10n.php index ad979a92870..197b2d6791b 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -73,8 +73,8 @@ class OC_L10N implements \OCP\IL10N { /** * get an L10N instance - * @param $app string - * @param $lang string|null + * @param string $app + * @param string|null $lang * @return OC_L10N */ public static function get($app, $lang=null) { @@ -87,8 +87,8 @@ class OC_L10N implements \OCP\IL10N { /** * @brief The constructor - * @param $app string app requesting l10n - * @param $lang string default: null Language + * @param string $app app requesting l10n + * @param string $lang default: null Language * @returns OC_L10N-Object * * If language is not set, the constructor tries to find the right @@ -237,7 +237,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Translating - * @param $text String The text we need a translation for + * @param string $text The text we need a translation for * @param array $parameters default:array() Parameters for sprintf * @return \OC_L10N_String Translation or the same text * @@ -250,9 +250,9 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Translating - * @param $text_singular String the string to translate for exactly one object - * @param $text_plural String the string to translate for n objects - * @param $count Integer Number of objects + * @param string $text_singular the string to translate for exactly one object + * @param string $text_plural the string to translate for n objects + * @param integer $count Number of objects * @param array $parameters default:array() Parameters for sprintf * @return \OC_L10N_String Translation or the same text * @@ -351,7 +351,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Localization - * @param $type Type of localization + * @param string $type Type of localization * @param $params parameters for this localization * @returns String or false * @@ -406,7 +406,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief Choose a language - * @param $texts Associative Array with possible strings + * @param array $text Associative Array with possible strings * @returns String * * $text is an array 'de' => 'hallo welt', 'en' => 'hello world', ... @@ -421,7 +421,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief find the best language - * @param $app Array or string, details below + * @param array|string $app details below * @returns string language * * If $app is an array, ownCloud assumes that these are the available @@ -494,7 +494,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief find the l10n directory - * @param $app App that needs to be translated + * @param string $app App that needs to be translated * @returns directory */ protected static function findI18nDir($app) { @@ -514,7 +514,7 @@ class OC_L10N implements \OCP\IL10N { /** * @brief find all available languages for an app - * @param $app App that needs to be translated + * @param string $app App that needs to be translated * @returns array an array of available languages */ public static function findAvailableLanguages($app=null) { @@ -533,7 +533,9 @@ class OC_L10N implements \OCP\IL10N { } /** + * @param string $app * @param string $lang + * @returns bool */ public static function languageExists($app, $lang) { if ($lang == 'en') {//english is always available diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php index 9bd07b89023..a216414c9dd 100644 --- a/lib/private/mimetypes.list.php +++ b/lib/private/mimetypes.list.php @@ -31,6 +31,7 @@ return array( 'bash' => 'text/x-shellscript', 'blend' => 'application/x-blender', 'bin' => 'application/x-bin', + 'bmp' => 'image/bmp', 'cb7' => 'application/x-cbr', 'cba' => 'application/x-cbr', 'cbr' => 'application/x-cbr', diff --git a/lib/private/ocs/cloud.php b/lib/private/ocs/cloud.php index cbbf3b626f5..06d6a8eb4b0 100644 --- a/lib/private/ocs/cloud.php +++ b/lib/private/ocs/cloud.php @@ -61,17 +61,29 @@ class OC_OCS_Cloud { * the user from whom the information will be returned */ public static function getUser($parameters) { + $return = array(); // Check if they are viewing information on themselves if($parameters['userid'] === OC_User::getUser()) { // Self lookup $storage = OC_Helper::getStorageInfo('/'); - $quota = array( + $return['quota'] = array( 'free' => $storage['free'], 'used' => $storage['used'], 'total' => $storage['total'], 'relative' => $storage['relative'], ); - return new OC_OCS_Result(array('quota' => $quota)); + } + if(OC_User::isAdminUser(OC_User::getUser()) + || OC_Subadmin::isUserAccessible(OC_User::getUser(), $parameters['userid'])) { + if(OC_User::userExists($parameters['userid'])) { + // Is an admin/subadmin so can see display name + $return['displayname'] = OC_User::getDisplayName($parameters['userid']); + } else { + return new OC_OCS_Result(null, 101); + } + } + if(count($return)) { + return new OC_OCS_Result($return); } else { // No permission to view this user data return new OC_OCS_Result(null, 997); diff --git a/lib/private/preview.php b/lib/private/preview.php index 80fd003ed8d..74051fbc2a3 100755 --- a/lib/private/preview.php +++ b/lib/private/preview.php @@ -53,6 +53,11 @@ class Preview { static private $registeredProviders = array(); /** + * @var \OCP\Files\FileInfo + */ + protected $info; + + /** * @brief check if thumbnail or bigger version of thumbnail of file is cached * @param string $user userid - if no user is given, OC_User::getUser will be used * @param string $root path of root @@ -61,12 +66,12 @@ class Preview { * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image * @param bool $scalingUp Disable/Enable upscaling of previews * @return mixed (bool / string) - * false if thumbnail does not exist - * path to thumbnail if thumbnail exists - */ - public function __construct($user='', $root='/', $file='', $maxX=1, $maxY=1, $scalingUp=true) { + * false if thumbnail does not exist + * path to thumbnail if thumbnail exists + */ + public function __construct($user = '', $root = '/', $file = '', $maxX = 1, $maxY = 1, $scalingUp = true) { //init fileviews - if($user === ''){ + if ($user === '') { $user = \OC_User::getUser(); } $this->fileView = new \OC\Files\View('/' . $user . '/' . $root); @@ -86,11 +91,11 @@ class Preview { $this->preview = null; //check if there are preview backends - if(empty(self::$providers)) { + if (empty(self::$providers)) { self::initProviders(); } - if(empty(self::$providers)) { + if (empty(self::$providers)) { \OC_Log::write('core', 'No preview providers exist', \OC_Log::ERROR); throw new \Exception('No preview providers'); } @@ -99,15 +104,15 @@ class Preview { /** * @brief returns the path of the file you want a thumbnail from * @return string - */ - public function getFile() { + */ + public function getFile() { return $this->file; } /** * @brief returns the max width of the preview * @return integer - */ + */ public function getMaxX() { return $this->maxX; } @@ -115,7 +120,7 @@ class Preview { /** * @brief returns the max height of the preview * @return integer - */ + */ public function getMaxY() { return $this->maxY; } @@ -123,7 +128,7 @@ class Preview { /** * @brief returns whether or not scalingup is enabled * @return bool - */ + */ public function getScalingUp() { return $this->scalingup; } @@ -131,7 +136,7 @@ class Preview { /** * @brief returns the name of the thumbnailfolder * @return string - */ + */ public function getThumbnailsFolder() { return self::THUMBNAILS_FOLDER; } @@ -139,7 +144,7 @@ class Preview { /** * @brief returns the max scale factor * @return string - */ + */ public function getMaxScaleFactor() { return $this->maxScaleFactor; } @@ -147,7 +152,7 @@ class Preview { /** * @brief returns the max width set in ownCloud's config * @return string - */ + */ public function getConfigMaxX() { return $this->configMaxX; } @@ -155,20 +160,28 @@ class Preview { /** * @brief returns the max height set in ownCloud's config * @return string - */ + */ public function getConfigMaxY() { return $this->configMaxY; } + protected function getFileInfo() { + if (!$this->info) { + $this->info = $this->fileView->getFileInfo($this->file); + } + return $this->info; + } + /** * @brief set the path of the file you want a thumbnail from * @param string $file * @return $this - */ + */ public function setFile($file) { $this->file = $file; + $this->info = null; if ($file !== '') { - $this->mimetype = $this->fileView->getMimeType($this->file); + $this->mimetype = $this->getFileInfo()->getMimetype(); } return $this; } @@ -185,14 +198,14 @@ class Preview { * @brief set the the max width of the preview * @param int $maxX * @return $this - */ - public function setMaxX($maxX=1) { - if($maxX <= 0) { + */ + public function setMaxX($maxX = 1) { + if ($maxX <= 0) { throw new \Exception('Cannot set width of 0 or smaller!'); } $configMaxX = $this->getConfigMaxX(); - if(!is_null($configMaxX)) { - if($maxX > $configMaxX) { + if (!is_null($configMaxX)) { + if ($maxX > $configMaxX) { \OC_Log::write('core', 'maxX reduced from ' . $maxX . ' to ' . $configMaxX, \OC_Log::DEBUG); $maxX = $configMaxX; } @@ -205,14 +218,14 @@ class Preview { * @brief set the the max height of the preview * @param int $maxY * @return $this - */ - public function setMaxY($maxY=1) { - if($maxY <= 0) { + */ + public function setMaxY($maxY = 1) { + if ($maxY <= 0) { throw new \Exception('Cannot set height of 0 or smaller!'); } $configMaxY = $this->getConfigMaxY(); - if(!is_null($configMaxY)) { - if($maxY > $configMaxY) { + if (!is_null($configMaxY)) { + if ($maxY > $configMaxY) { \OC_Log::write('core', 'maxX reduced from ' . $maxY . ' to ' . $configMaxY, \OC_Log::DEBUG); $maxY = $configMaxY; } @@ -225,9 +238,9 @@ class Preview { * @brief set whether or not scalingup is enabled * @param bool $scalingUp * @return $this - */ + */ public function setScalingup($scalingUp) { - if($this->getMaxScaleFactor() === 1) { + if ($this->getMaxScaleFactor() === 1) { $scalingUp = false; } $this->scalingup = $scalingUp; @@ -237,15 +250,15 @@ class Preview { /** * @brief check if all parameters are valid * @return bool - */ + */ public function isFileValid() { $file = $this->getFile(); - if($file === '') { + if ($file === '') { \OC_Log::write('core', 'No filename passed', \OC_Log::DEBUG); return false; } - if(!$this->fileView->file_exists($file)) { + if (!$this->fileView->file_exists($file)) { \OC_Log::write('core', 'File:"' . $file . '" not found', \OC_Log::DEBUG); return false; } @@ -256,40 +269,38 @@ class Preview { /** * @brief deletes previews of a file with specific x and y * @return bool - */ + */ public function deletePreview() { $file = $this->getFile(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $fileInfo = $this->getFileInfo($file); + $fileId = $fileInfo->getId(); $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/' . $this->getMaxX() . '-' . $this->getMaxY() . '.png'; - $this->userView->unlink($previewPath); - return !$this->userView->file_exists($previewPath); + return $this->userView->unlink($previewPath); } /** * @brief deletes all previews of a file * @return bool - */ + */ public function deleteAllPreviews() { $file = $this->getFile(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $fileInfo = $this->getFileInfo($file); + $fileId = $fileInfo->getId(); $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; $this->userView->deleteAll($previewPath); - $this->userView->rmdir($previewPath); - return !$this->userView->is_dir($previewPath); + return $this->userView->rmdir($previewPath); } /** * @brief check if thumbnail or bigger version of thumbnail of file is cached * @return mixed (bool / string) - * false if thumbnail does not exist - * path to thumbnail if thumbnail exists - */ + * false if thumbnail does not exist + * path to thumbnail if thumbnail exists + */ private function isCached() { $file = $this->getFile(); $maxX = $this->getMaxX(); @@ -297,75 +308,75 @@ class Preview { $scalingUp = $this->getScalingUp(); $maxScaleFactor = $this->getMaxScaleFactor(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $fileInfo = $this->getFileInfo($file); + $fileId = $fileInfo->getId(); - if(is_null($fileId)) { + if (is_null($fileId)) { return false; } $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; - if(!$this->userView->is_dir($previewPath)) { + if (!$this->userView->is_dir($previewPath)) { return false; } //does a preview with the wanted height and width already exist? - if($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) { + if ($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) { return $previewPath . $maxX . '-' . $maxY . '.png'; } - $wantedAspectRatio = (float) ($maxX / $maxY); + $wantedAspectRatio = (float)($maxX / $maxY); //array for usable cached thumbnails $possibleThumbnails = array(); $allThumbnails = $this->userView->getDirectoryContent($previewPath); - foreach($allThumbnails as $thumbnail) { + foreach ($allThumbnails as $thumbnail) { $name = rtrim($thumbnail['name'], '.png'); $size = explode('-', $name); - $x = (int) $size[0]; - $y = (int) $size[1]; + $x = (int)$size[0]; + $y = (int)$size[1]; - $aspectRatio = (float) ($x / $y); - if($aspectRatio !== $wantedAspectRatio) { + $aspectRatio = (float)($x / $y); + if ($aspectRatio !== $wantedAspectRatio) { continue; } - if($x < $maxX || $y < $maxY) { - if($scalingUp) { + if ($x < $maxX || $y < $maxY) { + if ($scalingUp) { $scalefactor = $maxX / $x; - if($scalefactor > $maxScaleFactor) { + if ($scalefactor > $maxScaleFactor) { continue; } - }else{ + } else { continue; } } $possibleThumbnails[$x] = $thumbnail['path']; } - if(count($possibleThumbnails) === 0) { + if (count($possibleThumbnails) === 0) { return false; } - if(count($possibleThumbnails) === 1) { + if (count($possibleThumbnails) === 1) { return current($possibleThumbnails); } ksort($possibleThumbnails); - if(key(reset($possibleThumbnails)) > $maxX) { + if (key(reset($possibleThumbnails)) > $maxX) { return current(reset($possibleThumbnails)); } - if(key(end($possibleThumbnails)) < $maxX) { + if (key(end($possibleThumbnails)) < $maxX) { return current(end($possibleThumbnails)); } - foreach($possibleThumbnails as $width => $path) { - if($width < $maxX) { + foreach ($possibleThumbnails as $width => $path) { + if ($width < $maxX) { continue; - }else{ + } else { return $path; } } @@ -374,9 +385,9 @@ class Preview { /** * @brief return a preview of a file * @return \OC_Image - */ + */ public function getPreview() { - if(!is_null($this->preview) && $this->preview->valid()){ + if (!is_null($this->preview) && $this->preview->valid()) { return $this->preview; } @@ -386,22 +397,25 @@ class Preview { $maxY = $this->getMaxY(); $scalingUp = $this->getScalingUp(); - $fileInfo = $this->fileView->getFileInfo($file); - $fileId = $fileInfo['fileid']; + $fileInfo = $this->getFileInfo($file); + $fileId = $fileInfo->getId(); $cached = $this->isCached(); - if($cached) { - $image = new \OC_Image($this->userView->file_get_contents($cached, 'r')); + if ($cached) { + $stream = $this->userView->fopen($cached, 'r'); + $image = new \OC_Image(); + $image->loadFromFileHandle($stream); $this->preview = $image->valid() ? $image : null; $this->resizeAndCrop(); + fclose($stream); } - if(is_null($this->preview)) { + if (is_null($this->preview)) { $preview = null; - foreach(self::$providers as $supportedMimetype => $provider) { - if(!preg_match($supportedMimetype, $this->mimetype)) { + foreach (self::$providers as $supportedMimetype => $provider) { + if (!preg_match($supportedMimetype, $this->mimetype)) { continue; } @@ -409,7 +423,7 @@ class Preview { $preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingUp, $this->fileView); - if(!($preview instanceof \OC_Image)) { + if (!($preview instanceof \OC_Image)) { continue; } @@ -419,11 +433,11 @@ class Preview { $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; $cachePath = $previewPath . $maxX . '-' . $maxY . '.png'; - if($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) { + if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) { $this->userView->mkdir($this->getThumbnailsFolder() . '/'); } - if($this->userView->is_dir($previewPath) === false) { + if ($this->userView->is_dir($previewPath) === false) { $this->userView->mkdir($previewPath); } @@ -433,7 +447,7 @@ class Preview { } } - if(is_null($this->preview)) { + if (is_null($this->preview)) { $this->preview = new \OC_Image(); } @@ -443,10 +457,10 @@ class Preview { /** * @brief show preview * @return void - */ + */ public function showPreview() { \OCP\Response::enableCaching(3600 * 24); // 24 hours - if(is_null($this->preview)) { + if (is_null($this->preview)) { $this->getPreview(); } $this->preview->show(); @@ -456,7 +470,7 @@ class Preview { /** * @brief show preview * @return void - */ + */ public function show() { $this->showPreview(); return; @@ -465,7 +479,7 @@ class Preview { /** * @brief resize, crop and fix orientation * @return void - */ + */ private function resizeAndCrop() { $image = $this->preview; $x = $this->getMaxX(); @@ -473,17 +487,17 @@ class Preview { $scalingUp = $this->getScalingUp(); $maxscalefactor = $this->getMaxScaleFactor(); - if(!($image instanceof \OC_Image)) { + if (!($image instanceof \OC_Image)) { \OC_Log::write('core', '$this->preview is not an instance of OC_Image', \OC_Log::DEBUG); return; } $image->fixOrientation(); - $realx = (int) $image->width(); - $realy = (int) $image->height(); + $realx = (int)$image->width(); + $realy = (int)$image->height(); - if($x === $realx && $y === $realy) { + if ($x === $realx && $y === $realy) { $this->preview = $image; return; } @@ -491,36 +505,36 @@ class Preview { $factorX = $x / $realx; $factorY = $y / $realy; - if($factorX >= $factorY) { + if ($factorX >= $factorY) { $factor = $factorX; - }else{ + } else { $factor = $factorY; } - if($scalingUp === false) { - if($factor > 1) { + if ($scalingUp === false) { + if ($factor > 1) { $factor = 1; } } - if(!is_null($maxscalefactor)) { - if($factor > $maxscalefactor) { + if (!is_null($maxscalefactor)) { + if ($factor > $maxscalefactor) { \OC_Log::write('core', 'scalefactor reduced from ' . $factor . ' to ' . $maxscalefactor, \OC_Log::DEBUG); $factor = $maxscalefactor; } } - $newXsize = (int) ($realx * $factor); - $newYsize = (int) ($realy * $factor); + $newXsize = (int)($realx * $factor); + $newYsize = (int)($realy * $factor); $image->preciseResize($newXsize, $newYsize); - if($newXsize === $x && $newYsize === $y) { + if ($newXsize === $x && $newYsize === $y) { $this->preview = $image; return; } - if($newXsize >= $x && $newYsize >= $y) { + if ($newXsize >= $x && $newYsize >= $y) { $cropX = floor(abs($x - $newXsize) * 0.5); //don't crop previews on the Y axis, this sucks if it's a document. //$cropY = floor(abs($y - $newYsize) * 0.5); @@ -532,19 +546,19 @@ class Preview { return; } - if($newXsize < $x || $newYsize < $y) { - if($newXsize > $x) { + if ($newXsize < $x || $newYsize < $y) { + if ($newXsize > $x) { $cropX = floor(($newXsize - $x) * 0.5); $image->crop($cropX, 0, $x, $newYsize); } - if($newYsize > $y) { + if ($newYsize > $y) { $cropY = floor(($newYsize - $y) * 0.5); $image->crop(0, $cropY, $newXsize, $y); } - $newXsize = (int) $image->width(); - $newYsize = (int) $image->height(); + $newXsize = (int)$image->width(); + $newYsize = (int)$image->height(); //create transparent background layer $backgroundlayer = imagecreatetruecolor($x, $y); @@ -573,8 +587,8 @@ class Preview { * @param array $options * @return void */ - public static function registerProvider($class, $options=array()) { - self::$registeredProviders[]=array('class'=>$class, 'options'=>$options); + public static function registerProvider($class, $options = array()) { + self::$registeredProviders[] = array('class' => $class, 'options' => $options); } /** @@ -582,19 +596,19 @@ class Preview { * @return void */ private static function initProviders() { - if(!\OC_Config::getValue('enable_previews', true)) { + if (!\OC_Config::getValue('enable_previews', true)) { $provider = new Preview\Unknown(array()); self::$providers = array($provider->getMimeType() => $provider); return; } - if(count(self::$providers)>0) { + if (count(self::$providers) > 0) { return; } - foreach(self::$registeredProviders as $provider) { - $class=$provider['class']; - $options=$provider['options']; + foreach (self::$registeredProviders as $provider) { + $class = $provider['class']; + $options = $provider['options']; $object = new $class($options); @@ -611,7 +625,7 @@ class Preview { public static function post_delete($args) { $path = $args['path']; - if(substr($path, 0, 1) === '/') { + if (substr($path, 0, 1) === '/') { $path = substr($path, 1); } $preview = new Preview(\OC_User::getUser(), 'files/', $path); @@ -622,19 +636,19 @@ class Preview { * @param string $mimetype */ public static function isMimeSupported($mimetype) { - if(!\OC_Config::getValue('enable_previews', true)) { + if (!\OC_Config::getValue('enable_previews', true)) { return false; } //check if there are preview backends - if(empty(self::$providers)) { + if (empty(self::$providers)) { self::initProviders(); } //remove last element because it has the mimetype * $providers = array_slice(self::$providers, 0, -1); - foreach($providers as $supportedMimetype => $provider) { - if(preg_match($supportedMimetype, $mimetype)) { + foreach ($providers as $supportedMimetype => $provider) { + if (preg_match($supportedMimetype, $mimetype)) { return true; } } diff --git a/lib/private/preview/movies.php b/lib/private/preview/movies.php index 71cd3bae057..7e0ff51ad2e 100644 --- a/lib/private/preview/movies.php +++ b/lib/private/preview/movies.php @@ -9,7 +9,7 @@ namespace OC\Preview; function findBinaryPath($program) { - exec('which ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode); + exec('command -v ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode); if ($returnCode === 0 && count($output) > 0) { return escapeshellcmd($output[0]); } diff --git a/lib/private/preview/office-cl.php b/lib/private/preview/office-cl.php index b11fed13ba1..6e4d4321eb7 100644 --- a/lib/private/preview/office-cl.php +++ b/lib/private/preview/office-cl.php @@ -64,12 +64,12 @@ if (!\OC_Util::runningOnWindows()) { $cmd = \OC_Config::getValue('preview_libreoffice_path', null); } - $whichLibreOffice = shell_exec('which libreoffice'); + $whichLibreOffice = shell_exec('command -v libreoffice'); if($cmd === '' && !empty($whichLibreOffice)) { $cmd = 'libreoffice'; } - $whichOpenOffice = shell_exec('which openoffice'); + $whichOpenOffice = shell_exec('command -v openoffice'); if($cmd === '' && !empty($whichOpenOffice)) { $cmd = 'openoffice'; } diff --git a/lib/private/preview/office.php b/lib/private/preview/office.php index 02bb22e9b94..882c4426e6d 100644 --- a/lib/private/preview/office.php +++ b/lib/private/preview/office.php @@ -6,24 +6,29 @@ * See the COPYING-README file. */ //both, libreoffice backend and php fallback, need imagick -if (extension_loaded('imagick') && count(@\Imagick::queryFormats("PDF")) === 1) { - $isShellExecEnabled = \OC_Helper::is_function_enabled('shell_exec'); +if (extension_loaded('imagick')) { - // LibreOffice preview is currently not supported on Windows - if (!\OC_Util::runningOnWindows()) { - $whichLibreOffice = ($isShellExecEnabled ? shell_exec('which libreoffice') : ''); - $isLibreOfficeAvailable = !empty($whichLibreOffice); - $whichOpenOffice = ($isShellExecEnabled ? shell_exec('which libreoffice') : ''); - $isOpenOfficeAvailable = !empty($whichOpenOffice); - //let's see if there is libreoffice or openoffice on this machine - if($isShellExecEnabled && ($isLibreOfficeAvailable || $isOpenOfficeAvailable || is_string(\OC_Config::getValue('preview_libreoffice_path', null)))) { - require_once('office-cl.php'); - }else{ + $checkImagick = new Imagick(); + + if(count($checkImagick->queryFormats('PDF')) === 1) { + $isShellExecEnabled = \OC_Helper::is_function_enabled('shell_exec'); + + // LibreOffice preview is currently not supported on Windows + if (!\OC_Util::runningOnWindows()) { + $whichLibreOffice = ($isShellExecEnabled ? shell_exec('command -v libreoffice') : ''); + $isLibreOfficeAvailable = !empty($whichLibreOffice); + $whichOpenOffice = ($isShellExecEnabled ? shell_exec('command -v libreoffice') : ''); + $isOpenOfficeAvailable = !empty($whichOpenOffice); + //let's see if there is libreoffice or openoffice on this machine + if($isShellExecEnabled && ($isLibreOfficeAvailable || $isOpenOfficeAvailable || is_string(\OC_Config::getValue('preview_libreoffice_path', null)))) { + require_once('office-cl.php'); + }else{ + //in case there isn't, use our fallback + require_once('office-fallback.php'); + } + } else { //in case there isn't, use our fallback require_once('office-fallback.php'); } - } else { - //in case there isn't, use our fallback - require_once('office-fallback.php'); } } diff --git a/lib/private/preview/pdf.php b/lib/private/preview/pdf.php index d390b4fc677..064a5a3b3d1 100644 --- a/lib/private/preview/pdf.php +++ b/lib/private/preview/pdf.php @@ -7,34 +7,41 @@ */ namespace OC\Preview; -if (extension_loaded('imagick') && count(@\Imagick::queryFormats("PDF")) === 1) { +use Imagick; - class PDF extends Provider { +if (extension_loaded('imagick')) { - public function getMimeType() { - return '/application\/pdf/'; - } + $checkImagick = new Imagick(); + + if(count($checkImagick->queryFormats('PDF')) === 1) { - public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - $tmpPath = $fileview->toTmpFile($path); + class PDF extends Provider { - //create imagick object from pdf - try{ - $pdf = new \imagick($tmpPath . '[0]'); - $pdf->setImageFormat('jpg'); - } catch (\Exception $e) { - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - return false; + public function getMimeType() { + return '/application\/pdf/'; } - unlink($tmpPath); + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $tmpPath = $fileview->toTmpFile($path); + + //create imagick object from pdf + try{ + $pdf = new Imagick($tmpPath . '[0]'); + $pdf->setImageFormat('jpg'); + } catch (\Exception $e) { + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; + } + + unlink($tmpPath); - //new image object - $image = new \OC_Image($pdf); - //check if image object is valid - return $image->valid() ? $image : false; + //new image object + $image = new \OC_Image($pdf); + //check if image object is valid + return $image->valid() ? $image : false; + } } - } - \OC\Preview::registerProvider('OC\Preview\PDF'); + \OC\Preview::registerProvider('OC\Preview\PDF'); + } } diff --git a/lib/private/preview/svg.php b/lib/private/preview/svg.php index 9a73fff9467..505122fddbf 100644 --- a/lib/private/preview/svg.php +++ b/lib/private/preview/svg.php @@ -7,40 +7,46 @@ */ namespace OC\Preview; -if (extension_loaded('imagick') && count(@\Imagick::queryFormats("SVG")) === 1) { +use Imagick; - class SVG extends Provider { +if (extension_loaded('imagick')) { - public function getMimeType() { - return '/image\/svg\+xml/'; - } + $checkImagick = new Imagick(); - public function getThumbnail($path,$maxX,$maxY,$scalingup,$fileview) { - try{ - $svg = new \Imagick(); - $svg->setBackgroundColor(new \ImagickPixel('transparent')); + if(count($checkImagick->queryFormats('SVG')) === 1) { - $content = stream_get_contents($fileview->fopen($path, 'r')); - if(substr($content, 0, 5) !== '<?xml') { - $content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content; - } + class SVG extends Provider { - $svg->readImageBlob($content); - $svg->setImageFormat('png32'); - } catch (\Exception $e) { - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - return false; + public function getMimeType() { + return '/image\/svg\+xml/'; } + public function getThumbnail($path,$maxX,$maxY,$scalingup,$fileview) { + try{ + $svg = new Imagick(); + $svg->setBackgroundColor(new \ImagickPixel('transparent')); + + $content = stream_get_contents($fileview->fopen($path, 'r')); + if(substr($content, 0, 5) !== '<?xml') { + $content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content; + } + + $svg->readImageBlob($content); + $svg->setImageFormat('png32'); + } catch (\Exception $e) { + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; + } - //new image object - $image = new \OC_Image(); - $image->loadFromData($svg); - //check if image object is valid - return $image->valid() ? $image : false; - } - } - \OC\Preview::registerProvider('OC\Preview\SVG'); + //new image object + $image = new \OC_Image(); + $image->loadFromData($svg); + //check if image object is valid + return $image->valid() ? $image : false; + } + } + \OC\Preview::registerProvider('OC\Preview\SVG'); + } }
\ No newline at end of file diff --git a/lib/private/request.php b/lib/private/request.php index afd3fda4f2d..8041c4f0048 100755 --- a/lib/private/request.php +++ b/lib/private/request.php @@ -13,6 +13,8 @@ class OC_Request { const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#'; const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#'; + const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)(:[0-9]+|)$/'; + /** * @brief Check overwrite condition * @param string $type @@ -25,49 +27,91 @@ class OC_Request { } /** - * @brief Checks whether a domain is considered as trusted. This is used to prevent Host Header Poisoning. + * @brief Checks whether a domain is considered as trusted from the list + * of trusted domains. If no trusted domains have been configured, returns + * true. + * This is used to prevent Host Header Poisoning. * @param string $host - * @return bool + * @return bool true if the given domain is trusted or if no trusted domains + * have been configured */ public static function isTrustedDomain($domain) { - $trustedList = \OC_Config::getValue('trusted_domains', array('')); + $trustedList = \OC_Config::getValue('trusted_domains', array()); + if (empty($trustedList)) { + return true; + } + if (preg_match(self::REGEX_LOCALHOST, $domain) === 1) { + return true; + } return in_array($domain, $trustedList); } /** - * @brief Returns the server host + * @brief Returns the unverified server host from the headers without checking + * whether it is a trusted domain * @returns string the server host * * Returns the server host, even if the website uses one or more * reverse proxies */ - public static function serverHost() { - if(OC::$CLI) { - return 'localhost'; - } - if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) { - return OC_Config::getValue('overwritehost'); - } + public static function insecureServerHost() { + $host = null; if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) { - $host = trim(array_pop(explode(",", $_SERVER['HTTP_X_FORWARDED_HOST']))); - } - else{ + $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); + $host = trim(current($parts)); + } else { $host = $_SERVER['HTTP_X_FORWARDED_HOST']; } } else { if (isset($_SERVER['HTTP_HOST'])) { $host = $_SERVER['HTTP_HOST']; - } - else if (isset($_SERVER['SERVER_NAME'])) { + } else if (isset($_SERVER['SERVER_NAME'])) { $host = $_SERVER['SERVER_NAME']; } } + return $host; + } + + /** + * Returns the overwritehost setting from the config if set and + * if the overwrite condition is met + * @return overwritehost value or null if not defined or the defined condition + * isn't met + */ + public static function getOverwriteHost() { + if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) { + return OC_Config::getValue('overwritehost'); + } + return null; + } + + /** + * @brief Returns the server host from the headers, or the first configured + * trusted domain if the host isn't in the trusted list + * @returns string the server host + * + * Returns the server host, even if the website uses one or more + * reverse proxies + */ + public static function serverHost() { + if(OC::$CLI) { + return 'localhost'; + } + + // overwritehost is always trusted + $host = self::getOverwriteHost(); + if ($host !== null) { + return $host; + } + + // get the host from the headers + $host = self::insecureServerHost(); // Verify that the host is a trusted domain if the trusted domains // are defined // If no trusted domain is provided the first trusted domain is returned - if(self::isTrustedDomain($host) || \OC_Config::getValue('trusted_domains', "") === "") { + if (self::isTrustedDomain($host)) { return $host; } else { $trustedList = \OC_Config::getValue('trusted_domains', array('')); diff --git a/lib/private/router.php b/lib/private/router.php index 19c1e4473ec..918e3b13206 100644 --- a/lib/private/router.php +++ b/lib/private/router.php @@ -158,28 +158,4 @@ class OC_Router { return $this->getGenerator()->generate($name, $parameters, $absolute); } - /** - * Generate JSON response for routing in javascript - */ - public static function JSRoutes() - { - $router = OC::getRouter(); - - $etag = $router->getCacheKey(); - OC_Response::enableCaching(); - OC_Response::setETagHeader($etag); - - $root = $router->getCollection('root'); - $routes = array(); - foreach($root->all() as $name => $route) { - $compiled_route = $route->compile(); - $defaults = $route->getDefaults(); - unset($defaults['action']); - $routes[$name] = array( - 'tokens' => $compiled_route->getTokens(), - 'defaults' => $defaults, - ); - } - OCP\JSON::success ( array( 'data' => $routes ) ); - } } diff --git a/lib/private/setup.php b/lib/private/setup.php index 3906204bda3..0d5bf424b33 100644 --- a/lib/private/setup.php +++ b/lib/private/setup.php @@ -147,7 +147,7 @@ class OC_Setup { $content.= "RewriteRule ^.well-known/host-meta /public.php?service=host-meta [QSA,L]\n"; $content.= "RewriteRule ^.well-known/carddav /remote.php/carddav/ [R]\n"; $content.= "RewriteRule ^.well-known/caldav /remote.php/caldav/ [R]\n"; - $content.= "RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L]\n"; + $content.= "RewriteRule ^apps/([^/]*)/(.*\.(php))$ index.php?app=$1&getfile=$2 [QSA,L]\n"; $content.= "RewriteRule ^remote/(.*) remote.php [QSA,L]\n"; $content.= "</IfModule>\n"; $content.= "<IfModule mod_mime.c>\n"; diff --git a/lib/private/template/base.php b/lib/private/template/base.php index 232a29939cc..7aa0cb4a956 100644 --- a/lib/private/template/base.php +++ b/lib/private/template/base.php @@ -61,7 +61,7 @@ class Base { /** * @brief Assign variables * @param string $key key - * @param string $value value + * @param array|bool|integer|string $value value * @return bool * * This function assigns a variable. It can be accessed via $_[$key] in diff --git a/lib/public/files/fileinfo.php b/lib/public/files/fileinfo.php index 68ce45d3fa1..37162e09336 100644 --- a/lib/public/files/fileinfo.php +++ b/lib/public/files/fileinfo.php @@ -9,7 +9,7 @@ namespace OCP\Files; interface FileInfo { const TYPE_FILE = 'file'; - const TYPE_FOLDER = 'folder'; + const TYPE_FOLDER = 'dir'; /** * Get the Etag of the file or folder diff --git a/lib/public/share.php b/lib/public/share.php index ebc555dba5f..2fed41488ca 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -1250,7 +1250,21 @@ class Share { // Remove root from file source paths if retrieving own shared items if (isset($uidOwner) && isset($row['path'])) { if (isset($row['parent'])) { - $row['path'] = '/Shared/'.basename($row['path']); + $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?'); + $parentResult = $query->execute(array($row['parent'])); + if (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', 'Can\'t select parent: ' . + \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, + \OC_Log::ERROR); + } else { + $parentRow = $parentResult->fetchRow(); + $splitPath = explode('/', $row['path']); + $tmpPath = '/Shared' . $parentRow['file_target']; + foreach (array_slice($splitPath, 2) as $pathPart) { + $tmpPath = $tmpPath . '/' . $pathPart; + } + $row['path'] = $tmpPath; + } } else { if (!isset($mounts[$row['storage']])) { $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']); diff --git a/settings/admin.php b/settings/admin.php index c0e4570658a..42477bfc1ca 100755 --- a/settings/admin.php +++ b/settings/admin.php @@ -21,6 +21,16 @@ $entries=OC_Log_Owncloud::getEntries(3); $entriesremain = count(OC_Log_Owncloud::getEntries(4)) > 3; $tmpl->assign('loglevel', OC_Config::getValue( "loglevel", 2 )); +$tmpl->assign('mail_domain', OC_Config::getValue( "mail_domain", '' )); +$tmpl->assign('mail_from_address', OC_Config::getValue( "mail_from_address", '' )); +$tmpl->assign('mail_smtpmode', OC_Config::getValue( "mail_smtpmode", '' )); +$tmpl->assign('mail_smtpsecure', OC_Config::getValue( "mail_smtpsecure", '' )); +$tmpl->assign('mail_smtphost', OC_Config::getValue( "mail_smtphost", '' )); +$tmpl->assign('mail_smtpport', OC_Config::getValue( "mail_smtpport", '' )); +$tmpl->assign('mail_smtpauthtype', OC_Config::getValue( "mail_smtpauthtype", '' )); +$tmpl->assign('mail_smtpauth', OC_Config::getValue( "mail_smtpauth", false )); +$tmpl->assign('mail_smtpname', OC_Config::getValue( "mail_smtpname", '' )); +$tmpl->assign('mail_smtppassword', OC_Config::getValue( "mail_smtppassword", '' )); $tmpl->assign('entries', $entries); $tmpl->assign('entriesremain', $entriesremain); $tmpl->assign('htaccessworking', $htaccessworking); diff --git a/settings/admin/controller.php b/settings/admin/controller.php new file mode 100644 index 00000000000..7019f9a4d2a --- /dev/null +++ b/settings/admin/controller.php @@ -0,0 +1,123 @@ +<?php +/** +* @author Joas Schilling +* @copyright 2014 Joas Schilling nickvergessen@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +namespace OC\Settings\Admin; + +class Controller { + /** + * Set mail settings + */ + public static function setMailSettings() { + \OC_Util::checkAdminUser(); + \OCP\JSON::callCheck(); + + $l = \OC_L10N::get('settings'); + + $smtp_settings = array( + 'mail_domain' => null, + 'mail_from_address' => null, + 'mail_smtpmode' => array('sendmail', 'smtp', 'qmail', 'php'), + 'mail_smtpsecure' => array('', 'ssl', 'tls'), + 'mail_smtphost' => null, + 'mail_smtpport' => null, + 'mail_smtpauthtype' => array('LOGIN', 'PLAIN', 'NTLM'), + 'mail_smtpauth' => true, + 'mail_smtpname' => null, + 'mail_smtppassword' => null, + ); + + foreach ($smtp_settings as $setting => $validate) { + if (!$validate) { + if (!isset($_POST[$setting]) || $_POST[$setting] === '') { + \OC_Config::deleteKey( $setting ); + } else { + \OC_Config::setValue( $setting, $_POST[$setting] ); + } + } + else if (is_bool($validate)) { + if (!empty($_POST[$setting])) { + \OC_Config::setValue( $setting, (bool) $_POST[$setting] ); + } else { + \OC_Config::deleteKey( $setting ); + } + } + else if (is_array($validate)) { + if (!isset($_POST[$setting]) || $_POST[$setting] === '') { + \OC_Config::deleteKey( $setting ); + } else if (in_array($_POST[$setting], $validate)) { + \OC_Config::setValue( $setting, $_POST[$setting] ); + } else { + $message = $l->t('Invalid value supplied for %s', array(self::getFieldname($setting, $l))); + \OC_JSON::error( array( "data" => array( "message" => $message)) ); + exit; + } + } + } + + \OC_JSON::success(array("data" => array( "message" => $l->t("Saved") ))); + } + + /** + * Send a mail to test the settings + */ + public static function sendTestMail() { + \OC_Util::checkAdminUser(); + \OCP\JSON::callCheck(); + + $l = \OC_L10N::get('settings'); + $email = \OC_Preferences::getValue(\OC_User::getUser(), 'settings', 'email', ''); + if (!empty($email)) { + $defaults = new \OC_Defaults(); + + try { + \OC_Mail::send($email, $_POST['user'], + $l->t('test email settings'), + $l->t('If you received this email, the settings seem to be correct.'), + \OCP\Util::getDefaultEmailAddress('no-reply'), $defaults->getName()); + } catch (\Exception $e) { + $message = $l->t('A problem occurred while sending the e-mail. Please revisit your settings.'); + \OC_JSON::error( array( "data" => array( "message" => $message)) ); + exit; + } + + \OC_JSON::success(array("data" => array( "message" => $l->t("Email sent") ))); + } else { + $message = $l->t('You need to set your user email before being able to send test emails.'); + \OC_JSON::error( array( "data" => array( "message" => $message)) ); + } + } + + /** + * Get the field name to use it in error messages + * + * @param $setting string + * @param $l \OC_L10N + * @return string + */ + public static function getFieldname($setting, $l) { + switch ($setting) { + case 'mail_smtpmode': + return $l->t( 'Send mode' ); + case 'mail_smtpsecure': + return $l->t( 'Encryption' ); + case 'mail_smtpauthtype': + return $l->t( 'Authentification method' ); + } + } +} diff --git a/settings/ajax/decryptall.php b/settings/ajax/decryptall.php index d7c104ab151..4782a4cfc81 100644 --- a/settings/ajax/decryptall.php +++ b/settings/ajax/decryptall.php @@ -24,6 +24,8 @@ if ($result !== false) { $successful = false; } + $util->closeEncryptionSession(); + if ($successful === true) { \OCP\JSON::success(array('data' => array('message' => 'Files decrypted successfully'))); } else { diff --git a/settings/css/settings.css b/settings/css/settings.css index 8a96885b789..a47e7bf6563 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -61,7 +61,12 @@ td.remove { width:1em; padding-right:1em; } tr:hover>td.password>span, tr:hover>td.displayName>span { margin:0; cursor:pointer; } tr:hover>td.remove>a, tr:hover>td.password>img,tr:hover>td.displayName>img, tr:hover>td.quota>img { visibility:visible; cursor:pointer; } tr:hover>td.remove>a { float:right; } -li.selected { background-color:#ddd; } + +li.selected, +#leftcontent li.selected { + background-color: #ddd; +} + table.grid { width:100%; } #rightcontent { padding-left: 10px; } div.quota { @@ -150,6 +155,18 @@ span.connectionwarning {color:#933; font-weight:bold; } input[type=radio] { width:1em; } table.shareAPI td { padding-bottom: 0.8em; } +#mail_settings p label:first-child { + display: inline-block; + width: 300px; + text-align: right; +} +#mail_settings p select:nth-child(2) { + width: 143px; +} +#mail_smtpport { + width: 40px; +} + /* HELP */ .pressed {background-color:#DDD;} diff --git a/settings/js/admin.js b/settings/js/admin.js index e957bd68f1f..c04c0505deb 100644 --- a/settings/js/admin.js +++ b/settings/js/admin.js @@ -34,4 +34,46 @@ $(document).ready(function(){ $('#security').change(function(){ $.post(OC.filePath('settings','ajax','setsecurity.php'), { enforceHTTPS: $('#forcessl').val() },function(){} ); }); + + $('#mail_smtpauth').change(function() { + if (!this.checked) { + $('#mail_credentials').addClass('hidden'); + } else { + $('#mail_credentials').removeClass('hidden'); + } + }); + + $('#mail_smtpmode').change(function() { + if ($(this).val() !== 'smtp') { + $('#setting_smtpauth').addClass('hidden'); + $('#setting_smtphost').addClass('hidden'); + $('#mail_smtpsecure_label').addClass('hidden'); + $('#mail_smtpsecure').addClass('hidden'); + $('#mail_credentials').addClass('hidden'); + } else { + $('#setting_smtpauth').removeClass('hidden'); + $('#setting_smtphost').removeClass('hidden'); + $('#mail_smtpsecure_label').removeClass('hidden'); + $('#mail_smtpsecure').removeClass('hidden'); + if ($('#mail_smtpauth').attr('checked')) { + $('#mail_credentials').removeClass('hidden'); + } + } + }); + + $('#mail_settings').change(function(){ + OC.msg.startSaving('#mail_settings_msg'); + var post = $( "#mail_settings" ).serialize(); + $.post(OC.generateUrl('/settings/admin/mailsettings'), post, function(data){ + OC.msg.finishedSaving('#mail_settings .msg', data); + }); + }); + + $('#sendtestemail').click(function(){ + OC.msg.startAction('#sendtestmail_msg', t('settings', 'Sending...')); + var post = $( "#sendtestemail" ).serialize(); + $.post(OC.generateUrl('/settings/admin/mailtest'), post, function(data){ + OC.msg.finishedAction('#sendtestmail_msg', data); + }); + }); }); diff --git a/settings/js/apps.js b/settings/js/apps.js index 2c6f77d9314..3dbc8a2f7c2 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -207,12 +207,24 @@ OC.Settings.Apps = OC.Settings.Apps || { a.prepend(filename); a.prepend(img); li.append(a); - // append the new app as last item in the list (.push is from sticky footer) + + // append the new app as last item in the list + // (.push is from sticky footer) $('#apps .wrapper .push').before(li); - // scroll the app navigation down so the newly added app is seen - $('#navigation').animate({ scrollTop: $('#navigation').height() }, 'slow'); - // draw attention to the newly added app entry by flashing it twice - container.children('li[data-id="'+entry.id+'"]').animate({opacity:.3}).animate({opacity:1}).animate({opacity:.3}).animate({opacity:1}); + + // scroll the app navigation down + // so the newly added app is seen + $('#navigation').animate({ + scrollTop: $('#navigation').height() + }, 'slow'); + + // draw attention to the newly added app entry + // by flashing it twice + container.children('li[data-id="' + entry.id + '"]') + .animate({opacity: 0.3}) + .animate({opacity: 1}) + .animate({opacity: 0.3}) + .animate({opacity: 1}); if (!SVGSupport() && entry.icon.match(/\.svg$/i)) { $(img).addClass('svg'); @@ -248,6 +260,8 @@ $(document).ready(function(){ var item = tgt.is('li') ? $(tgt) : $(tgt).parent(); var app = item.data('app'); OC.Settings.Apps.loadApp(app); + $('#leftcontent .selected').removeClass('selected'); + item.addClass('selected'); } return false; }); diff --git a/settings/js/personal.js b/settings/js/personal.js index 5944272067b..7a4257f1c97 100644 --- a/settings/js/personal.js +++ b/settings/js/personal.js @@ -67,7 +67,7 @@ function showAvatarCropper() { $cropper.prepend("<img>"); $cropperImage = $('#cropper img'); - $cropperImage.attr('src', OC.Router.generate('core_avatar_get_tmp')+'?requesttoken='+oc_requesttoken+'#'+Math.floor(Math.random()*1000)); + $cropperImage.attr('src', OC.generateUrl('/avatar/tmp')+'?requesttoken='+oc_requesttoken+'#'+Math.floor(Math.random()*1000)); // Looks weird, but on('load', ...) doesn't work in IE8 $cropperImage.ready(function(){ @@ -95,7 +95,7 @@ function sendCropData() { w: cropperdata.w, h: cropperdata.h }; - $.post(OC.Router.generate('core_avatar_post_cropped'), {crop: data}, avatarResponseHandler); + $.post(OC.generateUrl('/avatar/cropped'), {crop: data}, avatarResponseHandler); } function saveCoords(c) { @@ -132,7 +132,7 @@ $(document).ready(function(){ $('#passwordchanged').hide(); $('#passworderror').hide(); // Ajax foo - $.post(OC.Router.generate('settings_personal_changepassword'), post, function(data){ + $.post(OC.generateUrl('/settings/personal/changepassword'), post, function(data){ if( data.status === "success" ){ $('#pass1').val(''); $('#pass2').val(''); @@ -243,7 +243,7 @@ $(document).ready(function(){ OC.dialogs.filepicker( t('settings', "Select a profile picture"), function(path){ - $.post(OC.Router.generate('core_avatar_post'), {path: path}, avatarResponseHandler); + $.post(OC.generateUrl('/avatar/'), {path: path}, avatarResponseHandler); }, false, ["image/png", "image/jpeg"] @@ -253,7 +253,7 @@ $(document).ready(function(){ $('#removeavatar').click(function(){ $.ajax({ type: 'DELETE', - url: OC.Router.generate('core_avatar_delete'), + url: OC.generateUrl('/avatar/'), success: function(msg) { updateAvatar(true); } @@ -315,25 +315,3 @@ OC.Encryption.msg={ } } }; - -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'); - } - } -}; diff --git a/settings/js/users.js b/settings/js/users.js index 160d0a8d9d2..284976d3bad 100644 --- a/settings/js/users.js +++ b/settings/js/users.js @@ -225,7 +225,8 @@ var UserList = { } $('table+.loading').css('visibility', 'visible'); UserList.updating = true; - $.get(OC.Router.generate('settings_ajax_userlist', { offset: UserList.offset, limit: UserList.usersToLoad }), function (result) { + var query = $.param({ offset: UserList.offset, limit: UserList.usersToLoad }); + $.get(OC.generateUrl('/settings/ajax/userlist') + query, function (result) { var loadedUsers = 0; var trs = []; if (result.status === 'success') { @@ -371,9 +372,7 @@ $(document).ready(function () { UserList.doSort(); UserList.availableGroups = $('#content table').data('groups'); - OC.Router.registerLoadedCallback(function() { - $(window).scroll(function(e) {UserList._onScroll(e);}); - }); + $(window).scroll(function(e) {UserList._onScroll(e);}); $('table').after($('<div class="loading" style="height: 200px; visibility: hidden;"></div>')); $('select[multiple]').each(function (index, element) { @@ -401,7 +400,7 @@ $(document).ready(function () { if ($(this).val().length > 0) { var recoveryPasswordVal = $('input:password[id="recoveryPassword"]').val(); $.post( - OC.Router.generate('settings_users_changepassword'), + OC.generateUrl('/settings/users/changepassword'), {username: uid, password: $(this).val(), recoveryPassword: recoveryPasswordVal}, function (result) { if (result.status != 'success') { diff --git a/settings/routes.php b/settings/routes.php index 895a9f5ccea..6954bd3823d 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -6,6 +6,8 @@ * See the COPYING-README file. */ +/** @var $this OC_Router */ + // Settings pages $this->create('settings_help', '/settings/help') ->actionInclude('settings/help.php'); @@ -70,5 +72,11 @@ $this->create('settings_ajax_getlog', '/settings/ajax/getlog.php') ->actionInclude('settings/ajax/getlog.php'); $this->create('settings_ajax_setloglevel', '/settings/ajax/setloglevel.php') ->actionInclude('settings/ajax/setloglevel.php'); +$this->create('settings_mail_settings', '/settings/admin/mailsettings') + ->post() + ->action('OC\Settings\Admin\Controller', 'setMailSettings'); +$this->create('settings_admin_mail_test', '/settings/admin/mailtest') + ->post() + ->action('OC\Settings\Admin\Controller', 'sendTestMail'); $this->create('settings_ajax_setsecurity', '/settings/ajax/setsecurity.php') ->actionInclude('settings/ajax/setsecurity.php'); diff --git a/settings/templates/admin.php b/settings/templates/admin.php index 0eabffb9316..487da036e74 100644 --- a/settings/templates/admin.php +++ b/settings/templates/admin.php @@ -11,6 +11,27 @@ $levelLabels = array( $l->t( 'Errors and fatal issues' ), $l->t( 'Fatal issues only' ), ); + +$mail_smtpauthtype = array( + '' => $l->t('None'), + 'LOGIN' => $l->t('Login'), + 'PLAIN' => $l->t('Plain'), + 'NTLM' => $l->t('NT LAN Manager'), +); + +$mail_smtpsecure = array( + '' => $l->t('None'), + 'ssl' => $l->t('SSL'), + 'tls' => $l->t('TLS'), +); + +$mail_smtpmode = array( + 'sendmail', + 'smtp', + 'qmail', + 'php', +); + ?> <?php @@ -250,6 +271,88 @@ if (!$_['internetconnectionworking']) { </table> </fieldset> +<fieldset id="mail_settings" class="personalblock"> + <h2><?php p($l->t('Email Server'));?> <span id="mail_settings_msg" class="msg"></span></h2> + + <p><?php p($l->t('This is used for sending out notifications.')); ?></p> + + <p> + <label for="mail_smtpmode"><?php p($l->t( 'Send mode' )); ?></label> + <select name='mail_smtpmode' id='mail_smtpmode'> + <?php foreach ($mail_smtpmode as $smtpmode): + $selected = ''; + if ($smtpmode == $_['mail_smtpmode']): + $selected = 'selected="selected"'; + endif; ?> + <option value='<?php p($smtpmode)?>' <?php p($selected) ?>><?php p($smtpmode) ?></option> + <?php endforeach;?> + </select> + + <label id="mail_smtpsecure_label" for="mail_smtpsecure" + <?php if ($_['mail_smtpmode'] != 'smtp') print_unescaped(' class="hidden"'); ?>> + <?php p($l->t( 'Encryption' )); ?> + </label> + <select name="mail_smtpsecure" id="mail_smtpsecure" + <?php if ($_['mail_smtpmode'] != 'smtp') print_unescaped(' class="hidden"'); ?>> + <?php foreach ($mail_smtpsecure as $secure => $name): + $selected = ''; + if ($secure == $_['mail_smtpsecure']): + $selected = 'selected="selected"'; + endif; ?> + <option value='<?php p($secure)?>' <?php p($selected) ?>><?php p($name) ?></option> + <?php endforeach;?> + </select> + </p> + + <p> + <label for="mail_from_address"><?php p($l->t( 'From address' )); ?></label> + <input type="text" name='mail_from_address' id="mail_from_address" placeholder="<?php p('mail')?>" + value='<?php p($_['mail_from_address']) ?>' /> + @ + <input type="text" name='mail_domain' id="mail_domain" placeholder="<?php p('example.com')?>" + value='<?php p($_['mail_domain']) ?>' /> + </p> + + <p id="setting_smtpauth" <?php if ($_['mail_smtpmode'] != 'smtp') print_unescaped(' class="hidden"'); ?>> + <label for="mail_smtpauthtype"><?php p($l->t( 'Authentification method' )); ?></label> + <select name='mail_smtpauthtype' id='mail_smtpauthtype'> + <?php foreach ($mail_smtpauthtype as $authtype => $name): + $selected = ''; + if ($authtype == $_['mail_smtpauthtype']): + $selected = 'selected="selected"'; + endif; ?> + <option value='<?php p($authtype)?>' <?php p($selected) ?>><?php p($name) ?></option> + <?php endforeach;?> + </select> + + <input type="checkbox" name="mail_smtpauth" id="mail_smtpauth" value="1" + <?php if ($_['mail_smtpauth']) print_unescaped('checked="checked"'); ?> /> + <label for="mail_smtpauth"><?php p($l->t( 'Authentication required' )); ?></label> + </p> + + <p id="setting_smtphost" <?php if ($_['mail_smtpmode'] != 'smtp') print_unescaped(' class="hidden"'); ?>> + <label for="mail_smtphost"><?php p($l->t( 'Server address' )); ?></label> + <input type="text" name='mail_smtphost' id="mail_smtphost" placeholder="<?php p('smtp.example.com')?>" + value='<?php p($_['mail_smtphost']) ?>' /> + : + <input type="text" name='mail_smtpport' id="mail_smtpport" placeholder="<?php p($l->t('Port'))?>" + value='<?php p($_['mail_smtpport']) ?>' /> + </p> + + <p id="mail_credentials" <?php if (!$_['mail_smtpauth'] || $_['mail_smtpmode'] != 'smtp') print_unescaped(' class="hidden"'); ?>> + <label for="mail_smtpname"><?php p($l->t( 'Credentials' )); ?></label> + <input type="text" name='mail_smtpname' id="mail_smtpname" placeholder="<?php p($l->t('SMTP Username'))?>" + value='<?php p($_['mail_smtpname']) ?>' /> + <input type="password" name='mail_smtppassword' id="mail_smtppassword" + placeholder="<?php p($l->t('SMTP Password'))?>" value='<?php p($_['mail_smtppassword']) ?>' /> + </p> + + <br /> + <em><?php p($l->t( 'Test email settings' )); ?></em> + <input type="submit" name="sendtestemail" id="sendtestemail" value="<?php p($l->t( 'Send email' )); ?>"/> + <span id="sendtestmail_msg" class="msg"></span> +</fieldset> + <fieldset class="personalblock"> <h2><?php p($l->t('Log'));?></h2> <?php p($l->t('Log level'));?> <select name='loglevel' id='loglevel'> diff --git a/settings/templates/personal.php b/settings/templates/personal.php index 188ff75f96b..9024f435657 100644 --- a/settings/templates/personal.php +++ b/settings/templates/personal.php @@ -83,7 +83,7 @@ if($_['passwordChangeSupported']) { placeholder="<?php p($l->t('Your email address'));?>" autocomplete="on" autocapitalize="off" autocorrect="off" /> <span class="msg"></span><br /> - <em><?php p($l->t('Fill in an email address to enable password recovery'));?></em> + <em><?php p($l->t('Fill in an email address to enable password recovery and receive notifications'));?></em> </fieldset> </form> <?php @@ -108,7 +108,7 @@ if($_['passwordChangeSupported']) { <?php endif; ?> </div> <div id="cropper" class="hidden"> - <div class="inlineblock button" id="abortcropperbutton"><?php p($l->t('Abort')); ?></div> + <div class="inlineblock button" id="abortcropperbutton"><?php p($l->t('Cancel')); ?></div> <div class="inlineblock button primary" id="sendcropperbutton"><?php p($l->t('Choose as profile image')); ?></div> </div> </fieldset> diff --git a/tests/data/db_structure.xml b/tests/data/db_structure.xml index d98066c4b7e..858c9ab1002 100644 --- a/tests/data/db_structure.xml +++ b/tests/data/db_structure.xml @@ -223,4 +223,19 @@ </declaration> </table> +<table> + <name>*dbprefix*migratekeyword</name> + + <declaration> + + <field> + <name>select</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>255</length> + </field> + </declaration> +</table> + </database> diff --git a/tests/data/db_structure2.xml b/tests/data/db_structure2.xml index ae5f22e9573..568d90ab0a9 100644 --- a/tests/data/db_structure2.xml +++ b/tests/data/db_structure2.xml @@ -119,4 +119,19 @@ </declaration> </table> + <table> + <name>*dbprefix*migratekeyword</name> + + <declaration> + + <field> + <name>select</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>255</length> + </field> + </declaration> + </table> + </database> diff --git a/tests/lib/appframework/http/ResponseTest.php b/tests/lib/appframework/http/ResponseTest.php index 4f21d77a170..063ab8b5d33 100644 --- a/tests/lib/appframework/http/ResponseTest.php +++ b/tests/lib/appframework/http/ResponseTest.php @@ -78,7 +78,7 @@ class ResponseTest extends \PHPUnit_Framework_TestCase { public function testGetEtag() { $this->childResponse->setEtag('hi'); - $this->assertEquals('hi', $this->childResponse->getEtag()); + $this->assertSame('hi', $this->childResponse->getEtag()); } diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 3f5604b4d45..5182fac8b10 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -150,13 +150,15 @@ class Scanner extends \PHPUnit_Framework_TestCase { $this->cache->put('folder', array('mtime' => $this->storage->filemtime('folder'), 'storage_mtime' => $this->storage->filemtime('folder'))); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_SIZE); $newData = $this->cache->get(''); - $this->assertNotEquals($oldData['etag'], $newData['etag']); + $this->assertInternalType('string', $oldData['etag']); + $this->assertInternalType('string', $newData['etag']); + $this->assertNotSame($oldData['etag'], $newData['etag']); $this->assertEquals($oldData['size'], $newData['size']); $oldData = $newData; $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); $newData = $this->cache->get(''); - $this->assertEquals($oldData['etag'], $newData['etag']); + $this->assertSame($oldData['etag'], $newData['etag']); $this->assertEquals(-1, $newData['size']); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE); @@ -164,17 +166,17 @@ class Scanner extends \PHPUnit_Framework_TestCase { $this->assertNotEquals(-1, $oldData['size']); $this->scanner->scanFile('', \OC\Files\Cache\Scanner::REUSE_ETAG + \OC\Files\Cache\Scanner::REUSE_SIZE); $newData = $this->cache->get(''); - $this->assertEquals($oldData['etag'], $newData['etag']); + $this->assertSame($oldData['etag'], $newData['etag']); $this->assertEquals($oldData['size'], $newData['size']); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG + \OC\Files\Cache\Scanner::REUSE_SIZE); $newData = $this->cache->get(''); - $this->assertEquals($oldData['etag'], $newData['etag']); + $this->assertSame($oldData['etag'], $newData['etag']); $this->assertEquals($oldData['size'], $newData['size']); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG + \OC\Files\Cache\Scanner::REUSE_SIZE); $newData = $this->cache->get(''); - $this->assertEquals($oldData['etag'], $newData['etag']); + $this->assertSame($oldData['etag'], $newData['etag']); $this->assertEquals($oldData['size'], $newData['size']); } @@ -217,8 +219,11 @@ class Scanner extends \PHPUnit_Framework_TestCase { // manipulate etag to simulate an empty etag $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); $data0 = $this->cache->get('folder/bar.txt'); + $this->assertInternalType('string', $data0['etag']); $data1 = $this->cache->get('folder'); + $this->assertInternalType('string', $data1['etag']); $data2 = $this->cache->get(''); + $this->assertInternalType('string', $data2['etag']); $data0['etag'] = ''; $this->cache->put('folder/bar.txt', $data0); @@ -227,10 +232,15 @@ class Scanner extends \PHPUnit_Framework_TestCase { // verify cache content $newData0 = $this->cache->get('folder/bar.txt'); + $this->assertInternalType('string', $newData0['etag']); + $this->assertNotEmpty($newData0['etag']); + $newData1 = $this->cache->get('folder'); + $this->assertInternalType('string', $newData1['etag']); + $this->assertNotSame($data1['etag'], $newData1['etag']); + $newData2 = $this->cache->get(''); - $this->assertNotEmpty($newData0['etag']); - $this->assertNotEquals($data1['etag'], $newData1['etag']); - $this->assertNotEquals($data2['etag'], $newData2['etag']); + $this->assertInternalType('string', $newData2['etag']); + $this->assertNotSame($data2['etag'], $newData2['etag']); } } diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index 48986149a73..a6ee8c46661 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -96,10 +96,14 @@ class Updater extends \PHPUnit_Framework_TestCase { Filesystem::file_put_contents('foo.txt', 'asd'); $cachedData = $this->cache->get('foo.txt'); $this->assertEquals(3, $cachedData['size']); - $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $fooCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']); $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize + 3, $cachedData['size']); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $rootCachedData = $cachedData; $this->assertFalse($this->cache->inCache('bar.txt')); @@ -110,12 +114,15 @@ class Updater extends \PHPUnit_Framework_TestCase { $mtime = $cachedData['mtime']; $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize + 2 * 3, $cachedData['size']); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $mtime); } public function testWriteWithMountPoints() { $storage2 = new \OC\Files\Storage\Temporary(array()); + $storage2->getScanner()->scan(''); //initialize etags $cache2 = $storage2->getCache(); Filesystem::mount($storage2, array(), '/' . self::$user . '/files/folder/substorage'); $folderCachedData = $this->cache->get('folder'); @@ -127,11 +134,15 @@ class Updater extends \PHPUnit_Framework_TestCase { $mtime = $cachedData['mtime']; $cachedData = $cache2->get(''); - $this->assertNotEquals($substorageCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $substorageCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); $this->assertEquals($mtime, $cachedData['mtime']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); $this->assertEquals($mtime, $cachedData['mtime']); } @@ -146,19 +157,25 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertFalse($this->cache->inCache('foo.txt')); $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize, $cachedData['size']); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); $rootCachedData = $cachedData; Filesystem::mkdir('bar_folder'); $this->assertTrue($this->cache->inCache('bar_folder')); $cachedData = $this->cache->get(''); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $rootCachedData = $cachedData; Filesystem::rmdir('bar_folder'); $this->assertFalse($this->cache->inCache('bar_folder')); $cachedData = $this->cache->get(''); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); } @@ -174,11 +191,15 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertFalse($cache2->inCache('foo.txt')); $cachedData = $cache2->get(''); - $this->assertNotEquals($substorageCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $substorageCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($substorageCachedData['mtime'], $cachedData['mtime']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($folderCachedData['mtime'], $cachedData['mtime']); } @@ -199,7 +220,9 @@ class Updater extends \PHPUnit_Framework_TestCase { $mtime = $cachedData['mtime']; $cachedData = $this->cache->get(''); $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); } public function testRenameExtension() { @@ -227,12 +250,16 @@ class Updater extends \PHPUnit_Framework_TestCase { $mtime = $cachedData['mtime']; $cachedData = $cache2->get(''); - $this->assertNotEquals($substorageCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $substorageCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); // rename can cause mtime change - invalid assert // $this->assertEquals($mtime, $cachedData['mtime']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); // rename can cause mtime change - invalid assert // $this->assertEquals($mtime, $cachedData['mtime']); } @@ -242,11 +269,15 @@ class Updater extends \PHPUnit_Framework_TestCase { $fooCachedData = $this->cache->get('foo.txt'); Filesystem::touch('foo.txt'); $cachedData = $this->cache->get('foo.txt'); - $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $fooCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($fooCachedData['mtime'], $cachedData['mtime']); $cachedData = $this->cache->get(''); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); $rootCachedData = $cachedData; @@ -255,14 +286,20 @@ class Updater extends \PHPUnit_Framework_TestCase { $folderCachedData = $this->cache->get('folder'); Filesystem::touch('folder/bar.txt', $time); $cachedData = $this->cache->get('folder/bar.txt'); - $this->assertNotEquals($barCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $barCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($barCachedData['etag'], $cachedData['etag']); $this->assertEquals($time, $cachedData['mtime']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); $cachedData = $this->cache->get(''); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertEquals($time, $cachedData['mtime']); } @@ -279,14 +316,20 @@ class Updater extends \PHPUnit_Framework_TestCase { $time = 1371006070; Filesystem::touch('folder/substorage/foo.txt', $time); $cachedData = $cache2->get('foo.txt'); - $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $fooCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']); $this->assertEquals($time, $cachedData['mtime']); $cachedData = $cache2->get(''); - $this->assertNotEquals($substorageCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $substorageCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); $this->assertEquals($time, $cachedData['mtime']); } diff --git a/tests/lib/files/etagtest.php b/tests/lib/files/etagtest.php index ce05adb188a..af9f66835f0 100644 --- a/tests/lib/files/etagtest.php +++ b/tests/lib/files/etagtest.php @@ -65,7 +65,11 @@ class EtagTest extends \PHPUnit_Framework_TestCase { $scanner = new \OC\Files\Utils\Scanner($user1); $scanner->backgroundScan('/'); - $this->assertEquals($originalEtags, $this->getEtags($files)); + $newEtags = $this->getEtags($files); + // loop over array and use assertSame over assertEquals to prevent false positives + foreach ($originalEtags as $file => $originalEtag) { + $this->assertSame($originalEtag, $newEtags[$file]); + } } /** diff --git a/tests/lib/files/storage/wrapper/quota.php b/tests/lib/files/storage/wrapper/quota.php index 43eae78415d..bd2c69a7396 100644 --- a/tests/lib/files/storage/wrapper/quota.php +++ b/tests/lib/files/storage/wrapper/quota.php @@ -53,6 +53,22 @@ class Quota extends \Test\Files\Storage\Storage { $this->assertEquals(9, $instance->free_space('')); } + public function testFreeSpaceWithUsedSpace() { + $instance = $this->getLimitedStorage(9); + $instance->getCache()->put( + '', array('size' => 3, 'unencrypted_size' => 0) + ); + $this->assertEquals(6, $instance->free_space('')); + } + + public function testFreeSpaceWithUsedSpaceAndEncryption() { + $instance = $this->getLimitedStorage(9); + $instance->getCache()->put( + '', array('size' => 7, 'unencrypted_size' => 3) + ); + $this->assertEquals(6, $instance->free_space('')); + } + public function testFWriteNotEnoughSpace() { $instance = $this->getLimitedStorage(9); $stream = $instance->fopen('foo', 'w+'); diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 371d1ed1798..c85f1128dbe 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -563,6 +563,6 @@ class View extends \PHPUnit_Framework_TestCase { $scanner->scanFile('test', \OC\Files\Cache\Scanner::REUSE_ETAG); $info2 = $view->getFileInfo('/test/test'); - $this->assertEquals($info['etag'], $info2['etag']); + $this->assertSame($info['etag'], $info2['etag']); } } diff --git a/tests/lib/request.php b/tests/lib/request.php index 1d77acc70ae..bff84e1b03f 100644 --- a/tests/lib/request.php +++ b/tests/lib/request.php @@ -135,4 +135,141 @@ class Test_Request extends PHPUnit_Framework_TestCase { ), ); } + + public function testInsecureServerHost() { + unset($_SERVER['HTTP_X_FORWARDED_HOST']); + unset($_SERVER['HTTP_HOST']); + unset($_SERVER['SERVER_NAME']); + $_SERVER['SERVER_NAME'] = 'from.server.name:8080'; + $host = OC_Request::insecureServerHost(); + $this->assertEquals('from.server.name:8080', $host); + + $_SERVER['HTTP_HOST'] = 'from.host.header:8080'; + $host = OC_Request::insecureServerHost(); + $this->assertEquals('from.host.header:8080', $host); + + $_SERVER['HTTP_X_FORWARDED_HOST'] = 'from.forwarded.host:8080'; + $host = OC_Request::insecureServerHost(); + $this->assertEquals('from.forwarded.host:8080', $host); + + $_SERVER['HTTP_X_FORWARDED_HOST'] = 'from.forwarded.host2:8080,another.one:9000'; + $host = OC_Request::insecureServerHost(); + $this->assertEquals('from.forwarded.host2:8080', $host); + + // clean up + unset($_SERVER['HTTP_X_FORWARDED_HOST']); + unset($_SERVER['HTTP_HOST']); + unset($_SERVER['SERVER_NAME']); + } + + public function testGetOverwriteHost() { + unset($_SERVER['REMOTE_ADDR']); + OC_Config::deleteKey('overwritecondaddr'); + OC_Config::deleteKey('overwritehost'); + $host = OC_Request::getOverwriteHost(); + $this->assertNull($host); + + OC_Config::setValue('overwritehost', ''); + $host = OC_Request::getOverwriteHost(); + $this->assertNull($host); + + OC_Config::setValue('overwritehost', 'host.one.test:8080'); + $host = OC_Request::getOverwriteHost(); + $this->assertEquals('host.one.test:8080', $host); + + $_SERVER['REMOTE_ADDR'] = 'somehost.test:8080'; + OC_Config::setValue('overwritecondaddr', '^somehost\..*$'); + $host = OC_Request::getOverwriteHost(); + $this->assertEquals('host.one.test:8080', $host); + + OC_Config::setValue('overwritecondaddr', '^somethingelse.*$'); + $host = OC_Request::getOverwriteHost(); + $this->assertNull($host); + + // clean up + unset($_SERVER['REMOTE_ADDR']); + OC_Config::deleteKey('overwritecondaddr'); + OC_Config::deleteKey('overwritehost'); + } + + /** + * @dataProvider trustedDomainDataProvider + */ + public function testIsTrustedDomain($trustedDomains, $testDomain, $result) { + OC_Config::deleteKey('trusted_domains'); + if ($trustedDomains !== null) { + OC_Config::setValue('trusted_domains', $trustedDomains); + } + + $this->assertEquals($result, OC_Request::isTrustedDomain($testDomain)); + + // clean up + OC_Config::deleteKey('trusted_domains'); + } + + public function trustedDomainDataProvider() { + $trustedHostTestList = array('host.one.test:8080', 'host.two.test:8080'); + return array( + // empty defaults to true + array(null, 'host.one.test:8080', true), + array('', 'host.one.test:8080', true), + array(array(), 'host.one.test:8080', true), + + // trust list when defined + array($trustedHostTestList, 'host.two.test:8080', true), + array($trustedHostTestList, 'host.two.test:9999', false), + array($trustedHostTestList, 'host.three.test:8080', false), + + // trust localhost regardless of trust list + array($trustedHostTestList, 'localhost', true), + array($trustedHostTestList, 'localhost:8080', true), + array($trustedHostTestList, '127.0.0.1', true), + array($trustedHostTestList, '127.0.0.1:8080', true), + + // do not trust invalid localhosts + array($trustedHostTestList, 'localhost:1:2', false), + array($trustedHostTestList, 'localhost: evil.host', false), + ); + } + + public function testServerHost() { + OC_Config::deleteKey('overwritecondaddr'); + OC_Config::setValue('overwritehost', 'overwritten.host:8080'); + OC_Config::setValue( + 'trusted_domains', + array( + 'trusted.host:8080', + 'second.trusted.host:8080' + ) + ); + $_SERVER['HTTP_HOST'] = 'trusted.host:8080'; + + // CLI always gives localhost + $oldCLI = OC::$CLI; + OC::$CLI = true; + $host = OC_Request::serverHost(); + $this->assertEquals('localhost', $host); + OC::$CLI = false; + + // overwritehost overrides trusted domain + $host = OC_Request::serverHost(); + $this->assertEquals('overwritten.host:8080', $host); + + // trusted domain returned when used + OC_Config::deleteKey('overwritehost'); + $host = OC_Request::serverHost(); + $this->assertEquals('trusted.host:8080', $host); + + // trusted domain returned when untrusted one in header + $_SERVER['HTTP_HOST'] = 'untrusted.host:8080'; + OC_Config::deleteKey('overwritehost'); + $host = OC_Request::serverHost(); + $this->assertEquals('trusted.host:8080', $host); + + // clean up + OC_Config::deleteKey('overwritecondaddr'); + OC_Config::deleteKey('overwritehost'); + unset($_SERVER['HTTP_HOST']); + OC::$CLI = $oldCLI; + } } |