summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
authorBart Visscher <bartv@thisnet.nl>2014-03-31 21:38:54 +0200
committerBart Visscher <bartv@thisnet.nl>2014-03-31 21:38:54 +0200
commit6b061c236dd5730837b567f2c39a19af1617d33c (patch)
tree27de0d46deae2987ea398f2f2aee4e26786c6f48 /lib/private
parent8951328a87c16e5ebfe4d3e5c392347db1e54f92 (diff)
parentab696edba685cd6d2a64c2e48907f03197aae53f (diff)
downloadnextcloud-server-6b061c236dd5730837b567f2c39a19af1617d33c.tar.gz
nextcloud-server-6b061c236dd5730837b567f2c39a19af1617d33c.zip
Merge branch 'master' into type-hinting
Conflicts: lib/private/image.php lib/private/l10n.php lib/private/request.php lib/private/share/mailnotifications.php lib/private/template/base.php
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/api.php60
-rw-r--r--lib/private/app.php2
-rw-r--r--lib/private/appframework/routing/routeconfig.php7
-rw-r--r--lib/private/cache/file.php15
-rw-r--r--lib/private/config.php2
-rw-r--r--lib/private/connector/sabre/auth.php16
-rw-r--r--lib/private/connector/sabre/directory.php4
-rw-r--r--lib/private/connector/sabre/objecttree.php3
-rw-r--r--lib/private/db/mdb2schemareader.php2
-rw-r--r--lib/private/db/statementwrapper.php14
-rw-r--r--lib/private/defaults.php14
-rw-r--r--lib/private/eventsource.php3
-rw-r--r--lib/private/files.php3
-rw-r--r--lib/private/files/cache/cache.php14
-rw-r--r--lib/private/files/cache/homecache.php24
-rw-r--r--lib/private/files/cache/legacy.php139
-rw-r--r--lib/private/files/cache/scanner.php10
-rw-r--r--lib/private/files/cache/updater.php9
-rw-r--r--lib/private/files/cache/upgrade.php235
-rw-r--r--lib/private/files/fileinfo.php16
-rw-r--r--lib/private/files/filesystem.php89
-rw-r--r--lib/private/files/storage/common.php27
-rw-r--r--lib/private/files/storage/local.php4
-rw-r--r--lib/private/files/storage/mappedlocal.php4
-rw-r--r--lib/private/files/storage/wrapper/quota.php21
-rw-r--r--lib/private/files/view.php25
-rw-r--r--lib/private/forbiddenexception.php16
-rw-r--r--lib/private/helper.php49
-rw-r--r--lib/private/image.php188
-rw-r--r--lib/private/installer.php2
-rw-r--r--lib/private/l10n.php13
-rw-r--r--lib/private/mail.php24
-rw-r--r--lib/private/mimetypes.list.php2
-rw-r--r--lib/private/ocs/cloud.php16
-rw-r--r--lib/private/ocsclient.php16
-rwxr-xr-xlib/private/preview.php301
-rw-r--r--lib/private/preview/movies.php2
-rw-r--r--lib/private/preview/office-cl.php4
-rw-r--r--lib/private/preview/office.php35
-rw-r--r--lib/private/preview/pdf.php49
-rw-r--r--lib/private/preview/svg.php58
-rwxr-xr-xlib/private/request.php80
-rw-r--r--lib/private/response.php4
-rw-r--r--lib/private/route/cachingrouter.php43
-rw-r--r--lib/private/route/route.php (renamed from lib/private/route.php)12
-rw-r--r--lib/private/route/router.php233
-rw-r--r--lib/private/router.php185
-rw-r--r--lib/private/server.php23
-rw-r--r--lib/private/session/internal.php14
-rw-r--r--lib/private/session/memory.php20
-rw-r--r--lib/private/session/session.php12
-rw-r--r--lib/private/setup.php6
-rw-r--r--lib/private/setup/postgresql.php20
-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.php23
-rw-r--r--lib/private/share/share.php1619
-rw-r--r--lib/private/template/base.php2
-rw-r--r--lib/private/updater.php54
-rw-r--r--lib/private/urlgenerator.php7
-rw-r--r--lib/private/user.php2
-rwxr-xr-xlib/private/util.php142
63 files changed, 3277 insertions, 1115 deletions
diff --git a/lib/private/api.php b/lib/private/api.php
index 3f96196e6df..b3b5eb1067b 100644
--- a/lib/private/api.php
+++ b/lib/private/api.php
@@ -65,8 +65,8 @@ class OC_API {
$name = strtolower($method).$url;
$name = str_replace(array('/', '{', '}'), '_', $name);
if(!isset(self::$actions[$name])) {
- OC::getRouter()->useCollection('ocs');
- OC::getRouter()->create($name, $url)
+ OC::$server->getRouter()->useCollection('ocs');
+ OC::$server->getRouter()->create($name, $url)
->method($method)
->defaults($defaults)
->requirements($requirements)
@@ -116,9 +116,7 @@ class OC_API {
);
}
$response = self::mergeResponses($responses);
- $formats = array('json', 'xml');
-
- $format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml';
+ $format = self::requestedFormat();
if (self::$logoutRequired) {
OC_User::logout();
}
@@ -270,6 +268,18 @@ class OC_API {
* @return string|false (username, or false on failure)
*/
private static function loginUser(){
+
+ // reuse existing login
+ $loggedIn = OC_User::isLoggedIn();
+ $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false;
+ if ($loggedIn === true && $ocsApiRequest) {
+
+ // initialize the user's filesystem
+ \OC_Util::setUpFS(\OC_User::getUser());
+
+ return OC_User::getUser();
+ }
+
// basic auth
$authUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '';
$authPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
@@ -283,17 +293,6 @@ class OC_API {
return $authUser;
}
- // reuse existing login
- $loggedIn = OC_User::isLoggedIn();
- $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false;
- if ($loggedIn === true && $ocsApiRequest) {
-
- // initialize the user's filesystem
- \OC_Util::setUpFS(\OC_User::getUser());
-
- return OC_User::getUser();
- }
-
return false;
}
@@ -349,4 +348,33 @@ class OC_API {
}
}
+ /**
+ * @return string
+ */
+ public static function requestedFormat() {
+ $formats = array('json', 'xml');
+
+ $format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml';
+ return $format;
+ }
+
+ /**
+ * Based on the requested format the response content type is set
+ */
+ public static function setContentType() {
+ $format = \OC_API::requestedFormat();
+ if ($format === 'xml') {
+ header('Content-type: text/xml; charset=UTF-8');
+ return;
+ }
+
+ if ($format === 'json') {
+ header('Content-Type: application/json; charset=utf-8');
+ return;
+ }
+
+ header('Content-Type: application/octet-stream; charset=utf-8');
+ }
+
+
}
diff --git a/lib/private/app.php b/lib/private/app.php
index 048d4d4aeb1..58bf67c1d47 100644
--- a/lib/private/app.php
+++ b/lib/private/app.php
@@ -219,6 +219,8 @@ class OC_App{
$appdata=OC_OCSClient::getApplication($app);
$download=OC_OCSClient::getApplicationDownload($app, 1);
if(isset($download['downloadlink']) and $download['downloadlink']!='') {
+ // Replace spaces in download link without encoding entire URL
+ $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
$info = array('source'=>'http', 'href'=>$download['downloadlink'], 'appdata'=>$appdata);
$app=OC_Installer::installApp($info);
}
diff --git a/lib/private/appframework/routing/routeconfig.php b/lib/private/appframework/routing/routeconfig.php
index 716358444a2..35bee75cc4d 100644
--- a/lib/private/appframework/routing/routeconfig.php
+++ b/lib/private/appframework/routing/routeconfig.php
@@ -23,6 +23,7 @@
namespace OC\AppFramework\routing;
use OC\AppFramework\DependencyInjection\DIContainer;
+use OCP\Route\IRouter;
/**
* Class RouteConfig
@@ -36,10 +37,10 @@ class RouteConfig {
/**
* @param \OC\AppFramework\DependencyInjection\DIContainer $container
- * @param \OC_Router $router
+ * @param \OCP\Route\IRouter $router
* @internal param $appName
*/
- public function __construct(DIContainer $container, \OC_Router $router, $routes) {
+ public function __construct(DIContainer $container, IRouter $router, $routes) {
$this->routes = $routes;
$this->container = $container;
$this->router = $router;
@@ -47,7 +48,7 @@ class RouteConfig {
}
/**
- * The routes and resource will be registered to the \OC_Router
+ * The routes and resource will be registered to the \OCP\Route\IRouter
*/
public function register() {
diff --git a/lib/private/cache/file.php b/lib/private/cache/file.php
index 8a6ef39f61b..be6805a9a57 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');
}
}
diff --git a/lib/private/config.php b/lib/private/config.php
index 3649da84973..56f47256134 100644
--- a/lib/private/config.php
+++ b/lib/private/config.php
@@ -77,7 +77,7 @@ class Config {
/**
* @brief Gets a value from config.php
* @param string $key key
- * @param string|null $default = null default value
+ * @param array|bool|string|null $default = null default value
* @return string the value or $default
*
* This function gets the value from config.php. If it does not exist,
diff --git a/lib/private/connector/sabre/auth.php b/lib/private/connector/sabre/auth.php
index 0c84fa6b757..5577273df8c 100644
--- a/lib/private/connector/sabre/auth.php
+++ b/lib/private/connector/sabre/auth.php
@@ -73,6 +73,20 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic {
*/
public function authenticate(Sabre_DAV_Server $server, $realm) {
+ $result = $this->auth($server, $realm);
+
+ // close the session - right after authentication there is not need to write to the session any more
+ \OC::$session->close();
+
+ return $result;
+ }
+
+ /**
+ * @param Sabre_DAV_Server $server
+ * @param $realm
+ * @return bool
+ */
+ private function auth(Sabre_DAV_Server $server, $realm) {
if (OC_User::handleApacheAuth() || OC_User::isLoggedIn()) {
$user = OC_User::getUser();
OC_Util::setupFS($user);
@@ -81,5 +95,5 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic {
}
return parent::authenticate($server, $realm);
- }
+ }
}
diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php
index 02d1a9f4ba2..3ed9e94d69b 100644
--- a/lib/private/connector/sabre/directory.php
+++ b/lib/private/connector/sabre/directory.php
@@ -50,7 +50,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
*/
public function createFile($name, $data = null) {
- if ($name === 'Shared' && empty($this->path)) {
+ if (strtolower($name) === 'shared' && empty($this->path)) {
throw new \Sabre_DAV_Exception_Forbidden();
}
@@ -86,7 +86,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
*/
public function createDirectory($name) {
- if ($name === 'Shared' && empty($this->path)) {
+ if (strtolower($name) === 'shared' && empty($this->path)) {
throw new \Sabre_DAV_Exception_Forbidden();
}
diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php
index d2fa425b22c..accf020daa2 100644
--- a/lib/private/connector/sabre/objecttree.php
+++ b/lib/private/connector/sabre/objecttree.php
@@ -94,6 +94,9 @@ class ObjectTree extends \Sabre_DAV_ObjectTree {
}
if ($sourceDir !== $destinationDir) {
// for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir
+ if (ltrim($destinationDir, '/') === '' && strtolower($sourceNode->getName()) === 'shared') {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
if (!$fs->isUpdatable($sourceDir)) {
throw new \Sabre_DAV_Exception_Forbidden();
}
diff --git a/lib/private/db/mdb2schemareader.php b/lib/private/db/mdb2schemareader.php
index f9a76786c3e..1c16d03eab2 100644
--- a/lib/private/db/mdb2schemareader.php
+++ b/lib/private/db/mdb2schemareader.php
@@ -41,7 +41,9 @@ class MDB2SchemaReader {
*/
public function loadSchemaFromFile($file) {
$schema = new \Doctrine\DBAL\Schema\Schema();
+ $loadEntities = libxml_disable_entity_loader(false);
$xml = simplexml_load_file($file);
+ libxml_disable_entity_loader($loadEntities);
foreach ($xml->children() as $child) {
/**
* @var \SimpleXMLElement $child
diff --git a/lib/private/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 59630cda5c0..79be211b82f 100644
--- a/lib/private/defaults.php
+++ b/lib/private/defaults.php
@@ -21,6 +21,7 @@ class OC_Defaults {
private $defaultDocBaseUrl;
private $defaultSlogan;
private $defaultLogoClaim;
+ private $defaultMailHeaderColor;
function __construct() {
$this->l = OC_L10N::get('core');
@@ -33,6 +34,7 @@ class OC_Defaults {
$this->defaultDocBaseUrl = "http://doc.owncloud.org";
$this->defaultSlogan = $this->l->t("web services under your control");
$this->defaultLogoClaim = "";
+ $this->defaultMailHeaderColor = "#1d2d44"; /* header color of mail notifications */
if (class_exists("OC_Theme")) {
$this->theme = new OC_Theme();
@@ -181,4 +183,16 @@ class OC_Defaults {
return $this->getDocBaseUrl() . '/server/6.0/go.php?to=' . $key;
}
+ /**
+ * Returns mail header color
+ * @return mail header color
+ */
+ public function getMailHeaderColor() {
+ if ($this->themeExist('getMailHeaderColor')) {
+ return $this->theme->getMailHeaderColor();
+ } else {
+ return $this->defaultMailHeaderColor;
+ }
+ }
+
}
diff --git a/lib/private/eventsource.php b/lib/private/eventsource.php
index 4df0bc2e7cd..5a41ddd8b37 100644
--- a/lib/private/eventsource.php
+++ b/lib/private/eventsource.php
@@ -63,8 +63,9 @@ class OC_EventSource{
$type=null;
}
if($this->fallback) {
+ $fallBackId = OC_Util::sanitizeHTML($this->fallBackId);
$response='<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
- .$this->fallBackId.',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL;
+ .$fallBackId.',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL;
echo $response;
}else{
if($type) {
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 9b18257088c..abc11e76470 100644
--- a/lib/private/files/cache/cache.php
+++ b/lib/private/files/cache/cache.php
@@ -498,9 +498,10 @@ class Cache {
* update the folder size and the size of all parent folders
*
* @param string|boolean $path
+ * @param array $data (optional) meta data of the folder
*/
- public function correctFolderSize($path) {
- $this->calculateFolderSize($path);
+ public function correctFolderSize($path, $data = null) {
+ $this->calculateFolderSize($path, $data);
if ($path !== '') {
$parent = dirname($path);
if ($parent === '.' or $parent === '/') {
@@ -514,11 +515,14 @@ class Cache {
* get the size of a folder and set it in the cache
*
* @param string $path
+ * @param array $entry (optional) meta data of the folder
* @return int
*/
- public function calculateFolderSize($path) {
+ public function calculateFolderSize($path, $entry = null) {
$totalSize = 0;
- $entry = $this->get($path);
+ if (is_null($entry) or !isset($entry['fileid'])) {
+ $entry = $this->get($path);
+ }
if ($entry && $entry['mimetype'] === 'httpd/unix-directory') {
$id = $entry['fileid'];
$sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2, ' .
@@ -540,7 +544,7 @@ class Cache {
if ($entry['size'] !== $totalSize) {
$update['size'] = $totalSize;
}
- if ($entry['unencrypted_size'] !== $unencryptedSum) {
+ if (!isset($entry['unencrypted_size']) or $entry['unencrypted_size'] !== $unencryptedSum) {
$update['unencrypted_size'] = $unencryptedSum;
}
if (count($update) > 0) {
diff --git a/lib/private/files/cache/homecache.php b/lib/private/files/cache/homecache.php
index a7c310a3782..2326c46e8d0 100644
--- a/lib/private/files/cache/homecache.php
+++ b/lib/private/files/cache/homecache.php
@@ -13,26 +13,37 @@ class HomeCache extends Cache {
* get the size of a folder and set it in the cache
*
* @param string $path
+ * @param array $entry (optional) meta data of the folder
* @return int
*/
- public function calculateFolderSize($path) {
- if ($path !== '/' and $path !== '' and $path !== 'files') {
- return parent::calculateFolderSize($path);
+ public function calculateFolderSize($path, $entry = null) {
+ if ($path !== '/' and $path !== '' and $path !== 'files' and $path !== 'files_trashbin') {
+ return parent::calculateFolderSize($path, $entry);
+ } elseif ($path === '' or $path === '/') {
+ // since the size of / isn't used (the size of /files is used instead) there is no use in calculating it
+ return 0;
}
$totalSize = 0;
- $entry = $this->get($path);
+ if (is_null($entry)) {
+ $entry = $this->get($path);
+ }
if ($entry && $entry['mimetype'] === 'httpd/unix-directory') {
$id = $entry['fileid'];
- $sql = 'SELECT SUM(`size`) FROM `*PREFIX*filecache` ' .
+ $sql = 'SELECT SUM(`size`) AS f1, ' .
+ 'SUM(`unencrypted_size`) AS f2 FROM `*PREFIX*filecache` ' .
'WHERE `parent` = ? AND `storage` = ? AND `size` >= 0';
$result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId()));
if ($row = $result->fetchRow()) {
- list($sum) = array_values($row);
+ list($sum, $unencryptedSum) = array_values($row);
$totalSize = (int)$sum;
+ $unencryptedSize = (int)$unencryptedSum;
if ($entry['size'] !== $totalSize) {
$this->update($id, array('size' => $totalSize));
}
+ if ($entry['unencrypted_size'] !== $unencryptedSize) {
+ $this->update($id, array('unencrypted_size' => $unencryptedSize));
+ }
}
}
return $totalSize;
@@ -40,6 +51,7 @@ class HomeCache extends Cache {
/**
* @param string $path
+ * @return array
*/
public function get($path) {
$data = parent::get($path);
diff --git a/lib/private/files/cache/legacy.php b/lib/private/files/cache/legacy.php
deleted file mode 100644
index 4d5f58741e9..00000000000
--- a/lib/private/files/cache/legacy.php
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-namespace OC\Files\Cache;
-
-/**
- * Provide read only support for the old filecache
- */
-class Legacy {
- private $user;
-
- private $cacheHasItems = null;
-
- /**
- * @param string $user
- */
- public function __construct($user) {
- $this->user = $user;
- }
-
- /**
- * get the numbers of items in the legacy cache
- *
- * @return int
- */
- function getCount() {
- $sql = 'SELECT COUNT(`id`) AS `count` FROM `*PREFIX*fscache` WHERE `user` = ?';
- $result = \OC_DB::executeAudited($sql, array($this->user));
- if ($row = $result->fetchRow()) {
- return $row['count'];
- } else {
- return 0;
- }
- }
-
- /**
- * check if a legacy cache is present and holds items
- *
- * @return bool
- */
- function hasItems() {
- if (!is_null($this->cacheHasItems)) {
- return $this->cacheHasItems;
- }
- try {
- $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*fscache` WHERE `user` = ?',1);
- } catch (\Exception $e) {
- $this->cacheHasItems = false;
- return false;
- }
- try {
- $result = $query->execute(array($this->user));
- } catch (\Exception $e) {
- $this->cacheHasItems = false;
- return false;
- }
-
- if ($result === false || property_exists($result, 'error_message_prefix')) {
- $this->cacheHasItems = false;
- return false;
- }
-
- $this->cacheHasItems = (bool)$result->fetchRow();
- return $this->cacheHasItems;
- }
-
- /**
- * get an item from the legacy cache
- *
- * @param string $path
- * @return array
- */
- function get($path) {
- if (is_numeric($path)) {
- $sql = 'SELECT * FROM `*PREFIX*fscache` WHERE `id` = ?';
- } else {
- $sql = 'SELECT * FROM `*PREFIX*fscache` WHERE `path` = ?';
- }
- $result = \OC_DB::executeAudited($sql, array($path));
- $data = $result->fetchRow();
- $data['etag'] = $this->getEtag($data['path'], $data['user']);
- return $data;
- }
-
- /**
- * Get the ETag for the given path
- *
- * @param type $path
- * @return string
- */
- function getEtag($path, $user = null) {
- static $query = null;
-
- $pathDetails = explode('/', $path, 4);
- if((!$user) && !isset($pathDetails[1])) {
- //no user!? Too odd, return empty string.
- return '';
- } else if(!$user) {
- //guess user from path, if no user passed.
- $user = $pathDetails[1];
- }
-
- if(!isset($pathDetails[3]) || is_null($pathDetails[3])) {
- $relativePath = '';
- } else {
- $relativePath = $pathDetails[3];
- }
-
- if(is_null($query)){
- $query = \OC_DB::prepare('SELECT `propertyvalue` FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = \'{DAV:}getetag\'');
- }
- $result = \OC_DB::executeAudited($query,array($user, '/' . $relativePath));
- if ($row = $result->fetchRow()) {
- return trim($row['propertyvalue'], '"');
- } else {
- return '';
- }
- }
-
- /**
- * get all child items of an item from the legacy cache
- *
- * @param int $id
- * @return array
- */
- function getChildren($id) {
- $result = \OC_DB::executeAudited('SELECT * FROM `*PREFIX*fscache` WHERE `parent` = ?', array($id));
- $data = $result->fetchAll();
- foreach ($data as $i => $item) {
- $data[$i]['etag'] = $this->getEtag($item['path'], $item['user']);
- }
- return $data;
- }
-}
diff --git a/lib/private/files/cache/scanner.php b/lib/private/files/cache/scanner.php
index 92a4c01841b..79159724d16 100644
--- a/lib/private/files/cache/scanner.php
+++ b/lib/private/files/cache/scanner.php
@@ -155,7 +155,7 @@ class Scanner extends BasicEmitter {
}
}
if (!empty($newData)) {
- $this->cache->put($file, $newData);
+ $data['fileid'] = $this->cache->put($file, $newData);
$this->emit('\OC\Files\Cache\Scanner', 'postScanFile', array($file, $this->storageId));
\OC_Hook::emit('\OC\Files\Cache\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId));
}
@@ -173,14 +173,16 @@ class Scanner extends BasicEmitter {
* @param string $path
* @param bool $recursive
* @param int $reuse
- * @return int the size of the scanned folder or -1 if the size is unknown at this stage
+ * @return array with the meta data of the scanned file or folder
*/
public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1) {
if ($reuse === -1) {
$reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : 0;
}
- $this->scanFile($path, $reuse);
- return $this->scanChildren($path, $recursive, $reuse);
+ $data = $this->scanFile($path, $reuse);
+ $size = $this->scanChildren($path, $recursive, $reuse);
+ $data['size'] = $size;
+ return $data;
}
/**
diff --git a/lib/private/files/cache/updater.php b/lib/private/files/cache/updater.php
index 7a45b9e9e96..199ce5dee78 100644
--- a/lib/private/files/cache/updater.php
+++ b/lib/private/files/cache/updater.php
@@ -38,8 +38,8 @@ class Updater {
if ($storage) {
$cache = $storage->getCache($internalPath);
$scanner = $storage->getScanner($internalPath);
- $scanner->scan($internalPath, Scanner::SCAN_SHALLOW);
- $cache->correctFolderSize($internalPath);
+ $data = $scanner->scan($internalPath, Scanner::SCAN_SHALLOW);
+ $cache->correctFolderSize($internalPath, $data);
self::correctFolder($path, $storage->filemtime($internalPath));
self::correctParentStorageMtime($storage, $internalPath);
}
@@ -119,6 +119,9 @@ class Updater {
if ($uid != \OCP\User::getUser()) {
$info = \OC\Files\Filesystem::getFileInfo($filename);
+ if (!$info) {
+ return array($uid, '/files/' . $filename);
+ }
$ownerView = new \OC\Files\View('/' . $uid . '/files');
$filename = $ownerView->getPath($info['fileid']);
}
@@ -150,7 +153,7 @@ class Updater {
$cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath)));
if ($realPath !== '') {
$realPath = dirname($realPath);
- if($realPath === DIRECTORY_SEPARATOR ) {
+ if ($realPath === DIRECTORY_SEPARATOR) {
$realPath = "";
}
// check storage for parent in case we change the storage in this step
diff --git a/lib/private/files/cache/upgrade.php b/lib/private/files/cache/upgrade.php
deleted file mode 100644
index e3a46896cbf..00000000000
--- a/lib/private/files/cache/upgrade.php
+++ /dev/null
@@ -1,235 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-namespace OC\Files\Cache;
-
-class Upgrade {
- /**
- * @var Legacy $legacy
- */
- private $legacy;
-
- private $numericIds = array();
-
- private $mimeTypeIds = array();
-
- /**
- * @param Legacy $legacy
- */
- public function __construct($legacy) {
- $this->legacy = $legacy;
- }
-
- /**
- * Preform a upgrade a path and it's childs
- *
- * @param string $path
- * @param bool $mode
- */
- function upgradePath($path, $mode = Scanner::SCAN_RECURSIVE) {
- if (!$this->legacy->hasItems()) {
- return;
- }
- \OC_Hook::emit('\OC\Files\Cache\Upgrade', 'migrate_path', $path);
- if ($row = $this->legacy->get($path)) {
- $data = $this->getNewData($row);
- if ($data) {
- $this->insert($data);
- $this->upgradeChilds($data['id'], $mode);
- }
- }
- }
-
- /**
- * upgrade all child elements of an item
- *
- * @param int $id
- * @param bool $mode
- */
- function upgradeChilds($id, $mode = Scanner::SCAN_RECURSIVE) {
- $children = $this->legacy->getChildren($id);
- foreach ($children as $child) {
- $childData = $this->getNewData($child);
- \OC_Hook::emit('\OC\Files\Cache\Upgrade', 'migrate_path', $child['path']);
- if ($childData) {
- $this->insert($childData);
- if ($mode == Scanner::SCAN_RECURSIVE) {
- $this->upgradeChilds($child['id']);
- }
- }
- }
- }
-
- /**
- * insert data into the new cache
- *
- * @param array $data the data for the new cache
- */
- function insert($data) {
- static $insertQuery = null;
- if(is_null($insertQuery)) {
- $insertQuery = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`
- ( `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` )
- VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
- }
- if (!$this->inCache($data['storage'], $data['path_hash'], $data['id'])) {
- \OC_DB::executeAudited($insertQuery, array($data['id'], $data['storage'],
- $data['path'], $data['path_hash'], $data['parent'], $data['name'],
- $data['mimetype'], $data['mimepart'], $data['size'], $data['mtime'], $data['encrypted'], $data['etag']));
- }
- }
-
- /**
- * check if an item is already in the new cache
- *
- * @param string $storage
- * @param string $pathHash
- * @param string $id
- * @return bool
- */
- function inCache($storage, $pathHash, $id) {
- static $query = null;
- if(is_null($query)) {
- $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE (`storage` = ? AND `path_hash` = ?) OR `fileid` = ?');
- }
- $result = \OC_DB::executeAudited($query, array($storage, $pathHash, $id));
- return (bool)$result->fetchRow();
- }
-
- /**
- * get the new data array from the old one
- *
- * @param array $data the data from the old cache
- * Example data array
- * Array
- * (
- * [id] => 418
- * [path] => /tina/files/picture.jpg //relative to datadir
- * [path_hash] => 66d4547e372888deed80b24fec9b192b
- * [parent] => 234
- * [name] => picture.jpg
- * [user] => tina
- * [size] => 1265283
- * [ctime] => 1363909709
- * [mtime] => 1363909709
- * [mimetype] => image/jpeg
- * [mimepart] => image
- * [encrypted] => 0
- * [versioned] => 0
- * [writable] => 1
- * )
- *
- * @return array
- */
- function getNewData($data) {
- //Make sure there is a path, otherwise we can do nothing.
- if(!isset($data['path'])) {
- return false;
- }
- $newData = $data;
- /**
- * @var \OC\Files\Storage\Storage $storage
- * @var string $internalPath;
- */
- list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($data['path']);
- if ($storage) {
- $newData['etag'] = $data['etag'];
- $newData['path_hash'] = md5($internalPath);
- $newData['path'] = $internalPath;
- $newData['storage'] = $this->getNumericId($storage);
- $newData['parent'] = ($internalPath === '') ? -1 : $data['parent'];
- $newData['permissions'] = ($data['writable']) ? \OCP\PERMISSION_ALL : \OCP\PERMISSION_READ;
- $newData['storage_object'] = $storage;
- $newData['mimetype'] = $this->getMimetypeId($newData['mimetype'], $storage);
- $newData['mimepart'] = $this->getMimetypeId($newData['mimepart'], $storage);
- return $newData;
- } else {
- \OC_Log::write('core', 'Unable to migrate data from old cache for '.$data['path'].' because the storage was not found', \OC_Log::ERROR);
- return false;
- }
- }
-
- /**
- * get the numeric storage id
- *
- * @param \OC\Files\Storage\Storage $storage
- * @return int
- */
- function getNumericId($storage) {
- $storageId = $storage->getId();
- if (!isset($this->numericIds[$storageId])) {
- $cache = $storage->getCache();
- $this->numericIds[$storageId] = $cache->getNumericStorageId();
- }
- return $this->numericIds[$storageId];
- }
-
- /**
- * get the numeric id for a mimetype
- *
- * @param string $mimetype
- * @param \OC\Files\Storage\Storage $storage
- * @return int
- */
- function getMimetypeId($mimetype, $storage) {
- if (!isset($this->mimeTypeIds[$mimetype])) {
- $cache = new Cache($storage);
- $this->mimeTypeIds[$mimetype] = $cache->getMimetypeId($mimetype);
- }
- return $this->mimeTypeIds[$mimetype];
- }
-
- /**
- * check if a cache upgrade is required for $user
- *
- * @param string $user
- * @return bool
- */
- static function needUpgrade($user) {
- $cacheVersion = (int)\OCP\Config::getUserValue($user, 'files', 'cache_version', 4);
- if ($cacheVersion < 5) {
- $legacy = new \OC\Files\Cache\Legacy($user);
- if ($legacy->hasItems()) {
- return true;
- }
- self::upgradeDone($user);
- }
-
- return false;
- }
-
- /**
- * mark the filecache as upgrade
- *
- * @param string $user
- */
- static function upgradeDone($user) {
- \OCP\Config::setUserValue($user, 'files', 'cache_version', 5);
- }
-
- /**
- * Does a "silent" upgrade, i.e. without an Event-Source as triggered
- * on User-Login via Ajax. This method is called within the regular
- * ownCloud upgrade.
- *
- * @param string $user a User ID
- */
- public static function doSilentUpgrade($user) {
- if(!self::needUpgrade($user)) {
- return;
- }
- $legacy = new \OC\Files\Cache\Legacy($user);
- if ($legacy->hasItems()) {
- \OC_DB::beginTransaction();
- $upgrade = new \OC\Files\Cache\Upgrade($legacy);
- $upgrade->upgradePath('/' . $user . '/files');
- \OC_DB::commit();
- }
- \OC\Files\Cache\Upgrade::upgradeDone($user);
- }
-}
diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php
index 2dbdd80a26b..d6940f50bf1 100644
--- a/lib/private/files/fileinfo.php
+++ b/lib/private/files/fileinfo.php
@@ -53,7 +53,13 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
}
public function offsetGet($offset) {
- return $this->data[$offset];
+ if ($offset === 'type') {
+ return $this->getType();
+ } elseif (isset($this->data[$offset])) {
+ return $this->data[$offset];
+ } else {
+ return null;
+ }
}
/**
@@ -144,10 +150,14 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
* @return \OCP\Files\FileInfo::TYPE_FILE | \OCP\Files\FileInfo::TYPE_FOLDER
*/
public function getType() {
- return $this->data['type'];
+ if (isset($this->data['type'])) {
+ return $this->data['type'];
+ } else {
+ return $this->getMimetype() === 'httpd/unix-directory' ? self::TYPE_FOLDER : self::TYPE_FILE;
+ }
}
- public function getData(){
+ public function getData() {
return $this->data;
}
diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php
index 7f7b6f7f468..7e27650c557 100644
--- a/lib/private/files/filesystem.php
+++ b/lib/private/files/filesystem.php
@@ -320,79 +320,34 @@ class Filesystem {
else {
self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user);
}
- $datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data");
- $mount_file = \OC_Config::getValue("mount_file", $datadir . "/mount.json");
-
- //move config file to it's new position
- if (is_file(\OC::$SERVERROOT . '/config/mount.json')) {
- rename(\OC::$SERVERROOT . '/config/mount.json', $mount_file);
- }
- // Load system mount points
- if (is_file(\OC::$SERVERROOT . '/config/mount.php') or is_file($mount_file)) {
- if (is_file($mount_file)) {
- $mountConfig = json_decode(file_get_contents($mount_file), true);
- } elseif (is_file(\OC::$SERVERROOT . '/config/mount.php')) {
- $mountConfig = $parser->parsePHP(file_get_contents(\OC::$SERVERROOT . '/config/mount.php'));
- }
- if (isset($mountConfig['global'])) {
- foreach ($mountConfig['global'] as $mountPoint => $options) {
- self::mount($options['class'], $options['options'], $mountPoint);
- }
- }
- if (isset($mountConfig['group'])) {
- foreach ($mountConfig['group'] as $group => $mounts) {
- if (\OC_Group::inGroup($user, $group)) {
- foreach ($mounts as $mountPoint => $options) {
- $mountPoint = self::setUserVars($user, $mountPoint);
- foreach ($options as &$option) {
- $option = self::setUserVars($user, $option);
- }
- self::mount($options['class'], $options['options'], $mountPoint);
- }
- }
- }
- }
- if (isset($mountConfig['user'])) {
- foreach ($mountConfig['user'] as $mountUser => $mounts) {
- if ($mountUser === 'all' or strtolower($mountUser) === strtolower($user)) {
- foreach ($mounts as $mountPoint => $options) {
- $mountPoint = self::setUserVars($user, $mountPoint);
- foreach ($options as &$option) {
- $option = self::setUserVars($user, $option);
- }
- self::mount($options['class'], $options['options'], $mountPoint);
- }
- }
- }
- }
- }
- // Load personal mount points
- if (is_file($root . '/mount.php') or is_file($root . '/mount.json')) {
- if (is_file($root . '/mount.json')) {
- $mountConfig = json_decode(file_get_contents($root . '/mount.json'), true);
- } elseif (is_file($root . '/mount.php')) {
- $mountConfig = $parser->parsePHP(file_get_contents($root . '/mount.php'));
- }
- if (isset($mountConfig['user'][$user])) {
- foreach ($mountConfig['user'][$user] as $mountPoint => $options) {
- self::mount($options['class'], $options['options'], $mountPoint);
- }
- }
- }
+
+ self::mountCacheDir($user);
// Chance to mount for other storages
\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user, 'user_dir' => $root));
}
/**
- * fill in the correct values for $user
- *
- * @param string $user
- * @param string $input
- * @return string
+ * Mounts the cache directory
+ * @param string $user user name
*/
- private static function setUserVars($user, $input) {
- return str_replace('$user', $user, $input);
+ 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');
+ }
}
/**
@@ -761,7 +716,7 @@ class Filesystem {
*
* @param string $directory path under datadirectory
* @param string $mimetype_filter limit returned content to this mimetype or mimepart
- * @return array
+ * @return \OC\Files\FileInfo[]
*/
public static function getDirectoryContent($directory, $mimetype_filter = '') {
return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php
index 9e826dd6192..2b697141515 100644
--- a/lib/private/files/storage/common.php
+++ b/lib/private/files/storage/common.php
@@ -27,6 +27,11 @@ abstract class Common implements \OC\Files\Storage\Storage {
protected $watcher;
protected $storageCache;
+ /**
+ * @var string[]
+ */
+ protected $cachedFiles = array();
+
public function __construct($parameters) {
}
@@ -122,11 +127,13 @@ abstract class Common implements \OC\Files\Storage\Storage {
public function file_put_contents($path, $data) {
$handle = $this->fopen($path, "w");
+ $this->removeCachedFile($path);
return fwrite($handle, $data);
}
public function rename($path1, $path2) {
if ($this->copy($path1, $path2)) {
+ $this->removeCachedFile($path1);
return $this->unlink($path1);
} else {
return false;
@@ -137,6 +144,7 @@ abstract class Common implements \OC\Files\Storage\Storage {
$source = $this->fopen($path1, 'r');
$target = $this->fopen($path2, 'w');
list($count, $result) = \OC_Helper::streamCopy($source, $target);
+ $this->removeCachedFile($path2);
return $result;
}
@@ -152,8 +160,7 @@ 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);
+ $hash = hash_file($type, $tmpFile, $raw);
return $hash;
}
@@ -162,13 +169,14 @@ abstract class Common implements \OC\Files\Storage\Storage {
}
public function getLocalFile($path) {
- return $this->toTmpFile($path);
+ return $this->getCachedFile($path);
}
/**
* @param string $path
+ * @return string
*/
- private function toTmpFile($path) { //no longer in the storage api, still useful here
+ protected function toTmpFile($path) { //no longer in the storage api, still useful here
$source = $this->fopen($path, 'r');
if (!$source) {
return false;
@@ -352,4 +360,15 @@ abstract class Common implements \OC\Files\Storage\Storage {
// default, which is not local
return false;
}
+
+ protected function getCachedFile($path) {
+ if (!isset($this->cachedFiles[$path])) {
+ $this->cachedFiles[$path] = $this->toTmpFile($path);
+ }
+ return $this->cachedFiles[$path];
+ }
+
+ protected function removeCachedFile($path) {
+ unset($this->cachedFiles[$path]);
+ }
}
diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php
index a62230bdba5..571bf7f97c1 100644
--- a/lib/private/files/storage/local.php
+++ b/lib/private/files/storage/local.php
@@ -35,7 +35,7 @@ if (\OC_Util::runningOnWindows()) {
}
public function mkdir($path) {
- return @mkdir($this->datadir . $path);
+ return @mkdir($this->datadir . $path, 0777, true);
}
public function rmdir($path) {
@@ -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 1bab3489a28..94ee28ca763 100644
--- a/lib/private/files/storage/mappedlocal.php
+++ b/lib/private/files/storage/mappedlocal.php
@@ -31,7 +31,7 @@ class MappedLocal extends \OC\Files\Storage\Common{
return 'local::'.$this->datadir;
}
public function mkdir($path) {
- return @mkdir($this->buildPath($path));
+ return @mkdir($this->buildPath($path), 0777, true);
}
public function rmdir($path) {
try {
@@ -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/storage/wrapper/quota.php b/lib/private/files/storage/wrapper/quota.php
index 26c952e694a..a878b2c5cf6 100644
--- a/lib/private/files/storage/wrapper/quota.php
+++ b/lib/private/files/storage/wrapper/quota.php
@@ -30,12 +30,24 @@ class Quota extends Wrapper {
}
/**
+ * @return quota value
+ */
+ public function getQuota() {
+ return $this->quota;
+ }
+
+ /**
* @param string $path
*/
protected function getSize($path) {
$cache = $this->getCache();
$data = $cache->get($path);
if (is_array($data) and isset($data['size'])) {
+ if (isset($data['unencrypted_size'])
+ && $data['unencrypted_size'] > 0
+ ) {
+ return $data['unencrypted_size'];
+ }
return $data['size'];
} else {
return \OC\Files\SPACE_NOT_COMPUTED;
@@ -57,7 +69,14 @@ class Quota extends Wrapper {
return \OC\Files\SPACE_NOT_COMPUTED;
} else {
$free = $this->storage->free_space($path);
- return min($free, (max($this->quota - $used, 0)));
+ $quotaFree = max($this->quota - $used, 0);
+ // if free space is known
+ if ($free >= 0) {
+ $free = min($free, $quotaFree);
+ } else {
+ $free = $quotaFree;
+ }
+ return $free;
}
}
}
diff --git a/lib/private/files/view.php b/lib/private/files/view.php
index 2dbbf5b88c9..f06c2fcd66c 100644
--- a/lib/private/files/view.php
+++ b/lib/private/files/view.php
@@ -25,6 +25,8 @@
namespace OC\Files;
+use OC\Files\Cache\Updater;
+
class View {
private $fakeRoot = '';
private $internal_path_cache = array();
@@ -308,6 +310,9 @@ class View {
fclose($target);
fclose($data);
if ($this->shouldEmitHooks($path) && $result !== false) {
+ Updater::writeHook(array(
+ 'path' => $this->getHookPath($path)
+ ));
if (!$exists) {
\OC_Hook::emit(
Filesystem::CLASSNAME,
@@ -433,6 +438,7 @@ class View {
}
if ($this->shouldEmitHooks() && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
// if it was a rename from a part file to a regular file it was a write and not a rename operation
+ Updater::writeHook(array('path' => $this->getHookPath($path2)));
\OC_Hook::emit(
Filesystem::CLASSNAME,
Filesystem::signal_post_write,
@@ -441,6 +447,10 @@ class View {
)
);
} elseif ($this->shouldEmitHooks() && $result !== false) {
+ Updater::renameHook(array(
+ 'oldpath' => $this->getHookPath($path1),
+ 'newpath' => $this->getHookPath($path2)
+ ));
\OC_Hook::emit(
Filesystem::CLASSNAME,
Filesystem::signal_post_rename,
@@ -741,7 +751,10 @@ class View {
}
/**
+ * @param string[] $hooks
* @param string $path
+ * @param bool $post
+ * @return bool
*/
private function runHooks($hooks, $path, $post = false) {
$path = $this->getHookPath($path);
@@ -749,6 +762,16 @@ class View {
$run = true;
if ($this->shouldEmitHooks($path)) {
foreach ($hooks as $hook) {
+ // manually triger updater hooks to ensure they are called first
+ if ($post) {
+ if ($hook == 'write') {
+ Updater::writeHook(array('path' => $path));
+ } elseif ($hook == 'touch') {
+ Updater::touchHook(array('path' => $path));
+ } else if ($hook == 'delete') {
+ Updater::deleteHook(array('path' => $path));
+ }
+ }
if ($hook != 'read') {
\OC_Hook::emit(
Filesystem::CLASSNAME,
@@ -820,7 +843,7 @@ class View {
$data = $cache->get($internalPath);
}
- if ($data and $data['fileid']) {
+ if ($data and isset($data['fileid'])) {
if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
//add the sizes of other mountpoints to the folder
$mountPoints = Filesystem::getMountPoints($path);
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/helper.php b/lib/private/helper.php
index d8c4650f666..d7ac0b5f4fa 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);
}
/**
@@ -308,7 +307,7 @@ class OC_Helper {
/**
* @brief Make a computer file size
- * @param string $str file size in a fancy format
+ * @param string $str file size in human readable format
* @return int a file size in bytes
*
* Makes 2kB to 2048.
@@ -338,41 +337,12 @@ class OC_Helper {
$bytes *= $bytes_array[$matches[1]];
}
- $bytes = round($bytes, 2);
+ $bytes = round($bytes);
return $bytes;
}
/**
- * @brief Recursive editing of file permissions
- * @param string $path path to file or folder
- * @param int $filemode unix style file permissions
- * @return bool
- */
- static function chmodr($path, $filemode) {
- if (!is_dir($path))
- return chmod($path, $filemode);
- $dh = opendir($path);
- if(is_resource($dh)) {
- while (($file = readdir($dh)) !== false) {
- if ($file != '.' && $file != '..') {
- $fullpath = $path . '/' . $file;
- if (is_link($fullpath))
- return false;
- elseif (!is_dir($fullpath) && !@chmod($fullpath, $filemode))
- return false; elseif (!self::chmodr($fullpath, $filemode))
- return false;
- }
- }
- closedir($dh);
- }
- if (@chmod($path, $filemode))
- return true;
- else
- return false;
- }
-
- /**
* @brief Recursive copying of folders
* @param string $src source folder
* @param string $dest target folder
@@ -839,7 +809,7 @@ class OC_Helper {
* @return int number of bytes representing
*/
public static function maxUploadFilesize($dir, $freeSpace = null) {
- if (is_null($freeSpace)){
+ if (is_null($freeSpace) || $freeSpace < 0){
$freeSpace = self::freeSpace($dir);
}
return min($freeSpace, self::uploadLimit());
@@ -914,13 +884,22 @@ class OC_Helper {
if ($used < 0) {
$used = 0;
}
- $free = \OC\Files\Filesystem::free_space($path);
+ $quota = 0;
+ // TODO: need a better way to get total space from storage
+ $storage = $rootInfo->getStorage();
+ if ($storage instanceof \OC\Files\Storage\Wrapper\Quota) {
+ $quota = $storage->getQuota();
+ }
+ $free = $storage->free_space('');
if ($free >= 0) {
$total = $free + $used;
} else {
$total = $free; //either unknown or unlimited
}
if ($total > 0) {
+ if ($quota > 0 && $total > $quota) {
+ $total = $quota;
+ }
// prevent division by zero or error codes (negative values)
$relative = round(($used / $total) * 10000) / 100;
} else {
diff --git a/lib/private/image.php b/lib/private/image.php
index e0397ec8a00..f1b8acc41b7 100644
--- a/lib/private/image.php
+++ b/lib/private/image.php
@@ -35,24 +35,24 @@ class OC_Image {
/**
* @brief Get mime type for an image file.
* @param string|null $filePath The path to a local image file.
- * @returns string The mime type if the it could be determined, otherwise an empty string.
+ * @return string The mime type if the it could be determined, otherwise an empty string.
*/
static public function getMimeTypeForFile($filePath) {
// exif_imagetype throws "read error!" if file is less than 12 byte
if (filesize($filePath) > 11) {
$imageType = exif_imagetype($filePath);
- }
- else {
+ } else {
$imageType = false;
}
return $imageType ? image_type_to_mime_type($imageType) : '';
}
/**
- * @brief Constructor.
- * @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function.
- * @returns bool False on error
- */
+ * @brief Constructor.
+ * @param resource|string $imageref The path to a local file, a base64 encoded string or a resource created by
+ * an imagecreate* function.
+ * @return \OC_Image False on error
+ */
public function __construct($imageRef = null) {
//OC_Log::write('core',__METHOD__.'(): start', OC_Log::DEBUG);
if(!extension_loaded('gd') || !function_exists('gd_info')) {
@@ -71,7 +71,7 @@ class OC_Image {
/**
* @brief Determine whether the object contains an image resource.
- * @returns bool
+ * @return bool
*/
public function valid() { // apparently you can't name a method 'empty'...
return is_resource($this->resource);
@@ -79,7 +79,7 @@ class OC_Image {
/**
* @brief Returns the MIME type of the image or an empty string if no image is loaded.
- * @returns int
+ * @return int
*/
public function mimeType() {
return $this->valid() ? $this->mimeType : '';
@@ -87,7 +87,7 @@ class OC_Image {
/**
* @brief Returns the width of the image or -1 if no image is loaded.
- * @returns int
+ * @return int
*/
public function width() {
return $this->valid() ? imagesx($this->resource) : -1;
@@ -95,7 +95,7 @@ class OC_Image {
/**
* @brief Returns the height of the image or -1 if no image is loaded.
- * @returns int
+ * @return int
*/
public function height() {
return $this->valid() ? imagesy($this->resource) : -1;
@@ -103,7 +103,7 @@ class OC_Image {
/**
* @brief Returns the width when the image orientation is top-left.
- * @returns int
+ * @return int
*/
public function widthTopLeft() {
$o = $this->getOrientation();
@@ -115,20 +115,18 @@ class OC_Image {
case 3:
case 4: // Not tested
return $this->width();
- break;
case 5: // Not tested
case 6:
case 7: // Not tested
case 8:
return $this->height();
- break;
}
return $this->width();
}
/**
* @brief Returns the height when the image orientation is top-left.
- * @returns int
+ * @return int
*/
public function heightTopLeft() {
$o = $this->getOrientation();
@@ -140,46 +138,56 @@ class OC_Image {
case 3:
case 4: // Not tested
return $this->height();
- break;
case 5: // Not tested
case 6:
case 7: // Not tested
case 8:
return $this->width();
- break;
}
return $this->height();
}
/**
- * @brief Outputs the image.
- * @returns bool
- */
- public function show() {
- header('Content-Type: '.$this->mimeType());
- return $this->_output();
+ * @brief Outputs the image.
+ * @param string $mimeType
+ * @return bool
+ */
+ public function show($mimeType=null) {
+ if($mimeType === null) {
+ $mimeType = $this->mimeType();
+ }
+ header('Content-Type: '.$mimeType);
+ return $this->_output(null, $mimeType);
}
/**
- * @brief Saves the image.
- * @returns bool
- * @param string $filePath
- */
+ * @brief Saves the image.
+ * @param string $filePath
+ * @param string $mimeType
+ * @return bool
+ */
- public function save($filePath=null) {
+ public function save($filePath=null, $mimeType=null) {
+ if($mimeType === null) {
+ $mimeType = $this->mimeType();
+ }
if($filePath === null && $this->filePath === null) {
OC_Log::write('core', __METHOD__.'(): called with no path.', OC_Log::ERROR);
return false;
} elseif($filePath === null && $this->filePath !== null) {
$filePath = $this->filePath;
}
- return $this->_output($filePath);
+ return $this->_output($filePath, $mimeType);
}
/**
- * @brief Outputs/saves the image.
- */
- private function _output($filePath=null) {
+ * @brief Outputs/saves the image.
+ * @param string $filePath
+ * @param string $mimeType
+ * @return bool
+ * @throws Exception
+ */
+ private function _output($filePath=null, $mimeType=null) {
if($filePath) {
if (!file_exists(dirname($filePath)))
mkdir(dirname($filePath), 0777, true);
@@ -197,8 +205,30 @@ class OC_Image {
return false;
}
- $retVal = false;
- switch($this->imageType) {
+ $imageType = $this->imageType;
+ if($mimeType !== null) {
+ switch($mimeType) {
+ case 'image/gif':
+ $imageType = IMAGETYPE_GIF;
+ break;
+ case 'image/jpeg':
+ $imageType = IMAGETYPE_JPEG;
+ break;
+ case 'image/png':
+ $imageType = IMAGETYPE_PNG;
+ break;
+ case 'image/x-xbitmap':
+ $imageType = IMAGETYPE_XBM;
+ break;
+ case 'image/bmp':
+ $imageType = IMAGETYPE_BMP;
+ break;
+ default:
+ throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format');
+ }
+ }
+
+ switch($imageType) {
case IMAGETYPE_GIF:
$retVal = imagegif($this->resource, $filePath);
break;
@@ -209,7 +239,12 @@ class OC_Image {
$retVal = imagepng($this->resource, $filePath);
break;
case IMAGETYPE_XBM:
- $retVal = imagexbm($this->resource, $filePath);
+ if (function_exists('imagexbm')) {
+ $retVal = imagexbm($this->resource, $filePath);
+ } else {
+ throw new Exception('\OC_Image::_output(): imagexbm() is not supported.');
+ }
+
break;
case IMAGETYPE_WBMP:
$retVal = imagewbmp($this->resource, $filePath);
@@ -231,14 +266,14 @@ class OC_Image {
}
/**
- * @returns resource Returns the image resource in any.
+ * @return resource Returns the image resource in any.
*/
public function resource() {
return $this->resource;
}
/**
- * @returns Returns the raw image data.
+ * @return string Returns the raw image data.
*/
function data() {
ob_start();
@@ -264,8 +299,8 @@ class OC_Image {
}
/**
- * @returns Returns a base64 encoded string suitable for embedding in a VCard.
- */
+ * @return string - base64 encoded, which is suitable for embedding in a VCard.
+ */
function __toString() {
return base64_encode($this->data());
}
@@ -273,7 +308,7 @@ class OC_Image {
/**
* (I'm open for suggestions on better method name ;)
* @brief Get the orientation based on EXIF data.
- * @returns The orientation or -1 if no EXIF data is available.
+ * @return int The orientation or -1 if no EXIF data is available.
*/
public function getOrientation() {
if(!is_callable('exif_read_data')) {
@@ -301,53 +336,43 @@ class OC_Image {
/**
* (I'm open for suggestions on better method name ;)
* @brief Fixes orientation based on EXIF data.
- * @returns bool.
+ * @return bool.
*/
public function fixOrientation() {
$o = $this->getOrientation();
OC_Log::write('core', 'OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG);
$rotate = 0;
- $flip = false;
switch($o) {
case -1:
return false; //Nothing to fix
- break;
case 1:
$rotate = 0;
- $flip = false;
break;
case 2: // Not tested
$rotate = 0;
- $flip = true;
break;
case 3:
$rotate = 180;
- $flip = false;
break;
case 4: // Not tested
$rotate = 180;
- $flip = true;
break;
case 5: // Not tested
$rotate = 90;
- $flip = true;
break;
case 6:
//$rotate = 90;
$rotate = 270;
- $flip = false;
break;
case 7: // Not tested
$rotate = 270;
- $flip = true;
break;
case 8:
$rotate = 90;
- $flip = false;
break;
}
if($rotate) {
- $res = imagerotate($this->resource, $rotate, -1);
+ $res = imagerotate($this->resource, $rotate, 0);
if($res) {
if(imagealphablending($res, true)) {
if(imagesavealpha($res, true)) {
@@ -367,13 +392,14 @@ class OC_Image {
return false;
}
}
+ return false;
}
/**
- * @brief Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function.
- * @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function or a file resource (file handle ).
- * @returns An image resource or false on error
- */
+ * @brief Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function.
+ * @param resource|string $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function or a file resource (file handle ).
+ * @return resource|false An image resource or false on error
+ */
public function load($imageRef) {
if(is_resource($imageRef)) {
if(get_resource_type($imageRef) == 'gd') {
@@ -382,10 +408,10 @@ class OC_Image {
} elseif(in_array(get_resource_type($imageRef), array('file', 'stream'))) {
return $this->loadFromFileHandle($imageRef);
}
- } elseif($this->loadFromFile($imageRef) !== false) {
- return $this->resource;
} elseif($this->loadFromBase64($imageRef) !== false) {
return $this->resource;
+ } elseif($this->loadFromFile($imageRef) !== false) {
+ return $this->resource;
} elseif($this->loadFromData($imageRef) !== false) {
return $this->resource;
} else {
@@ -398,7 +424,7 @@ class OC_Image {
* @brief Loads an image from an open file handle.
* It is the responsibility of the caller to position the pointer at the correct place and to close the handle again.
* @param resource $handle
- * @returns An image resource or false on error
+ * @return An image resource or false on error
*/
public function loadFromFileHandle($handle) {
OC_Log::write('core', __METHOD__.'(): Trying', OC_Log::DEBUG);
@@ -410,13 +436,12 @@ class OC_Image {
/**
* @brief Loads an image from a local file.
- * @param $imagePath The path to a local file.
- * @returns An image resource or false on error
+ * @param bool|string $imagePath The path to a local file.
+ * @return bool|resource An image resource or false on error
*/
public function loadFromFile($imagePath=false) {
// exif_imagetype throws "read error!" if file is less than 12 byte
if(!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) {
- // Debug output disabled because this method is tried before loadFromBase64?
OC_Log::write('core', 'OC_Image->loadFromFile, couldn\'t load: ' . (string) urlencode($imagePath), OC_Log::DEBUG);
return false;
}
@@ -512,8 +537,8 @@ class OC_Image {
/**
* @brief Loads an image from a string of data.
- * @param $str A string of image data as read from a file.
- * @returns An image resource or false on error
+ * @param string $str A string of image data as read from a file.
+ * @return bool|resource An image resource or false on error
*/
public function loadFromData($str) {
if(is_resource($str)) {
@@ -537,8 +562,8 @@ class OC_Image {
/**
* @brief Loads an image from a base64 encoded string.
- * @param $str A string base64 encoded string of image data.
- * @returns An image resource or false on error
+ * @param string $str A string base64 encoded string of image data.
+ * @return bool|resource An image resource or false on error
*/
public function loadFromBase64($str) {
if(!is_string($str)) {
@@ -567,7 +592,7 @@ class OC_Image {
* @param string $fileName <p>
* Path to the BMP image.
* </p>
- * @return resource an image resource identifier on success, <b>FALSE</b> on errors.
+ * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors.
*/
private function imagecreatefrombmp($fileName) {
if (!($fh = fopen($fileName, 'rb'))) {
@@ -599,9 +624,9 @@ class OC_Image {
$meta['imagesize'] = $meta['filesize'] - $meta['offset'];
// in rare cases filesize is equal to offset so we need to read physical size
if ($meta['imagesize'] < 1) {
- $meta['imagesize'] = @filesize($filename) - $meta['offset'];
+ $meta['imagesize'] = @filesize($fileName) - $meta['offset'];
if ($meta['imagesize'] < 1) {
- trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING);
+ trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $fileName . '!', E_USER_WARNING);
return false;
}
}
@@ -706,7 +731,7 @@ class OC_Image {
/**
* @brief Resizes the image preserving ratio.
* @param integer $maxSize The maximum size of either the width or height.
- * @returns bool
+ * @return bool
*/
public function resize($maxSize) {
if(!$this->valid()) {
@@ -729,6 +754,11 @@ class OC_Image {
return true;
}
+ /**
+ * @param int $width
+ * @param int $height
+ * @return bool
+ */
public function preciseResize($width, $height) {
if (!$this->valid()) {
OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR);
@@ -764,8 +794,8 @@ class OC_Image {
/**
* @brief Crops the image to the middle square. If the image is already square it just returns.
- * @param int maximum size for the result (optional)
- * @returns bool for success or failure
+ * @param int $size maximum size for the result (optional)
+ * @return bool for success or failure
*/
public function centerCrop($size=0) {
if(!$this->valid()) {
@@ -823,11 +853,11 @@ class OC_Image {
/**
* @brief Crops the image from point $x$y with dimension $wx$h.
- * @param $x Horizontal position
- * @param $y Vertical position
- * @param $w Width
- * @param $h Height
- * @returns bool for success or failure
+ * @param int $x Horizontal position
+ * @param int $y Vertical position
+ * @param int $w Width
+ * @param int $h Height
+ * @return bool for success or failure
*/
public function crop($x, $y, $w, $h) {
if(!$this->valid()) {
@@ -855,7 +885,7 @@ class OC_Image {
* @brief Resizes the image to fit within a boundry while preserving ratio.
* @param integer $maxWidth
* @param integer $maxHeight
- * @returns bool
+ * @return bool
*/
public function fitIn($maxWidth, $maxHeight) {
if(!$this->valid()) {
@@ -947,7 +977,7 @@ if ( ! function_exists( 'imagebmp') ) {
$index = imagecolorat($im, $i, $j);
if ($index !== $lastIndex || $sameNum > 255) {
if ($sameNum != 0) {
- $bmpData .= chr($same_num) . chr($lastIndex);
+ $bmpData .= chr($sameNum) . chr($lastIndex);
}
$lastIndex = $index;
$sameNum = 1;
diff --git a/lib/private/installer.php b/lib/private/installer.php
index 11633a4d4a1..64e8e3a5e7a 100644
--- a/lib/private/installer.php
+++ b/lib/private/installer.php
@@ -464,7 +464,7 @@ class OC_Installer{
// is the code checker enabled?
if(OC_Config::getValue('appcodechecker', true)) {
// check if grep is installed
- $grep = exec('which grep');
+ $grep = exec('command -v grep');
if($grep=='') {
OC_Log::write('core',
'grep not installed. So checking the code of the app "'.$appname.'" was not possible',
diff --git a/lib/private/l10n.php b/lib/private/l10n.php
index f0b4f9a70f6..175360e27a3 100644
--- a/lib/private/l10n.php
+++ b/lib/private/l10n.php
@@ -405,7 +405,7 @@ class OC_L10N implements \OCP\IL10N {
/**
* @brief Choose a language
- * @param array $texts Associative Array with possible strings
+ * @param array $text Associative Array with possible strings
* @returns String
*
* $text is an array 'de' => 'hallo welt', 'en' => 'hello world', ...
@@ -413,14 +413,14 @@ class OC_L10N implements \OCP\IL10N {
* This function is useful to avoid loading thousands of files if only one
* simple string is needed, for example in appinfo.php
*/
- public static function selectLanguage($texts) {
- $lang = self::findLanguage(array_keys($texts));
- return $texts[$lang];
+ public static function selectLanguage($text) {
+ $lang = self::findLanguage(array_keys($text));
+ return $text[$lang];
}
/**
* @brief find the best language
- * @param array|string $app Array or string, details below
+ * @param array|string $app details below
* @returns string language
*
* If $app is an array, ownCloud assumes that these are the available
@@ -532,8 +532,9 @@ class OC_L10N implements \OCP\IL10N {
}
/**
- * @param string $lang
* @param string $app
+ * @param string $lang
+ * @returns bool
*/
public static function languageExists($app, $lang) {
if ($lang == 'en') {//english is always available
diff --git a/lib/private/mail.php b/lib/private/mail.php
index 90c3e343199..79f51609631 100644
--- a/lib/private/mail.php
+++ b/lib/private/mail.php
@@ -72,11 +72,9 @@ class OC_Mail {
$mailo->From = $fromaddress;
$mailo->FromName = $fromname;;
$mailo->Sender = $fromaddress;
- $a=explode(' ', $toaddress);
try {
- foreach($a as $ad) {
- $mailo->AddAddress($ad, $toname);
- }
+ $toaddress = self::buildAsciiEmail($toaddress);
+ $mailo->AddAddress($toaddress, $toname);
if($ccaddress<>'') $mailo->AddCC($ccaddress, $ccname);
if($bcc<>'') $mailo->AddBCC($bcc);
@@ -127,7 +125,23 @@ class OC_Mail {
* @param string $emailAddress a given email address to be validated
* @return bool
*/
- public static function ValidateAddress($emailAddress) {
+ public static function validateAddress($emailAddress) {
+ $emailAddress = self::buildAsciiEmail($emailAddress);
return PHPMailer::ValidateAddress($emailAddress);
}
+
+ /**
+ * IDN domains will be properly converted to ascii domains.
+ *
+ * @param string $emailAddress
+ * @return string
+ */
+ public static function buildAsciiEmail($emailAddress) {
+
+ list($name, $domain) = explode('@', $emailAddress, 2);
+ $domain = idn_to_ascii($domain);
+
+ return "$name@$domain";
+ }
+
}
diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php
index 9bd07b89023..91bcf584267 100644
--- a/lib/private/mimetypes.list.php
+++ b/lib/private/mimetypes.list.php
@@ -31,6 +31,7 @@ return array(
'bash' => 'text/x-shellscript',
'blend' => 'application/x-blender',
'bin' => 'application/x-bin',
+ 'bmp' => 'image/bmp',
'cb7' => 'application/x-cbr',
'cba' => 'application/x-cbr',
'cbr' => 'application/x-cbr',
@@ -76,6 +77,7 @@ return array(
'md' => 'text/markdown',
'mdb' => 'application/msaccess',
'mdwn' => 'text/markdown',
+ 'mkv' => 'video/x-matroska',
'mobi' => 'application/x-mobipocket-ebook',
'mov' => 'video/quicktime',
'mp3' => 'audio/mpeg',
diff --git a/lib/private/ocs/cloud.php b/lib/private/ocs/cloud.php
index cbbf3b626f5..06d6a8eb4b0 100644
--- a/lib/private/ocs/cloud.php
+++ b/lib/private/ocs/cloud.php
@@ -61,17 +61,29 @@ class OC_OCS_Cloud {
* the user from whom the information will be returned
*/
public static function getUser($parameters) {
+ $return = array();
// Check if they are viewing information on themselves
if($parameters['userid'] === OC_User::getUser()) {
// Self lookup
$storage = OC_Helper::getStorageInfo('/');
- $quota = array(
+ $return['quota'] = array(
'free' => $storage['free'],
'used' => $storage['used'],
'total' => $storage['total'],
'relative' => $storage['relative'],
);
- return new OC_OCS_Result(array('quota' => $quota));
+ }
+ if(OC_User::isAdminUser(OC_User::getUser())
+ || OC_Subadmin::isUserAccessible(OC_User::getUser(), $parameters['userid'])) {
+ if(OC_User::userExists($parameters['userid'])) {
+ // Is an admin/subadmin so can see display name
+ $return['displayname'] = OC_User::getDisplayName($parameters['userid']);
+ } else {
+ return new OC_OCS_Result(null, 101);
+ }
+ }
+ if(count($return)) {
+ return new OC_OCS_Result($return);
} else {
// No permission to view this user data
return new OC_OCS_Result(null, 997);
diff --git a/lib/private/ocsclient.php b/lib/private/ocsclient.php
index fa6e3fac1bb..68dc2c2d6ec 100644
--- a/lib/private/ocsclient.php
+++ b/lib/private/ocsclient.php
@@ -72,7 +72,9 @@ class OC_OCSClient{
if($xml==false) {
return null;
}
- $data=simplexml_load_string($xml);
+ $loadEntities = libxml_disable_entity_loader(true);
+ $data = simplexml_load_string($xml);
+ libxml_disable_entity_loader($loadEntities);
$tmp=$data->data;
$cats=array();
@@ -117,7 +119,9 @@ class OC_OCSClient{
if($xml==false) {
return null;
}
- $data=simplexml_load_string($xml);
+ $loadEntities = libxml_disable_entity_loader(true);
+ $data = simplexml_load_string($xml);
+ libxml_disable_entity_loader($loadEntities);
$tmp=$data->data->content;
for($i = 0; $i < count($tmp); $i++) {
@@ -159,7 +163,9 @@ class OC_OCSClient{
OC_Log::write('core', 'Unable to parse OCS content', OC_Log::FATAL);
return null;
}
- $data=simplexml_load_string($xml);
+ $loadEntities = libxml_disable_entity_loader(true);
+ $data = simplexml_load_string($xml);
+ libxml_disable_entity_loader($loadEntities);
$tmp=$data->data->content;
$app=array();
@@ -200,7 +206,9 @@ class OC_OCSClient{
OC_Log::write('core', 'Unable to parse OCS content', OC_Log::FATAL);
return null;
}
- $data=simplexml_load_string($xml);
+ $loadEntities = libxml_disable_entity_loader(true);
+ $data = simplexml_load_string($xml);
+ libxml_disable_entity_loader($loadEntities);
$tmp=$data->data->content;
$app=array();
diff --git a/lib/private/preview.php b/lib/private/preview.php
index 80fd003ed8d..0c1af3c9588 100755
--- a/lib/private/preview.php
+++ b/lib/private/preview.php
@@ -42,6 +42,10 @@ class Preview {
private $scalingup;
private $mimetype;
+ //filemapper used for deleting previews
+ // index is path, value is fileinfo
+ static public $deleteFileMapper = array();
+
//preview images object
/**
* @var \OC_Image
@@ -53,6 +57,11 @@ class Preview {
static private $registeredProviders = array();
/**
+ * @var \OCP\Files\FileInfo
+ */
+ protected $info;
+
+ /**
* @brief check if thumbnail or bigger version of thumbnail of file is cached
* @param string $user userid - if no user is given, OC_User::getUser will be used
* @param string $root path of root
@@ -61,12 +70,12 @@ class Preview {
* @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image
* @param bool $scalingUp Disable/Enable upscaling of previews
* @return mixed (bool / string)
- * false if thumbnail does not exist
- * path to thumbnail if thumbnail exists
- */
- public function __construct($user='', $root='/', $file='', $maxX=1, $maxY=1, $scalingUp=true) {
+ * false if thumbnail does not exist
+ * path to thumbnail if thumbnail exists
+ */
+ public function __construct($user = '', $root = '/', $file = '', $maxX = 1, $maxY = 1, $scalingUp = true) {
//init fileviews
- if($user === ''){
+ if ($user === '') {
$user = \OC_User::getUser();
}
$this->fileView = new \OC\Files\View('/' . $user . '/' . $root);
@@ -86,11 +95,11 @@ class Preview {
$this->preview = null;
//check if there are preview backends
- if(empty(self::$providers)) {
+ if (empty(self::$providers)) {
self::initProviders();
}
- if(empty(self::$providers)) {
+ if (empty(self::$providers)) {
\OC_Log::write('core', 'No preview providers exist', \OC_Log::ERROR);
throw new \Exception('No preview providers');
}
@@ -99,15 +108,15 @@ class Preview {
/**
* @brief returns the path of the file you want a thumbnail from
* @return string
- */
- public function getFile() {
+ */
+ public function getFile() {
return $this->file;
}
/**
* @brief returns the max width of the preview
* @return integer
- */
+ */
public function getMaxX() {
return $this->maxX;
}
@@ -115,7 +124,7 @@ class Preview {
/**
* @brief returns the max height of the preview
* @return integer
- */
+ */
public function getMaxY() {
return $this->maxY;
}
@@ -123,7 +132,7 @@ class Preview {
/**
* @brief returns whether or not scalingup is enabled
* @return bool
- */
+ */
public function getScalingUp() {
return $this->scalingup;
}
@@ -131,7 +140,7 @@ class Preview {
/**
* @brief returns the name of the thumbnailfolder
* @return string
- */
+ */
public function getThumbnailsFolder() {
return self::THUMBNAILS_FOLDER;
}
@@ -139,7 +148,7 @@ class Preview {
/**
* @brief returns the max scale factor
* @return string
- */
+ */
public function getMaxScaleFactor() {
return $this->maxScaleFactor;
}
@@ -147,7 +156,7 @@ class Preview {
/**
* @brief returns the max width set in ownCloud's config
* @return string
- */
+ */
public function getConfigMaxX() {
return $this->configMaxX;
}
@@ -155,20 +164,35 @@ class Preview {
/**
* @brief returns the max height set in ownCloud's config
* @return string
- */
+ */
public function getConfigMaxY() {
return $this->configMaxY;
}
+ protected function getFileInfo() {
+ $absPath = $this->fileView->getAbsolutePath($this->file);
+ $absPath = Files\Filesystem::normalizePath($absPath);
+ if(array_key_exists($absPath, self::$deleteFileMapper)) {
+ $this->info = self::$deleteFileMapper[$absPath];
+ } else if (!$this->info) {
+ $this->info = $this->fileView->getFileInfo($this->file);
+ }
+ return $this->info;
+ }
+
/**
* @brief set the path of the file you want a thumbnail from
* @param string $file
* @return $this
- */
+ */
public function setFile($file) {
$this->file = $file;
+ $this->info = null;
if ($file !== '') {
- $this->mimetype = $this->fileView->getMimeType($this->file);
+ $this->getFileInfo();
+ if($this->info !== null && $this->info !== false) {
+ $this->mimetype = $this->info->getMimetype();
+ }
}
return $this;
}
@@ -185,14 +209,14 @@ class Preview {
* @brief set the the max width of the preview
* @param int $maxX
* @return $this
- */
- public function setMaxX($maxX=1) {
- if($maxX <= 0) {
+ */
+ public function setMaxX($maxX = 1) {
+ if ($maxX <= 0) {
throw new \Exception('Cannot set width of 0 or smaller!');
}
$configMaxX = $this->getConfigMaxX();
- if(!is_null($configMaxX)) {
- if($maxX > $configMaxX) {
+ if (!is_null($configMaxX)) {
+ if ($maxX > $configMaxX) {
\OC_Log::write('core', 'maxX reduced from ' . $maxX . ' to ' . $configMaxX, \OC_Log::DEBUG);
$maxX = $configMaxX;
}
@@ -205,14 +229,14 @@ class Preview {
* @brief set the the max height of the preview
* @param int $maxY
* @return $this
- */
- public function setMaxY($maxY=1) {
- if($maxY <= 0) {
+ */
+ public function setMaxY($maxY = 1) {
+ if ($maxY <= 0) {
throw new \Exception('Cannot set height of 0 or smaller!');
}
$configMaxY = $this->getConfigMaxY();
- if(!is_null($configMaxY)) {
- if($maxY > $configMaxY) {
+ if (!is_null($configMaxY)) {
+ if ($maxY > $configMaxY) {
\OC_Log::write('core', 'maxX reduced from ' . $maxY . ' to ' . $configMaxY, \OC_Log::DEBUG);
$maxY = $configMaxY;
}
@@ -225,9 +249,9 @@ class Preview {
* @brief set whether or not scalingup is enabled
* @param bool $scalingUp
* @return $this
- */
+ */
public function setScalingup($scalingUp) {
- if($this->getMaxScaleFactor() === 1) {
+ if ($this->getMaxScaleFactor() === 1) {
$scalingUp = false;
}
$this->scalingup = $scalingUp;
@@ -237,15 +261,15 @@ class Preview {
/**
* @brief check if all parameters are valid
* @return bool
- */
+ */
public function isFileValid() {
$file = $this->getFile();
- if($file === '') {
+ if ($file === '') {
\OC_Log::write('core', 'No filename passed', \OC_Log::DEBUG);
return false;
}
- if(!$this->fileView->file_exists($file)) {
+ if (!$this->fileView->file_exists($file)) {
\OC_Log::write('core', 'File:"' . $file . '" not found', \OC_Log::DEBUG);
return false;
}
@@ -256,40 +280,44 @@ class Preview {
/**
* @brief deletes previews of a file with specific x and y
* @return bool
- */
+ */
public function deletePreview() {
$file = $this->getFile();
- $fileInfo = $this->fileView->getFileInfo($file);
- $fileId = $fileInfo['fileid'];
+ $fileInfo = $this->getFileInfo($file);
+ if($fileInfo !== null && $fileInfo !== false) {
+ $fileId = $fileInfo->getId();
- $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/' . $this->getMaxX() . '-' . $this->getMaxY() . '.png';
- $this->userView->unlink($previewPath);
- return !$this->userView->file_exists($previewPath);
+ $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/' . $this->getMaxX() . '-' . $this->getMaxY() . '.png';
+ return $this->userView->unlink($previewPath);
+ }
+ return false;
}
/**
* @brief deletes all previews of a file
* @return bool
- */
+ */
public function deleteAllPreviews() {
$file = $this->getFile();
- $fileInfo = $this->fileView->getFileInfo($file);
- $fileId = $fileInfo['fileid'];
+ $fileInfo = $this->getFileInfo($file);
+ if($fileInfo !== null && $fileInfo !== false) {
+ $fileId = $fileInfo->getId();
- $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/';
- $this->userView->deleteAll($previewPath);
- $this->userView->rmdir($previewPath);
- return !$this->userView->is_dir($previewPath);
+ $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/';
+ $this->userView->deleteAll($previewPath);
+ return $this->userView->rmdir($previewPath);
+ }
+ return false;
}
/**
* @brief check if thumbnail or bigger version of thumbnail of file is cached
* @return mixed (bool / string)
- * false if thumbnail does not exist
- * path to thumbnail if thumbnail exists
- */
+ * false if thumbnail does not exist
+ * path to thumbnail if thumbnail exists
+ */
private function isCached() {
$file = $this->getFile();
$maxX = $this->getMaxX();
@@ -297,75 +325,75 @@ class Preview {
$scalingUp = $this->getScalingUp();
$maxScaleFactor = $this->getMaxScaleFactor();
- $fileInfo = $this->fileView->getFileInfo($file);
- $fileId = $fileInfo['fileid'];
+ $fileInfo = $this->getFileInfo($file);
+ $fileId = $fileInfo->getId();
- if(is_null($fileId)) {
+ if (is_null($fileId)) {
return false;
}
$previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/';
- if(!$this->userView->is_dir($previewPath)) {
+ if (!$this->userView->is_dir($previewPath)) {
return false;
}
//does a preview with the wanted height and width already exist?
- if($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) {
+ if ($this->userView->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) {
return $previewPath . $maxX . '-' . $maxY . '.png';
}
- $wantedAspectRatio = (float) ($maxX / $maxY);
+ $wantedAspectRatio = (float)($maxX / $maxY);
//array for usable cached thumbnails
$possibleThumbnails = array();
$allThumbnails = $this->userView->getDirectoryContent($previewPath);
- foreach($allThumbnails as $thumbnail) {
+ foreach ($allThumbnails as $thumbnail) {
$name = rtrim($thumbnail['name'], '.png');
$size = explode('-', $name);
- $x = (int) $size[0];
- $y = (int) $size[1];
+ $x = (int)$size[0];
+ $y = (int)$size[1];
- $aspectRatio = (float) ($x / $y);
- if($aspectRatio !== $wantedAspectRatio) {
+ $aspectRatio = (float)($x / $y);
+ if ($aspectRatio !== $wantedAspectRatio) {
continue;
}
- if($x < $maxX || $y < $maxY) {
- if($scalingUp) {
+ if ($x < $maxX || $y < $maxY) {
+ if ($scalingUp) {
$scalefactor = $maxX / $x;
- if($scalefactor > $maxScaleFactor) {
+ if ($scalefactor > $maxScaleFactor) {
continue;
}
- }else{
+ } else {
continue;
}
}
$possibleThumbnails[$x] = $thumbnail['path'];
}
- if(count($possibleThumbnails) === 0) {
+ if (count($possibleThumbnails) === 0) {
return false;
}
- if(count($possibleThumbnails) === 1) {
+ if (count($possibleThumbnails) === 1) {
return current($possibleThumbnails);
}
ksort($possibleThumbnails);
- if(key(reset($possibleThumbnails)) > $maxX) {
+ if (key(reset($possibleThumbnails)) > $maxX) {
return current(reset($possibleThumbnails));
}
- if(key(end($possibleThumbnails)) < $maxX) {
+ if (key(end($possibleThumbnails)) < $maxX) {
return current(end($possibleThumbnails));
}
- foreach($possibleThumbnails as $width => $path) {
- if($width < $maxX) {
+ foreach ($possibleThumbnails as $width => $path) {
+ if ($width < $maxX) {
continue;
- }else{
+ } else {
return $path;
}
}
@@ -374,9 +402,9 @@ class Preview {
/**
* @brief return a preview of a file
* @return \OC_Image
- */
+ */
public function getPreview() {
- if(!is_null($this->preview) && $this->preview->valid()){
+ if (!is_null($this->preview) && $this->preview->valid()) {
return $this->preview;
}
@@ -386,22 +414,28 @@ class Preview {
$maxY = $this->getMaxY();
$scalingUp = $this->getScalingUp();
- $fileInfo = $this->fileView->getFileInfo($file);
- $fileId = $fileInfo['fileid'];
+ $fileInfo = $this->getFileInfo($file);
+ if($fileInfo === null || $fileInfo === false) {
+ return new \OC_Image();
+ }
+ $fileId = $fileInfo->getId();
$cached = $this->isCached();
- if($cached) {
- $image = new \OC_Image($this->userView->file_get_contents($cached, 'r'));
+ if ($cached) {
+ $stream = $this->userView->fopen($cached, 'r');
+ $image = new \OC_Image();
+ $image->loadFromFileHandle($stream);
$this->preview = $image->valid() ? $image : null;
$this->resizeAndCrop();
+ fclose($stream);
}
- if(is_null($this->preview)) {
+ if (is_null($this->preview)) {
$preview = null;
- foreach(self::$providers as $supportedMimetype => $provider) {
- if(!preg_match($supportedMimetype, $this->mimetype)) {
+ foreach (self::$providers as $supportedMimetype => $provider) {
+ if (!preg_match($supportedMimetype, $this->mimetype)) {
continue;
}
@@ -409,7 +443,7 @@ class Preview {
$preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingUp, $this->fileView);
- if(!($preview instanceof \OC_Image)) {
+ if (!($preview instanceof \OC_Image)) {
continue;
}
@@ -419,11 +453,11 @@ class Preview {
$previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/';
$cachePath = $previewPath . $maxX . '-' . $maxY . '.png';
- if($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) {
+ if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) {
$this->userView->mkdir($this->getThumbnailsFolder() . '/');
}
- if($this->userView->is_dir($previewPath) === false) {
+ if ($this->userView->is_dir($previewPath) === false) {
$this->userView->mkdir($previewPath);
}
@@ -433,7 +467,7 @@ class Preview {
}
}
- if(is_null($this->preview)) {
+ if (is_null($this->preview)) {
$this->preview = new \OC_Image();
}
@@ -443,20 +477,20 @@ class Preview {
/**
* @brief show preview
* @return void
- */
+ */
public function showPreview() {
\OCP\Response::enableCaching(3600 * 24); // 24 hours
- if(is_null($this->preview)) {
+ if (is_null($this->preview)) {
$this->getPreview();
}
- $this->preview->show();
+ $this->preview->show('image/png');
return;
}
/**
* @brief show preview
* @return void
- */
+ */
public function show() {
$this->showPreview();
return;
@@ -465,7 +499,7 @@ class Preview {
/**
* @brief resize, crop and fix orientation
* @return void
- */
+ */
private function resizeAndCrop() {
$image = $this->preview;
$x = $this->getMaxX();
@@ -473,17 +507,17 @@ class Preview {
$scalingUp = $this->getScalingUp();
$maxscalefactor = $this->getMaxScaleFactor();
- if(!($image instanceof \OC_Image)) {
+ if (!($image instanceof \OC_Image)) {
\OC_Log::write('core', '$this->preview is not an instance of OC_Image', \OC_Log::DEBUG);
return;
}
$image->fixOrientation();
- $realx = (int) $image->width();
- $realy = (int) $image->height();
+ $realx = (int)$image->width();
+ $realy = (int)$image->height();
- if($x === $realx && $y === $realy) {
+ if ($x === $realx && $y === $realy) {
$this->preview = $image;
return;
}
@@ -491,36 +525,36 @@ class Preview {
$factorX = $x / $realx;
$factorY = $y / $realy;
- if($factorX >= $factorY) {
+ if ($factorX >= $factorY) {
$factor = $factorX;
- }else{
+ } else {
$factor = $factorY;
}
- if($scalingUp === false) {
- if($factor > 1) {
+ if ($scalingUp === false) {
+ if ($factor > 1) {
$factor = 1;
}
}
- if(!is_null($maxscalefactor)) {
- if($factor > $maxscalefactor) {
+ if (!is_null($maxscalefactor)) {
+ if ($factor > $maxscalefactor) {
\OC_Log::write('core', 'scalefactor reduced from ' . $factor . ' to ' . $maxscalefactor, \OC_Log::DEBUG);
$factor = $maxscalefactor;
}
}
- $newXsize = (int) ($realx * $factor);
- $newYsize = (int) ($realy * $factor);
+ $newXsize = (int)($realx * $factor);
+ $newYsize = (int)($realy * $factor);
$image->preciseResize($newXsize, $newYsize);
- if($newXsize === $x && $newYsize === $y) {
+ if ($newXsize === $x && $newYsize === $y) {
$this->preview = $image;
return;
}
- if($newXsize >= $x && $newYsize >= $y) {
+ if ($newXsize >= $x && $newYsize >= $y) {
$cropX = floor(abs($x - $newXsize) * 0.5);
//don't crop previews on the Y axis, this sucks if it's a document.
//$cropY = floor(abs($y - $newYsize) * 0.5);
@@ -532,19 +566,19 @@ class Preview {
return;
}
- if($newXsize < $x || $newYsize < $y) {
- if($newXsize > $x) {
+ if ($newXsize < $x || $newYsize < $y) {
+ if ($newXsize > $x) {
$cropX = floor(($newXsize - $x) * 0.5);
$image->crop($cropX, 0, $x, $newYsize);
}
- if($newYsize > $y) {
+ if ($newYsize > $y) {
$cropY = floor(($newYsize - $y) * 0.5);
$image->crop(0, $cropY, $newXsize, $y);
}
- $newXsize = (int) $image->width();
- $newYsize = (int) $image->height();
+ $newXsize = (int)$image->width();
+ $newYsize = (int)$image->height();
//create transparent background layer
$backgroundlayer = imagecreatetruecolor($x, $y);
@@ -573,8 +607,8 @@ class Preview {
* @param array $options
* @return void
*/
- public static function registerProvider($class, $options=array()) {
- self::$registeredProviders[]=array('class'=>$class, 'options'=>$options);
+ public static function registerProvider($class, $options = array()) {
+ self::$registeredProviders[] = array('class' => $class, 'options' => $options);
}
/**
@@ -582,19 +616,19 @@ class Preview {
* @return void
*/
private static function initProviders() {
- if(!\OC_Config::getValue('enable_previews', true)) {
+ if (!\OC_Config::getValue('enable_previews', true)) {
$provider = new Preview\Unknown(array());
self::$providers = array($provider->getMimeType() => $provider);
return;
}
- if(count(self::$providers)>0) {
+ if (count(self::$providers) > 0) {
return;
}
- foreach(self::$registeredProviders as $provider) {
- $class=$provider['class'];
- $options=$provider['options'];
+ foreach (self::$registeredProviders as $provider) {
+ $class = $provider['class'];
+ $options = $provider['options'];
$object = new $class($options);
@@ -609,12 +643,35 @@ class Preview {
self::post_delete($args);
}
- public static function post_delete($args) {
+ public static function prepare_delete_files($args) {
+ self::prepare_delete($args, 'files/');
+ }
+
+ public static function prepare_delete($args, $prefix='') {
$path = $args['path'];
- if(substr($path, 0, 1) === '/') {
+ if (substr($path, 0, 1) === '/') {
$path = substr($path, 1);
}
- $preview = new Preview(\OC_User::getUser(), 'files/', $path);
+
+ $view = new \OC\Files\View('/' . \OC_User::getUser() . '/' . $prefix);
+ $info = $view->getFileInfo($path);
+
+ \OC\Preview::$deleteFileMapper = array_merge(
+ \OC\Preview::$deleteFileMapper,
+ array(
+ Files\Filesystem::normalizePath($view->getAbsolutePath($path)) => $info,
+ )
+ );
+ }
+
+ public static function post_delete_files($args) {
+ self::post_delete($args, 'files/');
+ }
+
+ public static function post_delete($args, $prefix='') {
+ $path = Files\Filesystem::normalizePath($args['path']);
+
+ $preview = new Preview(\OC_User::getUser(), $prefix, $path);
$preview->deleteAllPreviews();
}
@@ -622,19 +679,19 @@ class Preview {
* @param string $mimetype
*/
public static function isMimeSupported($mimetype) {
- if(!\OC_Config::getValue('enable_previews', true)) {
+ if (!\OC_Config::getValue('enable_previews', true)) {
return false;
}
//check if there are preview backends
- if(empty(self::$providers)) {
+ if (empty(self::$providers)) {
self::initProviders();
}
//remove last element because it has the mimetype *
$providers = array_slice(self::$providers, 0, -1);
- foreach($providers as $supportedMimetype => $provider) {
- if(preg_match($supportedMimetype, $mimetype)) {
+ foreach ($providers as $supportedMimetype => $provider) {
+ if (preg_match($supportedMimetype, $mimetype)) {
return true;
}
}
diff --git a/lib/private/preview/movies.php b/lib/private/preview/movies.php
index 71cd3bae057..7e0ff51ad2e 100644
--- a/lib/private/preview/movies.php
+++ b/lib/private/preview/movies.php
@@ -9,7 +9,7 @@
namespace OC\Preview;
function findBinaryPath($program) {
- exec('which ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode);
+ exec('command -v ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode);
if ($returnCode === 0 && count($output) > 0) {
return escapeshellcmd($output[0]);
}
diff --git a/lib/private/preview/office-cl.php b/lib/private/preview/office-cl.php
index b11fed13ba1..6e4d4321eb7 100644
--- a/lib/private/preview/office-cl.php
+++ b/lib/private/preview/office-cl.php
@@ -64,12 +64,12 @@ if (!\OC_Util::runningOnWindows()) {
$cmd = \OC_Config::getValue('preview_libreoffice_path', null);
}
- $whichLibreOffice = shell_exec('which libreoffice');
+ $whichLibreOffice = shell_exec('command -v libreoffice');
if($cmd === '' && !empty($whichLibreOffice)) {
$cmd = 'libreoffice';
}
- $whichOpenOffice = shell_exec('which openoffice');
+ $whichOpenOffice = shell_exec('command -v openoffice');
if($cmd === '' && !empty($whichOpenOffice)) {
$cmd = 'openoffice';
}
diff --git a/lib/private/preview/office.php b/lib/private/preview/office.php
index 02bb22e9b94..882c4426e6d 100644
--- a/lib/private/preview/office.php
+++ b/lib/private/preview/office.php
@@ -6,24 +6,29 @@
* See the COPYING-README file.
*/
//both, libreoffice backend and php fallback, need imagick
-if (extension_loaded('imagick') && count(@\Imagick::queryFormats("PDF")) === 1) {
- $isShellExecEnabled = \OC_Helper::is_function_enabled('shell_exec');
+if (extension_loaded('imagick')) {
- // LibreOffice preview is currently not supported on Windows
- if (!\OC_Util::runningOnWindows()) {
- $whichLibreOffice = ($isShellExecEnabled ? shell_exec('which libreoffice') : '');
- $isLibreOfficeAvailable = !empty($whichLibreOffice);
- $whichOpenOffice = ($isShellExecEnabled ? shell_exec('which libreoffice') : '');
- $isOpenOfficeAvailable = !empty($whichOpenOffice);
- //let's see if there is libreoffice or openoffice on this machine
- if($isShellExecEnabled && ($isLibreOfficeAvailable || $isOpenOfficeAvailable || is_string(\OC_Config::getValue('preview_libreoffice_path', null)))) {
- require_once('office-cl.php');
- }else{
+ $checkImagick = new Imagick();
+
+ if(count($checkImagick->queryFormats('PDF')) === 1) {
+ $isShellExecEnabled = \OC_Helper::is_function_enabled('shell_exec');
+
+ // LibreOffice preview is currently not supported on Windows
+ if (!\OC_Util::runningOnWindows()) {
+ $whichLibreOffice = ($isShellExecEnabled ? shell_exec('command -v libreoffice') : '');
+ $isLibreOfficeAvailable = !empty($whichLibreOffice);
+ $whichOpenOffice = ($isShellExecEnabled ? shell_exec('command -v libreoffice') : '');
+ $isOpenOfficeAvailable = !empty($whichOpenOffice);
+ //let's see if there is libreoffice or openoffice on this machine
+ if($isShellExecEnabled && ($isLibreOfficeAvailable || $isOpenOfficeAvailable || is_string(\OC_Config::getValue('preview_libreoffice_path', null)))) {
+ require_once('office-cl.php');
+ }else{
+ //in case there isn't, use our fallback
+ require_once('office-fallback.php');
+ }
+ } else {
//in case there isn't, use our fallback
require_once('office-fallback.php');
}
- } else {
- //in case there isn't, use our fallback
- require_once('office-fallback.php');
}
}
diff --git a/lib/private/preview/pdf.php b/lib/private/preview/pdf.php
index d390b4fc677..064a5a3b3d1 100644
--- a/lib/private/preview/pdf.php
+++ b/lib/private/preview/pdf.php
@@ -7,34 +7,41 @@
*/
namespace OC\Preview;
-if (extension_loaded('imagick') && count(@\Imagick::queryFormats("PDF")) === 1) {
+use Imagick;
- class PDF extends Provider {
+if (extension_loaded('imagick')) {
- public function getMimeType() {
- return '/application\/pdf/';
- }
+ $checkImagick = new Imagick();
+
+ if(count($checkImagick->queryFormats('PDF')) === 1) {
- public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
- $tmpPath = $fileview->toTmpFile($path);
+ class PDF extends Provider {
- //create imagick object from pdf
- try{
- $pdf = new \imagick($tmpPath . '[0]');
- $pdf->setImageFormat('jpg');
- } catch (\Exception $e) {
- \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR);
- return false;
+ public function getMimeType() {
+ return '/application\/pdf/';
}
- unlink($tmpPath);
+ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
+ $tmpPath = $fileview->toTmpFile($path);
+
+ //create imagick object from pdf
+ try{
+ $pdf = new Imagick($tmpPath . '[0]');
+ $pdf->setImageFormat('jpg');
+ } catch (\Exception $e) {
+ \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR);
+ return false;
+ }
+
+ unlink($tmpPath);
- //new image object
- $image = new \OC_Image($pdf);
- //check if image object is valid
- return $image->valid() ? $image : false;
+ //new image object
+ $image = new \OC_Image($pdf);
+ //check if image object is valid
+ return $image->valid() ? $image : false;
+ }
}
- }
- \OC\Preview::registerProvider('OC\Preview\PDF');
+ \OC\Preview::registerProvider('OC\Preview\PDF');
+ }
}
diff --git a/lib/private/preview/svg.php b/lib/private/preview/svg.php
index 9a73fff9467..505122fddbf 100644
--- a/lib/private/preview/svg.php
+++ b/lib/private/preview/svg.php
@@ -7,40 +7,46 @@
*/
namespace OC\Preview;
-if (extension_loaded('imagick') && count(@\Imagick::queryFormats("SVG")) === 1) {
+use Imagick;
- class SVG extends Provider {
+if (extension_loaded('imagick')) {
- public function getMimeType() {
- return '/image\/svg\+xml/';
- }
+ $checkImagick = new Imagick();
- public function getThumbnail($path,$maxX,$maxY,$scalingup,$fileview) {
- try{
- $svg = new \Imagick();
- $svg->setBackgroundColor(new \ImagickPixel('transparent'));
+ if(count($checkImagick->queryFormats('SVG')) === 1) {
- $content = stream_get_contents($fileview->fopen($path, 'r'));
- if(substr($content, 0, 5) !== '<?xml') {
- $content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content;
- }
+ class SVG extends Provider {
- $svg->readImageBlob($content);
- $svg->setImageFormat('png32');
- } catch (\Exception $e) {
- \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR);
- return false;
+ public function getMimeType() {
+ return '/image\/svg\+xml/';
}
+ public function getThumbnail($path,$maxX,$maxY,$scalingup,$fileview) {
+ try{
+ $svg = new Imagick();
+ $svg->setBackgroundColor(new \ImagickPixel('transparent'));
+
+ $content = stream_get_contents($fileview->fopen($path, 'r'));
+ if(substr($content, 0, 5) !== '<?xml') {
+ $content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content;
+ }
+
+ $svg->readImageBlob($content);
+ $svg->setImageFormat('png32');
+ } catch (\Exception $e) {
+ \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR);
+ return false;
+ }
- //new image object
- $image = new \OC_Image();
- $image->loadFromData($svg);
- //check if image object is valid
- return $image->valid() ? $image : false;
- }
- }
- \OC\Preview::registerProvider('OC\Preview\SVG');
+ //new image object
+ $image = new \OC_Image();
+ $image->loadFromData($svg);
+ //check if image object is valid
+ return $image->valid() ? $image : false;
+ }
+ }
+ \OC\Preview::registerProvider('OC\Preview\SVG');
+ }
} \ No newline at end of file
diff --git a/lib/private/request.php b/lib/private/request.php
index 59a9e395e85..8041c4f0048 100755
--- a/lib/private/request.php
+++ b/lib/private/request.php
@@ -13,6 +13,8 @@ class OC_Request {
const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
+ const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)(:[0-9]+|)$/';
+
/**
* @brief Check overwrite condition
* @param string $type
@@ -25,49 +27,91 @@ class OC_Request {
}
/**
- * @brief Checks whether a domain is considered as trusted. This is used to prevent Host Header Poisoning.
- * @param string $domain
- * @return bool
+ * @brief Checks whether a domain is considered as trusted from the list
+ * of trusted domains. If no trusted domains have been configured, returns
+ * true.
+ * This is used to prevent Host Header Poisoning.
+ * @param string $host
+ * @return bool true if the given domain is trusted or if no trusted domains
+ * have been configured
*/
public static function isTrustedDomain($domain) {
- $trustedList = \OC_Config::getValue('trusted_domains', array(''));
+ $trustedList = \OC_Config::getValue('trusted_domains', array());
+ if (empty($trustedList)) {
+ return true;
+ }
+ if (preg_match(self::REGEX_LOCALHOST, $domain) === 1) {
+ return true;
+ }
return in_array($domain, $trustedList);
}
/**
- * @brief Returns the server host
+ * @brief Returns the unverified server host from the headers without checking
+ * whether it is a trusted domain
* @returns string the server host
*
* Returns the server host, even if the website uses one or more
* reverse proxies
*/
- public static function serverHost() {
- if(OC::$CLI) {
- return 'localhost';
- }
- if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) {
- return OC_Config::getValue('overwritehost');
- }
+ public static function insecureServerHost() {
+ $host = null;
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) {
- $host = trim(array_pop(explode(",", $_SERVER['HTTP_X_FORWARDED_HOST'])));
- }
- else{
+ $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
+ $host = trim(current($parts));
+ } else {
$host = $_SERVER['HTTP_X_FORWARDED_HOST'];
}
} else {
if (isset($_SERVER['HTTP_HOST'])) {
$host = $_SERVER['HTTP_HOST'];
- }
- else if (isset($_SERVER['SERVER_NAME'])) {
+ } else if (isset($_SERVER['SERVER_NAME'])) {
$host = $_SERVER['SERVER_NAME'];
}
}
+ return $host;
+ }
+
+ /**
+ * Returns the overwritehost setting from the config if set and
+ * if the overwrite condition is met
+ * @return overwritehost value or null if not defined or the defined condition
+ * isn't met
+ */
+ public static function getOverwriteHost() {
+ if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) {
+ return OC_Config::getValue('overwritehost');
+ }
+ return null;
+ }
+
+ /**
+ * @brief Returns the server host from the headers, or the first configured
+ * trusted domain if the host isn't in the trusted list
+ * @returns string the server host
+ *
+ * Returns the server host, even if the website uses one or more
+ * reverse proxies
+ */
+ public static function serverHost() {
+ if(OC::$CLI) {
+ return 'localhost';
+ }
+
+ // overwritehost is always trusted
+ $host = self::getOverwriteHost();
+ if ($host !== null) {
+ return $host;
+ }
+
+ // get the host from the headers
+ $host = self::insecureServerHost();
// Verify that the host is a trusted domain if the trusted domains
// are defined
// If no trusted domain is provided the first trusted domain is returned
- if(self::isTrustedDomain($host) || \OC_Config::getValue('trusted_domains', "") === "") {
+ if (self::isTrustedDomain($host)) {
return $host;
} else {
$trustedList = \OC_Config::getValue('trusted_domains', array(''));
diff --git a/lib/private/response.php b/lib/private/response.php
index 71c538fb311..983c682bf3f 100644
--- a/lib/private/response.php
+++ b/lib/private/response.php
@@ -12,6 +12,7 @@ class OC_Response {
const STATUS_TEMPORARY_REDIRECT = 307;
const STATUS_NOT_FOUND = 404;
const STATUS_INTERNAL_SERVER_ERROR = 500;
+ const STATUS_SERVICE_UNAVAILABLE = 503;
/**
* @brief Enable response caching by sending correct HTTP headers
@@ -74,6 +75,9 @@ class OC_Response {
case self::STATUS_INTERNAL_SERVER_ERROR;
$status = $status . ' Internal Server Error';
break;
+ case self::STATUS_SERVICE_UNAVAILABLE;
+ $status = $status . ' Service Unavailable';
+ break;
}
header($protocol.' '.$status);
}
diff --git a/lib/private/route/cachingrouter.php b/lib/private/route/cachingrouter.php
new file mode 100644
index 00000000000..ad25372391f
--- /dev/null
+++ b/lib/private/route/cachingrouter.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Route;
+
+class CachingRouter extends Router {
+ /**
+ * @var \OCP\ICache
+ */
+ protected $cache;
+
+ /**
+ * @param \OCP\ICache $cache
+ */
+ public function __construct($cache) {
+ $this->cache = $cache;
+ parent::__construct();
+ }
+
+ /**
+ * Generate url based on $name and $parameters
+ *
+ * @param string $name Name of the route to use.
+ * @param array $parameters Parameters for the route
+ * @param bool $absolute
+ * @return string
+ */
+ public function generate($name, $parameters = array(), $absolute = false) {
+ $key = $name . json_encode($parameters) . $absolute;
+ if ($this->cache->hasKey($key)) {
+ return $this->cache->get($key);
+ } else {
+ $url = parent::generate($name, $parameters, $absolute);
+ $this->cache->set($key, $url, 3600);
+ return $url;
+ }
+ }
+}
diff --git a/lib/private/route.php b/lib/private/route/route.php
index fb7da456b62..6ade9ec15f6 100644
--- a/lib/private/route.php
+++ b/lib/private/route/route.php
@@ -6,13 +6,17 @@
* See the COPYING-README file.
*/
-use Symfony\Component\Routing\Route;
+namespace OC\Route;
-class OC_Route extends Route {
+use OCP\Route\IRoute;
+use Symfony\Component\Routing\Route as SymfonyRoute;
+
+class Route extends SymfonyRoute implements IRoute {
/**
* Specify the method when this route is to be used
*
* @param string $method HTTP method (uppercase)
+ * @return \OC\Route\Route
*/
public function method($method) {
$this->setRequirement('_method', strtoupper($method));
@@ -63,6 +67,7 @@ class OC_Route extends Route {
* Defaults to use for this route
*
* @param array $defaults The defaults
+ * @return \OC\Route\Route
*/
public function defaults($defaults) {
$action = $this->getDefault('action');
@@ -78,6 +83,7 @@ class OC_Route extends Route {
* Requirements for this route
*
* @param array $requirements The requirements
+ * @return \OC\Route\Route
*/
public function requirements($requirements) {
$method = $this->getRequirement('_method');
@@ -93,8 +99,10 @@ class OC_Route extends Route {
/**
* The action to execute when this route matches
+ *
* @param string|callable $class the class or a callable
* @param string $function the function to use with the class
+ * @return \OC\Route\Route
*
* This function is called with $class set to a callable or
* to the class with $function
diff --git a/lib/private/route/router.php b/lib/private/route/router.php
new file mode 100644
index 00000000000..1f0a23ee124
--- /dev/null
+++ b/lib/private/route/router.php
@@ -0,0 +1,233 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Route;
+
+use OCP\Route\IRouter;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RouteCollection;
+
+class Router implements IRouter {
+ /**
+ * @var \Symfony\Component\Routing\RouteCollection[]
+ */
+ protected $collections = array();
+
+ /**
+ * @var \Symfony\Component\Routing\RouteCollection
+ */
+ protected $collection = null;
+
+ /**
+ * @var \Symfony\Component\Routing\RouteCollection
+ */
+ protected $root = null;
+
+ /**
+ * @var \Symfony\Component\Routing\Generator\UrlGenerator
+ */
+ protected $generator = null;
+
+ /**
+ * @var string[]
+ */
+ protected $routingFiles;
+
+ /**
+ * @var string
+ */
+ protected $cacheKey;
+
+ protected $loaded = false;
+
+ protected $loadedApps = array();
+
+ public function __construct() {
+ $baseUrl = \OC_Helper::linkTo('', 'index.php');
+ if (!\OC::$CLI) {
+ $method = $_SERVER['REQUEST_METHOD'];
+ } else {
+ $method = 'GET';
+ }
+ $host = \OC_Request::serverHost();
+ $schema = \OC_Request::serverProtocol();
+ $this->context = new RequestContext($baseUrl, $method, $host, $schema);
+ // TODO cache
+ $this->root = $this->getCollection('root');
+ }
+
+ /**
+ * Get the files to load the routes from
+ *
+ * @return string[]
+ */
+ public function getRoutingFiles() {
+ if (!isset($this->routingFiles)) {
+ $this->routingFiles = array();
+ foreach (\OC_APP::getEnabledApps() as $app) {
+ $file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
+ if (file_exists($file)) {
+ $this->routingFiles[$app] = $file;
+ }
+ }
+ }
+ return $this->routingFiles;
+ }
+
+ public function getCacheKey() {
+ if (!isset($this->cacheKey)) {
+ $files = $this->getRoutingFiles();
+ $files[] = 'settings/routes.php';
+ $files[] = 'core/routes.php';
+ $files[] = 'ocs/routes.php';
+ $this->cacheKey = \OC_Cache::generateCacheKeyFromFiles($files);
+ }
+ return $this->cacheKey;
+ }
+
+ /**
+ * loads the api routes
+ */
+ public function loadRoutes($app = null) {
+ if ($this->loaded) {
+ return;
+ }
+ if (is_null($app)) {
+ $this->loaded = true;
+ $routingFiles = $this->getRoutingFiles();
+ } else {
+ if (isset($this->loadedApps[$app])) {
+ return;
+ }
+ $file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
+ if (file_exists($file)) {
+ $routingFiles = array($app => $file);
+ } else {
+ $routingFiles = array();
+ }
+ }
+ foreach ($routingFiles as $app => $file) {
+ if (!isset($this->loadedApps[$app])) {
+ $this->loadedApps[$app] = true;
+ $this->useCollection($app);
+ require_once $file;
+ $collection = $this->getCollection($app);
+ $collection->addPrefix('/apps/' . $app);
+ $this->root->addCollection($collection);
+ }
+ }
+ if (!isset($this->loadedApps['core'])) {
+ $this->loadedApps['core'] = true;
+ $this->useCollection('root');
+ require_once 'settings/routes.php';
+ require_once 'core/routes.php';
+
+ // include ocs routes
+ require_once 'ocs/routes.php';
+ $collection = $this->getCollection('ocs');
+ $collection->addPrefix('/ocs');
+ $this->root->addCollection($collection);
+ }
+ }
+
+ /**
+ * @param string $name
+ * @return \Symfony\Component\Routing\RouteCollection
+ */
+ protected function getCollection($name) {
+ if (!isset($this->collections[$name])) {
+ $this->collections[$name] = new RouteCollection();
+ }
+ return $this->collections[$name];
+ }
+
+ /**
+ * Sets the collection to use for adding routes
+ *
+ * @param string $name Name of the collection to use.
+ */
+ public function useCollection($name) {
+ $this->collection = $this->getCollection($name);
+ }
+
+ /**
+ * Create a \OC\Route\Route.
+ *
+ * @param string $name Name of the route to create.
+ * @param string $pattern The pattern to match
+ * @param array $defaults An array of default parameter values
+ * @param array $requirements An array of requirements for parameters (regexes)
+ * @return \OC\Route\Route
+ */
+ public function create($name, $pattern, array $defaults = array(), array $requirements = array()) {
+ $route = new Route($pattern, $defaults, $requirements);
+ $this->collection->add($name, $route);
+ return $route;
+ }
+
+ /**
+ * Find the route matching $url
+ *
+ * @param string $url The url to find
+ * @throws \Exception
+ */
+ public function match($url) {
+ if (substr($url, 0, 6) === '/apps/') {
+ // 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/') {
+ $this->loadRoutes('core');
+ } else {
+ $this->loadRoutes();
+ }
+ $matcher = new UrlMatcher($this->root, $this->context);
+ $parameters = $matcher->match($url);
+ if (isset($parameters['action'])) {
+ $action = $parameters['action'];
+ if (!is_callable($action)) {
+ var_dump($action);
+ throw new \Exception('not a callable action');
+ }
+ unset($parameters['action']);
+ call_user_func($action, $parameters);
+ } elseif (isset($parameters['file'])) {
+ include $parameters['file'];
+ } else {
+ throw new \Exception('no action available');
+ }
+ }
+
+ /**
+ * Get the url generator
+ *
+ */
+ public function getGenerator() {
+ if (null !== $this->generator) {
+ return $this->generator;
+ }
+
+ return $this->generator = new UrlGenerator($this->root, $this->context);
+ }
+
+ /**
+ * Generate url based on $name and $parameters
+ *
+ * @param string $name Name of the route to use.
+ * @param array $parameters Parameters for the route
+ * @param bool $absolute
+ * @return string
+ */
+ public function generate($name, $parameters = array(), $absolute = false) {
+ $this->loadRoutes();
+ return $this->getGenerator()->generate($name, $parameters, $absolute);
+ }
+
+}
diff --git a/lib/private/router.php b/lib/private/router.php
deleted file mode 100644
index 19c1e4473ec..00000000000
--- a/lib/private/router.php
+++ /dev/null
@@ -1,185 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-use Symfony\Component\Routing\Matcher\UrlMatcher;
-use Symfony\Component\Routing\Generator\UrlGenerator;
-use Symfony\Component\Routing\RequestContext;
-use Symfony\Component\Routing\RouteCollection;
-//use Symfony\Component\Routing\Route;
-
-class OC_Router {
- protected $collections = array();
- protected $collection = null;
- protected $root = null;
-
- protected $generator = null;
- protected $routing_files;
- protected $cache_key;
-
- public function __construct() {
- $baseUrl = OC_Helper::linkTo('', 'index.php');
- if ( !OC::$CLI) {
- $method = $_SERVER['REQUEST_METHOD'];
- }else{
- $method = 'GET';
- }
- $host = OC_Request::serverHost();
- $schema = OC_Request::serverProtocol();
- $this->context = new RequestContext($baseUrl, $method, $host, $schema);
- // TODO cache
- $this->root = $this->getCollection('root');
- }
-
- public function getRoutingFiles() {
- if (!isset($this->routing_files)) {
- $this->routing_files = array();
- foreach(OC_APP::getEnabledApps() as $app) {
- $file = OC_App::getAppPath($app).'/appinfo/routes.php';
- if(file_exists($file)) {
- $this->routing_files[$app] = $file;
- }
- }
- }
- return $this->routing_files;
- }
-
- public function getCacheKey() {
- if (!isset($this->cache_key)) {
- $files = $this->getRoutingFiles();
- $files[] = 'settings/routes.php';
- $files[] = 'core/routes.php';
- $files[] = 'ocs/routes.php';
- $this->cache_key = OC_Cache::generateCacheKeyFromFiles($files);
- }
- return $this->cache_key;
- }
-
- /**
- * loads the api routes
- */
- public function loadRoutes() {
- foreach($this->getRoutingFiles() as $app => $file) {
- $this->useCollection($app);
- require_once $file;
- $collection = $this->getCollection($app);
- $collection->addPrefix('/apps/'.$app);
- $this->root->addCollection($collection);
- }
- $this->useCollection('root');
- require_once 'settings/routes.php';
- require_once 'core/routes.php';
-
- // include ocs routes
- require_once 'ocs/routes.php';
- $collection = $this->getCollection('ocs');
- $collection->addPrefix('/ocs');
- $this->root->addCollection($collection);
- }
-
- protected function getCollection($name) {
- if (!isset($this->collections[$name])) {
- $this->collections[$name] = new RouteCollection();
- }
- return $this->collections[$name];
- }
-
- /**
- * Sets the collection to use for adding routes
- *
- * @param string $name Name of the colletion to use.
- */
- public function useCollection($name) {
- $this->collection = $this->getCollection($name);
- }
-
- /**
- * Create a OC_Route.
- *
- * @param string $name Name of the route to create.
- * @param string $pattern The pattern to match
- * @param array $defaults An array of default parameter values
- * @param array $requirements An array of requirements for parameters (regexes)
- */
- public function create($name, $pattern, array $defaults = array(), array $requirements = array()) {
- $route = new OC_Route($pattern, $defaults, $requirements);
- $this->collection->add($name, $route);
- return $route;
- }
-
- /**
- * Find the route matching $url.
- *
- * @param string $url The url to find
- */
- public function match($url) {
- $matcher = new UrlMatcher($this->root, $this->context);
- $parameters = $matcher->match($url);
- if (isset($parameters['action'])) {
- $action = $parameters['action'];
- if (!is_callable($action)) {
- var_dump($action);
- throw new Exception('not a callable action');
- }
- unset($parameters['action']);
- call_user_func($action, $parameters);
- } elseif (isset($parameters['file'])) {
- include $parameters['file'];
- } else {
- throw new Exception('no action available');
- }
- }
-
- /**
- * Get the url generator
- *
- */
- public function getGenerator()
- {
- if (null !== $this->generator) {
- return $this->generator;
- }
-
- return $this->generator = new UrlGenerator($this->root, $this->context);
- }
-
- /**
- * Generate url based on $name and $parameters
- *
- * @param string $name Name of the route to use.
- * @param array $parameters Parameters for the route
- */
- public function generate($name, $parameters = array(), $absolute = false)
- {
- return $this->getGenerator()->generate($name, $parameters, $absolute);
- }
-
- /**
- * Generate JSON response for routing in javascript
- */
- public static function JSRoutes()
- {
- $router = OC::getRouter();
-
- $etag = $router->getCacheKey();
- OC_Response::enableCaching();
- OC_Response::setETagHeader($etag);
-
- $root = $router->getCollection('root');
- $routes = array();
- foreach($root->all() as $name => $route) {
- $compiled_route = $route->compile();
- $defaults = $route->getDefaults();
- unset($defaults['action']);
- $routes[$name] = array(
- 'tokens' => $compiled_route->getTokens(),
- 'defaults' => $defaults,
- );
- }
- OCP\JSON::success ( array( 'data' => $routes ) );
- }
-}
diff --git a/lib/private/server.php b/lib/private/server.php
index 3255713e12a..3517d7b3548 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -158,6 +158,18 @@ class Server extends SimpleContainer implements IServerContainer {
$config = $c->getConfig();
return new \OC\BackgroundJob\JobList($c->getDatabaseConnection(), $config);
});
+ $this->registerService('Router', function ($c){
+ /**
+ * @var Server $c
+ */
+ $cacheFactory = $c->getMemCacheFactory();
+ if ($cacheFactory->isAvailable()) {
+ $router = new \OC\Route\CachingRouter($cacheFactory->create('route'));
+ } else {
+ $router = new \OC\Route\Router();
+ }
+ return $router;
+ });
}
/**
@@ -323,7 +335,7 @@ class Server extends SimpleContainer implements IServerContainer {
/**
* Returns an \OCP\CacheFactory instance
*
- * @return \OCP\CacheFactory
+ * @return \OCP\ICacheFactory
*/
function getMemCacheFactory() {
return $this->query('MemCacheFactory');
@@ -364,4 +376,13 @@ class Server extends SimpleContainer implements IServerContainer {
function getJobList(){
return $this->query('JobList');
}
+
+ /**
+ * Returns a router for generating and matching urls
+ *
+ * @return \OCP\Route\IRouter
+ */
+ function getRouter(){
+ return $this->query('Router');
+ }
}
diff --git a/lib/private/session/internal.php b/lib/private/session/internal.php
index a7c9e2fdefd..42ec9606dc9 100644
--- a/lib/private/session/internal.php
+++ b/lib/private/session/internal.php
@@ -26,8 +26,7 @@ class Internal extends Memory {
}
public function __destruct() {
- $_SESSION = array_merge($_SESSION, $this->data);
- session_write_close();
+ $this->close();
}
/**
@@ -47,4 +46,15 @@ class Internal extends Memory {
@session_start();
$this->data = $_SESSION = array();
}
+
+ public function close() {
+ $_SESSION = array_merge($_SESSION, $this->data);
+ session_write_close();
+
+ parent::close();
+ }
+
+ public function reopen() {
+ throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.');
+ }
}
diff --git a/lib/private/session/memory.php b/lib/private/session/memory.php
index 1b9ac452575..1497c0f8928 100644
--- a/lib/private/session/memory.php
+++ b/lib/private/session/memory.php
@@ -28,6 +28,7 @@ class Memory extends Session {
* @param integer $value
*/
public function set($key, $value) {
+ $this->validateSession();
$this->data[$key] = $value;
}
@@ -54,10 +55,29 @@ class Memory extends Session {
* @param string $key
*/
public function remove($key) {
+ $this->validateSession();
unset($this->data[$key]);
}
public function clear() {
$this->data = array();
}
+
+ /**
+ * Helper function for PHPUnit execution - don't use in non-test code
+ */
+ public function reopen() {
+ $this->sessionClosed = false;
+ }
+
+ /**
+ * In case the session has already been locked an exception will be thrown
+ *
+ * @throws \Exception
+ */
+ private function validateSession() {
+ if ($this->sessionClosed) {
+ throw new \Exception('Session has been closed - no further changes to the session as allowed');
+ }
+ }
}
diff --git a/lib/private/session/session.php b/lib/private/session/session.php
index fe160faa267..6f6c804f384 100644
--- a/lib/private/session/session.php
+++ b/lib/private/session/session.php
@@ -13,6 +13,11 @@ use OCP\ISession;
abstract class Session implements \ArrayAccess, ISession {
/**
+ * @var bool
+ */
+ protected $sessionClosed = false;
+
+ /**
* $name serves as a namespace for the session keys
*
* @param string $name
@@ -49,4 +54,11 @@ abstract class Session implements \ArrayAccess, ISession {
public function offsetUnset($offset) {
$this->remove($offset);
}
+
+ /**
+ * Close the session and release the lock
+ */
+ public function close() {
+ $this->sessionClosed = true;
+ }
}
diff --git a/lib/private/setup.php b/lib/private/setup.php
index 3906204bda3..b1061b3a25b 100644
--- a/lib/private/setup.php
+++ b/lib/private/setup.php
@@ -106,6 +106,10 @@ class OC_Setup {
//guess what this does
OC_Installer::installShippedApps();
+ // create empty file in data dir, so we can later find
+ // out that this is indeed an ownCloud data directory
+ file_put_contents(OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data').'/.ocdata', '');
+
//create htaccess files for apache hosts
if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) {
self::createHtaccess();
@@ -147,7 +151,7 @@ class OC_Setup {
$content.= "RewriteRule ^.well-known/host-meta /public.php?service=host-meta [QSA,L]\n";
$content.= "RewriteRule ^.well-known/carddav /remote.php/carddav/ [R]\n";
$content.= "RewriteRule ^.well-known/caldav /remote.php/caldav/ [R]\n";
- $content.= "RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L]\n";
+ $content.= "RewriteRule ^apps/([^/]*)/(.*\.(php))$ index.php?app=$1&getfile=$2 [QSA,L]\n";
$content.= "RewriteRule ^remote/(.*) remote.php [QSA,L]\n";
$content.= "</IfModule>\n";
$content.= "<IfModule mod_mime.c>\n";
diff --git a/lib/private/setup/postgresql.php b/lib/private/setup/postgresql.php
index 89d328ada19..4d0c9b52a4d 100644
--- a/lib/private/setup/postgresql.php
+++ b/lib/private/setup/postgresql.php
@@ -10,13 +10,20 @@ class PostgreSQL extends AbstractDatabase {
$e_user = addslashes($this->dbuser);
$e_password = addslashes($this->dbpassword);
+ // Fix database with port connection
+ if(strpos($e_host, ':')) {
+ list($e_host, $port)=explode(':', $e_host, 2);
+ } else {
+ $port=false;
+ }
+
//check if the database user has admin rights
- $connection_string = "host='$e_host' dbname=postgres user='$e_user' password='$e_password'";
+ $connection_string = "host='$e_host' dbname=postgres user='$e_user' port='$port' password='$e_password'";
$connection = @pg_connect($connection_string);
if(!$connection) {
// Try if we can connect to the DB with the specified name
$e_dbname = addslashes($this->dbname);
- $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' password='$e_password'";
+ $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' port='$port' password='$e_password'";
$connection = @pg_connect($connection_string);
if(!$connection)
@@ -63,7 +70,14 @@ class PostgreSQL extends AbstractDatabase {
$e_user = addslashes($this->dbuser);
$e_password = addslashes($this->dbpassword);
- $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' password='$e_password'";
+ // Fix database with port connection
+ if(strpos($e_host, ':')) {
+ list($e_host, $port)=explode(':', $e_host, 2);
+ } else {
+ $port=false;
+ }
+
+ $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' port='$port' password='$e_password'";
$connection = @pg_connect($connection_string);
if(!$connection) {
throw new \DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'),
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 afbf35aa397..4799db52330 100644
--- a/lib/private/share/mailnotifications.php
+++ b/lib/private/share/mailnotifications.php
@@ -96,7 +96,7 @@ class MailNotifications {
try {
\OCP\Util::sendMail($to, $recipientDisplayName, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail);
} catch (\Exception $e) {
- \OCP\Util::writeLog('sharing', "Can't send mail to inform the user abaut an internal share: " . $e->getMessage() , \OCP\Util::ERROR);
+ \OCP\Util::writeLog('sharing', "Can't send mail to inform the user about an internal share: " . $e->getMessage() , \OCP\Util::ERROR);
$noMail[] = $recipientDisplayName;
}
}
@@ -108,23 +108,26 @@ class MailNotifications {
/**
* @brief inform recipient about public link share
*
- * @param string recipient recipient email address
+ * @param string $recipient recipient email address
* @param string $filename the shared file
* @param string $link the public link
* @param int $expiration expiration date (timestamp)
- * @return string|boolean $result true or error message
+ * @return array $result of failed recipients
*/
public function sendLinkShareMail($recipient, $filename, $link, $expiration) {
$subject = (string)$this->l->t('%s shared »%s« with you', array($this->senderDisplayName, $filename));
list($htmlMail, $alttextMail) = $this->createMailBody($filename, $link, $expiration);
- try {
- \OCP\Util::sendMail($recipient, $recipient, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail);
- } catch (\Exception $e) {
- \OCP\Util::writeLog('sharing', "Can't send mail with public link: " . $e->getMessage(), \OCP\Util::ERROR);
- return $e->getMessage();
+ $rs = explode(' ', $recipient);
+ $failed = array();
+ foreach ($rs as $r) {
+ try {
+ \OCP\Util::sendMail($r, $r, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail);
+ } catch (\Exception $e) {
+ \OCP\Util::writeLog('sharing', "Can't send mail with public link to $r: " . $e->getMessage(), \OCP\Util::ERROR);
+ $failed[] = $r;
+ }
}
-
- return true;
+ return $failed;
}
/**
diff --git a/lib/private/share/share.php b/lib/private/share/share.php
new file mode 100644
index 00000000000..8238797600e
--- /dev/null
+++ b/lib/private/share/share.php
@@ -0,0 +1,1619 @@
+<?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 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`, `share_with`, `uid_owner`';
+ } else {
+ $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`';
+ }
+ } 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 8af2fce7737..3d7c685c1cf 100644
--- a/lib/private/template/base.php
+++ b/lib/private/template/base.php
@@ -61,7 +61,7 @@ class Base {
/**
* @brief Assign variables
* @param string $key key
- * @param mixed $value value
+ * @param array|bool|integer|string $value value
* @return bool
*
* This function assigns a variable. It can be accessed via $_[$key] in
diff --git a/lib/private/updater.php b/lib/private/updater.php
index f05d5038b76..9f57aa0b6a0 100644
--- a/lib/private/updater.php
+++ b/lib/private/updater.php
@@ -16,9 +16,6 @@ use OC\Hooks\BasicEmitter;
* - maintenanceStart()
* - maintenanceEnd()
* - dbUpgrade()
- * - filecacheStart()
- * - filecacheProgress(int $percentage)
- * - filecacheDone()
* - failure(string $message)
*/
class Updater extends BasicEmitter {
@@ -76,7 +73,9 @@ class Updater extends BasicEmitter {
if ($xml == false) {
return array();
}
+ $loadEntities = libxml_disable_entity_loader(true);
$data = @simplexml_load_string($xml);
+ libxml_disable_entity_loader($loadEntities);
$tmp = array();
$tmp['version'] = $data->version;
@@ -103,10 +102,15 @@ class Updater extends BasicEmitter {
}
$this->emit('\OC\Updater', 'maintenanceStart');
+ // create empty file in data dir, so we can later find
+ // out that this is indeed an ownCloud data directory
+ // (in case it didn't exist before)
+ file_put_contents(\OC_Config::getValue('datadirectory', \OC::$SERVERROOT.'/data').'/.ocdata', '');
+
/*
* START CONFIG CHANGES FOR OLDER VERSIONS
*/
- if (version_compare($currentVersion, '6.90.1', '<')) {
+ if (!\OC::$CLI && version_compare($installedVersion, '6.90.1', '<')) {
// Add the overwriteHost config if it is not existant
// This is added to prevent host header poisoning
\OC_Config::setValue('trusted_domains', \OC_Config::getValue('trusted_domains', array(\OC_Request::serverHost())));
@@ -120,9 +124,6 @@ class Updater extends BasicEmitter {
\OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
$this->emit('\OC\Updater', 'dbUpgrade');
- // do a file cache upgrade for users with files
- // this can take loooooooooooooooooooooooong
- $this->upgradeFileCache();
} catch (\Exception $exception) {
$this->emit('\OC\Updater', 'failure', array($exception->getMessage()));
}
@@ -134,46 +135,11 @@ class Updater extends BasicEmitter {
$repair = new Repair();
$repair->run();
+ //Invalidate update feed
+ \OC_Appconfig::setValue('core', 'lastupdatedat', 0);
\OC_Config::setValue('maintenance', false);
$this->emit('\OC\Updater', 'maintenanceEnd');
}
- private function upgradeFileCache() {
- try {
- $query = \OC_DB::prepare('
- SELECT DISTINCT `user`
- FROM `*PREFIX*fscache`
- ');
- $result = $query->execute();
- } catch (\Exception $e) {
- return;
- }
- $users = $result->fetchAll();
- if (count($users) == 0) {
- return;
- }
- $step = 100 / count($users);
- $percentCompleted = 0;
- $lastPercentCompletedOutput = 0;
- $startInfoShown = false;
- foreach ($users as $userRow) {
- $user = $userRow['user'];
- \OC\Files\Filesystem::initMountPoints($user);
- \OC\Files\Cache\Upgrade::doSilentUpgrade($user);
- if (!$startInfoShown) {
- //We show it only now, because otherwise Info about upgraded apps
- //will appear between this and progress info
- $this->emit('\OC\Updater', 'filecacheStart');
- $startInfoShown = true;
- }
- $percentCompleted += $step;
- $out = floor($percentCompleted);
- if ($out != $lastPercentCompletedOutput) {
- $this->emit('\OC\Updater', 'filecacheProgress', array($out));
- $lastPercentCompletedOutput = $out;
- }
- }
- $this->emit('\OC\Updater', 'filecacheDone');
- }
}
diff --git a/lib/private/urlgenerator.php b/lib/private/urlgenerator.php
index 60da34f2d6e..0d238737dde 100644
--- a/lib/private/urlgenerator.php
+++ b/lib/private/urlgenerator.php
@@ -39,7 +39,7 @@ class URLGenerator implements IURLGenerator {
* Returns a url to the given app and file.
*/
public function linkToRoute($route, $parameters = array()) {
- $urlLinkTo = \OC::getRouter()->generate($route, $parameters);
+ $urlLinkTo = \OC::$server->getRouter()->generate($route, $parameters);
return $urlLinkTo;
}
@@ -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,7 @@ class URLGenerator implements IURLGenerator {
*/
public function getAbsoluteURL($url) {
$separator = $url[0] === '/' ? '' : '/';
- return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . $separator . $url;
+
+ return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost(). \OC::$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/util.php b/lib/private/util.php
index 920161949ae..731b7c97503 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
@@ -290,13 +288,19 @@ class OC_Util {
* @return array arrays with error messages and hints
*/
public static function checkServer() {
+ $errors = array();
+ $CONFIG_DATADIRECTORY = OC_Config::getValue('datadirectory', OC::$SERVERROOT . '/data');
+
+ if (!\OC::needUpgrade() && OC_Config::getValue('installed', false)) {
+ // this check needs to be done every time
+ $errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
+ }
+
// Assume that if checkServer() succeeded before in this session, then all is fine.
if(\OC::$session->exists('checkServer_suceeded') && \OC::$session->get('checkServer_suceeded')) {
- return array();
+ return $errors;
}
- $errors = array();
-
$defaults = new \OC_Defaults();
$webServerRestart = false;
@@ -341,7 +345,6 @@ class OC_Util {
);
}
}
- $CONFIG_DATADIRECTORY = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" );
// Create root dir.
if(!is_dir($CONFIG_DATADIRECTORY)) {
$success=@mkdir($CONFIG_DATADIRECTORY);
@@ -485,6 +488,8 @@ class OC_Util {
);
}
+ $errors = array_merge($errors, self::checkDatabaseVersion());
+
// Cache the result of this function
\OC::$session->set('checkServer_suceeded', count($errors) == 0);
@@ -492,6 +497,39 @@ class OC_Util {
}
/**
+ * Check the database version
+ * @return array errors array
+ */
+ public static function checkDatabaseVersion() {
+ $errors = array();
+ $dbType = \OC_Config::getValue('dbtype', 'sqlite');
+ if ($dbType === 'pgsql') {
+ // check PostgreSQL version
+ try {
+ $result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
+ $data = $result->fetchRow();
+ if (isset($data['server_version'])) {
+ $version = $data['server_version'];
+ if (version_compare($version, '9.0.0', '<')) {
+ $errors[] = array(
+ 'error' => 'PostgreSQL >= 9 required',
+ 'hint' => 'Please upgrade your database version'
+ );
+ }
+ }
+ } catch (\Doctrine\DBAL\DBALException $e) {
+ \OCP\Util::logException('core', $e);
+ $errors[] = array(
+ 'error' => 'Error occurred while checking PostgreSQL version',
+ 'hint' => 'Please make sure you have PostgreSQL >= 9 or check the logs for more information about the error'
+ );
+ }
+ }
+ return $errors;
+ }
+
+
+ /**
* @brief check if there are still some encrypted files stored
* @return boolean
*/
@@ -526,7 +564,7 @@ class OC_Util {
.' cannot be listed by other users.';
$perms = substr(decoct(@fileperms($dataDirectory)), -3);
if (substr($perms, -1) != '0') {
- OC_Helper::chmodr($dataDirectory, 0770);
+ chmod($dataDirectory, 0770);
clearstatcache();
$perms = substr(decoct(@fileperms($dataDirectory)), -3);
if (substr($perms, 2, 1) != '0') {
@@ -541,6 +579,25 @@ class OC_Util {
}
/**
+ * Check that the data directory exists and is valid by
+ * checking the existence of the ".ocdata" file.
+ *
+ * @param string $dataDirectory data directory path
+ * @return bool true if the data directory is valid, false otherwise
+ */
+ public static function checkDataDirectoryValidity($dataDirectory) {
+ $errors = array();
+ if (!file_exists($dataDirectory.'/.ocdata')) {
+ $errors[] = array(
+ 'error' => 'Data directory (' . $dataDirectory . ') is invalid',
+ 'hint' => 'Please check that the data directory contains a file' .
+ ' ".ocdata" in its root.'
+ );
+ }
+ return $errors;
+ }
+
+ /**
* @return void
*/
public static function displayLoginPage($errors = array()) {
@@ -644,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);
@@ -1015,13 +1073,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', '') != '') {
@@ -1030,9 +1088,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;
@@ -1061,13 +1160,22 @@ class OC_Util {
}
/**
- * @return bool - well are we running on windows or not
+ * Checks whether the server is running on Windows
+ * @return bool true if running on Windows, false otherwise
*/
public static function runningOnWindows() {
return (substr(PHP_OS, 0, 3) === "WIN");
}
/**
+ * Checks whether the server is running on Mac OS X
+ * @return bool true if running on Mac OS X, false otherwise
+ */
+ public static function runningOnMac() {
+ return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
+ }
+
+ /**
* Handles the case that there may not be a theme, then check if a "default"
* theme exists and take that one
* @return string the theme