summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
authorRobin Appelman <icewind@owncloud.com>2014-04-15 20:03:00 +0200
committerRobin Appelman <icewind@owncloud.com>2014-04-15 20:03:00 +0200
commitc82adb8c5db01d6a49c4364f7eecb2931f59cfa1 (patch)
tree2c18fa58b47ff03aa3f872a1c13c0b2fe4968fed /lib/private
parent371a924c92bb751b81e2a819d8c581743be7a797 (diff)
parent2eeab60378a3e22e19c4f435aa2bf65cc9da7c2a (diff)
downloadnextcloud-server-c82adb8c5db01d6a49c4364f7eecb2931f59cfa1.tar.gz
nextcloud-server-c82adb8c5db01d6a49c4364f7eecb2931f59cfa1.zip
merge master into webdav-injection
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/appconfig.php6
-rw-r--r--lib/private/appframework/dependencyinjection/dicontainer.php10
-rw-r--r--lib/private/appframework/http/request.php87
-rw-r--r--lib/private/appframework/routing/routeconfig.php10
-rw-r--r--lib/private/cache.php2
-rw-r--r--lib/private/cache/file.php20
-rw-r--r--lib/private/config.php2
-rw-r--r--lib/private/connector/sabre/node.php9
-rw-r--r--lib/private/connector/sabre/quotaplugin.php11
-rw-r--r--lib/private/contactsmanager.php4
-rw-r--r--lib/private/davclient.php13
-rw-r--r--lib/private/db.php5
-rw-r--r--lib/private/db/statementwrapper.php14
-rw-r--r--lib/private/defaults.php4
-rw-r--r--lib/private/filechunking.php40
-rw-r--r--lib/private/files.php3
-rw-r--r--lib/private/files/cache/cache.php18
-rw-r--r--lib/private/files/filesystem.php25
-rw-r--r--lib/private/files/storage/common.php21
-rw-r--r--lib/private/files/storage/local.php2
-rw-r--r--lib/private/files/storage/mappedlocal.php2
-rw-r--r--lib/private/files/view.php22
-rw-r--r--lib/private/forbiddenexception.php16
-rw-r--r--lib/private/group/backend.php2
-rw-r--r--lib/private/group/database.php14
-rw-r--r--lib/private/group/dummy.php10
-rw-r--r--lib/private/group/group.php21
-rw-r--r--lib/private/helper.php12
-rw-r--r--lib/private/image.php2
-rw-r--r--lib/private/l10n.php5
-rw-r--r--lib/private/legacy/appconfig.php2
-rw-r--r--lib/private/legacy/config.php6
-rw-r--r--lib/private/mail.php3
-rw-r--r--lib/private/ocs/cloud.php27
-rw-r--r--lib/private/ocs/result.php2
-rwxr-xr-xlib/private/preview.php236
-rwxr-xr-xlib/private/request.php5
-rw-r--r--lib/private/route/router.php14
-rw-r--r--lib/private/server.php11
-rw-r--r--lib/private/share/constants.php44
-rw-r--r--lib/private/share/helper.php202
-rw-r--r--lib/private/share/hooks.php108
-rw-r--r--lib/private/share/mailnotifications.php1
-rw-r--r--lib/private/share/share.php1629
-rw-r--r--lib/private/template/base.php2
-rw-r--r--lib/private/template/functions.php9
-rw-r--r--lib/private/urlgenerator.php10
-rw-r--r--lib/private/user.php2
-rw-r--r--lib/private/user/manager.php11
-rwxr-xr-xlib/private/util.php66
50 files changed, 2516 insertions, 286 deletions
diff --git a/lib/private/appconfig.php b/lib/private/appconfig.php
index cdaaebb87e5..fed6989a438 100644
--- a/lib/private/appconfig.php
+++ b/lib/private/appconfig.php
@@ -147,7 +147,7 @@ class AppConfig implements \OCP\IAppConfig {
*/
public function hasKey($app, $key) {
$values = $this->getAppValues($app);
- return isset($values[$key]);
+ return array_key_exists($key, $values);
}
/**
@@ -218,8 +218,8 @@ class AppConfig implements \OCP\IAppConfig {
/**
* get multiply values, either the app or key can be used as wildcard by setting it to false
*
- * @param boolean $app
- * @param string $key
+ * @param string|false $app
+ * @param string|false $key
* @return array
*/
public function getValues($app, $key) {
diff --git a/lib/private/appframework/dependencyinjection/dicontainer.php b/lib/private/appframework/dependencyinjection/dicontainer.php
index 4821ecaf67b..e478225a53d 100644
--- a/lib/private/appframework/dependencyinjection/dicontainer.php
+++ b/lib/private/appframework/dependencyinjection/dicontainer.php
@@ -92,13 +92,13 @@ class DIContainer extends SimpleContainer implements IAppContainer{
return new SecurityMiddleware($app, $c['Request']);
});
- $middleWares = $this->middleWares;
- $this['MiddlewareDispatcher'] = $this->share(function($c) use ($middleWares) {
+ $middleWares = &$this->middleWares;
+ $this['MiddlewareDispatcher'] = $this->share(function($c) use (&$middleWares) {
$dispatcher = new MiddlewareDispatcher();
$dispatcher->registerMiddleware($c['SecurityMiddleware']);
foreach($middleWares as $middleWare) {
- $dispatcher->registerMiddleware($middleWare);
+ $dispatcher->registerMiddleware($c[$middleWare]);
}
return $dispatcher;
@@ -133,10 +133,10 @@ class DIContainer extends SimpleContainer implements IAppContainer{
}
/**
- * @param Middleware $middleWare
+ * @param string $middleWare
* @return boolean|null
*/
- function registerMiddleWare(Middleware $middleWare) {
+ function registerMiddleWare($middleWare) {
array_push($this->middleWares, $middleWare);
}
diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php
index 40f47a7bd2f..643fa685adc 100644
--- a/lib/private/appframework/http/request.php
+++ b/lib/private/appframework/http/request.php
@@ -60,7 +60,14 @@ class Request implements \ArrayAccess, \Countable, IRequest {
* @param string|false 'requesttoken' the requesttoken or false when not available
* @see http://www.php.net/manual/en/reserved.variables.php
*/
- public function __construct(array $vars=array()) {
+ public function __construct(array $vars=array(), $stream='php://input') {
+
+ $this->inputStream = $stream;
+ $this->items['params'] = array();
+
+ if(!array_key_exists('method', $vars)) {
+ $vars['method'] = 'GET';
+ }
foreach($this->allowedKeys as $name) {
$this->items[$name] = isset($vars[$name])
@@ -68,25 +75,32 @@ class Request implements \ArrayAccess, \Countable, IRequest {
: array();
}
- if (defined('PHPUNIT_RUN') && PHPUNIT_RUN
- && in_array('fakeinput', stream_get_wrappers())) {
- $this->inputStream = 'fakeinput://data';
- } else {
- $this->inputStream = 'php://input';
- }
-
- // Only 'application/x-www-form-urlencoded' requests are automatically
- // transformed by PHP, 'application/json' must be decoded manually.
- if ($this->method === 'POST'
- && strpos($this->getHeader('Content-Type'), 'application/json') !== false
- ) {
- $this->items['params'] = $this->items['post'] = json_decode(file_get_contents($this->inputStream), true);
- }
+ // 'application/json' must be decoded manually.
+ if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
+ $params = json_decode(file_get_contents($this->inputStream), true);
+ if(count($params) > 0) {
+ $this->items['params'] = $params;
+ if($vars['method'] === 'POST') {
+ $this->items['post'] = $params;
+ }
+ }
+ // Handle application/x-www-form-urlencoded for methods other than GET
+ // or post correctly
+ } elseif($vars['method'] !== 'GET'
+ && $vars['method'] !== 'POST'
+ && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
+
+ parse_str(file_get_contents($this->inputStream), $params);
+ if(is_array($params)) {
+ $this->items['params'] = $params;
+ }
+ }
$this->items['parameters'] = array_merge(
$this->items['get'],
$this->items['post'],
- $this->items['urlParams']
+ $this->items['urlParams'],
+ $this->items['params']
);
}
@@ -313,47 +327,22 @@ class Request implements \ArrayAccess, \Countable, IRequest {
* @throws \LogicException
*/
protected function getContent() {
- if ($this->content === false && $this->method === 'PUT') {
- throw new \LogicException(
- '"put" can only be accessed once if not '
- . 'application/x-www-form-urlencoded or application/json.'
- );
- }
-
// If the content can't be parsed into an array then return a stream resource.
if ($this->method === 'PUT'
&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
&& strpos($this->getHeader('Content-Type'), 'application/json') === false
) {
+ if ($this->content === false) {
+ throw new \LogicException(
+ '"put" can only be accessed once if not '
+ . 'application/x-www-form-urlencoded or application/json.'
+ );
+ }
$this->content = false;
return fopen($this->inputStream, 'rb');
+ } else {
+ return $this->parameters;
}
-
- if (is_null($this->content)) {
- $this->content = file_get_contents($this->inputStream);
-
- /*
- * Normal jquery ajax requests are sent as application/x-www-form-urlencoded
- * and in $_GET and $_POST PHP transformes the data into an array.
- * The first condition mimics this.
- * The second condition allows for sending raw application/json data while
- * still getting the result as an array.
- *
- */
- if (strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
- parse_str($this->content, $content);
- if(is_array($content)) {
- $this->content = $content;
- }
- } elseif (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
- $content = json_decode($this->content, true);
- if(is_array($content)) {
- $this->content = $content;
- }
- }
- }
-
- return $this->content;
}
/**
diff --git a/lib/private/appframework/routing/routeconfig.php b/lib/private/appframework/routing/routeconfig.php
index 35bee75cc4d..a3bbde6af53 100644
--- a/lib/private/appframework/routing/routeconfig.php
+++ b/lib/private/appframework/routing/routeconfig.php
@@ -84,7 +84,15 @@ class RouteConfig {
// register the route
$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
- $this->router->create($this->appName.'.'.$controller.'.'.$action, $url)->method($verb)->action($handler);
+ $router = $this->router->create($this->appName.'.'.$controller.'.'.$action, $url)
+ ->method($verb)
+ ->action($handler);
+
+ // optionally register requirements for route. This is used to
+ // tell the route parser how url parameters should be matched
+ if(array_key_exists('requirements', $simpleRoute)) {
+ $router->requirements($simpleRoute['requirements']);
+ }
}
}
diff --git a/lib/private/cache.php b/lib/private/cache.php
index a311f10a00f..961270c334c 100644
--- a/lib/private/cache.php
+++ b/lib/private/cache.php
@@ -97,7 +97,7 @@ class Cache {
/**
* creates cache key based on the files given
- * @param $files
+ * @param string[] $files
* @return string
*/
static public function generateCacheKeyFromFiles($files) {
diff --git a/lib/private/cache/file.php b/lib/private/cache/file.php
index 8a6ef39f61b..2fd77c437fe 100644
--- a/lib/private/cache/file.php
+++ b/lib/private/cache/file.php
@@ -1,6 +1,7 @@
<?php
/**
* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
@@ -10,22 +11,22 @@ namespace OC\Cache;
class File {
protected $storage;
+
+ /**
+ * Returns the cache storage for the logged in user
+ * @return cache storage
+ */
protected function getStorage() {
if (isset($this->storage)) {
return $this->storage;
}
if(\OC_User::isLoggedIn()) {
\OC\Files\Filesystem::initMountPoints(\OC_User::getUser());
- $subdir = 'cache';
- $view = new \OC\Files\View('/' . \OC_User::getUser());
- if(!$view->file_exists($subdir)) {
- $view->mkdir($subdir);
- }
- $this->storage = new \OC\Files\View('/' . \OC_User::getUser().'/'.$subdir);
+ $this->storage = new \OC\Files\View('/' . \OC_User::getUser() . '/cache');
return $this->storage;
}else{
\OC_Log::write('core', 'Can\'t get cache storage, user not logged in', \OC_Log::ERROR);
- return false;
+ throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in');
}
}
@@ -83,11 +84,6 @@ class File {
public function hasKey($key) {
$storage = $this->getStorage();
if ($storage && $storage->is_file($key)) {
- $mtime = $storage->filemtime($key);
- if ($mtime < time()) {
- $storage->unlink($key);
- return false;
- }
return true;
}
return false;
diff --git a/lib/private/config.php b/lib/private/config.php
index 56f47256134..6701ca0532b 100644
--- a/lib/private/config.php
+++ b/lib/private/config.php
@@ -172,7 +172,7 @@ class Config {
$result = @file_put_contents($this->configFilename, $content);
if (!$result) {
$defaults = new \OC_Defaults;
- $url = \OC_Helper::linkToDocs('admin-dir-permissions');
+ $url = \OC_Helper::linkToDocs('admin-dir_permissions');
throw new HintException(
"Can't write into config directory!",
'This can usually be fixed by '
diff --git a/lib/private/connector/sabre/node.php b/lib/private/connector/sabre/node.php
index 3a5c721dda5..55e626c4c85 100644
--- a/lib/private/connector/sabre/node.php
+++ b/lib/private/connector/sabre/node.php
@@ -115,11 +115,14 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
/**
* @brief Returns the last modification time, as a unix timestamp
- * @return int
+ * @return int timestamp as integer
*/
public function getLastModified() {
- return $this->info->getMtime();
-
+ $timestamp = $this->info->getMtime();
+ if (!empty($timestamp)) {
+ return (int)$timestamp;
+ }
+ return $timestamp;
}
/**
diff --git a/lib/private/connector/sabre/quotaplugin.php b/lib/private/connector/sabre/quotaplugin.php
index 13fb187eed3..78d3172725a 100644
--- a/lib/private/connector/sabre/quotaplugin.php
+++ b/lib/private/connector/sabre/quotaplugin.php
@@ -61,8 +61,19 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin {
$uri = '/' . $uri;
}
list($parentUri, $newName) = Sabre_DAV_URLUtil::splitPath($uri);
+ $req = $this->server->httpRequest;
+ if ($req->getHeader('OC-Chunked')) {
+ $info = OC_FileChunking::decodeName($newName);
+ $chunkHandler = new OC_FileChunking($info);
+ // substract the already uploaded size to see whether
+ // there is still enough space for the remaining chunks
+ $length -= $chunkHandler->getCurrentSize();
+ }
$freeSpace = $this->getFreeSpace($parentUri);
if ($freeSpace !== \OC\Files\SPACE_UNKNOWN && $length > $freeSpace) {
+ if (isset($chunkHandler)) {
+ $chunkHandler->cleanup();
+ }
throw new Sabre_DAV_Exception_InsufficientStorage();
}
}
diff --git a/lib/private/contactsmanager.php b/lib/private/contactsmanager.php
index fc6745b4505..1cb3da7098f 100644
--- a/lib/private/contactsmanager.php
+++ b/lib/private/contactsmanager.php
@@ -47,7 +47,7 @@ namespace OC {
* This function can be used to delete the contact identified by the given id
*
* @param object $id the unique identifier to a contact
- * @param $address_book_key
+ * @param string $address_book_key identifier of the address book in which the contact shall be deleted
* @return bool successful or not
*/
public function delete($id, $address_book_key) {
@@ -66,7 +66,7 @@ namespace OC {
* Otherwise the contact will be updated by replacing the entire data set.
*
* @param array $properties this array if key-value-pairs defines a contact
- * @param $address_book_key string to identify the address book in which the contact shall be created or updated
+ * @param string $address_book_key identifier of the address book in which the contact shall be created or updated
* @return array representing the contact just created or updated
*/
public function createOrUpdate($properties, $address_book_key) {
diff --git a/lib/private/davclient.php b/lib/private/davclient.php
index afa4e1103b4..916dc11d17a 100644
--- a/lib/private/davclient.php
+++ b/lib/private/davclient.php
@@ -29,6 +29,8 @@ class OC_DAVClient extends \Sabre_DAV_Client {
protected $requestTimeout;
+ protected $verifyHost;
+
/**
* @brief Sets the request timeout or 0 to disable timeout.
* @param integer $timeout in seconds or 0 to disable
@@ -37,10 +39,21 @@ class OC_DAVClient extends \Sabre_DAV_Client {
$this->requestTimeout = (int)$timeout;
}
+ /**
+ * @brief Sets the CURLOPT_SSL_VERIFYHOST setting
+ * @param integer $value value to set CURLOPT_SSL_VERIFYHOST to
+ */
+ public function setVerifyHost($value) {
+ $this->verifyHost = $value;
+ }
+
protected function curlRequest($url, $settings) {
if ($this->requestTimeout > 0) {
$settings[CURLOPT_TIMEOUT] = $this->requestTimeout;
}
+ if (!is_null($this->verifyHost)) {
+ $settings[CURLOPT_SSL_VERIFYHOST] = $this->verifyHost;
+ }
return parent::curlRequest($url, $settings);
}
}
diff --git a/lib/private/db.php b/lib/private/db.php
index cfdac766bff..322a13642ae 100644
--- a/lib/private/db.php
+++ b/lib/private/db.php
@@ -313,9 +313,8 @@ class OC_DB {
/**
* @brief Insert a row if a matching row doesn't exists.
- * @param string $table. The table to insert into in the form '*PREFIX*tableName'
- * @param array $input. An array of fieldname/value pairs
- * @param string $table
+ * @param string $table The table to insert into in the form '*PREFIX*tableName'
+ * @param array $input An array of fieldname/value pairs
* @return boolean number of updated rows
*/
public static function insertIfNotExist($table, $input) {
diff --git a/lib/private/db/statementwrapper.php b/lib/private/db/statementwrapper.php
index eaf215c7231..492209b883b 100644
--- a/lib/private/db/statementwrapper.php
+++ b/lib/private/db/statementwrapper.php
@@ -8,6 +8,11 @@
/**
* small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement
+ *
+ * @method boolean bindValue(mixed $param, mixed $value, integer $type = null);
+ * @method string errorCode();
+ * @method array errorInfo();
+ * @method integer rowCount();
*/
class OC_DB_StatementWrapper {
/**
@@ -161,6 +166,8 @@ class OC_DB_StatementWrapper {
/**
* provide an alias for fetch
+ *
+ * @return mixed
*/
public function fetchRow() {
return $this->statement->fetch();
@@ -168,12 +175,13 @@ class OC_DB_StatementWrapper {
/**
* Provide a simple fetchOne.
+ *
* fetch single column from the next row
- * @param int $colnum the column number to fetch
+ * @param int $column the column number to fetch
* @return string
*/
- public function fetchOne($colnum = 0) {
- return $this->statement->fetchColumn($colnum);
+ public function fetchOne($column = 0) {
+ return $this->statement->fetchColumn($column);
}
/**
diff --git a/lib/private/defaults.php b/lib/private/defaults.php
index 79be211b82f..fca798568c5 100644
--- a/lib/private/defaults.php
+++ b/lib/private/defaults.php
@@ -29,8 +29,8 @@ class OC_Defaults {
$this->defaultEntity = "ownCloud"; /* e.g. company name, used for footers and copyright notices */
$this->defaultName = "ownCloud"; /* short name, used when referring to the software */
$this->defaultTitle = "ownCloud"; /* can be a longer name, for titles */
- $this->defaultBaseUrl = "http://owncloud.org";
- $this->defaultSyncClientUrl = " http://owncloud.org/sync-clients/";
+ $this->defaultBaseUrl = "https://owncloud.org";
+ $this->defaultSyncClientUrl = "https://owncloud.org/sync-clients/";
$this->defaultDocBaseUrl = "http://doc.owncloud.org";
$this->defaultSlogan = $this->l->t("web services under your control");
$this->defaultLogoClaim = "";
diff --git a/lib/private/filechunking.php b/lib/private/filechunking.php
index be7f4e14a11..1da02fc81e3 100644
--- a/lib/private/filechunking.php
+++ b/lib/private/filechunking.php
@@ -64,20 +64,46 @@ class OC_FileChunking {
return $parts == $this->info['chunkcount'];
}
+ /**
+ * Assembles the chunks into the file specified by the path.
+ * Chunks are deleted afterwards.
+ *
+ * @param string $f target path
+ *
+ * @return assembled file size
+ *
+ * @throws \OC\InsufficientStorageException when file could not be fully
+ * assembled due to lack of free space
+ */
public function assemble($f) {
$cache = $this->getCache();
$prefix = $this->getPrefix();
$count = 0;
- for($i=0; $i < $this->info['chunkcount']; $i++) {
+ for ($i = 0; $i < $this->info['chunkcount']; $i++) {
$chunk = $cache->get($prefix.$i);
+ // remove after reading to directly save space
+ $cache->remove($prefix.$i);
$count += fwrite($f, $chunk);
}
- $this->cleanup();
return $count;
}
/**
+ * Returns the size of the chunks already present
+ * @return size in bytes
+ */
+ public function getCurrentSize() {
+ $cache = $this->getCache();
+ $prefix = $this->getPrefix();
+ $total = 0;
+ for ($i = 0; $i < $this->info['chunkcount']; $i++) {
+ $total += $cache->size($prefix.$i);
+ }
+ return $total;
+ }
+
+ /**
* Removes all chunks which belong to this transmission
*/
public function cleanup() {
@@ -128,7 +154,15 @@ class OC_FileChunking {
}
/**
- * @param string $path
+ * Assembles the chunks into the file specified by the path.
+ * Also triggers the relevant hooks and proxies.
+ *
+ * @param string $path target path
+ *
+ * @return assembled file size or false if file could not be created
+ *
+ * @throws \OC\InsufficientStorageException when file could not be fully
+ * assembled due to lack of free space
*/
public function file_assemble($path) {
$absolutePath = \OC\Files\Filesystem::normalizePath(\OC\Files\Filesystem::getView()->getAbsolutePath($path));
diff --git a/lib/private/files.php b/lib/private/files.php
index 7e7a27f48dc..bfe6d3c02da 100644
--- a/lib/private/files.php
+++ b/lib/private/files.php
@@ -148,8 +148,9 @@ class OC_Files {
set_time_limit($executionTime);
} else {
if ($xsendfile) {
+ $view = \OC\Files\Filesystem::getView();
/** @var $storage \OC\Files\Storage\Storage */
- list($storage) = \OC\Files\Filesystem::resolvePath($filename);
+ list($storage) = $view->resolvePath($filename);
if ($storage->isLocal()) {
self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename));
} else {
diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php
index abc11e76470..1c9de56f8c5 100644
--- a/lib/private/files/cache/cache.php
+++ b/lib/private/files/cache/cache.php
@@ -594,7 +594,25 @@ class Cache {
}
/**
+ * get the path of a file on this storage by it's id
+ *
+ * @param int $id
+ * @return string | null
+ */
+ public function getPathById($id) {
+ $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?';
+ $result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId()));
+ if ($row = $result->fetchRow()) {
+ return $row['path'];
+ } else {
+ return null;
+ }
+ }
+
+ /**
* get the storage id of the storage for a file and the internal path of the file
+ * unlike getPathById this does not limit the search to files on this storage and
+ * instead does a global search in the cache table
*
* @param int $id
* @return array, first element holding the storage id, second the path
diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php
index c31e0c38180..7e27650c557 100644
--- a/lib/private/files/filesystem.php
+++ b/lib/private/files/filesystem.php
@@ -321,11 +321,36 @@ class Filesystem {
self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user);
}
+ self::mountCacheDir($user);
+
// Chance to mount for other storages
\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user, 'user_dir' => $root));
}
/**
+ * Mounts the cache directory
+ * @param string $user user name
+ */
+ private static function mountCacheDir($user) {
+ $cacheBaseDir = \OC_Config::getValue('cache_path', '');
+ if ($cacheBaseDir === '') {
+ // use local cache dir relative to the user's home
+ $subdir = 'cache';
+ $view = new \OC\Files\View('/' . $user);
+ if(!$view->file_exists($subdir)) {
+ $view->mkdir($subdir);
+ }
+ } else {
+ $cacheDir = rtrim($cacheBaseDir, '/') . '/' . $user;
+ if (!file_exists($cacheDir)) {
+ mkdir($cacheDir, 0770, true);
+ }
+ // mount external cache dir to "/$user/cache" mount point
+ self::mount('\OC\Files\Storage\Local', array('datadir' => $cacheDir), '/' . $user . '/cache');
+ }
+ }
+
+ /**
* get the default filesystem view
*
* @return View
diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php
index 3c078d7b1b4..33b8549ff78 100644
--- a/lib/private/files/storage/common.php
+++ b/lib/private/files/storage/common.php
@@ -118,17 +118,17 @@ abstract class Common implements \OC\Files\Storage\Storage {
if (!$handle) {
return false;
}
- $size = $this->filesize($path);
- if ($size == 0) {
- return '';
- }
- return fread($handle, $size);
+ $data = stream_get_contents($handle);
+ fclose($handle);
+ return $data;
}
public function file_put_contents($path, $data) {
$handle = $this->fopen($path, "w");
$this->removeCachedFile($path);
- return fwrite($handle, $data);
+ $count = fwrite($handle, $data);
+ fclose($handle);
+ return $count;
}
public function rename($path1, $path2) {
@@ -159,10 +159,11 @@ abstract class Common implements \OC\Files\Storage\Storage {
}
public function hash($type, $path, $raw = false) {
- $tmpFile = $this->getLocalFile($path);
- $hash = hash($type, $tmpFile, $raw);
- unlink($tmpFile);
- return $hash;
+ $fh = $this->fopen($path, 'rb');
+ $ctx = hash_init($type);
+ hash_update_stream($ctx, $fh);
+ fclose($fh);
+ return hash_final($ctx, $raw);
}
public function search($query) {
diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php
index 0f906ec55b4..571bf7f97c1 100644
--- a/lib/private/files/storage/local.php
+++ b/lib/private/files/storage/local.php
@@ -256,7 +256,7 @@ if (\OC_Util::runningOnWindows()) {
return 0;
}
- public function hash($path, $type, $raw = false) {
+ public function hash($type, $path, $raw = false) {
return hash_file($type, $this->datadir . $path, $raw);
}
diff --git a/lib/private/files/storage/mappedlocal.php b/lib/private/files/storage/mappedlocal.php
index 026f6ec895e..94ee28ca763 100644
--- a/lib/private/files/storage/mappedlocal.php
+++ b/lib/private/files/storage/mappedlocal.php
@@ -276,7 +276,7 @@ class MappedLocal extends \OC\Files\Storage\Common{
return 0;
}
- public function hash($path, $type, $raw=false) {
+ public function hash($type, $path, $raw=false) {
return hash_file($type, $this->buildPath($path), $raw);
}
diff --git a/lib/private/files/view.php b/lib/private/files/view.php
index f06c2fcd66c..94be7114865 100644
--- a/lib/private/files/view.php
+++ b/lib/private/files/view.php
@@ -832,6 +832,9 @@ class View {
$user = \OC_User::getUser();
if (!$cache->inCache($internalPath)) {
+ if (!$storage->file_exists($internalPath)) {
+ return false;
+ }
$scanner = $storage->getScanner($internalPath);
$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
} else {
@@ -1129,15 +1132,22 @@ class View {
* @return string
*/
public function getPath($id) {
- list($storage, $internalPath) = Cache\Cache::getById($id);
- $mounts = Filesystem::getMountByStorageId($storage);
+ $manager = Filesystem::getMountManager();
+ $mounts = $manager->findIn($this->fakeRoot);
+ $mounts[] = $manager->find($this->fakeRoot);
+ // reverse the array so we start with the storage this view is in
+ // which is the most likely to contain the file we're looking for
+ $mounts = array_reverse($mounts);
foreach ($mounts as $mount) {
/**
- * @var \OC\Files\Mount $mount
+ * @var \OC\Files\Mount\Mount $mount
*/
- $fullPath = $mount->getMountPoint() . $internalPath;
- if (!is_null($path = $this->getRelativePath($fullPath))) {
- return $path;
+ $cache = $mount->getStorage()->getCache();
+ if ($internalPath = $cache->getPathById($id)) {
+ $fullPath = $mount->getMountPoint() . $internalPath;
+ if (!is_null($path = $this->getRelativePath($fullPath))) {
+ return $path;
+ }
}
}
return null;
diff --git a/lib/private/forbiddenexception.php b/lib/private/forbiddenexception.php
new file mode 100644
index 00000000000..14a4cd14984
--- /dev/null
+++ b/lib/private/forbiddenexception.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC;
+
+/**
+ * Exception thrown whenever access to a resource has
+ * been forbidden or whenever a user isn't authenticated.
+ */
+class ForbiddenException extends \Exception {
+}
diff --git a/lib/private/group/backend.php b/lib/private/group/backend.php
index 2e17b5d0b7f..b0ed0d90d76 100644
--- a/lib/private/group/backend.php
+++ b/lib/private/group/backend.php
@@ -34,6 +34,7 @@ define('OC_GROUP_BACKEND_DELETE_GROUP', 0x00000010);
define('OC_GROUP_BACKEND_ADD_TO_GROUP', 0x00000100);
define('OC_GROUP_BACKEND_REMOVE_FROM_GOUP', 0x00001000);
define('OC_GROUP_BACKEND_GET_DISPLAYNAME', 0x00010000);
+define('OC_GROUP_BACKEND_COUNT_USERS', 0x00100000);
/**
* Abstract base class for user management
@@ -45,6 +46,7 @@ abstract class OC_Group_Backend implements OC_Group_Interface {
OC_GROUP_BACKEND_ADD_TO_GROUP => 'addToGroup',
OC_GROUP_BACKEND_REMOVE_FROM_GOUP => 'removeFromGroup',
OC_GROUP_BACKEND_GET_DISPLAYNAME => 'displayNamesInGroup',
+ OC_GROUP_BACKEND_COUNT_USERS => 'countUsersInGroup',
);
/**
diff --git a/lib/private/group/database.php b/lib/private/group/database.php
index d0974685ff6..3815dcff2e5 100644
--- a/lib/private/group/database.php
+++ b/lib/private/group/database.php
@@ -212,6 +212,20 @@ class OC_Group_Database extends OC_Group_Backend {
}
/**
+ * @brief get the number of all users matching the search string in a group
+ * @param string $gid
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return int | false
+ */
+ public function countUsersInGroup($gid, $search = '') {
+ $stmt = OC_DB::prepare('SELECT COUNT(`uid`) AS `count` FROM `*PREFIX*group_user` WHERE `gid` = ? AND `uid` LIKE ?');
+ $result = $stmt->execute(array($gid, $search.'%'));
+ return $result->fetchOne();
+ }
+
+ /**
* @brief get a list of all display names in a group
* @param string $gid
* @param string $search
diff --git a/lib/private/group/dummy.php b/lib/private/group/dummy.php
index da26e1b910e..94cbb607ad1 100644
--- a/lib/private/group/dummy.php
+++ b/lib/private/group/dummy.php
@@ -157,4 +157,14 @@ class OC_Group_Dummy extends OC_Group_Backend {
}
}
+ /**
+ * @brief get the number of all users in a group
+ * @returns int | bool
+ */
+ public function countUsersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
+ if(isset($this->groups[$gid])) {
+ return count($this->groups[$gid]);
+ }
+ }
+
}
diff --git a/lib/private/group/group.php b/lib/private/group/group.php
index 8d2aa87a788..a2b8a0dcbea 100644
--- a/lib/private/group/group.php
+++ b/lib/private/group/group.php
@@ -187,6 +187,27 @@ class Group {
}
/**
+ * returns the number of users matching the search string
+ *
+ * @param string $search
+ * @return int | bool
+ */
+ public function count($search) {
+ $users = false;
+ foreach ($this->backends as $backend) {
+ if($backend->implementsActions(OC_GROUP_BACKEND_COUNT_USERS)) {
+ if($users === false) {
+ //we could directly add to a bool variable, but this would
+ //be ugly
+ $users = 0;
+ }
+ $users += $backend->countUsersInGroup($this->gid, $search);
+ }
+ }
+ return $users;
+ }
+
+ /**
* search for users in the group by displayname
*
* @param string $search
diff --git a/lib/private/helper.php b/lib/private/helper.php
index 98a86388d20..da3d3cd1c6e 100644
--- a/lib/private/helper.php
+++ b/lib/private/helper.php
@@ -78,8 +78,7 @@ class OC_Helper {
* Returns a absolute url to the given app and file.
*/
public static function linkToAbsolute($app, $file, $args = array()) {
- $urlLinkTo = self::linkTo($app, $file, $args);
- return self::makeURLAbsolute($urlLinkTo);
+ return self::linkTo($app, $file, $args);
}
/**
@@ -876,12 +875,15 @@ class OC_Helper {
* Calculate the disc space for the given path
*
* @param string $path
+ * @param \OCP\Files\FileInfo $rootInfo (optional)
* @return array
*/
- public static function getStorageInfo($path) {
+ public static function getStorageInfo($path, $rootInfo = null) {
// return storage info without adding mount points
- $rootInfo = \OC\Files\Filesystem::getFileInfo($path, false);
- $used = $rootInfo['size'];
+ if (is_null($rootInfo)) {
+ $rootInfo = \OC\Files\Filesystem::getFileInfo($path, false);
+ }
+ $used = $rootInfo->getSize();
if ($used < 0) {
$used = 0;
}
diff --git a/lib/private/image.php b/lib/private/image.php
index c987ce92c3c..f1b8acc41b7 100644
--- a/lib/private/image.php
+++ b/lib/private/image.php
@@ -34,7 +34,7 @@ class OC_Image {
/**
* @brief Get mime type for an image file.
- * @param string|null $filepath The path to a local image file.
+ * @param string|null $filePath The path to a local image file.
* @return string The mime type if the it could be determined, otherwise an empty string.
*/
static public function getMimeTypeForFile($filePath) {
diff --git a/lib/private/l10n.php b/lib/private/l10n.php
index 197b2d6791b..175360e27a3 100644
--- a/lib/private/l10n.php
+++ b/lib/private/l10n.php
@@ -75,7 +75,7 @@ class OC_L10N implements \OCP\IL10N {
* get an L10N instance
* @param string $app
* @param string|null $lang
- * @return OC_L10N
+ * @return \OC_L10N
*/
public static function get($app, $lang=null) {
if (is_null($lang)) {
@@ -89,7 +89,6 @@ class OC_L10N implements \OCP\IL10N {
* @brief The constructor
* @param string $app app requesting l10n
* @param string $lang default: null Language
- * @returns OC_L10N-Object
*
* If language is not set, the constructor tries to find the right
* language.
@@ -352,7 +351,7 @@ class OC_L10N implements \OCP\IL10N {
/**
* @brief Localization
* @param string $type Type of localization
- * @param $params parameters for this localization
+ * @param array $data parameters for this localization
* @returns String or false
*
* Returns the localized data.
diff --git a/lib/private/legacy/appconfig.php b/lib/private/legacy/appconfig.php
index b6c3542a673..cb5cef7e350 100644
--- a/lib/private/legacy/appconfig.php
+++ b/lib/private/legacy/appconfig.php
@@ -116,8 +116,6 @@ class OC_Appconfig {
/**
* get multiply values, either the app or key can be used as wildcard by setting it to false
*
- * @param app
- * @param key
* @param string|false $app
* @param string|false $key
* @return array
diff --git a/lib/private/legacy/config.php b/lib/private/legacy/config.php
index ab67c8d3020..6c2103179ab 100644
--- a/lib/private/legacy/config.php
+++ b/lib/private/legacy/config.php
@@ -63,8 +63,8 @@ class OC_Config {
/**
* @brief Gets a value from config.php
* @param string $key key
- * @param string $default = null default value
- * @return string the value or $default
+ * @param mixed $default = null default value
+ * @return mixed the value or $default
*
* This function gets the value from config.php. If it does not exist,
* $default will be returned.
@@ -76,7 +76,7 @@ class OC_Config {
/**
* @brief Sets a value
* @param string $key key
- * @param string $value value
+ * @param mixed $value value
*
* This function sets the value and writes the config.php.
*
diff --git a/lib/private/mail.php b/lib/private/mail.php
index 79f51609631..f9083cc4e64 100644
--- a/lib/private/mail.php
+++ b/lib/private/mail.php
@@ -137,6 +137,9 @@ class OC_Mail {
* @return string
*/
public static function buildAsciiEmail($emailAddress) {
+ if (!function_exists('idn_to_ascii')) {
+ return $emailAddress;
+ }
list($name, $domain) = explode('@', $emailAddress, 2);
$domain = idn_to_ascii($domain);
diff --git a/lib/private/ocs/cloud.php b/lib/private/ocs/cloud.php
index 06d6a8eb4b0..c8bb9425f1a 100644
--- a/lib/private/ocs/cloud.php
+++ b/lib/private/ocs/cloud.php
@@ -99,31 +99,4 @@ class OC_OCS_Cloud {
);
return new OC_OCS_Result($data);
}
-
- public static function getUserPublickey($parameters) {
-
- if(OC_User::userExists($parameters['user'])) {
- // calculate the disc space
- // TODO
- return new OC_OCS_Result(array());
- } else {
- return new OC_OCS_Result(null, 300);
- }
- }
-
- public static function getUserPrivatekey($parameters) {
- $user = OC_User::getUser();
- if(OC_User::isAdminUser($user) or ($user==$parameters['user'])) {
-
- if(OC_User::userExists($user)) {
- // calculate the disc space
- $txt = 'this is the private key of '.$parameters['user'];
- echo($txt);
- } else {
- return new OC_OCS_Result(null, 300, 'User does not exist');
- }
- } else {
- return new OC_OCS_Result('null', 300, 'You don´t have permission to access this ressource.');
- }
- }
}
diff --git a/lib/private/ocs/result.php b/lib/private/ocs/result.php
index 9f14e8da7e8..0e3b85d5905 100644
--- a/lib/private/ocs/result.php
+++ b/lib/private/ocs/result.php
@@ -96,7 +96,7 @@ class OC_OCS_Result{
* @return bool
*/
public function succeeded() {
- return (substr($this->statusCode, 0, 1) === '1');
+ return ($this->statusCode == 100);
}
diff --git a/lib/private/preview.php b/lib/private/preview.php
index 0c1af3c9588..0187b4aacbb 100755
--- a/lib/private/preview.php
+++ b/lib/private/preview.php
@@ -13,6 +13,8 @@
*/
namespace OC;
+use OC\Preview\Provider;
+
require_once 'preview/image.php';
require_once 'preview/movies.php';
require_once 'preview/mp3.php';
@@ -39,15 +41,16 @@ class Preview {
private $file;
private $maxX;
private $maxY;
- private $scalingup;
- private $mimetype;
+ private $scalingUp;
+ private $mimeType;
//filemapper used for deleting previews
// index is path, value is fileinfo
static public $deleteFileMapper = array();
- //preview images object
/**
+ * preview images object
+ *
* @var \OC_Image
*/
private $preview;
@@ -109,7 +112,7 @@ class Preview {
* @brief returns the path of the file you want a thumbnail from
* @return string
*/
- public function getFile() {
+ public function getFile() {
return $this->file;
}
@@ -134,7 +137,7 @@ class Preview {
* @return bool
*/
public function getScalingUp() {
- return $this->scalingup;
+ return $this->scalingUp;
}
/**
@@ -191,18 +194,18 @@ class Preview {
if ($file !== '') {
$this->getFileInfo();
if($this->info !== null && $this->info !== false) {
- $this->mimetype = $this->info->getMimetype();
+ $this->mimeType = $this->info->getMimetype();
}
}
return $this;
}
/**
- * @brief set mimetype explicitely
- * @param string $mimetype
+ * @brief set mime type explicitly
+ * @param string $mimeType
*/
- public function setMimetype($mimetype) {
- $this->mimetype = $mimetype;
+ public function setMimetype($mimeType) {
+ $this->mimeType = $mimeType;
}
/**
@@ -254,7 +257,7 @@ class Preview {
if ($this->getMaxScaleFactor() === 1) {
$scalingUp = false;
}
- $this->scalingup = $scalingUp;
+ $this->scalingUp = $scalingUp;
return $this;
}
@@ -314,35 +317,68 @@ class Preview {
/**
* @brief check if thumbnail or bigger version of thumbnail of file is cached
- * @return mixed (bool / string)
- * false if thumbnail does not exist
- * path to thumbnail if thumbnail exists
+ * @param int $fileId fileId of the original image
+ * @return string|false path to thumbnail if it exists or false
*/
- private function isCached() {
- $file = $this->getFile();
+ public function isCached($fileId) {
+ if (is_null($fileId)) {
+ return false;
+ }
+
$maxX = $this->getMaxX();
$maxY = $this->getMaxY();
- $scalingUp = $this->getScalingUp();
- $maxScaleFactor = $this->getMaxScaleFactor();
- $fileInfo = $this->getFileInfo($file);
- $fileId = $fileInfo->getId();
+ $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/';
+
+ //does a preview with the wanted height and width already exist?
+ if ($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) {
+ return $previewPath . $maxX . '-' . $maxY . '.png';
+ }
+
+ return $this->isCachedBigger($fileId);
+ }
+
+ /**
+ * @brief check if a bigger version of thumbnail of file is cached
+ * @param int $fileId fileId of the original image
+ * @return string|false path to bigger thumbnail if it exists or false
+ */
+ private function isCachedBigger($fileId) {
if (is_null($fileId)) {
return false;
}
- $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/';
- if (!$this->userView->is_dir($previewPath)) {
- return false;
+ $maxX = $this->getMaxX();
+
+ //array for usable cached thumbnails
+ $possibleThumbnails = $this->getPossibleThumbnails($fileId);
+
+ foreach ($possibleThumbnails as $width => $path) {
+ if ($width < $maxX) {
+ continue;
+ } else {
+ return $path;
+ }
}
- //does a preview with the wanted height and width already exist?
- if ($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) {
- return $previewPath . $maxX . '-' . $maxY . '.png';
+ return false;
+ }
+
+ /**
+ * @brief get possible bigger thumbnails of the given image
+ * @param int $fileId fileId of the original image
+ * @return array of paths to bigger thumbnails
+ */
+ private function getPossibleThumbnails($fileId) {
+
+ if (is_null($fileId)) {
+ return array();
}
- $wantedAspectRatio = (float)($maxX / $maxY);
+ $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/';
+
+ $wantedAspectRatio = (float) ($this->getMaxX() / $this->getMaxY());
//array for usable cached thumbnails
$possibleThumbnails = array();
@@ -350,53 +386,47 @@ class Preview {
$allThumbnails = $this->userView->getDirectoryContent($previewPath);
foreach ($allThumbnails as $thumbnail) {
$name = rtrim($thumbnail['name'], '.png');
- $size = explode('-', $name);
- $x = (int)$size[0];
- $y = (int)$size[1];
+ list($x, $y, $aspectRatio) = $this->getDimensionsFromFilename($name);
- $aspectRatio = (float)($x / $y);
- if ($aspectRatio !== $wantedAspectRatio) {
+ if (abs($aspectRatio - $wantedAspectRatio) >= 0.000001
+ || $this->unscalable($x, $y)
+ ) {
continue;
}
-
- if ($x < $maxX || $y < $maxY) {
- if ($scalingUp) {
- $scalefactor = $maxX / $x;
- if ($scalefactor > $maxScaleFactor) {
- continue;
- }
- } else {
- continue;
- }
- }
$possibleThumbnails[$x] = $thumbnail['path'];
}
- if (count($possibleThumbnails) === 0) {
- return false;
- }
-
- if (count($possibleThumbnails) === 1) {
- return current($possibleThumbnails);
- }
-
ksort($possibleThumbnails);
- if (key(reset($possibleThumbnails)) > $maxX) {
- return current(reset($possibleThumbnails));
- }
+ return $possibleThumbnails;
+ }
- if (key(end($possibleThumbnails)) < $maxX) {
- return current(end($possibleThumbnails));
- }
+ private function getDimensionsFromFilename($name) {
+ $size = explode('-', $name);
+ $x = (int) $size[0];
+ $y = (int) $size[1];
+ $aspectRatio = (float) ($x / $y);
+ return array($x, $y, $aspectRatio);
+ }
- foreach ($possibleThumbnails as $width => $path) {
- if ($width < $maxX) {
- continue;
+ private function unscalable($x, $y) {
+
+ $maxX = $this->getMaxX();
+ $maxY = $this->getMaxY();
+ $scalingUp = $this->getScalingUp();
+ $maxScaleFactor = $this->getMaxScaleFactor();
+
+ if ($x < $maxX || $y < $maxY) {
+ if ($scalingUp) {
+ $scalefactor = $maxX / $x;
+ if ($scalefactor > $maxScaleFactor) {
+ return true;
+ }
} else {
- return $path;
+ return true;
}
}
+ return false;
}
/**
@@ -420,7 +450,7 @@ class Preview {
}
$fileId = $fileInfo->getId();
- $cached = $this->isCached();
+ $cached = $this->isCached($fileId);
if ($cached) {
$stream = $this->userView->fopen($cached, 'r');
@@ -434,13 +464,14 @@ class Preview {
if (is_null($this->preview)) {
$preview = null;
- foreach (self::$providers as $supportedMimetype => $provider) {
- if (!preg_match($supportedMimetype, $this->mimetype)) {
+ foreach (self::$providers as $supportedMimeType => $provider) {
+ if (!preg_match($supportedMimeType, $this->mimeType)) {
continue;
}
\OC_Log::write('core', 'Generating preview for "' . $file . '" with "' . get_class($provider) . '"', \OC_Log::DEBUG);
+ /** @var $provider Provider */
$preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingUp, $this->fileView);
if (!($preview instanceof \OC_Image)) {
@@ -484,7 +515,6 @@ class Preview {
$this->getPreview();
}
$this->preview->show('image/png');
- return;
}
/**
@@ -493,7 +523,6 @@ class Preview {
*/
public function show() {
$this->showPreview();
- return;
}
/**
@@ -505,7 +534,7 @@ class Preview {
$x = $this->getMaxX();
$y = $this->getMaxY();
$scalingUp = $this->getScalingUp();
- $maxscalefactor = $this->getMaxScaleFactor();
+ $maxScaleFactor = $this->getMaxScaleFactor();
if (!($image instanceof \OC_Image)) {
\OC_Log::write('core', '$this->preview is not an instance of OC_Image', \OC_Log::DEBUG);
@@ -514,16 +543,16 @@ class Preview {
$image->fixOrientation();
- $realx = (int)$image->width();
- $realy = (int)$image->height();
+ $realX = (int)$image->width();
+ $realY = (int)$image->height();
- if ($x === $realx && $y === $realy) {
+ if ($x === $realX && $y === $realY) {
$this->preview = $image;
return;
}
- $factorX = $x / $realx;
- $factorY = $y / $realy;
+ $factorX = $x / $realX;
+ $factorY = $y / $realY;
if ($factorX >= $factorY) {
$factor = $factorX;
@@ -537,25 +566,25 @@ class Preview {
}
}
- if (!is_null($maxscalefactor)) {
- if ($factor > $maxscalefactor) {
- \OC_Log::write('core', 'scalefactor reduced from ' . $factor . ' to ' . $maxscalefactor, \OC_Log::DEBUG);
- $factor = $maxscalefactor;
+ if (!is_null($maxScaleFactor)) {
+ if ($factor > $maxScaleFactor) {
+ \OC_Log::write('core', 'scale factor reduced from ' . $factor . ' to ' . $maxScaleFactor, \OC_Log::DEBUG);
+ $factor = $maxScaleFactor;
}
}
- $newXsize = (int)($realx * $factor);
- $newYsize = (int)($realy * $factor);
+ $newXSize = (int)($realX * $factor);
+ $newYSize = (int)($realY * $factor);
- $image->preciseResize($newXsize, $newYsize);
+ $image->preciseResize($newXSize, $newYSize);
- if ($newXsize === $x && $newYsize === $y) {
+ if ($newXSize === $x && $newYSize === $y) {
$this->preview = $image;
return;
}
- if ($newXsize >= $x && $newYsize >= $y) {
- $cropX = floor(abs($x - $newXsize) * 0.5);
+ if ($newXSize >= $x && $newYSize >= $y) {
+ $cropX = floor(abs($x - $newXSize) * 0.5);
//don't crop previews on the Y axis, this sucks if it's a document.
//$cropY = floor(abs($y - $newYsize) * 0.5);
$cropY = 0;
@@ -566,36 +595,36 @@ class Preview {
return;
}
- if ($newXsize < $x || $newYsize < $y) {
- if ($newXsize > $x) {
- $cropX = floor(($newXsize - $x) * 0.5);
- $image->crop($cropX, 0, $x, $newYsize);
+ if ($newXSize < $x || $newYSize < $y) {
+ if ($newXSize > $x) {
+ $cropX = floor(($newXSize - $x) * 0.5);
+ $image->crop($cropX, 0, $x, $newYSize);
}
- if ($newYsize > $y) {
- $cropY = floor(($newYsize - $y) * 0.5);
- $image->crop(0, $cropY, $newXsize, $y);
+ if ($newYSize > $y) {
+ $cropY = floor(($newYSize - $y) * 0.5);
+ $image->crop(0, $cropY, $newXSize, $y);
}
- $newXsize = (int)$image->width();
- $newYsize = (int)$image->height();
+ $newXSize = (int)$image->width();
+ $newYSize = (int)$image->height();
//create transparent background layer
- $backgroundlayer = imagecreatetruecolor($x, $y);
- $white = imagecolorallocate($backgroundlayer, 255, 255, 255);
- imagefill($backgroundlayer, 0, 0, $white);
+ $backgroundLayer = imagecreatetruecolor($x, $y);
+ $white = imagecolorallocate($backgroundLayer, 255, 255, 255);
+ imagefill($backgroundLayer, 0, 0, $white);
$image = $image->resource();
- $mergeX = floor(abs($x - $newXsize) * 0.5);
- $mergeY = floor(abs($y - $newYsize) * 0.5);
+ $mergeX = floor(abs($x - $newXSize) * 0.5);
+ $mergeY = floor(abs($y - $newYSize) * 0.5);
- imagecopy($backgroundlayer, $image, $mergeX, $mergeY, 0, 0, $newXsize, $newYsize);
+ imagecopy($backgroundLayer, $image, $mergeX, $mergeY, 0, 0, $newXSize, $newYSize);
//$black = imagecolorallocate(0,0,0);
//imagecolortransparent($transparentlayer, $black);
- $image = new \OC_Image($backgroundlayer);
+ $image = new \OC_Image($backgroundLayer);
$this->preview = $image;
return;
@@ -630,6 +659,7 @@ class Preview {
$class = $provider['class'];
$options = $provider['options'];
+ /** @var $object Provider */
$object = new $class($options);
self::$providers[$object->getMimeType()] = $object;
@@ -640,7 +670,7 @@ class Preview {
}
public static function post_write($args) {
- self::post_delete($args);
+ self::post_delete($args, 'files/');
}
public static function prepare_delete_files($args) {
@@ -676,9 +706,9 @@ class Preview {
}
/**
- * @param string $mimetype
+ * @param string $mimeType
*/
- public static function isMimeSupported($mimetype) {
+ public static function isMimeSupported($mimeType) {
if (!\OC_Config::getValue('enable_previews', true)) {
return false;
}
@@ -690,8 +720,8 @@ class Preview {
//remove last element because it has the mimetype *
$providers = array_slice(self::$providers, 0, -1);
- foreach ($providers as $supportedMimetype => $provider) {
- if (preg_match($supportedMimetype, $mimetype)) {
+ foreach ($providers as $supportedMimeType => $provider) {
+ if (preg_match($supportedMimeType, $mimeType)) {
return true;
}
}
diff --git a/lib/private/request.php b/lib/private/request.php
index 8041c4f0048..7cbbb0676b1 100755
--- a/lib/private/request.php
+++ b/lib/private/request.php
@@ -166,10 +166,11 @@ class OC_Request {
*/
public static function scriptName() {
$name = $_SERVER['SCRIPT_NAME'];
- if (OC_Config::getValue('overwritewebroot', '') !== '' and self::isOverwriteCondition()) {
+ $overwriteWebRoot = OC_Config::getValue('overwritewebroot', '');
+ if ($overwriteWebRoot !== '' and self::isOverwriteCondition()) {
$serverroot = str_replace("\\", '/', substr(__DIR__, 0, -strlen('lib/private/')));
$suburi = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen($serverroot)));
- $name = OC_Config::getValue('overwritewebroot', '') . $suburi;
+ $name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
}
return $name;
}
diff --git a/lib/private/route/router.php b/lib/private/route/router.php
index bad74c925fa..fa0ad6ab95b 100644
--- a/lib/private/route/router.php
+++ b/lib/private/route/router.php
@@ -114,10 +114,10 @@ class Router implements IRouter {
}
}
foreach ($routingFiles as $app => $file) {
- if (!$this->loadedApps[$app]) {
+ if (!isset($this->loadedApps[$app])) {
$this->loadedApps[$app] = true;
$this->useCollection($app);
- require_once $file;
+ $this->requireRouteFile($file);
$collection = $this->getCollection($app);
$collection->addPrefix('/apps/' . $app);
$this->root->addCollection($collection);
@@ -183,7 +183,7 @@ class Router implements IRouter {
// empty string / 'apps' / $app / rest of the route
list(, , $app,) = explode('/', $url, 4);
$this->loadRoutes($app);
- } else if (substr($url, 0, 6) === '/core/' or substr($url, 0, 5) === '/ocs/' or substr($url, 0, 10) === '/settings/') {
+ } else if (substr($url, 0, 6) === '/core/' or substr($url, 0, 10) === '/settings/') {
$this->loadRoutes('core');
} else {
$this->loadRoutes();
@@ -230,4 +230,12 @@ class Router implements IRouter {
return $this->getGenerator()->generate($name, $parameters, $absolute);
}
+ /**
+ * To isolate the variable scope used inside the $file it is required in it's own method
+ * @param $file
+ */
+ private function requireRouteFile($file) {
+ require_once $file;
+ }
+
}
diff --git a/lib/private/server.php b/lib/private/server.php
index 5c83f3ef495..5d90a0b19fc 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -35,6 +35,13 @@ class Server extends SimpleContainer implements IServerContainer {
$requesttoken = false;
}
+ if (defined('PHPUNIT_RUN') && PHPUNIT_RUN
+ && in_array('fakeinput', stream_get_wrappers())) {
+ $stream = 'fakeinput://data';
+ } else {
+ $stream = 'php://input';
+ }
+
return new Request(
array(
'get' => $_GET,
@@ -48,7 +55,7 @@ class Server extends SimpleContainer implements IServerContainer {
: null,
'urlParams' => $urlParams,
'requesttoken' => $requesttoken,
- )
+ ), $stream
);
});
$this->registerService('PreviewManager', function($c) {
@@ -302,7 +309,7 @@ class Server extends SimpleContainer implements IServerContainer {
/**
* get an L10N instance
- * @param $app string appid
+ * @param string $app appid
* @return \OC_L10N
*/
function getL10N($app) {
diff --git a/lib/private/share/constants.php b/lib/private/share/constants.php
new file mode 100644
index 00000000000..7e4223d10fa
--- /dev/null
+++ b/lib/private/share/constants.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Share;
+
+class Constants {
+
+ const SHARE_TYPE_USER = 0;
+ const SHARE_TYPE_GROUP = 1;
+ const SHARE_TYPE_LINK = 3;
+ const SHARE_TYPE_EMAIL = 4;
+ const SHARE_TYPE_CONTACT = 5;
+ const SHARE_TYPE_REMOTE = 6;
+
+ const FORMAT_NONE = -1;
+ const FORMAT_STATUSES = -2;
+ const FORMAT_SOURCES = -3;
+
+ const TOKEN_LENGTH = 32; // see db_structure.xml
+
+ protected static $shareTypeUserAndGroups = -1;
+ protected static $shareTypeGroupUserUnique = 2;
+ protected static $backends = array();
+ protected static $backendTypes = array();
+ protected static $isResharingAllowed;
+}
diff --git a/lib/private/share/helper.php b/lib/private/share/helper.php
new file mode 100644
index 00000000000..fde55667281
--- /dev/null
+++ b/lib/private/share/helper.php
@@ -0,0 +1,202 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Share;
+
+class Helper extends \OC\Share\Constants {
+
+ /**
+ * Generate a unique target for the item
+ * @param string Item type
+ * @param string Item source
+ * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
+ * @param string User or group the item is being shared with
+ * @param string User that is the owner of shared item
+ * @param string The suggested target originating from a reshare (optional)
+ * @param int The id of the parent group share (optional)
+ * @return string Item target
+ */
+ public static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
+ $suggestedTarget = null, $groupParent = null) {
+ $backend = \OC\Share\Share::getBackend($itemType);
+ if ($shareType == self::SHARE_TYPE_LINK) {
+ if (isset($suggestedTarget)) {
+ return $suggestedTarget;
+ }
+ return $backend->generateTarget($itemSource, false);
+ } else {
+ if ($itemType == 'file' || $itemType == 'folder') {
+ $column = 'file_target';
+ $columnSource = 'file_source';
+ } else {
+ $column = 'item_target';
+ $columnSource = 'item_source';
+ }
+ if ($shareType == self::SHARE_TYPE_USER) {
+ // Share with is a user, so set share type to user and groups
+ $shareType = self::$shareTypeUserAndGroups;
+ $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith));
+ } else {
+ $userAndGroups = false;
+ }
+ $exclude = null;
+ // Backend has 3 opportunities to generate a unique target
+ for ($i = 0; $i < 2; $i++) {
+ // Check if suggested target exists first
+ if ($i == 0 && isset($suggestedTarget)) {
+ $target = $suggestedTarget;
+ } else {
+ if ($shareType == self::SHARE_TYPE_GROUP) {
+ $target = $backend->generateTarget($itemSource, false, $exclude);
+ } else {
+ $target = $backend->generateTarget($itemSource, $shareWith, $exclude);
+ }
+ if (is_array($exclude) && in_array($target, $exclude)) {
+ break;
+ }
+ }
+ // Check if target already exists
+ $checkTarget = \OC\Share\Share::getItems($itemType, $target, $shareType, $shareWith);
+ if (!empty($checkTarget)) {
+ foreach ($checkTarget as $item) {
+ // Skip item if it is the group parent row
+ if (isset($groupParent) && $item['id'] == $groupParent) {
+ if (count($checkTarget) == 1) {
+ return $target;
+ } else {
+ continue;
+ }
+ }
+ if ($item['uid_owner'] == $uidOwner) {
+ if ($itemType == 'file' || $itemType == 'folder') {
+ $meta = \OC\Files\Filesystem::getFileInfo($itemSource);
+ if ($item['file_source'] == $meta['fileid']) {
+ return $target;
+ }
+ } else if ($item['item_source'] == $itemSource) {
+ return $target;
+ }
+ }
+ }
+ if (!isset($exclude)) {
+ $exclude = array();
+ }
+ // Find similar targets to improve backend's chances to generate a unqiue target
+ if ($userAndGroups) {
+ if ($column == 'file_target') {
+ $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`'
+ .' WHERE `item_type` IN (\'file\', \'folder\')'
+ .' AND `share_type` IN (?,?,?)'
+ .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')');
+ $result = $checkTargets->execute(array(self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP,
+ self::$shareTypeGroupUserUnique));
+ } else {
+ $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`'
+ .' WHERE `item_type` = ? AND `share_type` IN (?,?,?)'
+ .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')');
+ $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER,
+ self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique));
+ }
+ } else {
+ if ($column == 'file_target') {
+ $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`'
+ .' WHERE `item_type` IN (\'file\', \'folder\')'
+ .' AND `share_type` = ? AND `share_with` = ?');
+ $result = $checkTargets->execute(array(self::SHARE_TYPE_GROUP, $shareWith));
+ } else {
+ $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`'
+ .' WHERE `item_type` = ? AND `share_type` = ? AND `share_with` = ?');
+ $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith));
+ }
+ }
+ while ($row = $result->fetchRow()) {
+ $exclude[] = $row[$column];
+ }
+ } else {
+ return $target;
+ }
+ }
+ }
+ $message = 'Sharing backend registered for '.$itemType.' did not generate a unique target for '.$itemSource;
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+
+ /**
+ * Delete all reshares of an item
+ * @param int Id of item to delete
+ * @param bool If true, exclude the parent from the delete (optional)
+ * @param string The user that the parent was shared with (optinal)
+ */
+ public static function delete($parent, $excludeParent = false, $uidOwner = null) {
+ $ids = array($parent);
+ $parents = array($parent);
+ while (!empty($parents)) {
+ $parents = "'".implode("','", $parents)."'";
+ // Check the owner on the first search of reshares, useful for
+ // finding and deleting the reshares by a single user of a group share
+ if (count($ids) == 1 && isset($uidOwner)) {
+ $query = \OC_DB::prepare('SELECT `id`, `uid_owner`, `item_type`, `item_target`, `parent`'
+ .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.') AND `uid_owner` = ?');
+ $result = $query->execute(array($uidOwner));
+ } else {
+ $query = \OC_DB::prepare('SELECT `id`, `item_type`, `item_target`, `parent`, `uid_owner`'
+ .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.')');
+ $result = $query->execute();
+ }
+ // Reset parents array, only go through loop again if items are found
+ $parents = array();
+ while ($item = $result->fetchRow()) {
+ // Search for a duplicate parent share, this occurs when an
+ // item is shared to the same user through a group and user or the
+ // same item is shared by different users
+ $userAndGroups = array_merge(array($item['uid_owner']), \OC_Group::getUserGroups($item['uid_owner']));
+ $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`'
+ .' WHERE `item_type` = ?'
+ .' AND `item_target` = ?'
+ .' AND `share_type` IN (?,?,?)'
+ .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'
+ .' AND `uid_owner` != ? AND `id` != ?');
+ $duplicateParent = $query->execute(array($item['item_type'], $item['item_target'],
+ self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique,
+ $item['uid_owner'], $item['parent']))->fetchRow();
+ if ($duplicateParent) {
+ // Change the parent to the other item id if share permission is granted
+ if ($duplicateParent['permissions'] & \OCP\PERMISSION_SHARE) {
+ $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` = ?');
+ $query->execute(array($duplicateParent['id'], $item['id']));
+ continue;
+ }
+ }
+ $ids[] = $item['id'];
+ $parents[] = $item['id'];
+ }
+ }
+ if ($excludeParent) {
+ unset($ids[0]);
+ }
+ if (!empty($ids)) {
+ $ids = "'".implode("','", $ids)."'";
+ $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$ids.')');
+ $query->execute();
+ }
+ }
+}
diff --git a/lib/private/share/hooks.php b/lib/private/share/hooks.php
new file mode 100644
index 00000000000..a33c71eedd2
--- /dev/null
+++ b/lib/private/share/hooks.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Share;
+
+class Hooks extends \OC\Share\Constants {
+ /**
+ * Function that is called after a user is deleted. Cleans up the shares of that user.
+ * @param array arguments
+ */
+ public static function post_deleteUser($arguments) {
+ // Delete any items shared with the deleted user
+ $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`'
+ .' WHERE `share_with` = ? AND `share_type` = ? OR `share_type` = ?');
+ $result = $query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
+ // Delete any items the deleted user shared
+ $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?');
+ $result = $query->execute(array($arguments['uid']));
+ while ($item = $result->fetchRow()) {
+ Helper::delete($item['id']);
+ }
+ }
+
+ /**
+ * Function that is called after a user is added to a group.
+ * TODO what does it do?
+ * @param array arguments
+ */
+ public static function post_addToGroup($arguments) {
+ // Find the group shares and check if the user needs a unique target
+ $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?');
+ $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid']));
+ $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`,'
+ .' `item_target`, `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`,'
+ .' `stime`, `file_source`, `file_target`) VALUES (?,?,?,?,?,?,?,?,?,?,?)');
+ while ($item = $result->fetchRow()) {
+ if ($item['item_type'] == 'file' || $item['item_type'] == 'file') {
+ $itemTarget = null;
+ } else {
+ $itemTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER,
+ $arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']);
+ }
+ if (isset($item['file_source'])) {
+ $fileTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER,
+ $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']);
+ } else {
+ $fileTarget = null;
+ }
+ // Insert an extra row for the group share if the item or file target is unique for this user
+ if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) {
+ $query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'],
+ self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'],
+ $item['stime'], $item['file_source'], $fileTarget));
+ \OC_DB::insertid('*PREFIX*share');
+ }
+ }
+ }
+
+ /**
+ * Function that is called after a user is removed from a group. Shares are cleaned up.
+ * @param array arguments
+ */
+ public static function post_removeFromGroup($arguments) {
+ $sql = 'SELECT `id`, `share_type` FROM `*PREFIX*share`'
+ .' WHERE (`share_type` = ? AND `share_with` = ?) OR (`share_type` = ? AND `share_with` = ?)';
+ $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'],
+ self::$shareTypeGroupUserUnique, $arguments['uid']));
+ while ($item = $result->fetchRow()) {
+ if ($item['share_type'] == self::SHARE_TYPE_GROUP) {
+ // Delete all reshares by this user of the group share
+ Helper::delete($item['id'], true, $arguments['uid']);
+ } else {
+ Helper::delete($item['id']);
+ }
+ }
+ }
+
+ /**
+ * Function that is called after a group is removed. Cleans up the shares to that group.
+ * @param array arguments
+ */
+ public static function post_deleteGroup($arguments) {
+ $sql = 'SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?';
+ $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid']));
+ while ($item = $result->fetchRow()) {
+ Helper::delete($item['id']);
+ }
+ }
+
+}
diff --git a/lib/private/share/mailnotifications.php b/lib/private/share/mailnotifications.php
index 45734818731..4799db52330 100644
--- a/lib/private/share/mailnotifications.php
+++ b/lib/private/share/mailnotifications.php
@@ -30,7 +30,6 @@ class MailNotifications {
/**
*
- * @param string $recipient user id
* @param string $sender user id (if nothing is set we use the currently logged-in user)
*/
public function __construct($sender = null) {
diff --git a/lib/private/share/share.php b/lib/private/share/share.php
new file mode 100644
index 00000000000..d4f08e8e016
--- /dev/null
+++ b/lib/private/share/share.php
@@ -0,0 +1,1629 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle, Michael Gapczynski
+ * @copyright 2012 Michael Gapczynski <mtgap@owncloud.com>
+ * 2014 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Share;
+
+/**
+ * This class provides the ability for apps to share their content between users.
+ * Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
+ *
+ * It provides the following hooks:
+ * - post_shared
+ */
+class Share extends \OC\Share\Constants {
+
+ /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
+ * Construct permissions for share() and setPermissions with Or (|) e.g.
+ * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
+ *
+ * Check if permission is granted with And (&) e.g. Check if delete is
+ * granted: if ($permissions & PERMISSION_DELETE)
+ *
+ * Remove permissions with And (&) and Not (~) e.g. Remove the update
+ * permission: $permissions &= ~PERMISSION_UPDATE
+ *
+ * Apps are required to handle permissions on their own, this class only
+ * stores and manages the permissions of shares
+ * @see lib/public/constants.php
+ */
+
+ /**
+ * Register a sharing backend class that implements OCP\Share_Backend for an item type
+ * @param string Item type
+ * @param string Backend class
+ * @param string (optional) Depends on item type
+ * @param array (optional) List of supported file extensions if this item type depends on files
+ * @return Returns true if backend is registered or false if error
+ */
+ public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
+ if (self::isEnabled()) {
+ if (!isset(self::$backendTypes[$itemType])) {
+ self::$backendTypes[$itemType] = array(
+ 'class' => $class,
+ 'collectionOf' => $collectionOf,
+ 'supportedFileExtensions' => $supportedFileExtensions
+ );
+ if(count(self::$backendTypes) === 1) {
+ \OC_Util::addScript('core', 'share');
+ \OC_Util::addStyle('core', 'share');
+ }
+ return true;
+ }
+ \OC_Log::write('OCP\Share',
+ 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
+ .' is already registered for '.$itemType,
+ \OC_Log::WARN);
+ }
+ return false;
+ }
+
+ /**
+ * Check if the Share API is enabled
+ * @return Returns true if enabled or false
+ *
+ * The Share API is enabled by default if not configured
+ */
+ public static function isEnabled() {
+ if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Find which users can access a shared item
+ * @param $path to the file
+ * @param $user owner of the file
+ * @param include owner to the list of users with access to the file
+ * @return array
+ * @note $path needs to be relative to user data dir, e.g. 'file.txt'
+ * not '/admin/data/file.txt'
+ */
+ public static function getUsersSharingFile($path, $user, $includeOwner = false) {
+
+ $shares = array();
+ $publicShare = false;
+ $source = -1;
+ $cache = false;
+
+ $view = new \OC\Files\View('/' . $user . '/files');
+ if ($view->file_exists($path)) {
+ $meta = $view->getFileInfo($path);
+ } else {
+ // if the file doesn't exists yet we start with the parent folder
+ $meta = $view->getFileInfo(dirname($path));
+ }
+
+ if($meta !== false) {
+ $source = $meta['fileid'];
+ $cache = new \OC\Files\Cache\Cache($meta['storage']);
+ }
+
+ while ($source !== -1) {
+
+ // Fetch all shares with another user
+ $query = \OC_DB::prepare(
+ 'SELECT `share_with`
+ FROM
+ `*PREFIX*share`
+ WHERE
+ `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
+ );
+
+ $result = $query->execute(array($source, self::SHARE_TYPE_USER));
+
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
+ } else {
+ while ($row = $result->fetchRow()) {
+ $shares[] = $row['share_with'];
+ }
+ }
+ // We also need to take group shares into account
+
+ $query = \OC_DB::prepare(
+ 'SELECT `share_with`
+ FROM
+ `*PREFIX*share`
+ WHERE
+ `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
+ );
+
+ $result = $query->execute(array($source, self::SHARE_TYPE_GROUP));
+
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
+ } else {
+ while ($row = $result->fetchRow()) {
+ $usersInGroup = \OC_Group::usersInGroup($row['share_with']);
+ $shares = array_merge($shares, $usersInGroup);
+ }
+ }
+
+ //check for public link shares
+ if (!$publicShare) {
+ $query = \OC_DB::prepare(
+ 'SELECT `share_with`
+ FROM
+ `*PREFIX*share`
+ WHERE
+ `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
+ );
+
+ $result = $query->execute(array($source, self::SHARE_TYPE_LINK));
+
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
+ } else {
+ if ($result->fetchRow()) {
+ $publicShare = true;
+ }
+ }
+ }
+
+ // let's get the parent for the next round
+ $meta = $cache->get((int)$source);
+ if($meta !== false) {
+ $source = (int)$meta['parent'];
+ } else {
+ $source = -1;
+ }
+ }
+ // Include owner in list of users, if requested
+ if ($includeOwner) {
+ $shares[] = $user;
+ }
+
+ return array("users" => array_unique($shares), "public" => $publicShare);
+ }
+
+ /**
+ * Get the items of item type shared with the current user
+ * @param string Item type
+ * @param int Format (optional) Format type must be defined by the backend
+ * @param mixed Parameters (optional)
+ * @param int Number of items to return (optional) Returns all by default
+ * @param bool include collections (optional)
+ * @return Return depends on format
+ */
+ public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE,
+ $parameters = null, $limit = -1, $includeCollections = false) {
+ return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
+ $parameters, $limit, $includeCollections);
+ }
+
+ /**
+ * Get the item of item type shared with the current user
+ * @param string $itemType
+ * @param string $itemTarget
+ * @param int $format (optional) Format type must be defined by the backend
+ * @param mixed Parameters (optional)
+ * @param bool include collections (optional)
+ * @return Return depends on format
+ */
+ public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
+ $parameters = null, $includeCollections = false) {
+ return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
+ $parameters, 1, $includeCollections);
+ }
+
+ /**
+ * Get the item of item type shared with a given user by source
+ * @param string $itemType
+ * @param string $itemSource
+ * @param string $user User user to whom the item was shared
+ * @return array Return list of items with file_target, permissions and expiration
+ */
+ public static function getItemSharedWithUser($itemType, $itemSource, $user) {
+
+ $shares = array();
+
+ // first check if there is a db entry for the specific user
+ $query = \OC_DB::prepare(
+ 'SELECT `file_target`, `permissions`, `expiration`
+ FROM
+ `*PREFIX*share`
+ WHERE
+ `item_source` = ? AND `item_type` = ? AND `share_with` = ?'
+ );
+
+ $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, $user));
+
+ while ($row = $result->fetchRow()) {
+ $shares[] = $row;
+ }
+
+ //if didn't found a result than let's look for a group share.
+ if(empty($shares)) {
+ $groups = \OC_Group::getUserGroups($user);
+
+ $query = \OC_DB::prepare(
+ 'SELECT `file_target`, `permissions`, `expiration`
+ FROM
+ `*PREFIX*share`
+ WHERE
+ `item_source` = ? AND `item_type` = ? AND `share_with` in (?)'
+ );
+
+ $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, implode(',', $groups)));
+
+ while ($row = $result->fetchRow()) {
+ $shares[] = $row;
+ }
+ }
+
+ return $shares;
+
+ }
+
+ /**
+ * Get the item of item type shared with the current user by source
+ * @param string Item type
+ * @param string Item source
+ * @param int Format (optional) Format type must be defined by the backend
+ * @param mixed Parameters
+ * @param bool include collections
+ * @return Return depends on format
+ */
+ public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
+ $parameters = null, $includeCollections = false) {
+ return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
+ $parameters, 1, $includeCollections, true);
+ }
+
+ /**
+ * Get the item of item type shared by a link
+ * @param string Item type
+ * @param string Item source
+ * @param string Owner of link
+ * @return Item
+ */
+ public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
+ return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE,
+ null, 1);
+ }
+
+ /**
+ * Based on the given token the share information will be returned - password protected shares will be verified
+ * @param string $token
+ * @return array | bool false will be returned in case the token is unknown or unauthorized
+ */
+ public static function getShareByToken($token, $checkPasswordProtection = true) {
+ $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
+ $result = $query->execute(array($token));
+ if (\OC_DB::isError($result)) {
+ \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::ERROR);
+ }
+ $row = $result->fetchRow();
+ if ($row === false) {
+ return false;
+ }
+ if (is_array($row) and self::expireItem($row)) {
+ return false;
+ }
+
+ // password protected shares need to be authenticated
+ if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) {
+ return false;
+ }
+
+ return $row;
+ }
+
+ /**
+ * resolves reshares down to the last real share
+ * @param $linkItem
+ * @return $fileOwner
+ */
+ public static function resolveReShare($linkItem)
+ {
+ if (isset($linkItem['parent'])) {
+ $parent = $linkItem['parent'];
+ while (isset($parent)) {
+ $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1);
+ $item = $query->execute(array($parent))->fetchRow();
+ if (isset($item['parent'])) {
+ $parent = $item['parent'];
+ } else {
+ return $item;
+ }
+ }
+ }
+ return $linkItem;
+ }
+
+
+ /**
+ * Get the shared items of item type owned by the current user
+ * @param string Item type
+ * @param int Format (optional) Format type must be defined by the backend
+ * @param mixed Parameters
+ * @param int Number of items to return (optional) Returns all by default
+ * @param bool include collections
+ * @return Return depends on format
+ */
+ public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
+ $limit = -1, $includeCollections = false) {
+ return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format,
+ $parameters, $limit, $includeCollections);
+ }
+
+ /**
+ * Get the shared item of item type owned by the current user
+ * @param string Item type
+ * @param string Item source
+ * @param int Format (optional) Format type must be defined by the backend
+ * @param mixed Parameters
+ * @param bool include collections
+ * @return Return depends on format
+ */
+ public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
+ $parameters = null, $includeCollections = false) {
+ return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
+ $parameters, -1, $includeCollections);
+ }
+
+ /**
+ * Get all users an item is shared with
+ * @param string Item type
+ * @param string Item source
+ * @param string Owner
+ * @param bool Include collections
+ * @praram bool check expire date
+ * @return Return array of users
+ */
+ public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) {
+
+ $users = array();
+ $items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate);
+ if ($items) {
+ foreach ($items as $item) {
+ if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
+ $users[] = $item['share_with'];
+ } else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
+ $users = array_merge($users, \OC_Group::usersInGroup($item['share_with']));
+ }
+ }
+ }
+ return $users;
+ }
+
+ /**
+ * Share an item with a user, group, or via private link
+ * @param string $itemType
+ * @param string $itemSource
+ * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
+ * @param string $shareWith User or group the item is being shared with
+ * @param int $permissions CRUDS
+ * @param null $itemSourceName
+ * @throws \Exception
+ * @internal param \OCP\Item $string type
+ * @internal param \OCP\Item $string source
+ * @internal param \OCP\SHARE_TYPE_USER $int , SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
+ * @internal param \OCP\User $string or group the item is being shared with
+ * @internal param \OCP\CRUDS $int permissions
+ * @return bool|string Returns true on success or false on failure, Returns token on success for links
+ */
+ public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null) {
+ $uidOwner = \OC_User::getUser();
+ $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global');
+
+ if (is_null($itemSourceName)) {
+ $itemSourceName = $itemSource;
+ }
+
+ // verify that the file exists before we try to share it
+ if ($itemType === 'file' or $itemType === 'folder') {
+ $path = \OC\Files\Filesystem::getPath($itemSource);
+ if (!$path) {
+ $message = 'Sharing ' . $itemSourceName . ' failed, because the file does not exist';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ }
+
+ // Verify share type and sharing conditions are met
+ if ($shareType === self::SHARE_TYPE_USER) {
+ if ($shareWith == $uidOwner) {
+ $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' is the item owner';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ if (!\OC_User::userExists($shareWith)) {
+ $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' does not exist';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ if ($sharingPolicy == 'groups_only') {
+ $inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith));
+ if (empty($inGroup)) {
+ $message = 'Sharing '.$itemSourceName.' failed, because the user '
+ .$shareWith.' is not a member of any groups that '.$uidOwner.' is a member of';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ }
+ // Check if the item source is already shared with the user, either from the same owner or a different user
+ if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
+ $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
+ // Only allow the same share to occur again if it is the same
+ // owner and is not a user share, this use case is for increasing
+ // permissions for a specific user
+ if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
+ $message = 'Sharing '.$itemSourceName.' failed, because this item is already shared with '.$shareWith;
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ }
+ } else if ($shareType === self::SHARE_TYPE_GROUP) {
+ if (!\OC_Group::groupExists($shareWith)) {
+ $message = 'Sharing '.$itemSourceName.' failed, because the group '.$shareWith.' does not exist';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ if ($sharingPolicy == 'groups_only' && !\OC_Group::inGroup($uidOwner, $shareWith)) {
+ $message = 'Sharing '.$itemSourceName.' failed, because '
+ .$uidOwner.' is not a member of the group '.$shareWith;
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ // Check if the item source is already shared with the group, either from the same owner or a different user
+ // The check for each user in the group is done inside the put() function
+ if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith,
+ null, self::FORMAT_NONE, null, 1, true, true)) {
+ // Only allow the same share to occur again if it is the same
+ // owner and is not a group share, this use case is for increasing
+ // permissions for a specific user
+ if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
+ $message = 'Sharing '.$itemSourceName.' failed, because this item is already shared with '.$shareWith;
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ }
+ // Convert share with into an array with the keys group and users
+ $group = $shareWith;
+ $shareWith = array();
+ $shareWith['group'] = $group;
+ $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner));
+ } else if ($shareType === self::SHARE_TYPE_LINK) {
+ if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
+ // when updating a link share
+ if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null,
+ $uidOwner, self::FORMAT_NONE, null, 1)) {
+ // remember old token
+ $oldToken = $checkExists['token'];
+ $oldPermissions = $checkExists['permissions'];
+ //delete the old share
+ Helper::delete($checkExists['id']);
+ }
+
+ // Generate hash of password - same method as user passwords
+ if (isset($shareWith)) {
+ $forcePortable = (CRYPT_BLOWFISH != 1);
+ $hasher = new \PasswordHash(8, $forcePortable);
+ $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', ''));
+ } else {
+ // reuse the already set password, but only if we change permissions
+ // otherwise the user disabled the password protection
+ if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
+ $shareWith = $checkExists['share_with'];
+ }
+ }
+
+ // Generate token
+ if (isset($oldToken)) {
+ $token = $oldToken;
+ } else {
+ $token = \OC_Util::generateRandomBytes(self::TOKEN_LENGTH);
+ }
+ $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions,
+ null, $token, $itemSourceName);
+ if ($result) {
+ return $token;
+ } else {
+ return false;
+ }
+ }
+ $message = 'Sharing '.$itemSourceName.' failed, because sharing with links is not allowed';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ return false;
+ } else {
+ // Future share types need to include their own conditions
+ $message = 'Share type '.$shareType.' is not valid for '.$itemSource;
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ // Put the item into the database
+ return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName);
+ }
+
+ /**
+ * Unshare an item from a user, group, or delete a private link
+ * @param string Item type
+ * @param string Item source
+ * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
+ * @param string User or group the item is being shared with
+ * @return Returns true on success or false on failure
+ */
+ public static function unshare($itemType, $itemSource, $shareType, $shareWith) {
+ $item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(),self::FORMAT_NONE, null, 1);
+ if (!empty($item)) {
+ self::unshareItem($item);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Unshare an item from all users, groups, and remove all links
+ * @param string Item type
+ * @param string Item source
+ * @return Returns true on success or false on failure
+ */
+ public static function unshareAll($itemType, $itemSource) {
+ // Get all of the owners of shares of this item.
+ $query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' );
+ $result = $query->execute(array($itemType, $itemSource));
+ $shares = array();
+ // Add each owner's shares to the array of all shares for this item.
+ while ($row = $result->fetchRow()) {
+ $shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner']));
+ }
+ if (!empty($shares)) {
+ // Pass all the vars we have for now, they may be useful
+ $hookParams = array(
+ 'itemType' => $itemType,
+ 'itemSource' => $itemSource,
+ 'shares' => $shares,
+ );
+ \OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams);
+ foreach ($shares as $share) {
+ self::unshareItem($share);
+ }
+ \OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Unshare an item shared with the current user
+ * @param string Item type
+ * @param string Item target
+ * @return Returns true on success or false on failure
+ *
+ * Unsharing from self is not allowed for items inside collections
+ */
+ public static function unshareFromSelf($itemType, $itemTarget) {
+ $item = self::getItemSharedWith($itemType, $itemTarget);
+ if (!empty($item)) {
+ if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
+ // Insert an extra row for the group share and set permission
+ // to 0 to prevent it from showing up for the user
+ $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`'
+ .' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,'
+ .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)'
+ .' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
+ $query->execute(array($item['item_type'], $item['item_source'], $item['item_target'],
+ $item['id'], self::$shareTypeGroupUserUnique,
+ \OC_User::getUser(), $item['uid_owner'], 0, $item['stime'], $item['file_source'],
+ $item['file_target']));
+ \OC_DB::insertid('*PREFIX*share');
+ // Delete all reshares by this user of the group share
+ Helper::delete($item['id'], true, \OC_User::getUser());
+ } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
+ // Set permission to 0 to prevent it from showing up for the user
+ $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
+ $query->execute(array(0, $item['id']));
+ Helper::delete($item['id'], true);
+ } else {
+ Helper::delete($item['id']);
+ }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * sent status if users got informed by mail about share
+ * @param string $itemType
+ * @param string $itemSource
+ * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
+ * @param bool $status
+ */
+ public static function setSendMailStatus($itemType, $itemSource, $shareType, $status) {
+ $status = $status ? 1 : 0;
+
+ $query = \OC_DB::prepare(
+ 'UPDATE `*PREFIX*share`
+ SET `mail_send` = ?
+ WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ?');
+
+ $result = $query->execute(array($status, $itemType, $itemSource, $shareType));
+
+ if($result === false) {
+ \OC_Log::write('OCP\Share', 'Couldn\'t set send mail status', \OC_Log::ERROR);
+ }
+ }
+
+ /**
+ * Set the permissions of an item for a specific user or group
+ * @param string Item type
+ * @param string Item source
+ * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
+ * @param string User or group the item is being shared with
+ * @param int CRUDS permissions
+ * @return Returns true on success or false on failure
+ */
+ public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
+ if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith,
+ \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
+ // Check if this item is a reshare and verify that the permissions
+ // granted don't exceed the parent shared item
+ if (isset($item['parent'])) {
+ $query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*share` WHERE `id` = ?', 1);
+ $result = $query->execute(array($item['parent']))->fetchRow();
+ if (~(int)$result['permissions'] & $permissions) {
+ $message = 'Setting permissions for '.$itemSource.' failed,'
+ .' because the permissions exceed permissions granted to '.\OC_User::getUser();
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ }
+ $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
+ $query->execute(array($permissions, $item['id']));
+ if ($itemType === 'file' || $itemType === 'folder') {
+ \OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
+ 'itemType' => $itemType,
+ 'itemSource' => $itemSource,
+ 'shareType' => $shareType,
+ 'shareWith' => $shareWith,
+ 'uidOwner' => \OC_User::getUser(),
+ 'permissions' => $permissions,
+ 'path' => $item['path'],
+ ));
+ }
+ // Check if permissions were removed
+ if ($item['permissions'] & ~$permissions) {
+ // If share permission is removed all reshares must be deleted
+ if (($item['permissions'] & \OCP\PERMISSION_SHARE) && (~$permissions & \OCP\PERMISSION_SHARE)) {
+ Helper::delete($item['id'], true);
+ } else {
+ $ids = array();
+ $parents = array($item['id']);
+ while (!empty($parents)) {
+ $parents = "'".implode("','", $parents)."'";
+ $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`'
+ .' WHERE `parent` IN ('.$parents.')');
+ $result = $query->execute();
+ // Reset parents array, only go through loop again if
+ // items are found that need permissions removed
+ $parents = array();
+ while ($item = $result->fetchRow()) {
+ // Check if permissions need to be removed
+ if ($item['permissions'] & ~$permissions) {
+ // Add to list of items that need permissions removed
+ $ids[] = $item['id'];
+ $parents[] = $item['id'];
+ }
+ }
+ }
+ // Remove the permissions for all reshares of this item
+ if (!empty($ids)) {
+ $ids = "'".implode("','", $ids)."'";
+ // TODO this should be done with Doctrine platform objects
+ if (\OC_Config::getValue( "dbtype") === 'oci') {
+ $andOp = 'BITAND(`permissions`, ?)';
+ } else {
+ $andOp = '`permissions` & ?';
+ }
+ $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
+ .' WHERE `id` IN ('.$ids.')');
+ $query->execute(array($permissions));
+ }
+ }
+ }
+ return true;
+ }
+ $message = 'Setting permissions for '.$itemSource.' failed, because the item was not found';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+
+ /**
+ * Set expiration date for a share
+ * @param string $itemType
+ * @param string $itemSource
+ * @param string $date expiration date
+ * @return \OCP\Share_Backend
+ */
+ public static function setExpirationDate($itemType, $itemSource, $date) {
+ $user = \OC_User::getUser();
+ $items = self::getItems($itemType, $itemSource, null, null, $user, self::FORMAT_NONE, null, -1, false);
+ if (!empty($items)) {
+ if ($date == '') {
+ $date = null;
+ } else {
+ $date = new \DateTime($date);
+ }
+ $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `id` = ?');
+ $query->bindValue(1, $date, 'datetime');
+ foreach ($items as $item) {
+ $query->bindValue(2, (int) $item['id']);
+ $query->execute();
+ \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array(
+ 'itemType' => $itemType,
+ 'itemSource' => $itemSource,
+ 'date' => $date,
+ 'uidOwner' => $user
+ ));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether a share has expired, calls unshareItem() if yes.
+ * @param array $item Share data (usually database row)
+ * @return bool True if item was expired, false otherwise.
+ */
+ protected static function expireItem(array $item) {
+ if (!empty($item['expiration'])) {
+ $now = new \DateTime();
+ $expires = new \DateTime($item['expiration']);
+ if ($now > $expires) {
+ self::unshareItem($item);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Unshares a share given a share data array
+ * @param array $item Share data (usually database row)
+ * @return null
+ */
+ protected static function unshareItem(array $item) {
+ // Pass all the vars we have for now, they may be useful
+ $hookParams = array(
+ 'itemType' => $item['item_type'],
+ 'itemSource' => $item['item_source'],
+ 'shareType' => $item['share_type'],
+ 'shareWith' => $item['share_with'],
+ 'itemParent' => $item['parent'],
+ 'uidOwner' => $item['uid_owner'],
+ );
+
+ \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams + array(
+ 'fileSource' => $item['file_source'],
+ ));
+ Helper::delete($item['id']);
+ \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
+ }
+
+ /**
+ * Get the backend class for the specified item type
+ * @param string $itemType
+ * @return \OCP\Share_Backend
+ */
+ public static function getBackend($itemType) {
+ if (isset(self::$backends[$itemType])) {
+ return self::$backends[$itemType];
+ } else if (isset(self::$backendTypes[$itemType]['class'])) {
+ $class = self::$backendTypes[$itemType]['class'];
+ if (class_exists($class)) {
+ self::$backends[$itemType] = new $class;
+ if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
+ $message = 'Sharing backend '.$class.' must implement the interface OCP\Share_Backend';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ return self::$backends[$itemType];
+ } else {
+ $message = 'Sharing backend '.$class.' not found';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ }
+ $message = 'Sharing backend for '.$itemType.' not found';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+
+ /**
+ * Check if resharing is allowed
+ * @return Returns true if allowed or false
+ *
+ * Resharing is allowed by default if not configured
+ */
+ private static function isResharingAllowed() {
+ if (!isset(self::$isResharingAllowed)) {
+ if (\OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
+ self::$isResharingAllowed = true;
+ } else {
+ self::$isResharingAllowed = false;
+ }
+ }
+ return self::$isResharingAllowed;
+ }
+
+ /**
+ * Get a list of collection item types for the specified item type
+ * @param string Item type
+ * @return array
+ */
+ private static function getCollectionItemTypes($itemType) {
+ $collectionTypes = array($itemType);
+ foreach (self::$backendTypes as $type => $backend) {
+ if (in_array($backend['collectionOf'], $collectionTypes)) {
+ $collectionTypes[] = $type;
+ }
+ }
+ // TODO Add option for collections to be collection of themselves, only 'folder' does it now...
+ if (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder') {
+ unset($collectionTypes[0]);
+ }
+ // Return array if collections were found or the item type is a
+ // collection itself - collections can be inside collections
+ if (count($collectionTypes) > 0) {
+ return $collectionTypes;
+ }
+ return false;
+ }
+
+ /**
+ * Get shared items from the database
+ * @param string Item type
+ * @param string Item source or target (optional)
+ * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
+ * @param string User or group the item is being shared with
+ * @param string User that is the owner of shared items (optional)
+ * @param int Format to convert items to with formatItems()
+ * @param mixed Parameters to pass to formatItems()
+ * @param int Number of items to return, -1 to return all matches (optional)
+ * @param bool Include collection item types (optional)
+ * @param bool TODO (optional)
+ * @prams bool check expire date
+ * @return array
+ *
+ * See public functions getItem(s)... for parameter usage
+ *
+ */
+ public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
+ $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
+ $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate = true) {
+ if (!self::isEnabled()) {
+ return array();
+ }
+ $backend = self::getBackend($itemType);
+ $collectionTypes = false;
+ // Get filesystem root to add it to the file target and remove from the
+ // file source, match file_source with the file cache
+ if ($itemType == 'file' || $itemType == 'folder') {
+ if(!is_null($uidOwner)) {
+ $root = \OC\Files\Filesystem::getRoot();
+ } else {
+ $root = '';
+ }
+ $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid`';
+ if (!isset($item)) {
+ $where .= ' WHERE `file_target` IS NOT NULL';
+ }
+ $fileDependent = true;
+ $queryArgs = array();
+ } else {
+ $fileDependent = false;
+ $root = '';
+ $collectionTypes = self::getCollectionItemTypes($itemType);
+ if ($includeCollections && !isset($item) && $collectionTypes) {
+ // If includeCollections is true, find collections of this item type, e.g. a music album contains songs
+ if (!in_array($itemType, $collectionTypes)) {
+ $itemTypes = array_merge(array($itemType), $collectionTypes);
+ } else {
+ $itemTypes = $collectionTypes;
+ }
+ $placeholders = join(',', array_fill(0, count($itemTypes), '?'));
+ $where = ' WHERE `item_type` IN ('.$placeholders.'))';
+ $queryArgs = $itemTypes;
+ } else {
+ $where = ' WHERE `item_type` = ?';
+ $queryArgs = array($itemType);
+ }
+ }
+ if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
+ $where .= ' AND `share_type` != ?';
+ $queryArgs[] = self::SHARE_TYPE_LINK;
+ }
+ if (isset($shareType)) {
+ // Include all user and group items
+ if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
+ $where .= ' AND `share_type` IN (?,?,?)';
+ $queryArgs[] = self::SHARE_TYPE_USER;
+ $queryArgs[] = self::SHARE_TYPE_GROUP;
+ $queryArgs[] = self::$shareTypeGroupUserUnique;
+ $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith));
+ $placeholders = join(',', array_fill(0, count($userAndGroups), '?'));
+ $where .= ' AND `share_with` IN ('.$placeholders.')';
+ $queryArgs = array_merge($queryArgs, $userAndGroups);
+ // Don't include own group shares
+ $where .= ' AND `uid_owner` != ?';
+ $queryArgs[] = $shareWith;
+ } else {
+ $where .= ' AND `share_type` = ?';
+ $queryArgs[] = $shareType;
+ if (isset($shareWith)) {
+ $where .= ' AND `share_with` = ?';
+ $queryArgs[] = $shareWith;
+ }
+ }
+ }
+ if (isset($uidOwner)) {
+ $where .= ' AND `uid_owner` = ?';
+ $queryArgs[] = $uidOwner;
+ if (!isset($shareType)) {
+ // Prevent unique user targets for group shares from being selected
+ $where .= ' AND `share_type` != ?';
+ $queryArgs[] = self::$shareTypeGroupUserUnique;
+ }
+ if ($fileDependent) {
+ $column = 'file_source';
+ } else {
+ $column = 'item_source';
+ }
+ } else {
+ if ($fileDependent) {
+ $column = 'file_target';
+ } else {
+ $column = 'item_target';
+ }
+ }
+ if (isset($item)) {
+ $collectionTypes = self::getCollectionItemTypes($itemType);
+ if ($includeCollections && $collectionTypes) {
+ $where .= ' AND (';
+ } else {
+ $where .= ' AND';
+ }
+ // If looking for own shared items, check item_source else check item_target
+ if (isset($uidOwner) || $itemShareWithBySource) {
+ // If item type is a file, file source needs to be checked in case the item was converted
+ if ($fileDependent) {
+ $where .= ' `file_source` = ?';
+ $column = 'file_source';
+ } else {
+ $where .= ' `item_source` = ?';
+ $column = 'item_source';
+ }
+ } else {
+ if ($fileDependent) {
+ $where .= ' `file_target` = ?';
+ $item = \OC\Files\Filesystem::normalizePath($item);
+ } else {
+ $where .= ' `item_target` = ?';
+ }
+ }
+ $queryArgs[] = $item;
+ if ($includeCollections && $collectionTypes) {
+ $placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
+ $where .= ' OR `item_type` IN ('.$placeholders.'))';
+ $queryArgs = array_merge($queryArgs, $collectionTypes);
+ }
+ }
+ if ($limit != -1 && !$includeCollections) {
+ if ($shareType == self::$shareTypeUserAndGroups) {
+ // Make sure the unique user target is returned if it exists,
+ // unique targets should follow the group share in the database
+ // If the limit is not 1, the filtering can be done later
+ $where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
+ }
+ // The limit must be at least 3, because filtering needs to be done
+ if ($limit < 3) {
+ $queryLimit = 3;
+ } else {
+ $queryLimit = $limit;
+ }
+ } else {
+ $queryLimit = null;
+ }
+ $select = self::createSelectStatement($format, $fileDependent, $uidOwner);
+ $root = strlen($root);
+ $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
+ $result = $query->execute($queryArgs);
+ if (\OC_DB::isError($result)) {
+ \OC_Log::write('OCP\Share',
+ \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where,
+ \OC_Log::ERROR);
+ }
+ $items = array();
+ $targets = array();
+ $switchedItems = array();
+ $mounts = array();
+ while ($row = $result->fetchRow()) {
+ self::transformDBResults($row);
+ // Filter out duplicate group shares for users with unique targets
+ if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
+ $row['share_type'] = self::SHARE_TYPE_GROUP;
+ $row['share_with'] = $items[$row['parent']]['share_with'];
+ // Remove the parent group share
+ unset($items[$row['parent']]);
+ if ($row['permissions'] == 0) {
+ continue;
+ }
+ } else if (!isset($uidOwner)) {
+ // Check if the same target already exists
+ if (isset($targets[$row[$column]])) {
+ // Check if the same owner shared with the user twice
+ // through a group and user share - this is allowed
+ $id = $targets[$row[$column]];
+ if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
+ // Switch to group share type to ensure resharing conditions aren't bypassed
+ if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
+ $items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
+ $items[$id]['share_with'] = $row['share_with'];
+ }
+ // Switch ids if sharing permission is granted on only
+ // one share to ensure correct parent is used if resharing
+ if (~(int)$items[$id]['permissions'] & \OCP\PERMISSION_SHARE
+ && (int)$row['permissions'] & \OCP\PERMISSION_SHARE) {
+ $items[$row['id']] = $items[$id];
+ $switchedItems[$id] = $row['id'];
+ unset($items[$id]);
+ $id = $row['id'];
+ }
+ // Combine the permissions for the item
+ $items[$id]['permissions'] |= (int)$row['permissions'];
+ continue;
+ }
+ } else {
+ $targets[$row[$column]] = $row['id'];
+ }
+ }
+ // Remove root from file source paths if retrieving own shared items
+ if (isset($uidOwner) && isset($row['path'])) {
+ if (isset($row['parent'])) {
+ // FIXME: Doesn't always construct the correct path, example:
+ // Folder '/a/b', share '/a' and '/a/b' to user2
+ // user2 reshares /Shared/b and ask for share status of /Shared/a/b
+ // expected result: path=/Shared/a/b; actual result /Shared/b because of the parent
+ $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
+ $parentResult = $query->execute(array($row['parent']));
+ if (\OC_DB::isError($result)) {
+ \OC_Log::write('OCP\Share', 'Can\'t select parent: ' .
+ \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where,
+ \OC_Log::ERROR);
+ } else {
+ $parentRow = $parentResult->fetchRow();
+ $tmpPath = '/Shared' . $parentRow['file_target'];
+ // find the right position where the row path continues from the target path
+ $pos = strrpos($row['path'], $parentRow['file_target']);
+ $subPath = substr($row['path'], $pos);
+ $splitPath = explode('/', $subPath);
+ foreach (array_slice($splitPath, 2) as $pathPart) {
+ $tmpPath = $tmpPath . '/' . $pathPart;
+ }
+ $row['path'] = $tmpPath;
+ }
+ } else {
+ if (!isset($mounts[$row['storage']])) {
+ $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
+ if (is_array($mountPoints)) {
+ $mounts[$row['storage']] = current($mountPoints);
+ }
+ }
+ if ($mounts[$row['storage']]) {
+ $path = $mounts[$row['storage']]->getMountPoint().$row['path'];
+ $row['path'] = substr($path, $root);
+ }
+ }
+ }
+ if($checkExpireDate) {
+ if (self::expireItem($row)) {
+ continue;
+ }
+ }
+ // Check if resharing is allowed, if not remove share permission
+ if (isset($row['permissions']) && !self::isResharingAllowed()) {
+ $row['permissions'] &= ~\OCP\PERMISSION_SHARE;
+ }
+ // Add display names to result
+ if ( isset($row['share_with']) && $row['share_with'] != '') {
+ $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
+ }
+ if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
+ $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
+ }
+
+ $items[$row['id']] = $row;
+ }
+ if (!empty($items)) {
+ $collectionItems = array();
+ foreach ($items as &$row) {
+ // Return only the item instead of a 2-dimensional array
+ if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
+ if ($format == self::FORMAT_NONE) {
+ return $row;
+ } else {
+ break;
+ }
+ }
+ // Check if this is a collection of the requested item type
+ if ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
+ if (($collectionBackend = self::getBackend($row['item_type']))
+ && $collectionBackend instanceof \OCP\Share_Backend_Collection) {
+ // Collections can be inside collections, check if the item is a collection
+ if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
+ $collectionItems[] = $row;
+ } else {
+ $collection = array();
+ $collection['item_type'] = $row['item_type'];
+ if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
+ $collection['path'] = basename($row['path']);
+ }
+ $row['collection'] = $collection;
+ // Fetch all of the children sources
+ $children = $collectionBackend->getChildren($row[$column]);
+ foreach ($children as $child) {
+ $childItem = $row;
+ $childItem['item_type'] = $itemType;
+ if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
+ $childItem['item_source'] = $child['source'];
+ $childItem['item_target'] = $child['target'];
+ }
+ if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
+ if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
+ $childItem['file_source'] = $child['source'];
+ } else { // TODO is this really needed if we already know that we use the file backend?
+ $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
+ $childItem['file_source'] = $meta['fileid'];
+ }
+ $childItem['file_target'] =
+ \OC\Files\Filesystem::normalizePath($child['file_path']);
+ }
+ if (isset($item)) {
+ if ($childItem[$column] == $item) {
+ // Return only the item instead of a 2-dimensional array
+ if ($limit == 1) {
+ if ($format == self::FORMAT_NONE) {
+ return $childItem;
+ } else {
+ // Unset the items array and break out of both loops
+ $items = array();
+ $items[] = $childItem;
+ break 2;
+ }
+ } else {
+ $collectionItems[] = $childItem;
+ }
+ }
+ } else {
+ $collectionItems[] = $childItem;
+ }
+ }
+ }
+ }
+ // Remove collection item
+ $toRemove = $row['id'];
+ if (array_key_exists($toRemove, $switchedItems)) {
+ $toRemove = $switchedItems[$toRemove];
+ }
+ unset($items[$toRemove]);
+ }
+ }
+ if (!empty($collectionItems)) {
+ $items = array_merge($items, $collectionItems);
+ }
+
+ return self::formatResult($items, $column, $backend, $format, $parameters);
+ }
+
+ return array();
+ }
+
+ /**
+ * Put shared item into the database
+ * @param string Item type
+ * @param string Item source
+ * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
+ * @param string User or group the item is being shared with
+ * @param string User that is the owner of shared item
+ * @param int CRUDS permissions
+ * @param bool|array Parent folder target (optional)
+ * @param string token (optional)
+ * @param string name of the source item (optional)
+ * @return bool Returns true on success or false on failure
+ */
+ private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
+ $permissions, $parentFolder = null, $token = null, $itemSourceName = null) {
+ $backend = self::getBackend($itemType);
+
+ // Check if this is a reshare
+ if ($checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true)) {
+
+ // Check if attempting to share back to owner
+ if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
+ $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' is the original sharer';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ // Check if share permissions is granted
+ if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\PERMISSION_SHARE) {
+ if (~(int)$checkReshare['permissions'] & $permissions) {
+ $message = 'Sharing '.$itemSourceName
+ .' failed, because the permissions exceed permissions granted to '.$uidOwner;
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ } else {
+ // TODO Don't check if inside folder
+ $parent = $checkReshare['id'];
+ $itemSource = $checkReshare['item_source'];
+ $fileSource = $checkReshare['file_source'];
+ $suggestedItemTarget = $checkReshare['item_target'];
+ $suggestedFileTarget = $checkReshare['file_target'];
+ $filePath = $checkReshare['file_target'];
+ }
+ } else {
+ $message = 'Sharing '.$itemSourceName.' failed, because resharing is not allowed';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ } else {
+ $parent = null;
+ $suggestedItemTarget = null;
+ $suggestedFileTarget = null;
+ if (!$backend->isValidSource($itemSource, $uidOwner)) {
+ $message = 'Sharing '.$itemSource.' failed, because the sharing backend for '
+ .$itemType.' could not find its source';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
+ $filePath = $backend->getFilePath($itemSource, $uidOwner);
+ if ($itemType == 'file' || $itemType == 'folder') {
+ $fileSource = $itemSource;
+ } else {
+ $meta = \OC\Files\Filesystem::getFileInfo($filePath);
+ $fileSource = $meta['fileid'];
+ }
+ if ($fileSource == -1) {
+ $message = 'Sharing '.$itemSource.' failed, because the file could not be found in the file cache';
+ \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
+ throw new \Exception($message);
+ }
+ } else {
+ $filePath = null;
+ $fileSource = null;
+ }
+ }
+ $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,'
+ .' `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
+ .' `file_target`, `token`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)');
+ // Share with a group
+ if ($shareType == self::SHARE_TYPE_GROUP) {
+ $groupItemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'],
+ $uidOwner, $suggestedItemTarget);
+ $run = true;
+ $error = '';
+ \OC_Hook::emit('OCP\Share', 'pre_shared', array(
+ 'itemType' => $itemType,
+ 'itemSource' => $itemSource,
+ 'itemTarget' => $groupItemTarget,
+ 'shareType' => $shareType,
+ 'shareWith' => $shareWith['group'],
+ 'uidOwner' => $uidOwner,
+ 'permissions' => $permissions,
+ 'fileSource' => $fileSource,
+ 'token' => $token,
+ 'run' => &$run,
+ 'error' => &$error
+ ));
+
+ if ($run === false) {
+ throw new \Exception($error);
+ }
+
+ if (isset($fileSource)) {
+ if ($parentFolder) {
+ if ($parentFolder === true) {
+ $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType,
+ $shareWith['group'], $uidOwner, $suggestedFileTarget);
+ // Set group default file target for future use
+ $parentFolders[0]['folder'] = $groupFileTarget;
+ } else {
+ // Get group default file target
+ $groupFileTarget = $parentFolder[0]['folder'].$itemSource;
+ $parent = $parentFolder[0]['id'];
+ }
+ } else {
+ $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith['group'],
+ $uidOwner, $suggestedFileTarget);
+ }
+ } else {
+ $groupFileTarget = null;
+ }
+ $query->execute(array($itemType, $itemSource, $groupItemTarget, $parent, $shareType,
+ $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget, $token));
+ // Save this id, any extra rows for this group share will need to reference it
+ $parent = \OC_DB::insertid('*PREFIX*share');
+ // Loop through all users of this group in case we need to add an extra row
+ foreach ($shareWith['users'] as $uid) {
+ $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid,
+ $uidOwner, $suggestedItemTarget, $parent);
+ if (isset($fileSource)) {
+ if ($parentFolder) {
+ if ($parentFolder === true) {
+ $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid,
+ $uidOwner, $suggestedFileTarget, $parent);
+ if ($fileTarget != $groupFileTarget) {
+ $parentFolders[$uid]['folder'] = $fileTarget;
+ }
+ } else if (isset($parentFolder[$uid])) {
+ $fileTarget = $parentFolder[$uid]['folder'].$itemSource;
+ $parent = $parentFolder[$uid]['id'];
+ }
+ } else {
+ $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER,
+ $uid, $uidOwner, $suggestedFileTarget, $parent);
+ }
+ } else {
+ $fileTarget = null;
+ }
+ // Insert an extra row for the group share if the item or file target is unique for this user
+ if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) {
+ $query->execute(array($itemType, $itemSource, $itemTarget, $parent,
+ self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(),
+ $fileSource, $fileTarget, $token));
+ $id = \OC_DB::insertid('*PREFIX*share');
+ }
+ }
+ \OC_Hook::emit('OCP\Share', 'post_shared', array(
+ 'itemType' => $itemType,
+ 'itemSource' => $itemSource,
+ 'itemTarget' => $groupItemTarget,
+ 'parent' => $parent,
+ 'shareType' => $shareType,
+ 'shareWith' => $shareWith['group'],
+ 'uidOwner' => $uidOwner,
+ 'permissions' => $permissions,
+ 'fileSource' => $fileSource,
+ 'fileTarget' => $groupFileTarget,
+ 'id' => $parent,
+ 'token' => $token
+ ));
+
+ if ($parentFolder === true) {
+ // Return parent folders to preserve file target paths for potential children
+ return $parentFolders;
+ }
+ } else {
+ $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
+ $suggestedItemTarget);
+ $run = true;
+ $error = '';
+ \OC_Hook::emit('OCP\Share', 'pre_shared', array(
+ 'itemType' => $itemType,
+ 'itemSource' => $itemSource,
+ 'itemTarget' => $itemTarget,
+ 'shareType' => $shareType,
+ 'shareWith' => $shareWith,
+ 'uidOwner' => $uidOwner,
+ 'permissions' => $permissions,
+ 'fileSource' => $fileSource,
+ 'token' => $token,
+ 'run' => &$run,
+ 'error' => &$error
+ ));
+
+ if ($run === false) {
+ throw new \Exception($error);
+ }
+
+ if (isset($fileSource)) {
+ if ($parentFolder) {
+ if ($parentFolder === true) {
+ $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith,
+ $uidOwner, $suggestedFileTarget);
+ $parentFolders['folder'] = $fileTarget;
+ } else {
+ $fileTarget = $parentFolder['folder'].$itemSource;
+ $parent = $parentFolder['id'];
+ }
+ } else {
+ $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner,
+ $suggestedFileTarget);
+ }
+ } else {
+ $fileTarget = null;
+ }
+ $query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner,
+ $permissions, time(), $fileSource, $fileTarget, $token));
+ $id = \OC_DB::insertid('*PREFIX*share');
+ \OC_Hook::emit('OCP\Share', 'post_shared', array(
+ 'itemType' => $itemType,
+ 'itemSource' => $itemSource,
+ 'itemTarget' => $itemTarget,
+ 'parent' => $parent,
+ 'shareType' => $shareType,
+ 'shareWith' => $shareWith,
+ 'uidOwner' => $uidOwner,
+ 'permissions' => $permissions,
+ 'fileSource' => $fileSource,
+ 'fileTarget' => $fileTarget,
+ 'id' => $id,
+ 'token' => $token
+ ));
+ if ($parentFolder === true) {
+ $parentFolders['id'] = $id;
+ // Return parent folder to preserve file target paths for potential children
+ return $parentFolders;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Delete all shares with type SHARE_TYPE_LINK
+ */
+ public static function removeAllLinkShares() {
+ // Delete any link shares
+ $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?');
+ $result = $query->execute(array(self::SHARE_TYPE_LINK));
+ while ($item = $result->fetchRow()) {
+ Helper::delete($item['id']);
+ }
+ }
+
+ /**
+ * In case a password protected link is not yet authenticated this function will return false
+ *
+ * @param array $linkItem
+ * @return bool
+ */
+ public static function checkPasswordProtectedShare(array $linkItem) {
+ if (!isset($linkItem['share_with'])) {
+ return true;
+ }
+ if (!isset($linkItem['share_type'])) {
+ return true;
+ }
+ if (!isset($linkItem['id'])) {
+ return true;
+ }
+
+ if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) {
+ return true;
+ }
+
+ if ( \OC::$session->exists('public_link_authenticated')
+ && \OC::$session->get('public_link_authenticated') === $linkItem['id'] ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @breif construct select statement
+ * @param int $format
+ * @param bool $fileDependent ist it a file/folder share or a generla share
+ * @param string $uidOwner
+ * @return string select statement
+ */
+ private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
+ $select = '*';
+ if ($format == self::FORMAT_STATUSES) {
+ if ($fileDependent) {
+ $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, `share_with`, `uid_owner` , `file_source`';
+ } else {
+ $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`';
+ }
+ } else {
+ if (isset($uidOwner)) {
+ if ($fileDependent) {
+ $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
+ . ' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,'
+ . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`';
+ } else {
+ $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,'
+ . ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
+ }
+ } else {
+ if ($fileDependent) {
+ if ($format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT) {
+ $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
+ . '`share_type`, `share_with`, `file_source`, `path`, `file_target`, '
+ . '`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
+ . '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `unencrypted_size`, `encrypted`, `etag`, `mail_send`';
+ } else {
+ $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,
+ `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,
+ `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`, `mail_send`';
+ }
+ }
+ }
+ }
+ return $select;
+ }
+
+
+ /**
+ * @brief transform db results
+ * @param array $row result
+ */
+ private static function transformDBResults(&$row) {
+ if (isset($row['id'])) {
+ $row['id'] = (int) $row['id'];
+ }
+ if (isset($row['share_type'])) {
+ $row['share_type'] = (int) $row['share_type'];
+ }
+ if (isset($row['parent'])) {
+ $row['parent'] = (int) $row['parent'];
+ }
+ if (isset($row['file_parent'])) {
+ $row['file_parent'] = (int) $row['file_parent'];
+ }
+ if (isset($row['file_source'])) {
+ $row['file_source'] = (int) $row['file_source'];
+ }
+ if (isset($row['permissions'])) {
+ $row['permissions'] = (int) $row['permissions'];
+ }
+ if (isset($row['storage'])) {
+ $row['storage'] = (int) $row['storage'];
+ }
+ if (isset($row['stime'])) {
+ $row['stime'] = (int) $row['stime'];
+ }
+ }
+
+ /**
+ * @brief format result
+ * @param array $items result
+ * @prams string $column is it a file share or a general share ('file_target' or 'item_target')
+ * @params \OCP\Share_Backend $backend sharing backend
+ * @param int $format
+ * @param array additional format parameters
+ * @return array formate result
+ */
+ private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
+ if ($format === self::FORMAT_NONE) {
+ return $items;
+ } else if ($format === self::FORMAT_STATUSES) {
+ $statuses = array();
+ foreach ($items as $item) {
+ if ($item['share_type'] === self::SHARE_TYPE_LINK) {
+ $statuses[$item[$column]]['link'] = true;
+ } else if (!isset($statuses[$item[$column]])) {
+ $statuses[$item[$column]]['link'] = false;
+ }
+ if ('file_target') {
+ $statuses[$item[$column]]['path'] = $item['path'];
+ }
+ }
+ return $statuses;
+ } else {
+ return $backend->formatItems($items, $format, $parameters);
+ }
+ }
+}
diff --git a/lib/private/template/base.php b/lib/private/template/base.php
index 7aa0cb4a956..3d7c685c1cf 100644
--- a/lib/private/template/base.php
+++ b/lib/private/template/base.php
@@ -77,7 +77,7 @@ class Base {
/**
* @brief Appends a variable
* @param string $key key
- * @param string $value value
+ * @param mixed $value value
* @return boolean|null
*
* This function assigns a variable in an array context. If the key already
diff --git a/lib/private/template/functions.php b/lib/private/template/functions.php
index a72d41f72da..3c42d441efa 100644
--- a/lib/private/template/functions.php
+++ b/lib/private/template/functions.php
@@ -7,16 +7,17 @@
*/
/**
- * Prints an XSS escaped string
- * @param string $string the string which will be escaped and printed
+ * Prints a sanitized string
+ * @param string|array $string the string which will be escaped and printed
*/
function p($string) {
print(OC_Util::sanitizeHTML($string));
}
/**
- * Prints an unescaped string
- * @param string $string the string which will be printed as it is
+ * Prints an unsanitized string - usage of this function may result into XSS.
+ * Consider using p() instead.
+ * @param string|array $string the string which will be printed as it is
*/
function print_unescaped($string) {
print($string);
diff --git a/lib/private/urlgenerator.php b/lib/private/urlgenerator.php
index 44b46ef6700..260eeb15108 100644
--- a/lib/private/urlgenerator.php
+++ b/lib/private/urlgenerator.php
@@ -60,7 +60,7 @@ class URLGenerator implements IURLGenerator {
$app_path = \OC_App::getAppPath($app);
// Check if the app is in the app folder
if ($app_path && file_exists($app_path . '/' . $file)) {
- if (substr($file, -3) == 'php' || substr($file, -3) == 'css') {
+ if (substr($file, -3) == 'php') {
$urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $app;
if ($frontControllerActive) {
@@ -148,6 +148,12 @@ class URLGenerator implements IURLGenerator {
*/
public function getAbsoluteURL($url) {
$separator = $url[0] === '/' ? '' : '/';
- return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . $separator . $url;
+
+ // The ownCloud web root can already be prepended.
+ $webRoot = substr($url, 0, strlen(\OC::$WEBROOT)) === \OC::$WEBROOT
+ ? ''
+ : \OC::$WEBROOT;
+
+ return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost(). $webRoot . $separator . $url;
}
}
diff --git a/lib/private/user.php b/lib/private/user.php
index a89b7286c10..dc4c7ec3b61 100644
--- a/lib/private/user.php
+++ b/lib/private/user.php
@@ -321,8 +321,6 @@ class OC_User {
*/
public static function isLoggedIn() {
if (\OC::$session->get('user_id') && self::$incognitoMode === false) {
- OC_App::loadApps(array('authentication'));
- self::setupBackends();
return self::userExists(\OC::$session->get('user_id'));
}
return false;
diff --git a/lib/private/user/manager.php b/lib/private/user/manager.php
index 8583a451f2d..a2ad9d17702 100644
--- a/lib/private/user/manager.php
+++ b/lib/private/user/manager.php
@@ -239,24 +239,25 @@ class Manager extends PublicEmitter {
* @return bool | \OC\User\User the created user of false
*/
public function createUser($uid, $password) {
+ $l = \OC_L10N::get('lib');
// Check the name for bad characters
// Allowed are: "a-z", "A-Z", "0-9" and "_.@-"
if (preg_match('/[^a-zA-Z0-9 _\.@\-]/', $uid)) {
- throw new \Exception('Only the following characters are allowed in a username:'
- . ' "a-z", "A-Z", "0-9", and "_.@-"');
+ throw new \Exception($l->t('Only the following characters are allowed in a username:'
+ . ' "a-z", "A-Z", "0-9", and "_.@-"'));
}
// No empty username
if (trim($uid) == '') {
- throw new \Exception('A valid username must be provided');
+ throw new \Exception($l->t('A valid username must be provided'));
}
// No empty password
if (trim($password) == '') {
- throw new \Exception('A valid password must be provided');
+ throw new \Exception($l->t('A valid password must be provided'));
}
// Check if user already exists
if ($this->userExists($uid)) {
- throw new \Exception('The username is already being used');
+ throw new \Exception($l->t('The username is already being used'));
}
$this->emit('\OC\User', 'preCreateUser', array($uid, $password));
diff --git a/lib/private/util.php b/lib/private/util.php
index 87e173fa765..e20de308e87 100755
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -30,9 +30,7 @@ class OC_Util {
}
// load all filesystem apps before, so no setup-hook gets lost
- if(!isset($RUNTIME_NOAPPS) || !$RUNTIME_NOAPPS) {
- OC_App::loadApps(array('filesystem'));
- }
+ OC_App::loadApps(array('filesystem'));
// the filesystem will finish when $user is not empty,
// mark fs setup here to avoid doing the setup from loading
@@ -703,17 +701,18 @@ class OC_Util {
* @return void
*/
public static function redirectToDefaultPage() {
+ $urlGenerator = \OC::$server->getURLGenerator();
if(isset($_REQUEST['redirect_url'])) {
- $location = OC_Helper::makeURLAbsolute(urldecode($_REQUEST['redirect_url']));
+ $location = urldecode($_REQUEST['redirect_url']);
}
else if (isset(OC::$REQUESTEDAPP) && !empty(OC::$REQUESTEDAPP)) {
- $location = OC_Helper::linkToAbsolute( OC::$REQUESTEDAPP, 'index.php' );
+ $location = $urlGenerator->getAbsoluteURL('/index.php/apps/'.OC::$REQUESTEDAPP.'/index.php');
} else {
$defaultPage = OC_Appconfig::getValue('core', 'defaultpage');
if ($defaultPage) {
- $location = OC_Helper::makeURLAbsolute(OC::$WEBROOT.'/'.$defaultPage);
+ $location = $urlGenerator->getAbsoluteURL($defaultPage);
} else {
- $location = OC_Helper::linkToAbsolute( 'files', 'index.php' );
+ $location = $urlGenerator->getAbsoluteURL('/index.php/files/index.php');
}
}
OC_Log::write('core', 'redirectToDefaultPage: '.$location, OC_Log::DEBUG);
@@ -806,7 +805,7 @@ class OC_Util {
array_walk_recursive($value, 'OC_Util::sanitizeHTML');
} else {
//Specify encoding for PHP<5.4
- $value = htmlentities((string)$value, ENT_QUOTES, 'UTF-8');
+ $value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}
return $value;
}
@@ -903,6 +902,8 @@ class OC_Util {
// for this self test we don't care if the ssl certificate is self signed and the peer cannot be verified.
$client->setVerifyPeer(false);
+ // also don't care if the host can't be verified
+ $client->setVerifyHost(0);
$return = true;
try {
@@ -1074,13 +1075,13 @@ class OC_Util {
public static function getUrlContent($url) {
if (function_exists('curl_init')) {
$curl = curl_init();
+ $max_redirects = 10;
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($curl, CURLOPT_MAXREDIRS, 10);
+
curl_setopt($curl, CURLOPT_USERAGENT, "ownCloud Server Crawler");
if(OC_Config::getValue('proxy', '') != '') {
@@ -1089,9 +1090,50 @@ class OC_Util {
if(OC_Config::getValue('proxyuserpwd', '') != '') {
curl_setopt($curl, CURLOPT_PROXYUSERPWD, OC_Config::getValue('proxyuserpwd'));
}
- $data = curl_exec($curl);
+
+ if (ini_get('open_basedir') === '' && ini_get('safe_mode' === 'Off')) {
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_MAXREDIRS, $max_redirects);
+ $data = curl_exec($curl);
+ } else {
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
+ $mr = $max_redirects;
+ if ($mr > 0) {
+ $newurl = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
+
+ $rcurl = curl_copy_handle($curl);
+ curl_setopt($rcurl, CURLOPT_HEADER, true);
+ curl_setopt($rcurl, CURLOPT_NOBODY, true);
+ curl_setopt($rcurl, CURLOPT_FORBID_REUSE, false);
+ curl_setopt($rcurl, CURLOPT_RETURNTRANSFER, true);
+ do {
+ curl_setopt($rcurl, CURLOPT_URL, $newurl);
+ $header = curl_exec($rcurl);
+ if (curl_errno($rcurl)) {
+ $code = 0;
+ } else {
+ $code = curl_getinfo($rcurl, CURLINFO_HTTP_CODE);
+ if ($code == 301 || $code == 302) {
+ preg_match('/Location:(.*?)\n/', $header, $matches);
+ $newurl = trim(array_pop($matches));
+ } else {
+ $code = 0;
+ }
+ }
+ } while ($code && --$mr);
+ curl_close($rcurl);
+ if ($mr > 0) {
+ curl_setopt($curl, CURLOPT_URL, $newurl);
+ }
+ }
+
+ if($mr == 0 && $max_redirects > 0) {
+ $data = false;
+ } else {
+ $data = curl_exec($curl);
+ }
+ }
curl_close($curl);
-
} else {
$contextArray = null;