summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/ajax/list.php1
-rw-r--r--apps/files/ajax/upload.php7
-rw-r--r--apps/files/css/files.css3
-rw-r--r--apps/files/index.php3
-rw-r--r--apps/files/js/file-upload.js2
-rw-r--r--apps/files/js/fileactions.js12
-rw-r--r--apps/files/js/filelist.js35
-rw-r--r--apps/files/js/files.js7
-rw-r--r--apps/files_encryption/appinfo/info.xml1
-rw-r--r--apps/files_encryption/hooks/hooks.php70
-rwxr-xr-xapps/files_encryption/lib/keymanager.php7
-rw-r--r--apps/files_encryption/lib/proxy.php56
-rw-r--r--apps/files_encryption/lib/stream.php3
-rw-r--r--apps/files_encryption/lib/util.php11
-rw-r--r--apps/files_encryption/settings-admin.php4
-rwxr-xr-xapps/files_encryption/tests/util.php28
-rw-r--r--apps/files_external/lib/smb.php19
-rw-r--r--apps/files_external/lib/streamwrapper.php36
-rw-r--r--apps/files_external/tests/smbfunctions.php41
-rw-r--r--apps/files_sharing/appinfo/app.php1
-rw-r--r--apps/files_sharing/appinfo/routes.php27
-rw-r--r--apps/files_sharing/appinfo/update.php22
-rw-r--r--apps/files_sharing/appinfo/version2
-rw-r--r--apps/files_sharing/js/share.js18
-rw-r--r--apps/files_sharing/lib/api.php531
-rw-r--r--apps/files_sharing/lib/cache.php97
-rw-r--r--apps/files_sharing/lib/updater.php30
-rw-r--r--apps/files_sharing/templates/public.php1
-rw-r--r--apps/files_sharing/tests/api.php597
-rw-r--r--apps/files_versions/js/versions.js8
-rw-r--r--apps/user_ldap/lib/access.php21
-rw-r--r--config/config.sample.php5
-rw-r--r--core/ajax/share.php2
-rw-r--r--core/js/js.js22
-rw-r--r--core/js/multiselect.js6
-rw-r--r--core/js/share.js107
-rw-r--r--core/templates/login.php2
-rw-r--r--lib/appconfig.php20
-rw-r--r--lib/base.php2
-rw-r--r--lib/cache/file.php18
-rw-r--r--lib/connector/sabre/directory.php61
-rw-r--r--lib/connector/sabre/file.php16
-rw-r--r--lib/connector/sabre/node.php11
-rw-r--r--lib/db.php124
-rw-r--r--lib/filechunking.php33
-rw-r--r--lib/files/cache/cache.php232
-rw-r--r--lib/files/cache/updater.php50
-rw-r--r--lib/files/filesystem.php26
-rw-r--r--lib/files/storage/home.php31
-rw-r--r--lib/files/view.php15
-rw-r--r--lib/helper.php24
-rw-r--r--lib/installer.php6
-rw-r--r--lib/log.php2
-rw-r--r--lib/log/owncloud.php24
-rw-r--r--lib/public/share.php27
-rw-r--r--lib/search/provider/file.php3
-rw-r--r--lib/search/result.php3
-rw-r--r--lib/setup.php8
-rw-r--r--lib/updater.php3
-rw-r--r--lib/user.php21
-rw-r--r--lib/user/database.php2
-rwxr-xr-xlib/util.php56
-rw-r--r--search/js/result.js55
-rw-r--r--tests/enable_all.php1
-rw-r--r--tests/lib/files/cache/updater.php17
-rw-r--r--tests/lib/files/filesystem.php57
-rw-r--r--tests/lib/files/storage/storage.php57
-rw-r--r--tests/lib/files/view.php70
-rw-r--r--tests/lib/helper.php34
-rw-r--r--tests/lib/share/share.php48
70 files changed, 2648 insertions, 354 deletions
diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php
index 878e4cb2159..d0f1f58f6e6 100644
--- a/apps/files/ajax/list.php
+++ b/apps/files/ajax/list.php
@@ -10,6 +10,7 @@ OCP\JSON::checkLoggedIn();
// Load the files
$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
+$dir = \OC\Files\Filesystem::normalizePath($dir);
$doBreadcrumb = isset( $_GET['breadcrumb'] ) ? true : false;
$data = array();
diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php
index 6eaa84f07e2..a81b8edb130 100644
--- a/apps/files/ajax/upload.php
+++ b/apps/files/ajax/upload.php
@@ -7,6 +7,8 @@ OCP\JSON::setContentTypeHeader('text/plain');
// If not, check the login.
// If no token is sent along, rely on login only
+$allowedPermissions = OCP\PERMISSION_ALL;
+
$l = OC_L10N::get('files');
if (empty($_POST['dirToken'])) {
// The standard case, files are uploaded through logged in users :)
@@ -17,6 +19,9 @@ if (empty($_POST['dirToken'])) {
die();
}
} else {
+ // return only read permissions for public upload
+ $allowedPermissions = OCP\PERMISSION_READ;
+
$linkItem = OCP\Share::getShareByToken($_POST['dirToken']);
if ($linkItem === false) {
OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('Invalid Token')))));
@@ -117,7 +122,7 @@ if (strpos($dir, '..') === false) {
'originalname' => $files['name'][$i],
'uploadMaxFilesize' => $maxUploadFileSize,
'maxHumanFilesize' => $maxHumanFileSize,
- 'permissions' => $meta['permissions'],
+ 'permissions' => $meta['permissions'] & $allowedPermissions
);
}
}
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index 37a9c7e2251..e16088b3b45 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -63,6 +63,9 @@
tbody tr { background-color:#fff; height:2.5em; }
tbody tr:hover, tbody tr:active, tbody tr.selected { background-color:#f8f8f8; }
tbody tr.selected { background-color:#eee; }
+#filestable tbody tr.searchresult {
+ background-color: rgb(240,240,240);
+}
tbody a { color:#000; }
span.extension, span.uploading, td.date { color:#999; }
span.extension { text-transform:lowercase; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; }
diff --git a/apps/files/index.php b/apps/files/index.php
index dd5ea445daf..2d1ae26bba2 100644
--- a/apps/files/index.php
+++ b/apps/files/index.php
@@ -35,6 +35,7 @@ OCP\Util::addscript('files', 'filelist');
OCP\App::setActiveNavigationEntry('files_index');
// Load the files
$dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : '';
+$dir = \OC\Files\Filesystem::normalizePath($dir);
// Redirect if directory does not exist
if (!\OC\Files\Filesystem::is_dir($dir . '/')) {
header('Location: ' . OCP\Util::getScriptName() . '');
@@ -132,7 +133,7 @@ if ($needUpgrade) {
$tmpl = new OCP\Template('files', 'index', 'user');
$tmpl->assign('fileList', $list->fetchPage());
$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage());
- $tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($dir));
+ $tmpl->assign('dir', $dir);
$tmpl->assign('isCreatable', \OC\Files\Filesystem::isCreatable($dir . '/'));
$tmpl->assign('permissions', $permissions);
$tmpl->assign('files', $files);
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js
index 402dd5075e7..42f08112619 100644
--- a/apps/files/js/file-upload.js
+++ b/apps/files/js/file-upload.js
@@ -260,6 +260,7 @@ $(document).ready(function() {
getMimeIcon(result.data.mime,function(path){
tr.find('td.filename').attr('style','background-image:url('+path+')');
});
+ FileActions.display(tr.find('td.filename'), true);
} else {
OC.dialogs.alert(result.data.message, t('core', 'Error'));
}
@@ -324,6 +325,7 @@ $(document).ready(function() {
getMimeIcon(mime,function(path){
tr.find('td.filename').attr('style','background-image:url('+path+')');
});
+ FileActions.display(tr.find('td.filename'), true);
});
eventSource.listen('error',function(error){
$('#uploadprogressbar').fadeOut();
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
index 5976bb49912..dbbc57c3b6a 100644
--- a/apps/files/js/fileactions.js
+++ b/apps/files/js/fileactions.js
@@ -61,7 +61,13 @@ var FileActions = {
var actions = this.get(mime, type, permissions);
return actions[name];
},
- display: function (parent) {
+ /**
+ * Display file actions for the given element
+ * @param parent "td" element of the file for which to display actions
+ * @param triggerEvent if true, triggers the fileActionsReady on the file
+ * list afterwards (false by default)
+ */
+ display: function (parent, triggerEvent) {
FileActions.currentFile = parent;
var actions = FileActions.get(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions());
var file = FileActions.getCurrentFile();
@@ -140,6 +146,10 @@ var FileActions = {
element.on('click', {a: null, elem: parent, actionFunc: actions['Delete']}, actionHandler);
parent.parent().children().last().append(element);
}
+
+ if (triggerEvent){
+ $('#fileList').trigger(jQuery.Event("fileActionsReady"));
+ }
},
getCurrentFile: function () {
return FileActions.currentFile.parent().attr('data-file');
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 40a4ae95c98..7863903fa19 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -130,7 +130,7 @@ var FileList={
if (hidden) {
tr.hide();
}
- FileActions.display(tr.find('td.filename'));
+ FileActions.display(tr.find('td.filename'), true);
return tr;
},
refresh:function(data) {
@@ -367,6 +367,37 @@ var FileList={
});
}
});
+ },
+ scrollTo:function(file) {
+ //scroll to and highlight preselected file
+ var $scrolltorow = $('tr[data-file="'+file+'"]');
+ if ($scrolltorow.exists()) {
+ $scrolltorow.addClass('searchresult');
+ $(window).scrollTop($scrolltorow.position().top);
+ //remove highlight when hovered over
+ $scrolltorow.one('hover', function() {
+ $scrolltorow.removeClass('searchresult');
+ });
+ }
+ },
+ filter:function(query) {
+ $('#fileList tr:not(.summary)').each(function(i,e) {
+ if ($(e).data('file').toString().toLowerCase().indexOf(query.toLowerCase()) !== -1) {
+ $(e).addClass("searchresult");
+ } else {
+ $(e).removeClass("searchresult");
+ }
+ });
+ //do not use scrollto to prevent removing searchresult css class
+ var first = $('#fileList tr.searchresult').first();
+ if (first.exists()) {
+ $(window).scrollTop(first.position().top);
+ }
+ },
+ unfilter:function() {
+ $('#fileList tr.searchresult').each(function(i,e) {
+ $(e).removeClass("searchresult");
+ });
}
};
@@ -473,7 +504,7 @@ $(document).ready(function(){
data.context.attr('data-permissions', file.permissions);
data.context.data('permissions', file.permissions);
}
- FileActions.display(data.context.find('td.filename'));
+ FileActions.display(data.context.find('td.filename'), true);
if (FileList.loadingDone) {
FileList.loadingDone(file.name, file.id);
}
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index 7581ff3ec56..d8aca4dd26a 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -546,6 +546,11 @@ $(document).ready(function() {
}
});
}
+
+ //scroll to and highlight preselected file
+ if (getURLParameter('scrollto')) {
+ FileList.scrollTo(getURLParameter('scrollto'));
+ }
});
function scanFiles(force, dir, users){
@@ -666,7 +671,7 @@ var folderDropOptions={
return false;
}
- var target=$.trim($(this).find('.nametext').text());
+ var target = $(this).closest('tr').data('file');
var files = ui.helper.find('tr');
$(files).each(function(i,row){
diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml
index 46f1375c987..9d495916d26 100644
--- a/apps/files_encryption/appinfo/info.xml
+++ b/apps/files_encryption/appinfo/info.xml
@@ -7,6 +7,7 @@
<author>Sam Tuke, Bjoern Schiessle, Florin Peter</author>
<require>4</require>
<shipped>true</shipped>
+ <rememberlogin>false</rememberlogin>
<types>
<filesystem/>
</types>
diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php
index 1103577ac64..90a327fb988 100644
--- a/apps/files_encryption/hooks/hooks.php
+++ b/apps/files_encryption/hooks/hooks.php
@@ -38,6 +38,11 @@ class Hooks {
* @note This method should never be called for users using client side encryption
*/
public static function login($params) {
+
+ if (\OCP\App::isEnabled('files_encryption') === false) {
+ return true;
+ }
+
$l = new \OC_L10N('files_encryption');
$view = new \OC_FilesystemView('/');
@@ -104,8 +109,6 @@ class Hooks {
}
// Encrypt existing user files:
- // This serves to upgrade old versions of the encryption
- // app (see appinfo/spec.txt)
if (
$util->encryptAll('/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'])
) {
@@ -131,11 +134,12 @@ class Hooks {
* @note This method should never be called for users using client side encryption
*/
public static function postCreateUser($params) {
- $view = new \OC_FilesystemView('/');
- $util = new Util($view, $params['uid']);
-
- Helper::setupUser($util, $params['password']);
+ if (\OCP\App::isEnabled('files_encryption')) {
+ $view = new \OC_FilesystemView('/');
+ $util = new Util($view, $params['uid']);
+ Helper::setupUser($util, $params['password']);
+ }
}
/**
@@ -143,26 +147,31 @@ class Hooks {
* @note This method should never be called for users using client side encryption
*/
public static function postDeleteUser($params) {
- $view = new \OC_FilesystemView('/');
- // cleanup public key
- $publicKey = '/public-keys/' . $params['uid'] . '.public.key';
+ if (\OCP\App::isEnabled('files_encryption')) {
+ $view = new \OC_FilesystemView('/');
- // Disable encryption proxy to prevent recursive calls
- $proxyStatus = \OC_FileProxy::$enabled;
- \OC_FileProxy::$enabled = false;
+ // cleanup public key
+ $publicKey = '/public-keys/' . $params['uid'] . '.public.key';
- $view->unlink($publicKey);
+ // Disable encryption proxy to prevent recursive calls
+ $proxyStatus = \OC_FileProxy::$enabled;
+ \OC_FileProxy::$enabled = false;
- \OC_FileProxy::$enabled = $proxyStatus;
+ $view->unlink($publicKey);
+
+ \OC_FileProxy::$enabled = $proxyStatus;
+ }
}
/**
* @brief If the password can't be changed within ownCloud, than update the key password in advance.
*/
public static function preSetPassphrase($params) {
- if ( ! \OC_User::canUserChangePassword($params['uid']) ) {
- self::setPassphrase($params);
+ if (\OCP\App::isEnabled('files_encryption')) {
+ if ( ! \OC_User::canUserChangePassword($params['uid']) ) {
+ self::setPassphrase($params);
+ }
}
}
@@ -172,6 +181,10 @@ class Hooks {
*/
public static function setPassphrase($params) {
+ if (\OCP\App::isEnabled('files_encryption') === false) {
+ return true;
+ }
+
// Only attempt to change passphrase if server-side encryption
// is in use (client-side encryption does not have access to
// the necessary keys)
@@ -242,6 +255,10 @@ class Hooks {
*/
public static function preShared($params) {
+ if (\OCP\App::isEnabled('files_encryption') === false) {
+ return true;
+ }
+
$l = new \OC_L10N('files_encryption');
$users = array();
$view = new \OC\Files\View('/public-keys/');
@@ -274,6 +291,10 @@ class Hooks {
*/
public static function postShared($params) {
+ if (\OCP\App::isEnabled('files_encryption') === false) {
+ return true;
+ }
+
// NOTE: $params has keys:
// [itemType] => file
// itemSource -> int, filecache file ID
@@ -380,6 +401,10 @@ class Hooks {
*/
public static function postUnshare($params) {
+ if (\OCP\App::isEnabled('files_encryption') === false) {
+ return true;
+ }
+
// NOTE: $params has keys:
// [itemType] => file
// [itemSource] => 13
@@ -468,6 +493,11 @@ class Hooks {
* of the stored versions along the actual file
*/
public static function postRename($params) {
+
+ if (\OCP\App::isEnabled('files_encryption') === false) {
+ return true;
+ }
+
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
@@ -560,9 +590,11 @@ class Hooks {
* @param array $params contains the app ID
*/
public static function preDisable($params) {
- if ($params['app'] === 'files_encryption') {
- $query = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `migration_status`=0');
- $query->execute();
+ if (\OCP\App::isEnabled('files_encryption')) {
+ if ($params['app'] === 'files_encryption') {
+ $query = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `migration_status`=0');
+ $query->execute();
+ }
}
}
diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php
index 0304d1134c4..7143fcff0f6 100755
--- a/apps/files_encryption/lib/keymanager.php
+++ b/apps/files_encryption/lib/keymanager.php
@@ -40,11 +40,14 @@ class Keymanager {
public static function getPrivateKey(\OC_FilesystemView $view, $user) {
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user . '.private.key';
+ $key = false;
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
- $key = $view->file_get_contents($path);
+ if ($view->file_exists($path)) {
+ $key = $view->file_get_contents($path);
+ }
\OC_FileProxy::$enabled = $proxyStatus;
@@ -569,4 +572,4 @@ class Keymanager {
return $targetPath;
}
-} \ No newline at end of file
+}
diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php
index ee6458b2fd2..25005e9c41f 100644
--- a/apps/files_encryption/lib/proxy.php
+++ b/apps/files_encryption/lib/proxy.php
@@ -38,8 +38,6 @@ class Proxy extends \OC_FileProxy {
private static $blackList = null; //mimetypes blacklisted from encryption
- private static $enableEncryption = null;
-
/**
* Check if a file requires encryption
* @param string $path
@@ -49,47 +47,22 @@ class Proxy extends \OC_FileProxy {
*/
private static function shouldEncrypt($path) {
- if (is_null(self::$enableEncryption)) {
-
- if (
- \OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true') === 'true'
- && Crypt::mode() === 'server'
- ) {
-
- self::$enableEncryption = true;
-
- } else {
-
- self::$enableEncryption = false;
-
- }
-
- }
-
- if (!self::$enableEncryption) {
-
+ if (\OCP\App::isEnabled('files_encryption') === false || Crypt::mode() !== 'server') {
return false;
-
}
if (is_null(self::$blackList)) {
-
self::$blackList = explode(',', \OCP\Config::getAppValue('files_encryption', 'type_blacklist', ''));
-
}
if (Crypt::isCatfileContent($path)) {
-
return true;
-
}
$extension = substr($path, strrpos($path, '.') + 1);
if (array_search($extension, self::$blackList) === false) {
-
return true;
-
}
return false;
@@ -203,7 +176,7 @@ class Proxy extends \OC_FileProxy {
*/
public function preUnlink($path) {
- // let the trashbin handle this
+ // let the trashbin handle this
if (\OCP\App::isEnabled('files_trashbin')) {
return true;
}
@@ -294,7 +267,7 @@ class Proxy extends \OC_FileProxy {
// Close the original encrypted file
fclose($result);
- // Open the file using the crypto stream wrapper
+ // Open the file using the crypto stream wrapper
// protocol and let it do the decryption work instead
$result = fopen('crypt://' . $relativePath, $meta['mode']);
@@ -321,7 +294,7 @@ class Proxy extends \OC_FileProxy {
public function postGetFileInfo($path, $data) {
// if path is a folder do nothing
- if (is_array($data) && array_key_exists('size', $data)) {
+ if (\OCP\App::isEnabled('files_encryption') && is_array($data) && array_key_exists('size', $data)) {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
@@ -346,6 +319,16 @@ class Proxy extends \OC_FileProxy {
$view = new \OC_FilesystemView('/');
+ $userId = \OCP\User::getUser();
+ $util = new Util($view, $userId);
+
+ // if encryption is no longer enabled or if the files aren't migrated yet
+ // we return the default file size
+ if(!\OCP\App::isEnabled('files_encryption') ||
+ $util->getMigrationStatus() !== Util::MIGRATION_COMPLETED) {
+ return $size;
+ }
+
// if path is a folder do nothing
if ($view->is_dir($path)) {
return $size;
@@ -367,6 +350,15 @@ class Proxy extends \OC_FileProxy {
// if file is encrypted return real file size
if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
+ // try to fix unencrypted file size if it doesn't look plausible
+ if ((int)$fileInfo['size'] > 0 && (int)$fileInfo['unencrypted_size'] === 0) {
+ $fixSize = $util->getFileSize($path);
+ $fileInfo['unencrypted_size'] = $fixSize;
+ // put file info if not .part file
+ if (!Keymanager::isPartialFilePath($relativePath)) {
+ $view->putFileInfo($path, $fileInfo);
+ }
+ }
$size = $fileInfo['unencrypted_size'];
} else {
// self healing if file was removed from file cache
@@ -374,8 +366,6 @@ class Proxy extends \OC_FileProxy {
$fileInfo = array();
}
- $userId = \OCP\User::getUser();
- $util = new Util($view, $userId);
$fixSize = $util->getFileSize($path);
if ($fixSize > 0) {
$size = $fixSize;
diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php
index 08024b8090d..80ff87370ab 100644
--- a/apps/files_encryption/lib/stream.php
+++ b/apps/files_encryption/lib/stream.php
@@ -490,9 +490,10 @@ class Stream {
// Get all users sharing the file includes current user
$uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $this->userId);
+ $checkedUserIds = $util->filterShareReadyUsers($uniqueUserIds);
// Fetch public keys for all sharing users
- $publicKeys = Keymanager::getPublicKeys($this->rootView, $uniqueUserIds);
+ $publicKeys = Keymanager::getPublicKeys($this->rootView, $checkedUserIds['ready']);
// Encrypt enc key for all sharing users
$this->encKeyfiles = Crypt::multiKeyEncrypt($this->plainKey, $publicKeys);
diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php
index a2aa305b64c..121b1c4f573 100644
--- a/apps/files_encryption/lib/util.php
+++ b/apps/files_encryption/lib/util.php
@@ -587,11 +587,18 @@ class Util {
) {
// get the size from filesystem
- $fullPath = $this->view->getLocalFile($path);
$size = $this->view->filesize($path);
+ // fast path, else the calculation for $lastChunkNr is bogus
+ if ($size === 0) {
+ \OC_FileProxy::$enabled = $proxyStatus;
+ return 0;
+ }
+
// calculate last chunk nr
- $lastChunkNr = floor($size / 8192);
+ // next highest is end of chunks, one subtracted is last one
+ // we have to read the last chunk, we can't just calculate it (because of padding etc)
+ $lastChunkNr = ceil($size/ 8192) - 1;
$lastChunkSize = $size - ($lastChunkNr * 8192);
// open stream
diff --git a/apps/files_encryption/settings-admin.php b/apps/files_encryption/settings-admin.php
index 53676058982..9ad9bfb8877 100644
--- a/apps/files_encryption/settings-admin.php
+++ b/apps/files_encryption/settings-admin.php
@@ -11,9 +11,7 @@
$tmpl = new OCP\Template('files_encryption', 'settings-admin');
// Check if an adminRecovery account is enabled for recovering files after lost pwd
-$view = new OC_FilesystemView('');
-
-$recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled');
+$recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled', '0');
$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php
index bf45b9c1d34..acd3ff810c6 100755
--- a/apps/files_encryption/tests/util.php
+++ b/apps/files_encryption/tests/util.php
@@ -231,6 +231,34 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
$this->view->unlink($this->userId . '/files/' . $filename);
}
+ /**
+ * @brief Test that data that is read by the crypto stream wrapper
+ */
+ function testGetFileSize() {
+ \Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
+
+ $filename = 'tmp-' . time();
+ $externalFilename = '/' . $this->userId . '/files/' . $filename;
+
+ // Test for 0 byte files
+ $problematicFileSizeData = "";
+ $cryptedFile = $this->view->file_put_contents($externalFilename, $problematicFileSizeData);
+ $this->assertTrue(is_int($cryptedFile));
+ $this->assertEquals($this->util->getFileSize($externalFilename), 0);
+ $decrypt = $this->view->file_get_contents($externalFilename);
+ $this->assertEquals($problematicFileSizeData, $decrypt);
+ $this->view->unlink($this->userId . '/files/' . $filename);
+
+ // Test a file with 18377 bytes as in https://github.com/owncloud/mirall/issues/1009
+ $problematicFileSizeData = str_pad("", 18377, "abc");
+ $cryptedFile = $this->view->file_put_contents($externalFilename, $problematicFileSizeData);
+ $this->assertTrue(is_int($cryptedFile));
+ $this->assertEquals($this->util->getFileSize($externalFilename), 18377);
+ $decrypt = $this->view->file_get_contents($externalFilename);
+ $this->assertEquals($problematicFileSizeData, $decrypt);
+ $this->view->unlink($this->userId . '/files/' . $filename);
+ }
+
function testIsSharedPath() {
$sharedPath = '/user1/files/Shared/test';
$path = '/user1/files/test';
diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php
index fede9fe392f..fb6259930ba 100644
--- a/apps/files_external/lib/smb.php
+++ b/apps/files_external/lib/smb.php
@@ -47,8 +47,13 @@ class SMB extends \OC\Files\Storage\StreamWrapper{
public function constructUrl($path) {
if (substr($path, -1)=='/') {
- $path=substr($path, 0, -1);
+ $path = substr($path, 0, -1);
}
+ if (substr($path, 0, 1)=='/') {
+ $path = substr($path, 1);
+ }
+ // remove trailing dots which some versions of samba don't seem to like
+ $path = rtrim($path, '.');
$path = urlencode($path);
$user = urlencode($this->user);
$pass = urlencode($this->password);
@@ -67,6 +72,18 @@ class SMB extends \OC\Files\Storage\StreamWrapper{
}
/**
+ * Unlinks file
+ * @param string @path
+ */
+ public function unlink($path) {
+ unlink($this->constructUrl($path));
+ clearstatcache();
+ // smb4php still returns false even on success so
+ // check here whether file was really deleted
+ return !file_exists($path);
+ }
+
+ /**
* check if a file or folder has been updated since $time
* @param string $path
* @param int $time
diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php
index beb4ec5605f..4a63dfb6e02 100644
--- a/apps/files_external/lib/streamwrapper.php
+++ b/apps/files_external/lib/streamwrapper.php
@@ -8,7 +8,7 @@
namespace OC\Files\Storage;
-abstract class StreamWrapper extends Common{
+abstract class StreamWrapper extends Common {
abstract public function constructUrl($path);
public function mkdir($path) {
@@ -16,7 +16,15 @@ abstract class StreamWrapper extends Common{
}
public function rmdir($path) {
- if($this->file_exists($path)) {
+ if ($this->file_exists($path)) {
+ $dh = $this->opendir($path);
+ while (($file = readdir($dh)) !== false) {
+ if ($this->is_dir($path . '/' . $file)) {
+ $this->rmdir($path . '/' . $file);
+ } else {
+ $this->unlink($path . '/' . $file);
+ }
+ }
$success = rmdir($this->constructUrl($path));
clearstatcache();
return $success;
@@ -34,11 +42,11 @@ abstract class StreamWrapper extends Common{
}
public function isReadable($path) {
- return true;//not properly supported
+ return true; //not properly supported
}
public function isUpdatable($path) {
- return true;//not properly supported
+ return true; //not properly supported
}
public function file_exists($path) {
@@ -55,15 +63,19 @@ abstract class StreamWrapper extends Common{
return fopen($this->constructUrl($path), $mode);
}
- public function touch($path, $mtime=null) {
- if(is_null($mtime)) {
- $fh = $this->fopen($path, 'a');
- fwrite($fh, '');
- fclose($fh);
-
- return true;
+ public function touch($path, $mtime = null) {
+ if ($this->file_exists($path)) {
+ if (is_null($mtime)) {
+ $fh = $this->fopen($path, 'a');
+ fwrite($fh, '');
+ fclose($fh);
+
+ return true;
+ } else {
+ return false; //not supported
+ }
} else {
- return false;//not supported
+ $this->file_put_contents($path, '');
}
}
diff --git a/apps/files_external/tests/smbfunctions.php b/apps/files_external/tests/smbfunctions.php
new file mode 100644
index 00000000000..749906d0136
--- /dev/null
+++ b/apps/files_external/tests/smbfunctions.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Copyright (c) 2013 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Storage;
+
+class SMBFunctions extends \PHPUnit_Framework_TestCase {
+
+ public function setUp() {
+ $id = uniqid();
+ // dummy config
+ $this->config = array(
+ 'run'=>false,
+ 'user'=>'test',
+ 'password'=>'testpassword',
+ 'host'=>'smbhost',
+ 'share'=>'/sharename',
+ 'root'=>'/rootdir/',
+ );
+
+ $this->instance = new \OC\Files\Storage\SMB($this->config);
+ }
+
+ public function tearDown() {
+ }
+
+ public function testGetId() {
+ $this->assertEquals('smb::test@smbhost//sharename//rootdir/', $this->instance->getId());
+ }
+
+ public function testConstructUrl() {
+ $this->assertEquals("smb://test:testpassword@smbhost/sharename/rootdir/abc", $this->instance->constructUrl('/abc'));
+ $this->assertEquals("smb://test:testpassword@smbhost/sharename/rootdir/abc", $this->instance->constructUrl('/abc/'));
+ $this->assertEquals("smb://test:testpassword@smbhost/sharename/rootdir/abc%2F", $this->instance->constructUrl('/abc/.'));
+ $this->assertEquals("smb://test:testpassword@smbhost/sharename/rootdir/abc%2Fdef", $this->instance->constructUrl('/abc/def'));
+ }
+}
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index 9363a5431fa..5f73a0767eb 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -7,6 +7,7 @@ OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'files_sharing/lib/cache.php';
OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'files_sharing/lib/permissions.php';
OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.php';
OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'files_sharing/lib/watcher.php';
+OC::$CLASSPATH['OCA\Files\Share\Api'] = 'files_sharing/lib/api.php';
OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
OCP\Share::registerBackend('file', 'OC_Share_Backend_File');
OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file');
diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php
new file mode 100644
index 00000000000..1936687e5e4
--- /dev/null
+++ b/apps/files_sharing/appinfo/routes.php
@@ -0,0 +1,27 @@
+<?php
+// OCS API
+
+OC_API::register('get',
+ '/apps/files_sharing/api/v1/shares',
+ array('\OCA\Files\Share\Api', 'getAllShares'),
+ 'files_sharing');
+
+OC_API::register('post',
+ '/apps/files_sharing/api/v1/shares',
+ array('\OCA\Files\Share\Api', 'createShare'),
+ 'files_sharing');
+
+OC_API::register('get',
+ '/apps/files_sharing/api/v1/shares/{id}',
+ array('\OCA\Files\Share\Api', 'getShare'),
+ 'files_sharing');
+
+OC_API::register('put',
+ '/apps/files_sharing/api/v1/shares/{id}',
+ array('\OCA\Files\Share\Api', 'updateShare'),
+ 'files_sharing');
+
+OC_API::register('delete',
+ '/apps/files_sharing/api/v1/shares/{id}',
+ array('\OCA\Files\Share\Api', 'deleteShare'),
+ 'files_sharing');
diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php
index 48e41e93048..0d827da28ea 100644
--- a/apps/files_sharing/appinfo/update.php
+++ b/apps/files_sharing/appinfo/update.php
@@ -68,11 +68,21 @@ if (version_compare($installedVersion, '0.3', '<')) {
// $query = OCP\DB::prepare('DROP TABLE `*PREFIX*sharing`');
// $query->execute();
}
-if (version_compare($installedVersion, '0.3.3', '<')) {
- OC_User::useBackend(new OC_User_Database());
- OC_App::loadApps(array('authentication'));
- $users = OC_User::getUsers();
- foreach ($users as $user) {
-// OC_FileCache::delete('Shared', '/'.$user.'/files/');
+
+// clean up oc_share table from files which are no longer exists
+if (version_compare($installedVersion, '0.3.5', '<')) {
+
+ // get all shares where the original file no longer exists
+ $findShares = \OC_DB::prepare('SELECT `file_source` FROM `*PREFIX*share` LEFT JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` WHERE `*PREFIX*filecache`.`fileid` IS NULL AND `*PREFIX*share`.`item_type` IN (\'file\', \'folder\')');
+ $sharesFound = $findShares->execute(array())->fetchAll();
+
+ // delete those shares from the oc_share table
+ if (is_array($sharesFound) && !empty($sharesFound)) {
+ $delArray = array();
+ foreach ($sharesFound as $share) {
+ $delArray[] = $share['file_source'];
+ }
+ $removeShares = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `file_source` IN (?)');
+ $result = $removeShares->execute(array(implode(',', $delArray)));
}
}
diff --git a/apps/files_sharing/appinfo/version b/apps/files_sharing/appinfo/version
index 87a0871112f..09e9157034c 100644
--- a/apps/files_sharing/appinfo/version
+++ b/apps/files_sharing/appinfo/version
@@ -1 +1 @@
-0.3.3 \ No newline at end of file
+0.3.5 \ No newline at end of file
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index b2efafde4e7..68f6f3ba76f 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -1,11 +1,19 @@
$(document).ready(function() {
- var disableSharing = $('#disableSharing').data('status');
+ var disableSharing = $('#disableSharing').data('status'),
+ sharesLoaded = false;
if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) {
-
- $('#fileList').one('fileActionsReady',function(){
- OC.Share.loadIcons('file');
+ $('#fileList').on('fileActionsReady',function(){
+ if (!sharesLoaded){
+ OC.Share.loadIcons('file');
+ // assume that we got all shares, so switching directories
+ // will not invalidate that list
+ sharesLoaded = true;
+ }
+ else{
+ OC.Share.updateIcons('file');
+ }
});
FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) {
@@ -38,4 +46,4 @@ $(document).ready(function() {
}
});
}
-}); \ No newline at end of file
+});
diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php
new file mode 100644
index 00000000000..1bd0003ce07
--- /dev/null
+++ b/apps/files_sharing/lib/api.php
@@ -0,0 +1,531 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files\Share;
+
+class Api {
+
+ /**
+ * @brief get all shares
+ *
+ * @param array $params option 'file' to limit the result to a specific file/folder
+ * @return \OC_OCS_Result share information
+ */
+ public static function getAllShares($params) {
+ // if a file is specified, get the share for this file
+ if (isset($_GET['path'])) {
+ $params['itemSource'] = self::getFileId($_GET['path']);
+ $params['path'] = $_GET['path'];
+ $params['itemType'] = self::getItemType($_GET['path']);
+
+ if ( isset($_GET['reshares']) && $_GET['reshares'] !== 'false' ) {
+ $params['reshares'] = true;
+ } else {
+ $params['reshares'] = false;
+ }
+
+ if (isset($_GET['subfiles']) && $_GET['subfiles'] !== 'false') {
+ return self::getSharesFromFolder($params);
+ }
+ return self::collectShares($params);
+ }
+
+ $share = \OCP\Share::getItemShared('file', null);
+
+ if ($share === false) {
+ return new \OC_OCS_Result(null, 404, 'could not get shares');
+ } else {
+ return new \OC_OCS_Result($share);
+ }
+
+ }
+
+ /**
+ * @brief get share information for a given share
+ *
+ * @param array $params which contains a 'id'
+ * @return \OC_OCS_Result share information
+ */
+ public static function getShare($params) {
+
+ $s = self::getShareFromId($params['id']);
+ $params['itemSource'] = $s['item_source'];
+ $params['itemType'] = $s['item_type'];
+ $params['specificShare'] = true;
+
+ return self::collectShares($params);
+ }
+
+ /**
+ * @brief collect all share information, either of a specific share or all
+ * shares for a given path
+ * @param array $params
+ * @return \OC_OCS_Result
+ */
+ private static function collectShares($params) {
+
+ $itemSource = $params['itemSource'];
+ $itemType = $params['itemType'];
+ $getSpecificShare = isset($params['specificShare']) ? $params['specificShare'] : false;
+
+ if ($itemSource !== null) {
+ $shares = \OCP\Share::getItemShared($itemType, $itemSource);
+ $receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $itemSource);
+ // if a specific share was specified only return this one
+ if ($getSpecificShare === true) {
+ foreach ($shares as $share) {
+ if ($share['id'] === (int) $params['id']) {
+ $shares = array('element' => $share);
+ break;
+ }
+ }
+ }
+
+ // include also reshares in the lists. This means that the result
+ // will contain every user with access to the file.
+ if (isset($params['reshares']) && $params['reshares'] === true) {
+ $shares = self::addReshares($shares, $itemSource);
+ }
+
+ if ($receivedFrom) {
+ $shares['received_from'] = $receivedFrom['uid_owner'];
+ $shares['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']);
+ }
+ } else {
+ $shares = null;
+ }
+
+ if ($shares === null || empty($shares)) {
+ return new \OC_OCS_Result(null, 404, 'share doesn\'t exist');
+ } else {
+ return new \OC_OCS_Result($shares);
+ }
+ }
+
+ /**
+ * @brief add reshares to a array of shares
+ * @param array $shares array of shares
+ * @param int $itemSource item source ID
+ * @return array new shares array which includes reshares
+ */
+ private static function addReshares($shares, $itemSource) {
+
+ // if there are no shares than there are also no reshares
+ $firstShare = reset($shares);
+ if ($firstShare) {
+ $path = $firstShare['path'];
+ } else {
+ return $shares;
+ }
+
+ $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `file_source`, `path` , `permissions`, `stime`, `expiration`, `token`, `storage`';
+ $getReshares = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` WHERE `*PREFIX*share`.`file_source` = ? AND `*PREFIX*share`.`item_type` IN (\'file\', \'folder\') AND `uid_owner` != ?');
+ $reshares = $getReshares->execute(array($itemSource, \OCP\User::getUser()))->fetchAll();
+
+ foreach ($reshares as $key => $reshare) {
+ if (isset($reshare['share_with']) && $reshare['share_with'] !== '') {
+ $reshares[$key]['share_with_displayname'] = \OCP\User::getDisplayName($reshare['share_with']);
+ }
+ // add correct path to the result
+ $reshares[$key]['path'] = $path;
+ }
+
+ return array_merge($shares, $reshares);
+ }
+
+ /**
+ * @brief get share from all files in a given folder (non-recursive)
+ * @param array $params contains 'path' to the folder
+ * @return \OC_OCS_Result
+ */
+ private static function getSharesFromFolder($params) {
+ $path = $params['path'];
+ $view = new \OC\Files\View('/'.\OCP\User::getUser().'/files');
+
+ if(!$view->is_dir($path)) {
+ return new \OC_OCS_Result(null, 404, "not a directory");
+ }
+
+ $content = $view->getDirectoryContent($path);
+
+ $result = array();
+ foreach ($content as $file) {
+ // 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) {
+ $share['filename'] = $file['name'];
+ $result[] = $share;
+ }
+ }
+
+ return new \OC_OCS_Result($result);
+ }
+
+ /**
+ * @breif create a new share
+ * @param array $params
+ * @return \OC_OCS_Result
+ */
+ public static function createShare($params) {
+
+ $path = isset($_POST['path']) ? $_POST['path'] : null;
+
+ if($path === null) {
+ return new \OC_OCS_Result(null, 400, "please specify a file or folder path");
+ }
+ $itemSource = self::getFileId($path);
+ $itemType = self::getItemType($path);
+
+ if($itemSource === null) {
+ return new \OC_OCS_Result(null, 404, "wrong path, file/folder doesn't exist.");
+ }
+
+ $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
+ $shareType = isset($_POST['shareType']) ? (int)$_POST['shareType'] : null;
+
+ switch($shareType) {
+ case \OCP\Share::SHARE_TYPE_USER:
+ $permissions = isset($_POST['permissions']) ? (int)$_POST['permissions'] : 31;
+ break;
+ case \OCP\Share::SHARE_TYPE_GROUP:
+ $permissions = isset($_POST['permissions']) ? (int)$_POST['permissions'] : 31;
+ break;
+ case \OCP\Share::SHARE_TYPE_LINK:
+ //allow password protection
+ $shareWith = isset($_POST['password']) ? $_POST['password'] : null;
+ //check public link share
+ $publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
+ $encryptionEnabled = \OC_App::isEnabled('files_encryption');
+ if(isset($_POST['publicUpload']) &&
+ ($encryptionEnabled || $publicUploadEnabled !== 'yes')) {
+ return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator");
+ }
+ $publicUpload = isset($_POST['publicUpload']) ? $_POST['publicUpload'] : 'false';
+ // read, create, update (7) if public upload is enabled or
+ // read (1) if public upload is disabled
+ $permissions = $publicUpload === 'true' ? 7 : 1;
+ break;
+ default:
+ return new \OC_OCS_Result(null, 404, "unknown share type");
+ }
+
+ try {
+ $token = \OCP\Share::shareItem(
+ $itemType,
+ $itemSource,
+ $shareType,
+ $shareWith,
+ $permissions
+ );
+ } catch (\Exception $e) {
+ return new \OC_OCS_Result(null, 404, $e->getMessage());
+ }
+
+ if ($token) {
+ $data = array();
+ $data['id'] = 'unknown';
+ $shares = \OCP\Share::getItemShared($itemType, $itemSource);
+ if(is_string($token)) { //public link share
+ foreach ($shares as $share) {
+ if ($share['token'] === $token) {
+ $data['id'] = $share['id'];
+ break;
+ }
+ }
+ $url = \OCP\Util::linkToPublic('files&t='.$token);
+ $data['url'] = $url; // '&' gets encoded to $amp;
+ $data['token'] = $token;
+
+ } else {
+ foreach ($shares as $share) {
+ if ($share['share_with'] === $shareWith && $share['share_type'] === $shareType) {
+ $data['id'] = $share['id'];
+ break;
+ }
+ }
+ }
+ return new \OC_OCS_Result($data);
+ } else {
+ return new \OC_OCS_Result(null, 404, "couldn't share file");
+ }
+ }
+
+ /**
+ * update shares, e.g. password, permissions, etc
+ * @param array $params shareId 'id' and the parameter we want to update
+ * currently supported: permissions, password, publicUpload
+ * @return \OC_OCS_Result
+ */
+ public static function updateShare($params) {
+
+ $share = self::getShareFromId($params['id']);
+ $itemSource = isset($share['item_source']) ? $share['item_source'] : null;
+
+ if($itemSource === null) {
+ return new \OC_OCS_Result(null, 404, "wrong share Id, share doesn't exist.");
+ }
+
+ try {
+ if(isset($params['_put']['permissions'])) {
+ return self::updatePermissions($share, $params);
+ } elseif (isset($params['_put']['password'])) {
+ return self::updatePassword($share, $params);
+ } elseif (isset($params['_put']['publicUpload'])) {
+ return self::updatePublicUpload($share, $params);
+ }
+ } catch (\Exception $e) {
+ return new \OC_OCS_Result(null, 400, $e->getMessage());
+ }
+
+ return new \OC_OCS_Result(null, 400, "Wrong or no update parameter given");
+
+ }
+
+ /**
+ * @brief update permissions for a share
+ * @param array $share information about the share
+ * @param array $params contains 'permissions'
+ * @return \OC_OCS_Result
+ */
+ private static function updatePermissions($share, $params) {
+
+ $itemSource = $share['item_source'];
+ $itemType = $share['item_type'];
+ $shareWith = $share['share_with'];
+ $shareType = $share['share_type'];
+ $permissions = isset($params['_put']['permissions']) ? (int)$params['_put']['permissions'] : null;
+
+ $publicUploadStatus = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
+ $encryptionEnabled = \OC_App::isEnabled('files_encryption');
+ $publicUploadEnabled = false;
+ if(!$encryptionEnabled && $publicUploadStatus === 'yes') {
+ $publicUploadEnabled = true;
+ }
+
+ // only change permissions for public shares if public upload is enabled
+ // and we want to set permissions to 1 (read only) or 7 (allow upload)
+ if ( (int)$shareType === \OCP\Share::SHARE_TYPE_LINK ) {
+ if ($publicUploadEnabled === false || ($permissions !== 7 && $permissions !== 1)) {
+ return new \OC_OCS_Result(null, 400, "can't change permission for public link share");
+ }
+ }
+
+ try {
+ $return = \OCP\Share::setPermissions(
+ $itemType,
+ $itemSource,
+ $shareType,
+ $shareWith,
+ $permissions
+ );
+ } catch (\Exception $e) {
+ return new \OC_OCS_Result(null, 404, $e->getMessage());
+ }
+
+ if ($return) {
+ return new \OC_OCS_Result();
+ } else {
+ return new \OC_OCS_Result(null, 404, "couldn't set permissions");
+ }
+ }
+
+ /**
+ * @brief enable/disable public upload
+ * @param array $share information about the share
+ * @param array $params contains 'publicUpload' which can be 'yes' or 'no'
+ * @return \OC_OCS_Result
+ */
+ private static function updatePublicUpload($share, $params) {
+
+ $publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
+ $encryptionEnabled = \OC_App::isEnabled('files_encryption');
+ if($encryptionEnabled || $publicUploadEnabled !== 'yes') {
+ return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator");
+ }
+
+ if ($share['item_type'] !== 'folder' ||
+ (int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK ) {
+ return new \OC_OCS_Result(null, 404, "public upload is only possible for public shared folders");
+ }
+
+ // read, create, update (7) if public upload is enabled or
+ // read (1) if public upload is disabled
+ $params['_put']['permissions'] = $params['_put']['publicUpload'] === 'true' ? 7 : 1;
+
+ return self::updatePermissions($share, $params);
+
+ }
+
+ /**
+ * @brief update password for public link share
+ * @param array $share information about the share
+ * @param type $params 'password'
+ * @return \OC_OCS_Result
+ */
+ private static function updatePassword($share, $params) {
+
+ $itemSource = $share['item_source'];
+ $itemType = $share['item_type'];
+
+ if( (int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK) {
+ return new \OC_OCS_Result(null, 400, "password protection is only supported for public shares");
+ }
+
+ $shareWith = isset($params['_put']['password']) ? $params['_put']['password'] : null;
+
+ if($shareWith === '') {
+ $shareWith = null;
+ }
+
+ $items = \OCP\Share::getItemShared($itemType, $itemSource);
+
+ $checkExists = false;
+ foreach ($items as $item) {
+ if($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+ $checkExists = true;
+ $permissions = $item['permissions'];
+ }
+ }
+
+ if (!$checkExists) {
+ return new \OC_OCS_Result(null, 404, "share doesn't exists, can't change password");
+ }
+
+ $result = \OCP\Share::shareItem(
+ $itemType,
+ $itemSource,
+ \OCP\Share::SHARE_TYPE_LINK,
+ $shareWith,
+ $permissions
+ );
+ if($result) {
+ return new \OC_OCS_Result();
+ }
+
+ return new \OC_OCS_Result(null, 404, "couldn't set password");
+ }
+
+ /**
+ * @brief unshare a file/folder
+ * @param array $params contains the shareID 'id' which should be unshared
+ * @return \OC_OCS_Result
+ */
+ public static function deleteShare($params) {
+
+ $share = self::getShareFromId($params['id']);
+ $itemSource = isset($share['item_source']) ? $share['item_source'] : null;
+ $itemType = isset($share['item_type']) ? $share['item_type'] : null;;
+
+ if($itemSource === null) {
+ return new \OC_OCS_Result(null, 404, "wrong share ID, share doesn't exist.");
+ }
+
+ $shareWith = isset($share['share_with']) ? $share['share_with'] : null;
+ $shareType = isset($share['share_type']) ? (int)$share['share_type'] : null;
+
+ if( $shareType === \OCP\Share::SHARE_TYPE_LINK) {
+ $shareWith = null;
+ }
+
+ try {
+ $return = \OCP\Share::unshare(
+ $itemType,
+ $itemSource,
+ $shareType,
+ $shareWith);
+ } catch (\Exception $e) {
+ return new \OC_OCS_Result(null, 404, $e->getMessage());
+ }
+
+ if ($return) {
+ return new \OC_OCS_Result();
+ } else {
+ $msg = "Unshare Failed";
+ return new \OC_OCS_Result(null, 404, $msg);
+ }
+ }
+
+ /**
+ * @brief get file ID from a given path
+ * @param string $path
+ * @return string fileID or null
+ */
+ private static function getFileId($path) {
+
+ $view = new \OC\Files\View('/'.\OCP\User::getUser().'/files');
+ $fileId = null;
+ $fileInfo = $view->getFileInfo($path);
+ if ($fileInfo) {
+ $fileId = $fileInfo['fileid'];
+ }
+
+ return $fileId;
+ }
+
+ /**
+ * @brief get itemType
+ * @param string $path
+ * @return string type 'file', 'folder' or null of file/folder doesn't exists
+ */
+ private static function getItemType($path) {
+ $view = new \OC\Files\View('/'.\OCP\User::getUser().'/files');
+ $itemType = null;
+
+ if ($view->is_dir($path)) {
+ $itemType = "folder";
+ } elseif ($view->is_file($path)) {
+ $itemType = "file";
+ }
+
+ return $itemType;
+ }
+
+ /**
+ * @brief get some information from a given share
+ * @param int $shareID
+ * @return array with: item_source, share_type, share_with, item_type, permissions
+ */
+ private static function getShareFromId($shareID) {
+ $sql = 'SELECT `item_source`, `share_type`, `share_with`, `item_type`, `permissions` FROM `*PREFIX*share` WHERE `id` = ?';
+ $args = array($shareID);
+ $query = \OCP\DB::prepare($sql);
+ $result = $query->execute($args);
+
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('files_sharing', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return null;
+ }
+ if ($share = $result->fetchRow()) {
+ return $share;
+ }
+
+ return null;
+
+ }
+
+}
diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php
index a2b7e22d1b3..baa632ec9c1 100644
--- a/apps/files_sharing/lib/cache.php
+++ b/apps/files_sharing/lib/cache.php
@@ -20,6 +20,7 @@
*/
namespace OC\Files\Cache;
+use OCP\Share_Backend_Collection;
/**
* Metadata cache for shared files
@@ -218,31 +219,86 @@ class Shared_Cache extends Cache {
* @return array of file data
*/
public function search($pattern) {
- // TODO
+
+ $where = '`name` LIKE ? AND ';
+
+ // normalize pattern
+ $value = $this->normalize($pattern);
+
+ // we have one ? in our where clause
+ $chunksize = self::MAX_SQL_CHUNK_SIZE - 1;
+
+ return $this->searchWithWhere($where, $value, $chunksize);
+
}
/**
* search for files by mimetype
*
- * @param string $part1
- * @param string $part2
+ * @param string $mimetype
* @return array
*/
public function searchByMime($mimetype) {
+
if (strpos($mimetype, '/')) {
- $where = '`mimetype` = ?';
+ $where = '`mimetype` = ? AND ';
} else {
- $where = '`mimepart` = ?';
+ $where = '`mimepart` = ? AND ';
}
- $mimetype = $this->getMimetypeId($mimetype);
+
+ $value = $this->getMimetypeId($mimetype);
+
+ // we have one ? in our where clause
+ $chunksize = self::MAX_SQL_CHUNK_SIZE - 1;
+
+ return $this->searchWithWhere($where, $value, $chunksize);
+
+ }
+
+ /**
+ * The maximum number of placeholders that can be used in an SQL query.
+ * Value MUST be < 1000.
+ * Also see ORA-01795 maximum number of expressions in a list is 1000
+ */
+ const MAX_SQL_CHUNK_SIZE = 999;
+
+ /**
+ * search for files with a custom where clause and value
+ * the $wherevalue will be array_merge()d with the file id chunks
+ *
+ * @param string $sqlwhere
+ * @param string $wherevalue
+ * @return array
+ */
+ private function searchWithWhere($sqlwhere, $wherevalue, $chunksize = self::MAX_SQL_CHUNK_SIZE) {
+
$ids = $this->getAll();
- $placeholders = join(',', array_fill(0, count($ids), '?'));
- $query = \OC_DB::prepare('
- SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`
- FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN (' . $placeholders . ')'
- );
- $result = $query->execute(array_merge(array($mimetype), $ids));
- return $result->fetchAll();
+
+ $files = array();
+
+ // divide into 1k chunks
+ $chunks = array_chunk($ids, $chunksize);
+
+ foreach ($chunks as $chunk) {
+ $placeholders = join(',', array_fill(0, count($chunk), '?'));
+ $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
+ `encrypted`, `unencrypted_size`, `etag`
+ FROM `*PREFIX*filecache` WHERE ' . $sqlwhere . ' `fileid` IN (' . $placeholders . ')';
+
+ $stmt = \OC_DB::prepare($sql);
+
+ $result = $stmt->execute(array_merge(array($wherevalue), $chunk));
+
+ while ($row = $result->fetchRow()) {
+ if (substr($row['path'], 0, 6) === 'files/') {
+ $row['path'] = substr($row['path'], 6); // remove 'files/' from path as it's relative to '/Shared'
+ }
+ $row['mimetype'] = $this->getMimetype($row['mimetype']);
+ $row['mimepart'] = $this->getMimetype($row['mimepart']);
+ $files[] = $row;
+ }
+ }
+ return $files;
}
/**
@@ -264,7 +320,20 @@ class Shared_Cache extends Cache {
* @return int[]
*/
public function getAll() {
- return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL);
+ $ids = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL);
+ $folderBackend = \OCP\Share::getBackend('folder');
+ if ($folderBackend instanceof Share_Backend_Collection) {
+ foreach ($ids as $file) {
+ /** @var $folderBackend Share_Backend_Collection */
+ $children = $folderBackend->getChildren($file);
+ foreach ($children as $child) {
+ $ids[] = (int)$child['source'];
+ }
+
+ }
+ }
+
+ return $ids;
}
}
diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php
index a43ab2e2a0a..9d08f278923 100644
--- a/apps/files_sharing/lib/updater.php
+++ b/apps/files_sharing/lib/updater.php
@@ -32,28 +32,40 @@ class Shared_Updater {
$uid = \OCP\User::getUser();
$uidOwner = \OC\Files\Filesystem::getOwner($target);
$info = \OC\Files\Filesystem::getFileInfo($target);
+ $checkedUser = array($uidOwner);
// Correct Shared folders of other users shared with
$users = \OCP\Share::getUsersItemShared('file', $info['fileid'], $uidOwner, true);
if (!empty($users)) {
while (!empty($users)) {
$reshareUsers = array();
foreach ($users as $user) {
- if ( $user !== $uidOwner ) {
+ if ( !in_array($user, $checkedUser) ) {
$etag = \OC\Files\Filesystem::getETag('');
\OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag);
// Look for reshares
$reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $info['fileid'], $user, true));
+ $checkedUser[] = $user;
}
}
$users = $reshareUsers;
}
- // Correct folders of shared file owner
- $target = substr($target, 8);
- if ($uidOwner !== $uid && $source = \OC_Share_Backend_File::getSource($target)) {
- \OC\Files\Filesystem::initMountPoints($uidOwner);
- $source = '/'.$uidOwner.'/'.$source['path'];
- \OC\Files\Cache\Updater::correctFolder($source, $info['mtime']);
- }
+ }
+ }
+
+ /**
+ * @brief remove all shares for a given file if the file was deleted
+ *
+ * @param string $path
+ */
+ private static function removeShare($path) {
+ $fileInfo = \OC\Files\Filesystem::getFileInfo($path);
+ $fileSource = $fileInfo['fileid'];
+
+ $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `file_source`=?');
+ try {
+ $query->execute(array($fileSource));
+ } catch (\Exception $e) {
+ \OCP\Util::writeLog('files_sharing', "can't remove share: " . $e->getMessage(), \OCP\Util::WARN);
}
}
@@ -77,8 +89,10 @@ class Shared_Updater {
*/
static public function deleteHook($params) {
self::correctFolders($params['path']);
+ self::removeShare($params['path']);
}
+
/**
* @param array $params
*/
diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php
index ef5ad424fb6..fd0a861142d 100644
--- a/apps/files_sharing/templates/public.php
+++ b/apps/files_sharing/templates/public.php
@@ -4,6 +4,7 @@
<?php $defaults = new OCP\Defaults(); // initialize themable default strings and urls ?>
+<input type="hidden" id="isPublic" name="isPublic" value="1">
<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
<input type="hidden" name="downloadURL" value="<?php p($_['downloadURL']) ?>" id="downloadURL">
<input type="hidden" name="filename" value="<?php p($_['filename']) ?>" id="filename">
diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php
new file mode 100644
index 00000000000..44fc4d8b7b3
--- /dev/null
+++ b/apps/files_sharing/tests/api.php
@@ -0,0 +1,597 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2013 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+require_once __DIR__ . '/../../../lib/base.php';
+
+use OCA\Files\Share;
+
+/**
+ * Class Test_Files_Sharing_Api
+ */
+class Test_Files_Sharing_Api extends \PHPUnit_Framework_TestCase {
+
+ const TEST_FILES_SHARING_API_USER1 = "test-share-user1";
+ const TEST_FILES_SHARING_API_USER2 = "test-share-user2";
+ const TEST_FILES_SHARING_API_USER3 = "test-share-user3";
+
+ public $stateFilesEncryption;
+ public $filename;
+ public $data;
+ /**
+ * @var OC_FilesystemView
+ */
+ public $view;
+ public $folder;
+
+ public static function setUpBeforeClass() {
+ // reset backend
+ \OC_User::clearBackends();
+ \OC_User::useBackend('database');
+
+ // clear share hooks
+ \OC_Hook::clear('OCP\\Share');
+ \OC::registerShareHooks();
+ \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
+
+ // create users
+ self::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1, true);
+ self::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, true);
+ self::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3, true);
+
+ }
+
+ function setUp() {
+
+ //login as user1
+ \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
+
+ $this->data = 'foobar';
+ $this->view = new \OC_FilesystemView('/' . \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1 . '/files');
+
+ $this->folder = '/folder_share_api_test';
+
+ $this->filename = 'share-api-test.txt';
+
+ // remember files_encryption state
+ $this->stateFilesEncryption = \OC_App::isEnabled('files_encryption');
+
+ //we don't want to tests with app files_encryption enabled
+ \OC_App::disable('files_encryption');
+
+
+ $this->assertTrue(!\OC_App::isEnabled('files_encryption'));
+
+ // save file with content
+ $this->view->file_put_contents($this->filename, $this->data);
+ $this->view->mkdir($this->folder);
+ $this->view->file_put_contents($this->folder.'/'.$this->filename, $this->data);
+
+ }
+
+ function tearDown() {
+ $this->view->unlink($this->filename);
+ $this->view->deleteAll($this->folder);
+ // reset app files_encryption
+ if ($this->stateFilesEncryption) {
+ \OC_App::enable('files_encryption');
+ } else {
+ \OC_App::disable('files_encryption');
+ }
+ }
+
+ public static function tearDownAfterClass() {
+
+ // cleanup users
+ \OC_User::deleteUser(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
+ \OC_User::deleteUser(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+ \OC_User::deleteUser(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3);
+ }
+
+ /**
+ * @medium
+ */
+ function testCreateShare() {
+
+ // share to user
+
+ // simulate a post request
+ $_POST['path'] = $this->filename;
+ $_POST['shareWith'] = \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2;
+ $_POST['shareType'] = \OCP\Share::SHARE_TYPE_USER;
+
+ $result = Share\Api::createShare(array());
+
+ $this->assertTrue($result->succeeded());
+ $data = $result->getData();
+
+ $share = $this->getShareFromId($data['id']);
+
+ $items = \OCP\Share::getItemShared('file', $share['item_source']);
+
+ $this->assertTrue(!empty($items));
+
+ // share link
+
+ // simulate a post request
+ $_POST['path'] = $this->folder;
+ $_POST['shareType'] = \OCP\Share::SHARE_TYPE_LINK;
+
+ $result = Share\Api::createShare(array());
+
+ // check if API call was successful
+ $this->assertTrue($result->succeeded());
+
+ $data = $result->getData();
+
+ // check if we have a token
+ $this->assertTrue(is_string($data['token']));
+
+ $share = $this->getShareFromId($data['id']);
+
+ $items = \OCP\Share::getItemShared('file', $share['item_source']);
+
+ $this->assertTrue(!empty($items));
+
+ $fileinfo = $this->view->getFileInfo($this->filename);
+
+ \OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+ $fileinfo = $this->view->getFileInfo($this->folder);
+
+ \OCP\Share::unshare('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+
+
+ }
+
+ /**
+ * @medium
+ * @depends testCreateShare
+ */
+ function testGetAllShares() {
+
+ $fileinfo = $this->view->getFileInfo($this->filename);
+
+ \OCP\Share::shareItem('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+ $result = Share\Api::getAllShares(array());
+
+ $this->assertTrue($result->succeeded());
+
+ // test should return two shares created from testCreateShare()
+ $this->assertTrue(count($result->getData()) === 1);
+
+ \OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+ }
+
+ /**
+ * @medium
+ * @depends testCreateShare
+ */
+ function testGetShareFromSource() {
+
+ $fileInfo = $this->view->getFileInfo($this->filename);
+
+ \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+ \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+ null, 1);
+
+ $_GET['path'] = $this->filename;
+
+ $result = Share\Api::getAllShares(array());
+
+ $this->assertTrue($result->succeeded());
+
+ // test should return one share created from testCreateShare()
+ $this->assertTrue(count($result->getData()) === 2);
+
+ \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+ \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+ }
+
+ /**
+ * @medium
+ * @depends testCreateShare
+ */
+ function testGetShareFromSourceWithReshares() {
+
+ $fileInfo = $this->view->getFileInfo($this->filename);
+
+ // share the file as user1 to user2
+ \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+ // login as user2 and reshare the file to user3
+ \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+ \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3, 31);
+
+ // login as user1 again
+ \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
+
+ $_GET['path'] = $this->filename;
+
+ $result = Share\Api::getAllShares(array());
+
+ $this->assertTrue($result->succeeded());
+
+ // test should return one share
+ $this->assertTrue(count($result->getData()) === 1);
+
+ // now also ask for the reshares
+ $_GET['reshares'] = 'true';
+
+ $result = Share\Api::getAllShares(array());
+
+ $this->assertTrue($result->succeeded());
+
+ // now we should get two shares, the initial share and the reshare
+ $this->assertTrue(count($result->getData()) === 2);
+
+ // unshare files again
+
+ \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+ \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3);
+
+ \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
+
+ \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+ }
+
+ /**
+ * @medium
+ * @depends testCreateShare
+ */
+ function testGetShareFromId() {
+
+ $fileInfo = $this->view->getFileInfo($this->filename);
+
+ $result = \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+ // share was successful?
+ $this->assertTrue($result);
+
+ // get item to determine share ID
+ $result = \OCP\Share::getItemShared('file', $fileInfo['fileid']);
+
+ $this->assertEquals(1, count($result));
+
+ // get first element
+ $share = reset($result);
+
+ // call getShare() with share ID
+ $params = array('id' => $share['id']);
+ $result = Share\Api::getShare($params);
+
+ $this->assertTrue($result->succeeded());
+
+ // test should return one share created from testCreateShare()
+ $this->assertEquals(1, count($result->getData()));
+
+ \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+ }
+
+ /**
+ * @medium
+ */
+ function testGetShareFromFolder() {
+
+ $fileInfo1 = $this->view->getFileInfo($this->filename);
+ $fileInfo2 = $this->view->getFileInfo($this->folder.'/'.$this->filename);
+
+ $result = \OCP\Share::shareItem('file', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+ // share was successful?
+ $this->assertTrue($result);
+
+ $result = \OCP\Share::shareItem('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+ null, 1);
+
+ // share was successful?
+ $this->assertTrue(is_string($result));
+
+ $_GET['path'] = $this->folder;
+ $_GET['subfiles'] = 'true';
+
+ $result = Share\Api::getAllShares(array());
+
+ $this->assertTrue($result->succeeded());
+
+ // test should return one share within $this->folder
+ $this->assertTrue(count($result->getData()) === 1);
+
+ \OCP\Share::unshare('file', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+ \OCP\Share::unshare('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+ }
+
+ /**
+ * @medium
+ */
+ function testGetShareFromUnknownId() {
+
+ $params = array('id' => 0);
+
+ $result = Share\Api::getShare($params);
+
+ $this->assertEquals(404, $result->getStatusCode());
+ $meta = $result->getMeta();
+ $this->assertEquals('share doesn\'t exist', $meta['message']);
+
+ }
+
+ /**
+ * @medium
+ * @depends testCreateShare
+ */
+ function testUpdateShare() {
+
+ $fileInfo = $this->view->getFileInfo($this->filename);
+
+ $result = \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+ // share was successful?
+ $this->assertTrue($result);
+
+ $result = \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+ null, 1);
+
+ // share was successful?
+ $this->assertTrue(is_string($result));
+
+ $items = \OCP\Share::getItemShared('file', null);
+
+ // make sure that we found a link share and a user share
+ $this->assertEquals(count($items), 2);
+
+ $linkShare = null;
+ $userShare = null;
+
+ foreach ($items as $item) {
+ if ($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+ $linkShare = $item;
+ }
+ if ($item['share_type'] === \OCP\Share::SHARE_TYPE_USER) {
+ $userShare = $item;
+ }
+ }
+
+ // make sure that we found a link share and a user share
+ $this->assertTrue(is_array($linkShare));
+ $this->assertTrue(is_array($userShare));
+
+ // update permissions
+
+ $this->assertEquals('31', $userShare['permissions']);
+
+ $params = array();
+ $params['id'] = $userShare['id'];
+ $params['_put'] = array();
+ $params['_put']['permissions'] = 1;
+
+ $result = Share\Api::updateShare($params);
+
+ $meta = $result->getMeta();
+ $this->assertTrue($result->succeeded(), $meta['message']);
+
+ $items = \OCP\Share::getItemShared('file', $userShare['file_source']);
+
+ $newUserShare = null;
+ foreach ($items as $item) {
+ if ($item['share_with'] === \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2) {
+ $newUserShare = $item;
+ break;
+ }
+ }
+
+ $this->assertTrue(is_array($newUserShare));
+
+ $this->assertEquals('1', $newUserShare['permissions']);
+
+ // update password for link share
+
+ $this->assertTrue(empty($linkShare['share_with']));
+
+ $params = array();
+ $params['id'] = $linkShare['id'];
+ $params['_put'] = array();
+ $params['_put']['password'] = 'foo';
+
+ $result = Share\Api::updateShare($params);
+
+ $this->assertTrue($result->succeeded());
+
+ $items = \OCP\Share::getItemShared('file', $linkShare['file_source']);
+
+ $newLinkShare = null;
+ foreach ($items as $item) {
+ if ($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+ $newLinkShare = $item;
+ break;
+ }
+ }
+
+ $this->assertTrue(is_array($newLinkShare));
+ $this->assertTrue(!empty($newLinkShare['share_with']));
+
+ \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+ \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+ }
+
+ /**
+ * @medium
+ */
+ function testUpdateShareUpload() {
+
+ $fileInfo = $this->view->getFileInfo($this->folder);
+
+ $result = \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+ null, 1);
+
+ // share was successful?
+ $this->assertTrue(is_string($result));
+
+ $items = \OCP\Share::getItemShared('file', null);
+
+ // make sure that we found a link share and a user share
+ $this->assertEquals(count($items), 1);
+
+ $linkShare = null;
+
+ foreach ($items as $item) {
+ if ($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+ $linkShare = $item;
+ }
+ }
+
+ // make sure that we found a link share
+ $this->assertTrue(is_array($linkShare));
+
+ // update public upload
+
+ $params = array();
+ $params['id'] = $linkShare['id'];
+ $params['_put'] = array();
+ $params['_put']['publicUpload'] = 'true';
+
+ $result = Share\Api::updateShare($params);
+
+ $this->assertTrue($result->succeeded());
+
+ $items = \OCP\Share::getItemShared('file', $linkShare['file_source']);
+
+ $updatedLinkShare = null;
+ foreach ($items as $item) {
+ if ($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+ $updatedLinkShare = $item;
+ break;
+ }
+ }
+
+ $this->assertTrue(is_array($updatedLinkShare));
+ $this->assertEquals(7, $updatedLinkShare['permissions']);
+
+ // cleanup
+
+ \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+ }
+
+ /**
+ * @medium
+ * @depends testCreateShare
+ */
+ function testDeleteShare() {
+
+ $fileInfo = $this->view->getFileInfo($this->filename);
+
+ \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+ \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+ \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+ null, 1);
+
+ $items = \OCP\Share::getItemShared('file', null);
+
+ $this->assertEquals(2, count($items));
+
+ foreach ($items as $item) {
+ $result = Share\Api::deleteShare(array('id' => $item['id']));
+
+ $this->assertTrue($result->succeeded());
+ }
+
+ $itemsAfterDelete = \OCP\Share::getItemShared('file', null);
+
+ $this->assertTrue(empty($itemsAfterDelete));
+
+ }
+
+ /**
+ * @param $user
+ * @param bool $create
+ * @param bool $password
+ */
+ private static function loginHelper($user, $create = false, $password = false) {
+ if ($create) {
+ \OC_User::createUser($user, $user);
+ }
+
+ if ($password === false) {
+ $password = $user;
+ }
+
+ \OC_Util::tearDownFS();
+ \OC_User::setUserId('');
+ \OC\Files\Filesystem::tearDown();
+ \OC_Util::setupFS($user);
+ \OC_User::setUserId($user);
+
+ $params['uid'] = $user;
+ $params['password'] = $password;
+ }
+
+ /**
+ * @brief get some information from a given share
+ * @param int $shareID
+ * @return array with: item_source, share_type, share_with, item_type, permissions
+ */
+ private function getShareFromId($shareID) {
+ $sql = 'SELECT `item_source`, `share_type`, `share_with`, `item_type`, `permissions` FROM `*PREFIX*share` WHERE `id` = ?';
+ $args = array($shareID);
+ $query = \OCP\DB::prepare($sql);
+ $result = $query->execute($args);
+
+ $share = Null;
+
+ if ($result && $result->numRows() > 0) {
+ $share = $result->fetchRow();
+ }
+
+ return $share;
+
+ }
+
+}
diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js
index 2a8b491b9b2..c0a2f7df59b 100644
--- a/apps/files_versions/js/versions.js
+++ b/apps/files_versions/js/versions.js
@@ -1,4 +1,12 @@
$(document).ready(function(){
+
+ if ($('#isPublic').val()){
+ // no versions actions in public mode
+ // beware of https://github.com/owncloud/core/issues/4545
+ // as enabling this might hang Chrome
+ return;
+ }
+
if (typeof FileActions !== 'undefined') {
// Add versions button to 'files/index.php'
FileActions.register(
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index dbe998aad8e..aa8db295518 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -28,6 +28,8 @@ abstract class Access {
//never ever check this var directly, always use getPagedSearchResultState
protected $pagedSearchedSuccessful;
+ protected $cookies = array();
+
public function setConnector(Connection &$connection) {
$this->connection = $connection;
}
@@ -59,6 +61,8 @@ abstract class Access {
\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
return false;
}
+ //all or nothing! otherwise we get in trouble with.
+ $this->initPagedSearch($filter, array($dn), $attr, 99999, 0);
$dn = $this->DNasBaseParameter($dn);
$rr = @ldap_read($cr, $dn, $filter, array($attr));
if(!is_resource($rr)) {
@@ -893,7 +897,9 @@ abstract class Access {
if(!$testConnection->setConfiguration($credentials)) {
return false;
}
- return $testConnection->bind();
+ $result=$testConnection->bind();
+ $this->connection->bind();
+ return $result;
}
/**
@@ -1004,7 +1010,7 @@ abstract class Access {
$bases = $this->sanitizeDN($bases);
foreach($bases as $base) {
$belongsToBase = true;
- if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base))) {
+ if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
$belongsToBase = false;
}
if($belongsToBase) {
@@ -1029,9 +1035,12 @@ abstract class Access {
$offset -= $limit;
//we work with cache here
$cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . $limit . '-' . $offset;
- $cookie = $this->connection->getFromCache($cachekey);
- if(is_null($cookie)) {
- $cookie = '';
+ $cookie = '';
+ if(isset($this->cookies[$cachekey])) {
+ $cookie = $this->cookies[$cachekey];
+ if(is_null($cookie)) {
+ $cookie = '';
+ }
}
return $cookie;
}
@@ -1048,7 +1057,7 @@ abstract class Access {
private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
if(!empty($cookie)) {
$cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .$limit . '-' . $offset;
- $cookie = $this->connection->writeToCache($cachekey, $cookie);
+ $this->cookies[$cachekey] = $cookie;
}
}
diff --git a/config/config.sample.php b/config/config.sample.php
index 5c09269ea3c..092480d4f5d 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -181,5 +181,8 @@ $CONFIG = array(
'customclient_ios' => '', //https://itunes.apple.com/us/app/owncloud/id543672169?mt=8
// date format to be used while writing to the owncloud logfile
-'logdateformat' => 'F d, Y H:i:s'
+'logdateformat' => 'F d, Y H:i:s',
+
+/* timezone used while writing to the owncloud logfile (default: UTC) */
+'logtimezone' => 'Europe/Berlin',
);
diff --git a/core/ajax/share.php b/core/ajax/share.php
index aed1caff378..387ac8bc27f 100644
--- a/core/ajax/share.php
+++ b/core/ajax/share.php
@@ -188,7 +188,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
while ($count < 15 && count($users) == $limit) {
$limit = 15 - $count;
if ($sharePolicy == 'groups_only') {
- $users = OC_Group::DisplayNamesInGroups($groups, $_GET['search'], $limit, $offset);
+ $users = OC_Group::DisplayNamesInGroups($usergroups, $_GET['search'], $limit, $offset);
} else {
$users = OC_User::getDisplayNames($_GET['search'], $limit, $offset);
}
diff --git a/core/js/js.js b/core/js/js.js
index 586d698f3a2..73711b3fbc5 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -666,11 +666,17 @@ $(document).ready(function(){
}
}else if(event.keyCode===27){//esc
OC.search.hide();
+ if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system
+ FileList.unfilter();
+ }
}else{
var query=$('#searchbox').val();
if(OC.search.lastQuery!==query){
OC.search.lastQuery=query;
OC.search.currentResult=-1;
+ if (FileList && typeof FileList.filter === 'function') { //TODO add hook system
+ FileList.filter(query);
+ }
if(query.length>2){
OC.search(query);
}else{
@@ -808,6 +814,13 @@ function formatDate(date){
return $.datepicker.formatDate(datepickerFormatDate, date)+' '+date.getHours()+':'+((date.getMinutes()<10)?'0':'')+date.getMinutes();
}
+// taken from http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
+function getURLParameter(name) {
+ return decodeURI(
+ (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || [, null])[1]
+ );
+}
+
/**
* takes an absolute timestamp and return a string with a human-friendly relative date
* @param int a Unix timestamp
@@ -892,6 +905,15 @@ $.fn.selectRange = function(start, end) {
};
/**
+ * check if an element exists.
+ * allows you to write if ($('#myid').exists()) to increase readability
+ * @link http://stackoverflow.com/questions/31044/is-there-an-exists-function-for-jquery
+ */
+jQuery.fn.exists = function(){
+ return this.length > 0;
+};
+
+/**
* Calls the server periodically every 15 mins to ensure that session doesnt
* time out
*/
diff --git a/core/js/multiselect.js b/core/js/multiselect.js
index bc4223feb64..48d521e1856 100644
--- a/core/js/multiselect.js
+++ b/core/js/multiselect.js
@@ -176,10 +176,10 @@
});
button.parent().data('preventHide',false);
if(settings.createText){
- var li=$('<li class="creator">+ <em>'+settings.createText+'<em></li>');
+ var li=$('<li class="creator">+ '+settings.createText+'</li>');
li.click(function(event){
li.empty();
- var input=$('<input class="new">');
+ var input=$('<input type="text" class="new">');
li.append(input);
input.focus();
input.css('width',button.innerWidth());
@@ -316,4 +316,4 @@
return span;
};
-})( jQuery ); \ No newline at end of file
+})( jQuery );
diff --git a/core/js/share.js b/core/js/share.js
index 4664594e657..a34542a8abd 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -4,57 +4,76 @@ OC.Share={
SHARE_TYPE_LINK:3,
SHARE_TYPE_EMAIL:4,
itemShares:[],
- statuses:[],
+ statuses:{},
droppedDown:false,
+ /**
+ * Loads ALL share statuses from server, stores them in OC.Share.statuses then
+ * calls OC.Share.updateIcons() to update the files "Share" icon to "Shared"
+ * according to their share status and share type.
+ */
loadIcons:function(itemType) {
// Load all share icons
$.get(OC.filePath('core', 'ajax', 'share.php'), { fetch: 'getItemsSharedStatuses', itemType: itemType }, function(result) {
if (result && result.status === 'success') {
+ OC.Share.statuses = {};
$.each(result.data, function(item, data) {
OC.Share.statuses[item] = data;
- var hasLink = data['link'];
- // Links override shared in terms of icon display
- if (hasLink) {
- var image = OC.imagePath('core', 'actions/public');
- } else {
- var image = OC.imagePath('core', 'actions/shared');
- }
- if (itemType != 'file' && itemType != 'folder') {
- $('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center');
- } else {
- var file = $('tr[data-id="'+item+'"]');
- if (file.length > 0) {
- var action = $(file).find('.fileactions .action[data-action="Share"]');
- var img = action.find('img').attr('src', image);
- action.addClass('permanent');
- action.html(' '+t('core', 'Shared')).prepend(img);
- } else {
- var dir = $('#dir').val();
- if (dir.length > 1) {
- var last = '';
- var path = dir;
- // Search for possible parent folders that are shared
- while (path != last) {
- if (path == data['path']) {
- var actions = $('.fileactions .action[data-action="Share"]');
- $.each(actions, function(index, action) {
- var img = $(action).find('img');
- if (img.attr('src') != OC.imagePath('core', 'actions/public')) {
- img.attr('src', image);
- $(action).addClass('permanent');
- $(action).html(' '+t('core', 'Shared')).prepend(img);
- }
- });
+ });
+ OC.Share.updateIcons(itemType);
+ }
+ });
+ },
+ /**
+ * Updates the files' "Share" icons according to the known
+ * sharing states stored in OC.Share.statuses.
+ * (not reloaded from server)
+ */
+ updateIcons:function(itemType){
+ var item;
+ for (item in OC.Share.statuses){
+ var data = OC.Share.statuses[item];
+
+ var hasLink = data['link'];
+ // Links override shared in terms of icon display
+ if (hasLink) {
+ var image = OC.imagePath('core', 'actions/public');
+ } else {
+ var image = OC.imagePath('core', 'actions/shared');
+ }
+ if (itemType != 'file' && itemType != 'folder') {
+ $('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center');
+ } else {
+ var file = $('tr[data-id="'+item+'"]');
+ if (file.length > 0) {
+ var action = $(file).find('.fileactions .action[data-action="Share"]');
+ var img = action.find('img').attr('src', image);
+ action.addClass('permanent');
+ action.html(' '+t('core', 'Shared')).prepend(img);
+ } else {
+ var dir = $('#dir').val();
+ if (dir.length > 1) {
+ var last = '';
+ var path = dir;
+ // Search for possible parent folders that are shared
+ while (path != last) {
+ if (path == data['path'] && !data['link']) {
+ var actions = $('.fileactions .action[data-action="Share"]');
+ $.each(actions, function(index, action) {
+ var img = $(action).find('img');
+ if (img.attr('src') != OC.imagePath('core', 'actions/public')) {
+ img.attr('src', image);
+ $(action).addClass('permanent');
+ $(action).html(' '+t('core', 'Shared')).prepend(img);
}
- last = path;
- path = OC.Share.dirname(path);
- }
+ });
}
+ last = path;
+ path = OC.Share.dirname(path);
}
}
- });
+ }
}
- });
+ }
},
updateIcon:function(itemType, itemSource) {
var shares = false;
@@ -214,7 +233,9 @@ OC.Share={
if (data.shares) {
$.each(data.shares, function(index, share) {
if (share.share_type == OC.Share.SHARE_TYPE_LINK) {
- OC.Share.showLink(share.token, share.share_with, itemSource);
+ if ( !('file_target' in share) ) {
+ OC.Share.showLink(share.token, share.share_with, itemSource);
+ }
} else {
if (share.collection) {
OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, share.collection);
@@ -233,6 +254,7 @@ OC.Share={
// } else {
$.get(OC.filePath('core', 'ajax', 'share.php'), { fetch: 'getShareWith', search: search.term, itemShares: OC.Share.itemShares }, function(result) {
if (result.status == 'success' && result.data.length > 0) {
+ $( "#shareWith" ).autocomplete( "option", "autoFocus", true );
response(result.data);
} else {
// Suggest sharing via email if valid email address
@@ -240,6 +262,7 @@ OC.Share={
// if (pattern.test(search.term)) {
// response([{label: t('core', 'Share via email:')+' '+search.term, value: {shareType: OC.Share.SHARE_TYPE_EMAIL, shareWith: search.term}}]);
// } else {
+ $( "#shareWith" ).autocomplete( "option", "autoFocus", false );
response([t('core', 'No people found')]);
// }
}
@@ -412,7 +435,7 @@ OC.Share={
dateFormat : 'dd-mm-yy'
});
}
-}
+};
$(document).ready(function() {
@@ -501,7 +524,7 @@ $(document).ready(function() {
$(document).on('change', '#dropdown .permissions', function() {
if ($(this).attr('name') == 'edit') {
- var li = $(this).parent().parent()
+ var li = $(this).parent().parent();
var checkboxes = $('.permissions', li);
var checked = $(this).is(':checked');
// Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck
diff --git a/core/templates/login.php b/core/templates/login.php
index 882ce234cf4..3874e998bf9 100644
--- a/core/templates/login.php
+++ b/core/templates/login.php
@@ -35,8 +35,10 @@
<label for="password" class="infield"><?php p($l->t('Password')); ?></label>
<img class="svg" id="password-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt=""/>
</p>
+ <?php if ($_['rememberLoginAllowed'] === true) : ?>
<input type="checkbox" name="remember_login" value="1" id="remember_login" checked /><label
for="remember_login"><?php p($l->t('remember')); ?></label>
+ <?php endif; ?>
<input type="hidden" name="timezone-offset" id="timezone-offset"/>
<input type="submit" id="submit" class="login primary" value="<?php p($l->t('Log in')); ?>"/>
</fieldset>
diff --git a/lib/appconfig.php b/lib/appconfig.php
index e615d838173..8bc68f45de0 100644
--- a/lib/appconfig.php
+++ b/lib/appconfig.php
@@ -125,14 +125,22 @@ class OC_Appconfig{
public static function setValue( $app, $key, $value ) {
// Does the key exist? yes: update. No: insert
if(! self::hasKey($app, $key)) {
- $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*appconfig` ( `appid`, `configkey`, `configvalue` )'
- .' VALUES( ?, ?, ? )' );
- $query->execute( array( $app, $key, $value ));
+ OC_DB::executeAudited('
+ INSERT INTO `*PREFIX*appconfig` (
+ `appid`, `configkey`, `configvalue`
+ ) VALUES (
+ ?, ?, ?
+ )
+ ', array( $app, $key, $value )
+ );
}
else{
- $query = OC_DB::prepare( 'UPDATE `*PREFIX*appconfig` SET `configvalue` = ?'
- .' WHERE `appid` = ? AND `configkey` = ?' );
- $query->execute( array( $value, $app, $key ));
+ OC_DB::executeAudited('
+ UPDATE `*PREFIX*appconfig`
+ SET `configvalue` = ?
+ WHERE `appid` = ? AND `configkey` = ?
+ ', array( $value, $app, $key )
+ );
}
}
diff --git a/lib/base.php b/lib/base.php
index 5a1bd4ecbe3..e8f67db8881 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -746,6 +746,7 @@ class OC {
|| !isset($_COOKIE["oc_token"])
|| !isset($_COOKIE["oc_username"])
|| !$_COOKIE["oc_remember_login"]
+ || !OC_Util::rememberLoginAllowed()
) {
return false;
}
@@ -768,6 +769,7 @@ class OC {
OC_User::setMagicInCookie($_COOKIE['oc_username'], $token);
// login
OC_User::setUserId($_COOKIE['oc_username']);
+ OC_User::setDisplayName($_COOKIE['oc_username'], $_COOKIE['display_name']);
OC_Util::redirectToDefaultPage();
// doesn't return
}
diff --git a/lib/cache/file.php b/lib/cache/file.php
index 361138e4736..eed2637c981 100644
--- a/lib/cache/file.php
+++ b/lib/cache/file.php
@@ -40,6 +40,24 @@ class OC_Cache_File{
return $result;
}
+ /**
+ * Returns the size of the stored/cached data
+ *
+ * @param $key
+ * @return int
+ */
+ public function size($key) {
+ $result = 0;
+ $proxyStatus = \OC_FileProxy::$enabled;
+ \OC_FileProxy::$enabled = false;
+ if ($this->hasKey($key)) {
+ $storage = $this->getStorage();
+ $result = $storage->filesize($key);
+ }
+ \OC_FileProxy::$enabled = $proxyStatus;
+ return $result;
+ }
+
public function set($key, $value, $ttl=0) {
$storage = $this->getStorage();
$result = false;
diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php
index 1434e970587..3cccf6ef3d0 100644
--- a/lib/connector/sabre/directory.php
+++ b/lib/connector/sabre/directory.php
@@ -50,23 +50,24 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
*/
public function createFile($name, $data = null) {
- if (!\OC\Files\Filesystem::isCreatable($this->path)) {
- throw new \Sabre_DAV_Exception_Forbidden();
- }
-
if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
$info = OC_FileChunking::decodeName($name);
if (empty($info)) {
throw new Sabre_DAV_Exception_NotImplemented();
}
- $chunk_handler = new OC_FileChunking($info);
- $chunk_handler->store($info['index'], $data);
- if ($chunk_handler->isComplete()) {
- $newPath = $this->path . '/' . $info['name'];
- $chunk_handler->file_assemble($newPath);
- return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
+
+ if (!\OC\Files\Filesystem::isCreatable($this->path) &&
+ !\OC\Files\Filesystem::isUpdatable($this->path . '/' . $info['name'])) {
+ throw new \Sabre_DAV_Exception_Forbidden();
}
+
+ return $this->createFileChunked($name, $data);
} else {
+
+ if (!\OC\Files\Filesystem::isCreatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
$newPath = $this->path . '/' . $name;
// mark file as partial while uploading (ignored by the scanner)
@@ -88,7 +89,13 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
}
// rename to correct path
- \OC\Files\Filesystem::rename($partpath, $newPath);
+ $renameOkay = \OC\Files\Filesystem::rename($partpath, $newPath);
+ $fileExists = \OC\Files\Filesystem::file_exists($newPath);
+ if ($renameOkay === false || $fileExists === false) {
+ \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR);
+ \OC\Files\Filesystem::unlink($partpath);
+ throw new Sabre_DAV_Exception();
+ }
// allow sync clients to send the mtime along in a header
$mtime = OC_Request::hasModificationTime();
@@ -251,7 +258,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
* If the array is empty, all properties should be returned
*
* @param array $properties
- * @return void
+ * @return array
*/
public function getProperties($properties) {
$props = parent::getProperties($properties);
@@ -261,4 +268,34 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
}
return $props;
}
+
+ private function createFileChunked($name, $data)
+ {
+ $info = OC_FileChunking::decodeName($name);
+ if (empty($info)) {
+ throw new Sabre_DAV_Exception_NotImplemented();
+ }
+ $chunk_handler = new OC_FileChunking($info);
+ $bytesWritten = $chunk_handler->store($info['index'], $data);
+
+ //detect aborted upload
+ if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT' ) {
+ if (isset($_SERVER['CONTENT_LENGTH'])) {
+ $expected = $_SERVER['CONTENT_LENGTH'];
+ if ($bytesWritten != $expected) {
+ $chunk_handler->remove($info['index']);
+ throw new Sabre_DAV_Exception_BadRequest(
+ 'expected filesize ' . $expected . ' got ' . $bytesWritten);
+ }
+ }
+ }
+
+ if ($chunk_handler->isComplete()) {
+ $newPath = $this->path . '/' . $info['name'];
+ $chunk_handler->file_assemble($newPath);
+ return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
+ }
+
+ return null;
+ }
}
diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php
index 06ab73e3e4d..bbfb27a8a9e 100644
--- a/lib/connector/sabre/file.php
+++ b/lib/connector/sabre/file.php
@@ -53,6 +53,13 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D
// mark file as partial while uploading (ignored by the scanner)
$partpath = $this->path . '.part';
+ // if file is located in /Shared we write the part file to the users
+ // root folder because we can't create new files in /shared
+ // we extend the name with a random number to avoid overwriting a existing file
+ if (dirname($partpath) === '/Shared') {
+ $partpath = pathinfo($partpath, PATHINFO_FILENAME) . rand() . '.part';
+ }
+
\OC\Files\Filesystem::file_put_contents($partpath, $data);
//detect aborted upload
@@ -69,7 +76,14 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D
}
// rename to correct path
- \OC\Files\Filesystem::rename($partpath, $this->path);
+ $renameOkay = \OC\Files\Filesystem::rename($partpath, $this->path);
+ $fileExists = \OC\Files\Filesystem::file_exists($this->path);
+ if ($renameOkay === false || $fileExists === false) {
+ \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR);
+ \OC\Files\Filesystem::unlink($partpath);
+ throw new Sabre_DAV_Exception();
+ }
+
//allow sync clients to send the mtime along in a header
$mtime = OC_Request::hasModificationTime();
diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php
index 1ffa048d6b2..f6a1c56edb8 100644
--- a/lib/connector/sabre/node.php
+++ b/lib/connector/sabre/node.php
@@ -78,6 +78,11 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
*/
public function setName($name) {
+ // rename is only allowed if the update privilege is granted
+ if (!\OC\Files\Filesystem::isUpdatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
@@ -135,6 +140,12 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
* Even if the modification time is set to a custom value the access time is set to now.
*/
public function touch($mtime) {
+
+ // touch is only allowed if the update privilege is granted
+ if (!\OC\Files\Filesystem::isUpdatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
\OC\Files\Filesystem::touch($this->path, $mtime);
}
diff --git a/lib/db.php b/lib/db.php
index 56fa1ce43e2..84b05dfe6e5 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -236,7 +236,7 @@ class OC_DB {
// Prepare options array
$options = array(
- 'portability' => MDB2_PORTABILITY_ALL - MDB2_PORTABILITY_FIX_CASE,
+ 'portability' => MDB2_PORTABILITY_ALL - MDB2_PORTABILITY_FIX_CASE - MDB2_PORTABILITY_RTRIM,
'log_line_break' => '<br>',
'idxname_format' => '%s',
'debug' => true,
@@ -427,7 +427,56 @@ class OC_DB {
}
return false;
}
-
+
+ /**
+ * @brief execute a prepared statement, on error write log and throw exception
+ * @param mixed $stmt OC_DB_StatementWrapper,
+ * an array with 'sql' and optionally 'limit' and 'offset' keys
+ * .. or a simple sql query string
+ * @param array $parameters
+ * @return MDB2_Result|PDOStatementWrapper|bool|int result
+ * @throws DatabaseException
+ */
+ static public function executeAudited( $stmt, array $parameters = null) {
+ if (is_string($stmt)) {
+ // convert to an array with 'sql'
+ if (stripos($stmt,'LIMIT') !== false) { //OFFSET requires LIMIT, se we only neet to check for LIMIT
+ // TODO try to convert LIMIT OFFSET notation to parameters, see fixLimitClauseForMSSQL
+ $message = 'LIMIT and OFFSET are forbidden for portability reasons,'
+ . ' pass an array with \'limit\' and \'offset\' instead';
+ throw new DatabaseException($message, $stmt);
+ }
+ $stmt = array('sql' => $stmt, 'limit' => null, 'offset' => null);
+ }
+ if (is_array($stmt)){
+ // convert to prepared statement
+ if ( ! array_key_exists('sql', $stmt) ) {
+ $message = 'statement array must at least contain key \'sql\'';
+ throw new DatabaseException($message, '');
+ }
+ if ( ! array_key_exists('limit', $stmt) ) {
+ $stmt['limit'] = null;
+ }
+ if ( ! array_key_exists('limit', $stmt) ) {
+ $stmt['offset'] = null;
+ }
+ $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']);
+ }
+ if ($stmt instanceof PDOStatementWrapper || $stmt instanceof MDB2_Statement_Common) {
+ /** @var $stmt PDOStatementWrapper|MDB2_Statement_Common */
+ $result = $stmt->execute($parameters);
+ self::raiseExceptionOnError($result, 'Could not execute statement', $parameters);
+ } else {
+ if (is_object($stmt)) {
+ $message = 'Expected a prepared statement or array got ' . get_class($stmt);
+ } else {
+ $message = 'Expected a prepared statement or array got ' . gettype($stmt);
+ }
+ throw new DatabaseException($message, '');
+ }
+ return $result;
+ }
+
/**
* @brief gets last value of autoincrement
* @param string $table The optional table name (will replace *PREFIX*) and add sequence suffix
@@ -512,9 +561,29 @@ class OC_DB {
* TODO: write more documentation
*/
public static function createDbFromStructure( $file ) {
- $CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" );
- $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" );
- $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
+ $CONFIG_DBNAME = OC_Config::getValue('dbname', 'owncloud');
+ $CONFIG_DBTABLEPREFIX = OC_Config::getValue('dbtableprefix', 'oc_');
+ $CONFIG_DBTYPE = OC_Config::getValue('dbtype', 'sqlite');
+ $CONFIG_DBHOST = OC_Config::getValue('dbhost', '');
+
+ if( $CONFIG_DBTYPE === 'oci'
+ && $CONFIG_DBNAME === ''
+ && ! empty($CONFIG_DBHOST)
+ ) {
+ // we are connecting by user name, pwd and SID (host)
+ $CONFIG_DBUSER = OC_Config::getValue('dbuser', '');
+ $CONFIG_DBPASSWORD = OC_Config::getValue('dbpassword');
+ if ($CONFIG_DBUSER !== ''
+ && $CONFIG_DBPASSWORD !== ''
+ ) {
+ // use dbuser as dbname
+ $CONFIG_DBNAME = $CONFIG_DBUSER;
+ } else {
+ throw new DatabaseException('Please specify '
+ .'username and password when '
+ .'connecting via SID as the hostname.', '');
+ }
+ }
// cleanup the cached queries
self::$preparedQueries = array();
@@ -543,6 +612,8 @@ class OC_DB {
file_put_contents( $file2, $content );
+ \OC_Log::write('db','creating table from schema: '.$content,\OC_Log::DEBUG);
+
// Try to create tables
$definition = self::$schema->parseDatabaseDefinitionFile( $file2 );
@@ -735,7 +806,8 @@ class OC_DB {
$stmt = self::prepare($query);
$result = $stmt->execute($inserts);
if (self::isError($result)) {
- OC_Log::write('core', self::getErrorMessage($result), OC_Log::FATAL);
+ $entry = self::getErrorMessage($result);
+ OC_Log::write('core', $entry, OC_Log::FATAL);
OC_Template::printErrorPage( $entry );
}
@@ -1001,6 +1073,41 @@ class OC_DB {
return false;
}
+ /**
+ * check if a result is an error, writes a log entry and throws an exception, works with MDB2 and PDOException
+ * @param mixed $result
+ * @param string $message
+ * @return void
+ * @throws DatabaseException
+ */
+ public static function raiseExceptionOnError($result, $message = null, array $params = null) {
+ if(self::isError($result)) {
+ if ($message === null) {
+ $message = self::getErrorMessage($result);
+ } else {
+ $message .= ', Root cause:' . self::getErrorMessage($result);
+ }
+ if ($params) {
+ $message .= ', params: ' . json_encode($params);
+ }
+ throw new DatabaseException($message, self::getErrorCode($result));
+ }
+ }
+
+ /**
+ * @param mixed $error
+ * @return int|mixed|null
+ */
+ public static function getErrorCode($error) {
+ $code = null;
+ if ( self::$backend==self::BACKEND_MDB2 and PEAR::isError($error) ) {
+ /** @var $error PEAR_Error */
+ $code = $error->getCode();
+ } elseif ( self::$backend==self::BACKEND_PDO and self::$PDO ) {
+ $code = self::$PDO->errorCode();
+ }
+ return $code;
+ }
/**
* returns the error code and message as a string for logging
@@ -1059,6 +1166,9 @@ class PDOStatementWrapper{
/**
* make execute return the result or updated row count instead of a bool
+ *
+ * @param array $input
+ * @return $this|bool|int
*/
public function execute($input=array()) {
$this->lastArguments = $input;
@@ -1183,7 +1293,7 @@ class PDOStatementWrapper{
public function numRows() {
$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i';
if (preg_match($regex, $this->statement->queryString, $output) > 0) {
- $query = OC_DB::prepare("SELECT COUNT(*) FROM {$output[1]}", PDO::FETCH_NUM);
+ $query = OC_DB::prepare("SELECT COUNT(*) FROM {$output[1]}");
return $query->execute($this->lastArguments)->fetchColumn();
}else{
return $this->statement->rowCount();
diff --git a/lib/filechunking.php b/lib/filechunking.php
index e6d69273a44..bee6f702298 100644
--- a/lib/filechunking.php
+++ b/lib/filechunking.php
@@ -34,10 +34,19 @@ class OC_FileChunking {
return $this->cache;
}
+ /**
+ * Stores the given $data under the given $key - the number of stored bytes is returned
+ *
+ * @param $index
+ * @param $data
+ * @return int
+ */
public function store($index, $data) {
$cache = $this->getCache();
$name = $this->getPrefix().$index;
$cache->set($name, $data);
+
+ return $cache->size($name);
}
public function isComplete() {
@@ -58,12 +67,34 @@ class OC_FileChunking {
$count = 0;
for($i=0; $i < $this->info['chunkcount']; $i++) {
$chunk = $cache->get($prefix.$i);
- $cache->remove($prefix.$i);
$count += fwrite($f, $chunk);
}
+
+ $this->cleanup();
return $count;
}
+ /**
+ * Removes all chunks which belong to this transmission
+ */
+ public function cleanup() {
+ $cache = $this->getCache();
+ $prefix = $this->getPrefix();
+ for($i=0; $i < $this->info['chunkcount']; $i++) {
+ $cache->remove($prefix.$i);
+ }
+ }
+
+ /**
+ * Removes one specific chunk
+ * @param $index
+ */
+ public function remove($index) {
+ $cache = $this->getCache();
+ $prefix = $this->getPrefix();
+ $cache->remove($prefix.$index);
+ }
+
public function signature_split($orgfile, $input) {
$info = unpack('n', fread($input, 2));
$blocksize = $info[1];
diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php
index cb48ab446be..32cad6b84b2 100644
--- a/lib/files/cache/cache.php
+++ b/lib/files/cache/cache.php
@@ -52,13 +52,17 @@ class Cache {
$this->storageId = md5($this->storageId);
}
- $query = \OC_DB::prepare('SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?');
- $result = $query->execute(array($this->storageId));
+ $result = \OC_DB::executeAudited(
+ 'SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?',
+ array($this->storageId)
+ );
if ($row = $result->fetchRow()) {
$this->numericId = $row['numeric_id'];
} else {
- $query = \OC_DB::prepare('INSERT INTO `*PREFIX*storages`(`id`) VALUES(?)');
- $query->execute(array($this->storageId));
+ \OC_DB::executeAudited(
+ 'INSERT INTO `*PREFIX*storages`(`id`) VALUES(?)',
+ array($this->storageId)
+ );
$this->numericId = \OC_DB::insertid('*PREFIX*storages');
}
}
@@ -67,6 +71,19 @@ class Cache {
return $this->numericId;
}
+ public static function storageExists($storageId) {
+ if (strlen($storageId) > 64) {
+ $storageId = md5($storageId);
+ }
+ $query = \OC_DB::prepare('SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?');
+ $result = $query->execute(array($storageId));
+ if ($row = $result->fetchRow()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
/**
* normalize mimetypes
*
@@ -75,13 +92,17 @@ class Cache {
*/
public function getMimetypeId($mime) {
if (!isset($this->mimetypeIds[$mime])) {
- $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ?');
- $result = $query->execute(array($mime));
+ $result = \OC_DB::executeAudited(
+ 'SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ?',
+ array($mime)
+ );
if ($row = $result->fetchRow()) {
$this->mimetypeIds[$mime] = $row['id'];
} else {
- $query = \OC_DB::prepare('INSERT INTO `*PREFIX*mimetypes`(`mimetype`) VALUES(?)');
- $query->execute(array($mime));
+ \OC_DB::executeAudited(
+ 'INSERT INTO `*PREFIX*mimetypes`(`mimetype`) VALUES(?)',
+ array($mime)
+ );
$this->mimetypeIds[$mime] = \OC_DB::insertid('*PREFIX*mimetypes');
}
$this->mimetypes[$this->mimetypeIds[$mime]] = $mime;
@@ -91,8 +112,10 @@ class Cache {
public function getMimetype($id) {
if (!isset($this->mimetypes[$id])) {
- $query = \OC_DB::prepare('SELECT `mimetype` FROM `*PREFIX*mimetypes` WHERE `id` = ?');
- $result = $query->execute(array($id));
+ $result = \OC_DB::executeAudited(
+ 'SELECT `mimetype` FROM `*PREFIX*mimetypes` WHERE `id` = ?',
+ array($id)
+ );
if ($row = $result->fetchRow()) {
$this->mimetypes[$id] = $row['mimetype'];
} else {
@@ -119,10 +142,13 @@ class Cache {
$where = 'WHERE `fileid` = ?';
$params = array($file);
}
- $query = \OC_DB::prepare(
- 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag`
- FROM `*PREFIX*filecache` ' . $where);
- $result = $query->execute($params);
+ $result = \OC_DB::executeAudited(
+ 'SELECT `fileid`, `storage`, `path`, `parent`, `name`,
+ `mimetype`, `mimepart`, `size`, `mtime`,
+ `encrypted`, `unencrypted_size`, `etag`
+ FROM `*PREFIX*filecache` ' . $where,
+ $params
+ );
$data = $result->fetchRow();
//FIXME hide this HACK in the next database layer, or just use doctrine and get rid of MDB2 and PDO
@@ -160,13 +186,16 @@ class Cache {
public function getFolderContents($folder) {
$fileId = $this->getId($folder);
if ($fileId > -1) {
- $query = \OC_DB::prepare(
- 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size` , `etag`
- FROM `*PREFIX*filecache` WHERE `parent` = ? ORDER BY `name` ASC');
- $result = $query->execute(array($fileId));
- if (\OC_DB::isError($result)) {
- \OCP\Util::writeLog('cache', 'getFolderContents failed: ' . $result->getMessage(), \OCP\Util::ERROR);
- }
+ $result = \OC_DB::executeAudited('
+ SELECT `fileid`, `storage`, `path`, `parent`,
+ `name`, `mimetype`, `mimepart`, `size`,
+ `mtime`, `encrypted`,
+ `unencrypted_size`, `etag`
+ FROM `*PREFIX*filecache`
+ WHERE `parent` = ?
+ ORDER BY `name` ASC',
+ array($fileId)
+ );
$files = $result->fetchAll();
foreach ($files as &$file) {
$file['mimetype'] = $this->getMimetype($file['mimetype']);
@@ -216,12 +245,11 @@ class Cache {
$params[] = $this->numericId;
$valuesPlaceholder = array_fill(0, count($queryParts), '?');
- $query = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ')'
- . ' VALUES(' . implode(', ', $valuesPlaceholder) . ')');
- $result = $query->execute($params);
- if (\OC_DB::isError($result)) {
- \OCP\Util::writeLog('cache', 'Insert to cache failed: ' . $result->getMessage(), \OCP\Util::ERROR);
- }
+ \OC_DB::executeAudited('
+ INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ')
+ VALUES(' . implode(', ', $valuesPlaceholder) . ')',
+ $params
+ );
return (int)\OC_DB::insertid('*PREFIX*filecache');
}
@@ -247,9 +275,12 @@ class Cache {
list($queryParts, $params) = $this->buildParts($data);
$params[] = $id;
- $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=?'
- . ' WHERE `fileid` = ?');
- $query->execute($params);
+ \OC_DB::executeAudited('
+ UPDATE `*PREFIX*filecache`
+ SET ' . implode(' = ?, ', $queryParts) . '=?
+ WHERE `fileid` = ?',
+ $params
+ );
}
/**
@@ -294,8 +325,11 @@ class Cache {
$pathHash = md5($file);
- $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?');
- $result = $query->execute(array($this->numericId, $pathHash));
+ $result = \OC_DB::executeAudited('
+ SELECT `fileid` FROM `*PREFIX*filecache`
+ WHERE `storage` = ? AND `path_hash` = ?',
+ array($this->numericId, $pathHash)
+ );
if ($row = $result->fetchRow()) {
return $row['fileid'];
@@ -345,8 +379,10 @@ class Cache {
$this->remove($child['path']);
}
}
- $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?');
- $query->execute(array($entry['fileid']));
+ \OC_DB::executeAudited('
+ DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?',
+ array($entry['fileid'])
+ );
$permissionsCache = new Permissions($this->storageId);
$permissionsCache->remove($entry['fileid']);
@@ -369,32 +405,48 @@ class Cache {
if ($sourceData['mimetype'] === 'httpd/unix-directory') {
//find all child entries
- $query = \OC_DB::prepare('SELECT `path`, `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path` LIKE ?');
- $result = $query->execute(array($this->getNumericStorageId(), $source . '/%'));
+ $result = \OC_DB::executeAudited('
+ SELECT `path`, `fileid` FROM `*PREFIX*filecache`
+ WHERE `storage` = ? AND `path` LIKE ?',
+ array($this->getNumericStorageId(), $source . '/%')
+ );
$childEntries = $result->fetchAll();
$sourceLength = strlen($source);
$query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET `path` = ?, `path_hash` = ? WHERE `fileid` = ?');
foreach ($childEntries as $child) {
$targetPath = $target . substr($child['path'], $sourceLength);
- $query->execute(array($targetPath, md5($targetPath), $child['fileid']));
+ \OC_DB::executeAudited($query,
+ array(
+ $targetPath,
+ md5($targetPath),
+ $child['fileid']
+ )
+ );
}
}
- $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET `path` = ?, `path_hash` = ?, `name` = ?, `parent` =?'
- . ' WHERE `fileid` = ?');
- $query->execute(array($target, md5($target), basename($target), $newParentId, $sourceId));
+ \OC_DB::executeAudited('
+ UPDATE `*PREFIX*filecache`
+ SET `path` = ?, `path_hash` = ?, `name` = ?, `parent` =?
+ WHERE `fileid` = ?',
+ array($target, md5($target), basename($target), $newParentId, $sourceId)
+ );
}
/**
* remove all entries for files that are stored on the storage from the cache
*/
public function clear() {
- $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?');
- $query->execute(array($this->numericId));
+ \OC_DB::executeAudited('
+ DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?',
+ array($this->numericId)
+ );
- $query = \OC_DB::prepare('DELETE FROM `*PREFIX*storages` WHERE `id` = ?');
- $query->execute(array($this->storageId));
+ \OC_DB::executeAudited('
+ DELETE FROM `*PREFIX*storages` WHERE `id` = ?',
+ array($this->storageId)
+ );
}
/**
@@ -407,11 +459,12 @@ class Cache {
$file = $this->normalize($file);
$pathHash = md5($file);
- $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?');
- $result = $query->execute(array($this->numericId, $pathHash));
- if( \OC_DB::isError($result)) {
- \OCP\Util::writeLog('cache', 'get status failed: ' . $result->getMessage(), \OCP\Util::ERROR);
- }
+ $result = \OC_DB::executeAudited('
+ SELECT `size`
+ FROM `*PREFIX*filecache`
+ WHERE `storage` = ? AND `path_hash` = ?',
+ array($this->numericId, $pathHash)
+ );
if ($row = $result->fetchRow()) {
if ((int)$row['size'] === -1) {
return self::SHALLOW;
@@ -437,11 +490,14 @@ class Cache {
// normalize pattern
$pattern = $this->normalize($pattern);
- $query = \OC_DB::prepare('
- SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag`
- FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `storage` = ?'
+ $result = \OC_DB::executeAudited('
+ SELECT `fileid`, `storage`, `path`, `parent`, `name`,
+ `mimetype`, `mimepart`, `size`, `mtime`,
+ `encrypted`, `unencrypted_size`, `etag`
+ FROM `*PREFIX*filecache`
+ WHERE `name` LIKE ? AND `storage` = ?',
+ array($pattern, $this->numericId)
);
- $result = $query->execute(array($pattern, $this->numericId));
$files = array();
while ($row = $result->fetchRow()) {
$row['mimetype'] = $this->getMimetype($row['mimetype']);
@@ -463,12 +519,17 @@ class Cache {
} else {
$where = '`mimepart` = ?';
}
- $query = \OC_DB::prepare('
- SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag`
- FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?'
- );
$mimetype = $this->getMimetypeId($mimetype);
- $result = $query->execute(array($mimetype, $this->numericId));
+
+ $result = \OC_DB::executeAudited('
+ SELECT `fileid`, `storage`, `path`, `parent`, `name`,
+ `mimetype`, `mimepart`, `size`, `mtime`,
+ `encrypted`, `unencrypted_size`, `etag`
+ FROM `*PREFIX*filecache`
+ WHERE ' . $where . ' AND `storage` = ?',
+ array($mimetype, $this->numericId)
+ );
+
$files = array();
while ($row = $result->fetchRow()) {
$row['mimetype'] = $this->getMimetype($row['mimetype']);
@@ -505,9 +566,12 @@ class Cache {
$entry = $this->get($path);
if ($entry && $entry['mimetype'] === 'httpd/unix-directory') {
$id = $entry['fileid'];
- $query = \OC_DB::prepare('SELECT SUM(`size`), MIN(`size`) FROM `*PREFIX*filecache` '.
- 'WHERE `parent` = ? AND `storage` = ?');
- $result = $query->execute(array($id, $this->getNumericStorageId()));
+ $result = \OC_DB::executeAudited('
+ SELECT SUM(`size`), MIN(`size`)
+ FROM `*PREFIX*filecache`
+ WHERE `parent` = ? AND `storage` = ?',
+ array($id, $this->getNumericStorageId())
+ );
if ($row = $result->fetchRow()) {
list($sum, $min) = array_values($row);
$sum = (int)$sum;
@@ -520,7 +584,7 @@ class Cache {
if ($entry['size'] !== $totalSize) {
$this->update($id, array('size' => $totalSize));
}
-
+
}
}
return $totalSize;
@@ -532,8 +596,12 @@ class Cache {
* @return int[]
*/
public function getAll() {
- $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?');
- $result = $query->execute(array($this->numericId));
+ $result = \OC_DB::executeAudited('
+ SELECT `fileid`
+ FROM `*PREFIX*filecache`
+ WHERE `storage` = ?',
+ array($this->numericId)
+ );
$ids = array();
while ($row = $result->fetchRow()) {
$ids[] = $row['fileid'];
@@ -551,12 +619,15 @@ class Cache {
* @return string|bool the path of the folder or false when no folder matched
*/
public function getIncomplete() {
- $query = \OC_DB::prepare('SELECT `path` FROM `*PREFIX*filecache`'
- . ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC',1);
- $result = $query->execute(array($this->numericId));
- if (\OC_DB::isError($result)) {
- \OCP\Util::writeLog('cache', 'getIncomplete failed: ' . $result->getMessage(), \OCP\Util::ERROR);
- }
+ $query = \OC_DB::prepare('
+ SELECT `path`
+ FROM `*PREFIX*filecache`
+ WHERE `storage` = ? AND `size` = -1
+ ORDER BY `fileid` DESC', 1);
+ $result = \OC_DB::executeAudited(
+ $query,
+ array($this->numericId)
+ );
if ($row = $result->fetchRow()) {
return $row['path'];
} else {
@@ -570,8 +641,12 @@ class Cache {
* @return array, first element holding the storage id, second the path
*/
static public function getById($id) {
- $query = \OC_DB::prepare('SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?');
- $result = $query->execute(array($id));
+ $result = \OC_DB::executeAudited('
+ SELECT `storage`, `path`
+ FROM `*PREFIX*filecache`
+ WHERE `fileid` = ?',
+ array($id)
+ );
if ($row = $result->fetchRow()) {
$numericId = $row['storage'];
$path = $row['path'];
@@ -579,8 +654,12 @@ class Cache {
return null;
}
- $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*storages` WHERE `numeric_id` = ?');
- $result = $query->execute(array($numericId));
+ $result = \OC_DB::executeAudited('
+ SELECT `id`
+ FROM `*PREFIX*storages`
+ WHERE `numeric_id` = ?',
+ array($numericId)
+ );
if ($row = $result->fetchRow()) {
return array($row['id'], $path);
} else {
@@ -594,6 +673,7 @@ class Cache {
* @return string
*/
public function normalize($path) {
-
return \OC_Util::normalizeUnicode($path);
- }}
+ }
+
+}
diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php
index 9781c76d69c..c9d67f8ee56 100644
--- a/lib/files/cache/updater.php
+++ b/lib/files/cache/updater.php
@@ -78,30 +78,58 @@ class Updater {
}
/**
+ * @brief get file owner and path
+ * @param string $filename
+ * @return array with the oweners uid and the owners path
+ */
+ private static function getUidAndFilename($filename) {
+ $uid = \OC\Files\Filesystem::getOwner($filename);
+ \OC\Files\Filesystem::initMountPoints($uid);
+ if ( $uid != \OCP\User::getUser() ) {
+ $info = \OC\Files\Filesystem::getFileInfo($filename);
+ $ownerView = new \OC\Files\View('/'.$uid.'/files');
+ $filename = $ownerView->getPath($info['fileid']);
+ }
+ return array($uid, '/files/'.$filename);
+ }
+
+ /**
* Update the mtime and ETag of all parent folders
*
* @param string $path
* @param string $time
*/
static public function correctFolder($path, $time) {
+
if ($path !== '' && $path !== '/') {
- $parent = dirname($path);
- if ($parent === '.') {
- $parent = '';
- }
+
+ list($owner, $realPath) = self::getUidAndFilename(dirname($path));
+
/**
* @var \OC\Files\Storage\Storage $storage
* @var string $internalPath
*/
- list($storage, $internalPath) = self::resolvePath($parent);
- if ($storage) {
- $cache = $storage->getCache();
- $id = $cache->getId($internalPath);
- if ($id !== -1) {
- $cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath)));
- self::correctFolder($parent, $time);
+
+ $view = new \OC\Files\View('/' . $owner);
+
+ list($storage, $internalPath) = $view->resolvePath($realPath);
+ $cache = $storage->getCache();
+ $id = $cache->getId($internalPath);
+
+ while ($id !== -1) {
+ $cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath)));
+ $realPath = dirname($realPath);
+ // check storage for parent in case we change the storage in this step
+ list($storage, $internalPath) = $view->resolvePath($realPath);
+ if ($internalPath) {
+ $cache = $storage->getCache();
+ $id = $cache->getId($internalPath);
+ } else {
+ $id = -1;
}
+
}
+
}
}
diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php
index 4b445588a65..1850d50f971 100644
--- a/lib/files/filesystem.php
+++ b/lib/files/filesystem.php
@@ -221,7 +221,11 @@ class Filesystem {
$parser = new \OC\ArrayParser();
$root = \OC_User::getHome($user);
- self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user);
+ if (\OC\Files\Cache\Cache::storageExists('local::' . $root . '/') or is_null($user)) {
+ self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user);
+ } else {
+ self::mount('\OC\Files\Storage\Home', array('user' => $user, 'datadir' => $root), $user);
+ }
$datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data");
//move config file to it's new position
@@ -585,18 +589,32 @@ class Filesystem {
}
//no windows style slashes
$path = str_replace('\\', '/', $path);
+
//add leading slash
if ($path[0] !== '/') {
$path = '/' . $path;
}
- //remove duplicate slashes
- while (strpos($path, '//') !== false) {
- $path = str_replace('//', '/', $path);
+
+ // remove '/./'
+ // ugly, but str_replace() can't replace them all in one go
+ // as the replacement itself is part of the search string
+ // which will only be found during the next iteration
+ while (strpos($path, '/./') !== false) {
+ $path = str_replace('/./', '/', $path);
}
+ // remove sequences of slashes
+ $path = preg_replace('#/{2,}#', '/', $path);
+
//remove trailing slash
if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') {
$path = substr($path, 0, -1);
}
+
+ // remove trailing '/.'
+ if (substr($path, -2) == '/.') {
+ $path = substr($path, 0, -2);
+ }
+
//normalize unicode if possible
$path = \OC_Util::normalizeUnicode($path);
return $path;
diff --git a/lib/files/storage/home.php b/lib/files/storage/home.php
new file mode 100644
index 00000000000..533fe5fe390
--- /dev/null
+++ b/lib/files/storage/home.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Storage;
+
+/**
+ * Specialized version of Local storage for home directory usage
+ */
+class Home extends Local {
+
+ /**
+ * @var string $user
+ */
+ protected $user;
+
+ public function __construct($arguments) {
+ $this->user = $arguments['user'];
+ $datadir = $arguments['datadir'];
+
+ parent::__construct(array('datadir' => $datadir));
+ }
+
+ public function getId() {
+ return 'home::' . $this->user;
+ }
+}
diff --git a/lib/files/view.php b/lib/files/view.php
index 1e2a95751ff..65c5a527a1b 100644
--- a/lib/files/view.php
+++ b/lib/files/view.php
@@ -110,7 +110,9 @@ class View {
* @return array consisting of the storage and the internal path
*/
public function resolvePath($path) {
- return Filesystem::resolvePath($this->getAbsolutePath($path));
+ $a = $this->getAbsolutePath($path);
+ $p = Filesystem::normalizePath($a);
+ return Filesystem::resolvePath($p);
}
/**
@@ -252,7 +254,11 @@ class View {
$hooks[] = 'write';
}
- return $this->basicOperation('touch', $path, $hooks, $mtime);
+ $result = $this->basicOperation('touch', $path, array('write'), $mtime);
+ if (!$result) { //if native touch fails, we emulate it by changing the mtime in the cache
+ $this->putFileInfo($path, array('mtime' => $mtime));
+ }
+ return true;
}
public function file_get_contents($path) {
@@ -698,7 +704,10 @@ class View {
return false;
}
$defaultRoot = Filesystem::getRoot();
- return (strlen($this->fakeRoot) >= strlen($defaultRoot)) && (substr($this->fakeRoot, 0, strlen($defaultRoot)) === $defaultRoot);
+ if($this->fakeRoot === $defaultRoot){
+ return true;
+ }
+ return (strlen($this->fakeRoot) > strlen($defaultRoot)) && (substr($this->fakeRoot, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
}
private function runHooks($hooks, $path, $post = false) {
diff --git a/lib/helper.php b/lib/helper.php
index ddd6ce9a149..819b1e87f4a 100644
--- a/lib/helper.php
+++ b/lib/helper.php
@@ -162,17 +162,35 @@ class OC_Helper {
// Read the selected theme from the config file
$theme = OC_Util::getTheme();
+ //if a theme has a png but not an svg always use the png
+ $basename = substr(basename($image),0,-4);
+
// Check if the app is in the app folder
if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/img/$image" )) {
return OC::$WEBROOT."/themes/$theme/apps/$app/img/$image";
+ }elseif( ! file_exists( OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.svg")
+ && file_exists( OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.png")) {
+ return OC::$WEBROOT . "/themes/$theme/apps/$app/img/$basename.png";
}elseif( file_exists(OC_App::getAppPath($app)."/img/$image" )) {
return OC_App::getAppWebPath($app)."/img/$image";
+ }elseif( ! file_exists( OC_App::getAppPath($app) . "/img/$basename.svg")
+ && file_exists( OC_App::getAppPath($app) . "/img/$basename.png")) {
+ return OC_App::getAppPath($app) . "/img/$basename.png";
}elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/themes/$theme/$app/img/$image" )) {
return OC::$WEBROOT."/themes/$theme/$app/img/$image";
+ }elseif( !empty($app) and (!file_exists( OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.svg")
+ && file_exists( OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.png"))) {
+ return OC::$WEBROOT . "/themes/$theme/$app/img/$basename.png";
}elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/$app/img/$image" )) {
return OC::$WEBROOT."/$app/img/$image";
+ }elseif( !empty($app) and (!file_exists( OC::$SERVERROOT . "/$app/img/$basename.svg")
+ && file_exists( OC::$SERVERROOT . "/$app/img/$basename.png"))) {
+ return OC::$WEBROOT . "/$app/img/$basename.png";
}elseif( file_exists( OC::$SERVERROOT."/themes/$theme/core/img/$image" )) {
return OC::$WEBROOT."/themes/$theme/core/img/$image";
+ }elseif( ! file_exists( OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg")
+ && file_exists( OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) {
+ return OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
}elseif( file_exists( OC::$SERVERROOT."/core/img/$image" )) {
return OC::$WEBROOT."/core/img/$image";
}else{
@@ -517,11 +535,11 @@ class OC_Helper {
* copy the contents of one stream to another
* @param resource $source
* @param resource $target
- * @return int the number of bytes copied
+ * @return array the number of bytes copied and result
*/
public static function streamCopy($source, $target) {
- if(!$source or !$target) {
- return false;
+ if (!$source or !$target) {
+ return array(0, false);
}
$result = true;
$count = 0;
diff --git a/lib/installer.php b/lib/installer.php
index 467e486dd29..3299fb07390 100644
--- a/lib/installer.php
+++ b/lib/installer.php
@@ -196,7 +196,11 @@ class OC_Installer{
//install the database
if(is_file($basedir.'/appinfo/database.xml')) {
- OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
+ if (OC_Appconfig::getValue($info['id'], 'installed_version') === null) {
+ OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
+ } else {
+ OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml');
+ }
}
//run appinfo/install.php
diff --git a/lib/log.php b/lib/log.php
index 3f3334801e5..e31e2a893c4 100644
--- a/lib/log.php
+++ b/lib/log.php
@@ -36,6 +36,8 @@ class OC_Log {
call_user_func(array(self::$class, 'init'));
}
$log_class=self::$class;
+ // remove username/passswords from URLs before writing the to the log file
+ $message = preg_replace('/\/\/(.*):(.*)@/', '//xxx:xxx@', $message);
$log_class::write($app, $message, $level);
}
}
diff --git a/lib/log/owncloud.php b/lib/log/owncloud.php
index d16b9537a16..aecf78b2f3d 100644
--- a/lib/log/owncloud.php
+++ b/lib/log/owncloud.php
@@ -35,7 +35,17 @@ class OC_Log_Owncloud {
public static function init() {
$defaultLogFile = OC_Config::getValue("datadirectory", OC::$SERVERROOT.'/data').'/owncloud.log';
self::$logFile = OC_Config::getValue("logfile", $defaultLogFile);
- if (!file_exists(self::$logFile)) {
+
+ /*
+ * Fall back to default log file if specified logfile does not exist
+ * and can not be created. Error suppression is required in order to
+ * not end up in the error handler which will try to log the error.
+ * A better solution (compared to error suppression) would be checking
+ * !is_writable(dirname(self::$logFile)) before touch(), but
+ * is_writable() on directories used to be pretty unreliable on Windows
+ * for at least some time.
+ */
+ if (!file_exists(self::$logFile) && !@touch(self::$logFile)) {
self::$logFile = $defaultLogFile;
}
}
@@ -51,8 +61,16 @@ class OC_Log_Owncloud {
if($level>=$minLevel) {
// default to ISO8601
$format = OC_Config::getValue('logdateformat', 'c');
- $time = date($format, time());
- $entry=array('app'=>$app, 'message'=>$message, 'level'=>$level, 'time'=> $time);
+ $logtimezone=OC_Config::getValue( "logtimezone", 'UTC' );
+ try {
+ $timezone = new DateTimeZone($logtimezone);
+ } catch (Exception $e) {
+ $timezone = new DateTimeZone('UTC');
+ }
+ $time = new DateTime(null, $timezone);
+ $entry=array('app'=>$app, 'message'=>$message, 'level'=>$level, 'time'=> $time->format($format));
+
+
$handle = @fopen(self::$logFile, 'a');
if ($handle) {
fwrite($handle, json_encode($entry)."\n");
diff --git a/lib/public/share.php b/lib/public/share.php
index 0a88ea4ea44..7ba5fa50246 100644
--- a/lib/public/share.php
+++ b/lib/public/share.php
@@ -155,13 +155,13 @@ class Share {
while ($source !== -1) {
- // Fetch all shares of this file path from DB
+ // Fetch all shares with another user
$query = \OC_DB::prepare(
'SELECT `share_with`
FROM
`*PREFIX*share`
WHERE
- `item_source` = ? AND `share_type` = ?'
+ `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
);
$result = $query->execute(array($source, self::SHARE_TYPE_USER));
@@ -180,7 +180,7 @@ class Share {
FROM
`*PREFIX*share`
WHERE
- `item_source` = ? AND `share_type` = ?'
+ `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
);
$result = $query->execute(array($source, self::SHARE_TYPE_GROUP));
@@ -201,7 +201,7 @@ class Share {
FROM
`*PREFIX*share`
WHERE
- `item_source` = ? AND `share_type` = ?'
+ `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
);
$result = $query->execute(array($source, self::SHARE_TYPE_LINK));
@@ -293,7 +293,18 @@ class Share {
if (\OC_DB::isError($result)) {
\OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::ERROR);
}
- return $result->fetchRow();
+ $row = $result->fetchRow();
+
+ if (!empty($row['expiration'])) {
+ $now = new \DateTime();
+ $expirationDate = new \DateTime($row['expiration'], new \DateTimeZone('UTC'));
+ if ($now > $expirationDate) {
+ self::delete($row['id']);
+ return false;
+ }
+ }
+
+ return $row;
}
/**
@@ -749,10 +760,10 @@ class Share {
/**
* @brief Get the backend class for the specified item type
- * @param string Item type
- * @return Sharing backend object
+ * @param string $itemType
+ * @return Share_Backend
*/
- private static function getBackend($itemType) {
+ public static function getBackend($itemType) {
if (isset(self::$backends[$itemType])) {
return self::$backends[$itemType];
} else if (isset(self::$backendTypes[$itemType]['class'])) {
diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php
index 4d88c2a87f1..9bd50931517 100644
--- a/lib/search/provider/file.php
+++ b/lib/search/provider/file.php
@@ -10,6 +10,7 @@ class OC_Search_Provider_File extends OC_Search_Provider{
$mime = $fileData['mimetype'];
$name = basename($path);
+ $container = dirname($path);
$text = '';
$skip = false;
if($mime=='httpd/unix-directory') {
@@ -37,7 +38,7 @@ class OC_Search_Provider_File extends OC_Search_Provider{
}
}
if(!$skip) {
- $results[] = new OC_Search_Result($name, $text, $link, $type);
+ $results[] = new OC_Search_Result($name, $text, $link, $type, $container);
}
}
return $results;
diff --git a/lib/search/result.php b/lib/search/result.php
index 08beaea151c..6a6300bc2fc 100644
--- a/lib/search/result.php
+++ b/lib/search/result.php
@@ -15,10 +15,11 @@ class OC_Search_Result{
* @param string $link link for the result
* @param string $type the type of result as human readable string ('File', 'Music', etc)
*/
- public function __construct($name, $text, $link, $type) {
+ public function __construct($name, $text, $link, $type, $container) {
$this->name=$name;
$this->text=$text;
$this->link=$link;
$this->type=$type;
+ $this->container=$container;
}
}
diff --git a/lib/setup.php b/lib/setup.php
index 5d5ef77899f..2a43f7b4475 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -328,7 +328,13 @@ class OC_Setup {
$connection_string = "host='$e_host' dbname=postgres user='$e_user' password='$e_password'";
$connection = @pg_connect($connection_string);
if(!$connection) {
- throw new Exception($l->t('PostgreSQL username and/or password not valid'));
+ // Try if we can connect to the DB with the specified name
+ $e_dbname = addslashes($dbname);
+ $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' password='$e_password'";
+ $connection = @pg_connect($connection_string);
+
+ if(!$connection)
+ throw new DatabaseSetupException($l->t('PostgreSQL username and/or password not valid'));
}
$e_user = pg_escape_string($dbuser);
//check for roles creation rights in postgresql
diff --git a/lib/updater.php b/lib/updater.php
index cbba543217b..d0ae1fb4715 100644
--- a/lib/updater.php
+++ b/lib/updater.php
@@ -27,7 +27,6 @@ class OC_Updater extends BasicEmitter {
* @return array | bool
*/
static public function check($updaterUrl='http://apps.owncloud.com/updater.php') {
- OC_Appconfig::setValue('core', 'lastupdatedat', microtime(true));
if ((\OC_Appconfig::getValue('core', 'lastupdatedat') + 1800) > time()) {
return json_decode(\OC_Appconfig::getValue('core', 'lastupdateResult'), true);
}
@@ -75,7 +74,7 @@ class OC_Updater extends BasicEmitter {
if(OC_Config::getValue('updatechecker', true)==true) {
$data=OC_Updater::check();
- if(isset($data['version']) and $data['version']<>'') {
+ if(isset($data['version']) && !empty($data['version'])) {
$txt='<span style="color:#AA0000; font-weight:bold;">'
.$l->t('%s is available. Get <a href="%s">more information</a>',
array($data['versionstring'], $data['web'])).'</span>';
diff --git a/lib/user.php b/lib/user.php
index 55ffc26e77f..1bac4839aa9 100644
--- a/lib/user.php
+++ b/lib/user.php
@@ -372,6 +372,8 @@ class OC_User {
return self::determineDisplayName($user);
} else if( isset($_SESSION['display_name']) AND $_SESSION['display_name'] ) {
return $_SESSION['display_name'];
+ } else if ( $user = self::getUser() ) {
+ return self::determineDisplayName($user);
}
else{
return false;
@@ -637,20 +639,25 @@ class OC_User {
public static function setMagicInCookie($username, $token) {
$secure_cookie = OC_Config::getValue("forcessl", false);
$expires = time() + OC_Config::getValue('remember_login_cookie_lifetime', 60*60*24*15);
- setcookie("oc_username", $username, $expires, '', '', $secure_cookie);
- setcookie("oc_token", $token, $expires, '', '', $secure_cookie, true);
- setcookie("oc_remember_login", true, $expires, '', '', $secure_cookie);
+ setcookie("oc_username", $username, $expires, \OC::$WEBROOT, '', $secure_cookie);
+ setcookie("oc_token", $token, $expires, \OC::$WEBROOT, '', $secure_cookie, true);
+ setcookie("oc_remember_login", true, $expires, \OC::$WEBROOT, '', $secure_cookie);
}
/**
* @brief Remove cookie for "remember username"
*/
public static function unsetMagicInCookie() {
- unset($_COOKIE["oc_username"]);
+ unset($_COOKIE["oc_username"]); //TODO: DI
unset($_COOKIE["oc_token"]);
unset($_COOKIE["oc_remember_login"]);
- setcookie("oc_username", null, -1);
- setcookie("oc_token", null, -1);
- setcookie("oc_remember_login", null, -1);
+ setcookie('oc_username', '', time()-3600, \OC::$WEBROOT);
+ setcookie('oc_token', '', time()-3600, \OC::$WEBROOT);
+ setcookie('oc_remember_login', '', time()-3600, \OC::$WEBROOT);
+ // old cookies might be stored under /webroot/ instead of /webroot
+ // and Firefox doesn't like it!
+ setcookie('oc_username', '', time()-3600, \OC::$WEBROOT . '/');
+ setcookie('oc_token', '', time()-3600, \OC::$WEBROOT . '/');
+ setcookie('oc_remember_login', '', time()-3600, \OC::$WEBROOT . '/');
}
}
diff --git a/lib/user/database.php b/lib/user/database.php
index 35cc68e3c05..cbdccd4503a 100644
--- a/lib/user/database.php
+++ b/lib/user/database.php
@@ -85,7 +85,7 @@ class OC_User_Database extends OC_User_Backend {
*/
public function deleteUser( $uid ) {
// Delete user-group-relation
- $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*users` WHERE uid = ?' );
+ $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*users` WHERE `uid` = ?' );
$query->execute( array( $uid ));
return true;
}
diff --git a/lib/util.php b/lib/util.php
index 71921f0aedd..97e0fdba75b 100755
--- a/lib/util.php
+++ b/lib/util.php
@@ -77,7 +77,7 @@ class OC_Util {
public static function getVersion() {
// hint: We only can count up. Reset minor/patchlevel when
// updating major/minor version number.
- return array(5, 00, 21);
+ return array(5, 00, 25);
}
/**
@@ -85,7 +85,7 @@ class OC_Util {
* @return string
*/
public static function getVersionString() {
- return '5.0.12 RC1';
+ return '5.0.13';
}
/**
@@ -312,9 +312,27 @@ class OC_Util {
}
/**
- * Check for correct file permissions of data directory
- * @return array arrays with error messages and hints
- */
+ * @brief check if there are still some encrypted files stored
+ * @return boolean
+ */
+ public static function encryptedFiles() {
+ //check if encryption was enabled in the past
+ $encryptedFiles = false;
+ if (OC_App::isEnabled('files_encryption') === false) {
+ $view = new OC\Files\View('/' . OCP\User::getUser());
+ if ($view->file_exists('/files_encryption/keyfiles')) {
+ $encryptedFiles = true;
+ }
+ }
+
+ return $encryptedFiles;
+ }
+
+ /**
+ * @brief Check for correct file permissions of data directory
+ * @paran string $dataDirectory
+ * @return array arrays with error messages and hints
+ */
public static function checkDataDirectoryPermissions($dataDirectory) {
$errors = array();
if (stristr(PHP_OS, 'WIN')) {
@@ -354,6 +372,7 @@ class OC_Util {
}
$parameters['alt_login'] = OC_App::getAlternativeLogIns();
+ $parameters['rememberLoginAllowed'] = self::rememberLoginAllowed();
OC_Template::printGuestPage("", "login", $parameters);
}
@@ -385,6 +404,7 @@ class OC_Util {
* Check if the user is a admin, redirects to home if not
*/
public static function checkAdminUser() {
+ \OC_Util::checkLoggedIn();
if( !OC_User::isAdminUser(OC_User::getUser())) {
header( 'Location: '.OC_Helper::linkToAbsolute( '', 'index.php' ));
exit();
@@ -392,10 +412,32 @@ class OC_Util {
}
/**
- * Check if the user is a subadmin, redirects to home if not
+ * Check if it is allowed to remember login.
+ *
+ * @note Every app can set 'rememberlogin' to 'false' to disable the remember login feature
+ *
+ * @return bool
+ */
+ public static function rememberLoginAllowed() {
+
+ $apps = OC_App::getEnabledApps();
+
+ foreach ($apps as $app) {
+ $appInfo = OC_App::getAppInfo($app);
+ if (isset($appInfo['rememberlogin']) && $appInfo['rememberlogin'] === 'false') {
+ return false;
+ }
+
+ }
+ return true;
+ }
+
+ /**
+ * @brief Check if the user is a subadmin, redirects to home if not
* @return array $groups where the current user is subadmin
*/
public static function checkSubAdminUser() {
+ \OC_Util::checkLoggedIn();
if(!OC_SubAdmin::isSubAdmin(OC_User::getUser())) {
header( 'Location: '.OC_Helper::linkToAbsolute( '', 'index.php' ));
exit();
@@ -536,7 +578,6 @@ class OC_Util {
return $value;
}
-
/**
* Check if the htaccess file is working by creating a test file in the data directory and trying to access via http
*/
@@ -677,7 +718,6 @@ class OC_Util {
}
}
-
}
/**
diff --git a/search/js/result.js b/search/js/result.js
index 78fa8efc8e9..2bad993f4a4 100644
--- a/search/js/result.js
+++ b/search/js/result.js
@@ -8,15 +8,23 @@ OC.search.catagorizeResults=function(results){
types[type].push(results[i]);
}
return types;
-}
+};
OC.search.hide=function(){
$('#searchresults').hide();
if($('#searchbox').val().length>2){
$('#searchbox').val('');
+ if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system
+ FileList.unfilter();
+ }
};
+ if ($('#searchbox').val().length === 0) {
+ if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system
+ FileList.unfilter();
}
+ }
+};
OC.search.showResults=function(results){
- if(results.length==0){
+ if(results.length === 0){
return;
}
if(!OC.search.showResults.loaded){
@@ -30,6 +38,9 @@ OC.search.showResults=function(results){
});
$(document).click(function(event){
OC.search.hide();
+ if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system
+ FileList.unfilter();
+ }
});
OC.search.lastResults=results;
OC.search.showResults(results);
@@ -39,30 +50,52 @@ OC.search.showResults=function(results){
$('#searchresults').show();
$('#searchresults tr.result').remove();
var index=0;
- for(var name in types){
- var type=types[name];
+ for(var typeid in types){
+ var type=types[typeid];
if(type.length>0){
for(var i=0;i<type.length;i++){
var row=$('#searchresults tr.template').clone();
row.removeClass('template');
row.addClass('result');
- if (i == 0){
- row.children('td.type').text(name);
+ row.data('type', typeid);
+ row.data('name', type[i].name);
+ row.data('text', type[i].text);
+ row.data('container', type[i].container);
+ if (i === 0){
+ row.children('td.type').text(typeid);
}
- row.find('td.result a').attr('href',type[i].link);
row.find('td.result div.name').text(type[i].name);
row.find('td.result div.text').text(type[i].text);
+ if (type[i].container) {
+ var containerName = OC.basename(type[i].container);
+ if (containerName === '') {
+ containerName = '/';
+ }
+ var containerLink = OC.linkTo('files', 'index.php')
+ +'?dir='+encodeURIComponent(type[i].container)
+ +'&scrollto='+encodeURIComponent(type[i].name);
+ row.find('td.result a')
+ .attr('href', containerLink)
+ .attr('title', t('core', 'Show in {folder}', {folder: containerName}));
+ } else {
+ row.find('td.result a').attr('href', type[i].link);
+ }
row.data('index',index);
index++;
- if(OC.search.customResults[name]){//give plugins the ability to customize the entries in here
- OC.search.customResults[name](row,type[i]);
+ if(OC.search.customResults[typeid]){//give plugins the ability to customize the entries in here
+ OC.search.customResults[typeid](row,type[i]);
}
$('#searchresults tbody').append(row);
}
}
}
+ $('#searchresults').on('click', 'result', function () {
+ if ($(this).data('type') === 'Files') {
+ //FIXME use ajax to navigate to folder & highlight file
+ }
+ });
}
-}
+};
OC.search.showResults.loaded=false;
OC.search.renderCurrent=function(){
@@ -71,4 +104,4 @@ OC.search.renderCurrent=function(){
$('#searchresults tr.result').removeClass('current');
$(result).addClass('current');
}
-}
+};
diff --git a/tests/enable_all.php b/tests/enable_all.php
index 111ed0e1357..43ee16a72f8 100644
--- a/tests/enable_all.php
+++ b/tests/enable_all.php
@@ -8,6 +8,7 @@
require_once __DIR__.'/../lib/base.php';
+OC_App::enable('files_sharing');
OC_App::enable('files_encryption');
OC_App::enable('calendar');
OC_App::enable('contacts');
diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php
index 198d601b1bb..03d061cc444 100644
--- a/tests/lib/files/cache/updater.php
+++ b/tests/lib/files/cache/updater.php
@@ -21,6 +21,8 @@ class Updater extends \PHPUnit_Framework_TestCase {
*/
private $scanner;
+ private $stateFilesEncryption;
+
/**
* @var \OC\Files\Cache\Cache $cache
*/
@@ -29,6 +31,12 @@ class Updater extends \PHPUnit_Framework_TestCase {
private static $user;
public function setUp() {
+
+ // remember files_encryption state
+ $this->stateFilesEncryption = \OC_App::isEnabled('files_encryption');
+ // we want to tests with the encryption app disabled
+ \OC_App::disable('files_encryption');
+
$this->storage = new \OC\Files\Storage\Temporary(array());
$textData = "dummy file data\n";
$imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png');
@@ -46,6 +54,10 @@ class Updater extends \PHPUnit_Framework_TestCase {
if (!self::$user) {
self::$user = uniqid();
}
+
+ \OC_User::createUser(self::$user, 'password');
+ \OC_User::setUserId(self::$user);
+
\OC\Files\Filesystem::init(self::$user, '/' . self::$user . '/files');
Filesystem::clearMounts();
@@ -63,7 +75,12 @@ class Updater extends \PHPUnit_Framework_TestCase {
if ($this->cache) {
$this->cache->clear();
}
+ \OC_User::deleteUser(self::$user);
Filesystem::tearDown();
+ // reset app files_encryption
+ if ($this->stateFilesEncryption) {
+ \OC_App::enable('files_encryption');
+ }
}
public function testWrite() {
diff --git a/tests/lib/files/filesystem.php b/tests/lib/files/filesystem.php
index bef70cc725b..16b9237150a 100644
--- a/tests/lib/files/filesystem.php
+++ b/tests/lib/files/filesystem.php
@@ -66,17 +66,72 @@ class Filesystem extends \PHPUnit_Framework_TestCase {
}
public function testNormalize() {
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath(''));
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('/'));
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('/', false));
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('//'));
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('//', false));
$this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('/path/'));
$this->assertEquals('/path/', \OC\Files\Filesystem::normalizePath('/path/', false));
$this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('path'));
- $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\path'));
$this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo//bar/'));
+ $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('/foo//bar/', false));
$this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo////bar'));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/////bar'));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/bar/.'));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/bar/./'));
+ $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('/foo/bar/./', false));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/bar/./.'));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/bar/././'));
+ $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('/foo/bar/././', false));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo/./bar/'));
+ $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('/foo/./bar/', false));
+ $this->assertEquals('/foo/.bar', \OC\Files\Filesystem::normalizePath('/foo/.bar/'));
+ $this->assertEquals('/foo/.bar/', \OC\Files\Filesystem::normalizePath('/foo/.bar/', false));
+ $this->assertEquals('/foo/.bar/tee', \OC\Files\Filesystem::normalizePath('/foo/.bar/tee'));
+
+ // normalize does not resolve '..' (by design)
+ $this->assertEquals('/foo/..', \OC\Files\Filesystem::normalizePath('/foo/../'));
+
if (class_exists('Patchwork\PHP\Shim\Normalizer')) {
$this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("/foo/baru\xCC\x88"));
}
}
+ public function testNormalizeWindowsPaths() {
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath(''));
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\'));
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\', false));
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\\\'));
+ $this->assertEquals('/', \OC\Files\Filesystem::normalizePath('\\\\', false));
+ $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\\path'));
+ $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\\path', false));
+ $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\\path\\'));
+ $this->assertEquals('/path/', \OC\Files\Filesystem::normalizePath('\\path\\', false));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\\\bar\\'));
+ $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('\\foo\\\\bar\\', false));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\\\\\\\bar'));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\\\\\\\\\bar'));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.'));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\'));
+ $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\', false));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\.'));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\.\\'));
+ $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('\\foo\\bar\\.\\.\\', false));
+ $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('\\foo\\.\\bar\\'));
+ $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::normalizePath('\\foo\\.\\bar\\', false));
+ $this->assertEquals('/foo/.bar', \OC\Files\Filesystem::normalizePath('\\foo\\.bar\\'));
+ $this->assertEquals('/foo/.bar/', \OC\Files\Filesystem::normalizePath('\\foo\\.bar\\', false));
+ $this->assertEquals('/foo/.bar/tee', \OC\Files\Filesystem::normalizePath('\\foo\\.bar\\tee'));
+
+ // normalize does not resolve '..' (by design)
+ $this->assertEquals('/foo/..', \OC\Files\Filesystem::normalizePath('\\foo\\..\\'));
+
+ if (class_exists('Patchwork\PHP\Shim\Normalizer')) {
+ $this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("\\foo\\baru\xCC\x88"));
+ }
+ }
+
public function testHooks() {
if(\OC\Files\Filesystem::getView()){
$user = \OC_User::getUser();
diff --git a/tests/lib/files/storage/storage.php b/tests/lib/files/storage/storage.php
index 0e22f26ae83..4587a3b2d06 100644
--- a/tests/lib/files/storage/storage.php
+++ b/tests/lib/files/storage/storage.php
@@ -42,18 +42,21 @@ abstract class Storage extends \PHPUnit_Framework_TestCase {
$this->assertTrue($this->instance->isUpdatable('/'), 'Root folder is not writable');
}
- public function testDirectories() {
- $this->assertFalse($this->instance->file_exists('/folder'));
+ /**
+ * @dataProvider directoryProvider
+ */
+ public function testDirectories($directory) {
+ $this->assertFalse($this->instance->file_exists('/'.$directory));
- $this->assertTrue($this->instance->mkdir('/folder'));
+ $this->assertTrue($this->instance->mkdir('/'.$directory));
- $this->assertTrue($this->instance->file_exists('/folder'));
- $this->assertTrue($this->instance->is_dir('/folder'));
- $this->assertFalse($this->instance->is_file('/folder'));
- $this->assertEquals('dir', $this->instance->filetype('/folder'));
- $this->assertEquals(0, $this->instance->filesize('/folder'));
- $this->assertTrue($this->instance->isReadable('/folder'));
- $this->assertTrue($this->instance->isUpdatable('/folder'));
+ $this->assertTrue($this->instance->file_exists('/'.$directory));
+ $this->assertTrue($this->instance->is_dir('/'.$directory));
+ $this->assertFalse($this->instance->is_file('/'.$directory));
+ $this->assertEquals('dir', $this->instance->filetype('/'.$directory));
+ $this->assertEquals(0, $this->instance->filesize('/'.$directory));
+ $this->assertTrue($this->instance->isReadable('/'.$directory));
+ $this->assertTrue($this->instance->isUpdatable('/'.$directory));
$dh = $this->instance->opendir('/');
$content = array();
@@ -62,14 +65,14 @@ abstract class Storage extends \PHPUnit_Framework_TestCase {
$content[] = $file;
}
}
- $this->assertEquals(array('folder'), $content);
+ $this->assertEquals(array($directory), $content);
- $this->assertFalse($this->instance->mkdir('/folder')); //cant create existing folders
- $this->assertTrue($this->instance->rmdir('/folder'));
+ $this->assertFalse($this->instance->mkdir('/'.$directory)); //cant create existing folders
+ $this->assertTrue($this->instance->rmdir('/'.$directory));
- $this->assertFalse($this->instance->file_exists('/folder'));
+ $this->assertFalse($this->instance->file_exists('/'.$directory));
- $this->assertFalse($this->instance->rmdir('/folder')); //cant remove non existing folders
+ $this->assertFalse($this->instance->rmdir('/'.$directory)); //cant remove non existing folders
$dh = $this->instance->opendir('/');
$content = array();
@@ -81,6 +84,14 @@ abstract class Storage extends \PHPUnit_Framework_TestCase {
$this->assertEquals(array(), $content);
}
+ public function directoryProvider()
+ {
+ return array(
+ array('folder'),
+ array(' folder'),
+ array('folder '),
+ );
+ }
/**
* test the various uses of file_get_contents and file_put_contents
*/
@@ -171,8 +182,9 @@ abstract class Storage extends \PHPUnit_Framework_TestCase {
$this->assertTrue($this->instance->hasUpdated('/lorem.txt', $ctimeStart - 1));
$this->assertTrue($this->instance->hasUpdated('/', $ctimeStart - 1));
- $this->assertTrue(($ctimeStart - 1) <= $mTime);
- $this->assertTrue($mTime <= ($ctimeEnd + 1));
+ // check that ($ctimeStart - 1) <= $mTime <= ($ctimeEnd + 1)
+ $this->assertGreaterThanOrEqual(($ctimeStart - 1), $mTime);
+ $this->assertLessThanOrEqual(($ctimeEnd + 1), $mTime);
$this->assertEquals(filesize($textFile), $this->instance->filesize('/lorem.txt'));
$stat = $this->instance->stat('/lorem.txt');
@@ -238,6 +250,17 @@ abstract class Storage extends \PHPUnit_Framework_TestCase {
$this->assertContains('/sub/logo-wide.png', $result);
}
+ public function testUnlink() {
+ $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
+ $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile));
+
+ $this->assertTrue($this->instance->file_exists('/lorem.txt'));
+
+ $this->assertTrue($this->instance->unlink('/lorem.txt'));
+
+ $this->assertFalse($this->instance->file_exists('/lorem.txt'));
+ }
+
public function testFOpen() {
$textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php
index 1cd07773513..aceb36aa01e 100644
--- a/tests/lib/files/view.php
+++ b/tests/lib/files/view.php
@@ -7,6 +7,12 @@
namespace Test\Files;
+class TemporaryNoTouch extends \OC\Files\Storage\Temporary {
+ public function touch($path, $mtime = null) {
+ return false;
+ }
+}
+
class View extends \PHPUnit_Framework_TestCase {
/**
* @var \OC\Files\Storage\Storage[] $storages;
@@ -262,12 +268,38 @@ class View extends \PHPUnit_Framework_TestCase {
$this->hookPath = $params['path'];
}
+ function testTouch() {
+ $storage = $this->getTestStorage(true, '\Test\Files\TemporaryNoTouch');
+
+ \OC\Files\Filesystem::mount($storage, array(), '/');
+
+ $rootView = new \OC\Files\View('');
+ $oldCachedData = $rootView->getFileInfo('foo.txt');
+ $newMTime = $oldCachedData['mtime'] + 500;
+
+ $rootView->touch('foo.txt', $newMTime);
+
+ $cachedData = $rootView->getFileInfo('foo.txt');
+ $this->assertEquals($newMTime, $cachedData['mtime']);
+
+ // reset mtime to original to make sure the next file access
+ // gets a higher mtime
+ $rootView->touch('foo.txt', $oldCachedData['mtime']);
+
+ $rootView->file_put_contents('foo.txt', 'asd');
+ $cachedData = $rootView->getFileInfo('foo.txt');
+ $this->assertGreaterThanOrEqual($cachedData['mtime'], $oldCachedData['mtime']);
+ }
+
/**
* @param bool $scan
* @return \OC\Files\Storage\Storage
*/
- private function getTestStorage($scan = true) {
- $storage = new \OC\Files\Storage\Temporary(array());
+ private function getTestStorage($scan = true, $class = '\OC\Files\Storage\Temporary') {
+ /**
+ * @var \OC\Files\Storage\Storage $storage
+ */
+ $storage = new $class(array());
$textData = "dummy file data\n";
$imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png');
$storage->mkdir('folder');
@@ -282,4 +314,38 @@ class View extends \PHPUnit_Framework_TestCase {
$this->storages[] = $storage;
return $storage;
}
+
+ /**
+ * @dataProvider resolvePathTestProvider
+ */
+ public function testResolvePath($expected, $pathToTest) {
+ $storage1 = $this->getTestStorage();
+ \OC\Files\Filesystem::mount($storage1, array(), '/');
+
+ $view = new \OC\Files\View('');
+
+ $result = $view->resolvePath($pathToTest);
+ $this->assertEquals($expected, $result[1]);
+
+ $exists = $view->file_exists($pathToTest);
+ $this->assertTrue($exists);
+
+ $exists = $view->file_exists($result[1]);
+ $this->assertTrue($exists);
+ }
+
+ function resolvePathTestProvider() {
+ return array(
+ array('foo.txt', 'foo.txt'),
+ array('foo.txt', '/foo.txt'),
+ array('folder', 'folder'),
+ array('folder', '/folder'),
+ array('folder', 'folder/'),
+ array('folder', '/folder/'),
+ array('folder/bar.txt', 'folder/bar.txt'),
+ array('folder/bar.txt', '/folder/bar.txt'),
+ array('', ''),
+ array('', '/'),
+ );
+ }
}
diff --git a/tests/lib/helper.php b/tests/lib/helper.php
index 336e8f8b3c5..7de63b74b49 100644
--- a/tests/lib/helper.php
+++ b/tests/lib/helper.php
@@ -137,4 +137,38 @@ class Test_Helper extends PHPUnit_Framework_TestCase {
$result = OC_Helper::recursiveArraySearch($haystack, "NotFound");
$this->assertFalse($result);
}
+
+ /**
+ * @dataProvider streamCopyDataProvider
+ */
+ public function testStreamCopy($expectedCount, $expectedResult, $source, $target) {
+
+ if (is_string($source)) {
+ $source = fopen($source, 'r');
+ }
+ if (is_string($target)) {
+ $target = fopen($target, 'w');
+ }
+
+ list($count, $result) = \OC_Helper::streamCopy($source, $target);
+
+ if (is_resource($source)) {
+ fclose($source);
+ }
+ if (is_resource($target)) {
+ fclose($target);
+ }
+
+ $this->assertSame($expectedCount, $count);
+ $this->assertSame($expectedResult, $result);
+ }
+
+
+ function streamCopyDataProvider() {
+ return array(
+ array(0, false, false, false),
+ array(0, false, \OC::$SERVERROOT . '/tests/data/lorem.txt', false),
+ array(446, true, \OC::$SERVERROOT . '/tests/data/lorem.txt', \OC::$SERVERROOT . '/tests/data/lorem-copy.txt'),
+ );
+ }
}
diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php
index e02b0e4354d..8e9eef65d32 100644
--- a/tests/lib/share/share.php
+++ b/tests/lib/share/share.php
@@ -535,4 +535,52 @@ class Test_Share extends PHPUnit_Framework_TestCase {
'Failed asserting that user 3 still has access to test.txt after expiration date has been set.'
);
}
+
+ protected function getShareByValidToken($token) {
+ $row = OCP\Share::getShareByToken($token);
+ $this->assertInternalType(
+ 'array',
+ $row,
+ "Failed asserting that a share for token $token exists."
+ );
+ return $row;
+ }
+
+ public function testShareItemWithLink() {
+ OC_User::setUserId($this->user1);
+ $token = OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_LINK, null, OCP\PERMISSION_READ);
+ $this->assertInternalType(
+ 'string',
+ $token,
+ 'Failed asserting that user 1 successfully shared text.txt as link with token.'
+ );
+
+ // testGetShareByTokenNoExpiration
+ $row = $this->getShareByValidToken($token);
+ $this->assertEmpty(
+ $row['expiration'],
+ 'Failed asserting that the returned row does not have an expiration date.'
+ );
+
+ // testGetShareByTokenExpirationValid
+ $this->assertTrue(
+ OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture),
+ 'Failed asserting that user 1 successfully set a future expiration date for the test.txt share.'
+ );
+ $row = $this->getShareByValidToken($token);
+ $this->assertNotEmpty(
+ $row['expiration'],
+ 'Failed asserting that the returned row has an expiration date.'
+ );
+
+ // testGetShareByTokenExpirationExpired
+ $this->assertTrue(
+ OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInPast),
+ 'Failed asserting that user 1 successfully set a past expiration date for the test.txt share.'
+ );
+ $this->assertFalse(
+ OCP\Share::getShareByToken($token),
+ 'Failed asserting that an expired share could not be found.'
+ );
+ }
}