summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/activitymanager.php196
-rw-r--r--lib/private/api.php115
-rw-r--r--lib/private/app.php26
-rw-r--r--lib/private/app/codechecker/abstractcheck.php139
-rw-r--r--lib/private/app/codechecker/codechecker.php (renamed from lib/private/app/codechecker.php)42
-rw-r--r--lib/private/app/codechecker/deprecationcheck.php147
-rw-r--r--lib/private/app/codechecker/emptycheck.php67
-rw-r--r--lib/private/app/codechecker/icheck.php55
-rw-r--r--lib/private/app/codechecker/nodevisitor.php306
-rw-r--r--lib/private/app/codechecker/privatecheck.php86
-rw-r--r--lib/private/app/codechecker/strongcomparisoncheck.php80
-rw-r--r--lib/private/app/codecheckvisitor.php133
-rw-r--r--lib/private/appframework/db/db.php9
-rw-r--r--lib/private/appframework/dependencyinjection/dicontainer.php69
-rw-r--r--lib/private/appframework/http/request.php16
-rw-r--r--lib/private/appframework/middleware/security/corsmiddleware.php37
-rw-r--r--lib/private/appframework/middleware/security/securitymiddleware.php14
-rw-r--r--lib/private/appframework/utility/simplecontainer.php51
-rw-r--r--lib/private/capabilitiesmanager.php64
-rw-r--r--lib/private/connector/sabre/exceptionloggerplugin.php3
-rw-r--r--lib/private/connector/sabre/file.php5
-rw-r--r--lib/private/connector/sabre/filesplugin.php18
-rw-r--r--lib/private/connector/sabre/objecttree.php6
-rw-r--r--lib/private/connector/sabre/serverfactory.php107
-rw-r--r--lib/private/db.php2
-rw-r--r--lib/private/db/connection.php52
-rw-r--r--lib/private/db/connectionfactory.php6
-rw-r--r--lib/private/db/mdb2schemamanager.php3
-rw-r--r--lib/private/db/querybuilder/compositeexpression.php92
-rw-r--r--lib/private/db/querybuilder/expressionbuilder.php312
-rw-r--r--lib/private/db/querybuilder/literal.php40
-rw-r--r--lib/private/db/querybuilder/parameter.php40
-rw-r--r--lib/private/db/querybuilder/querybuilder.php1013
-rw-r--r--lib/private/db/querybuilder/queryfunction.php40
-rw-r--r--lib/private/db/querybuilder/quotehelper.php75
-rw-r--r--lib/private/encryption/util.php29
-rw-r--r--lib/private/files.php91
-rw-r--r--lib/private/files/cache/storage.php48
-rw-r--r--lib/private/files/node/root.php29
-rw-r--r--lib/private/files/objectstore/objectstorestorage.php5
-rw-r--r--lib/private/files/storage/common.php21
-rw-r--r--lib/private/files/storage/dav.php55
-rw-r--r--lib/private/files/storage/local.php1
-rw-r--r--lib/private/files/storage/wrapper/availability.php462
-rw-r--r--lib/private/files/storage/wrapper/encryption.php122
-rw-r--r--lib/private/files/storage/wrapper/permissionsmask.php4
-rw-r--r--lib/private/files/storage/wrapper/wrapper.php18
-rw-r--r--lib/private/files/type/detection.php129
-rw-r--r--lib/private/files/utils/scanner.php3
-rw-r--r--lib/private/files/view.php15
-rw-r--r--lib/private/helper.php165
-rw-r--r--lib/private/installer.php4
-rw-r--r--lib/private/memcache/factory.php49
-rw-r--r--lib/private/mimetypes.list.php200
-rw-r--r--lib/private/ocs.php204
-rw-r--r--lib/private/ocs/cloud.php9
-rw-r--r--lib/private/ocs/corecapabilities.php56
-rw-r--r--lib/private/ocs/exception.php (renamed from lib/private/db/mssqlmigrator.php)17
-rw-r--r--lib/private/ocs/privatedata.php2
-rw-r--r--lib/private/ocs/result.php47
-rw-r--r--lib/private/ocsclient.php2
-rw-r--r--lib/private/repair.php10
-rw-r--r--lib/private/route/router.php6
-rw-r--r--lib/private/security/crypto.php17
-rw-r--r--lib/private/server.php81
-rw-r--r--lib/private/setup.php63
-rw-r--r--lib/private/setup/abstractdatabase.php47
-rw-r--r--lib/private/setup/mssql.php203
-rw-r--r--lib/private/setup/mysql.php187
-rw-r--r--lib/private/setup/oci.php38
-rw-r--r--lib/private/setup/postgresql.php40
-rw-r--r--lib/private/share/hooks.php115
-rw-r--r--lib/private/share/share.php103
-rw-r--r--lib/private/systemconfig.php8
-rw-r--r--lib/private/template.php4
-rw-r--r--lib/private/updater.php7
-rw-r--r--lib/private/user.php2
-rw-r--r--lib/private/user/manager.php2
-rw-r--r--lib/private/util.php17
79 files changed, 4659 insertions, 1544 deletions
diff --git a/lib/private/activitymanager.php b/lib/private/activitymanager.php
index 7b1d5d29f2e..938335a87e1 100644
--- a/lib/private/activitymanager.php
+++ b/lib/private/activitymanager.php
@@ -56,14 +56,16 @@ class ActivityManager implements IManager {
$this->config = $config;
}
- /**
- * @var \Closure[]
- */
+ /** @var \Closure[] */
+ private $consumersClosures = array();
+
+ /** @var IConsumer[] */
private $consumers = array();
- /**
- * @var \Closure[]
- */
+ /** @var \Closure[] */
+ private $extensionsClosures = array();
+
+ /** @var IExtension[] */
private $extensions = array();
/** @var array list of filters "name" => "is valid" */
@@ -80,6 +82,48 @@ class ActivityManager implements IManager {
protected $specialParameters = array();
/**
+ * @return \OCP\Activity\IConsumer[]
+ */
+ protected function getConsumers() {
+ if (!empty($this->consumers)) {
+ return $this->consumers;
+ }
+
+ $this->consumers = [];
+ foreach($this->consumersClosures as $consumer) {
+ $c = $consumer();
+ if ($c instanceof IConsumer) {
+ $this->consumers[] = $c;
+ } else {
+ throw new \InvalidArgumentException('The given consumer does not implement the \OCP\Activity\IConsumer interface');
+ }
+ }
+
+ return $this->consumers;
+ }
+
+ /**
+ * @return \OCP\Activity\IExtension[]
+ */
+ protected function getExtensions() {
+ if (!empty($this->extensions)) {
+ return $this->extensions;
+ }
+
+ $this->extensions = [];
+ foreach($this->extensionsClosures as $extension) {
+ $e = $extension();
+ if ($e instanceof IExtension) {
+ $this->extensions[] = $e;
+ } else {
+ throw new \InvalidArgumentException('The given extension does not implement the \OCP\Activity\IExtension interface');
+ }
+ }
+
+ return $this->extensions;
+ }
+
+ /**
* @param $app
* @param $subject
* @param $subjectParams
@@ -93,10 +137,8 @@ class ActivityManager implements IManager {
* @return mixed
*/
function publishActivity($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority) {
- foreach($this->consumers as $consumer) {
- $c = $consumer();
- if ($c instanceof IConsumer) {
- try {
+ foreach($this->getConsumers() as $c) {
+ try {
$c->receive(
$app,
$subject,
@@ -108,11 +150,9 @@ class ActivityManager implements IManager {
$affectedUser,
$type,
$priority);
- } catch (\Exception $ex) {
- // TODO: log the exception
- }
+ } catch (\Exception $ex) {
+ // TODO: log the exception
}
-
}
}
@@ -125,7 +165,8 @@ class ActivityManager implements IManager {
* @param \Closure $callable
*/
function registerConsumer(\Closure $callable) {
- array_push($this->consumers, $callable);
+ array_push($this->consumersClosures, $callable);
+ $this->consumers = [];
}
/**
@@ -138,7 +179,8 @@ class ActivityManager implements IManager {
* @return void
*/
function registerExtension(\Closure $callable) {
- array_push($this->extensions, $callable);
+ array_push($this->extensionsClosures, $callable);
+ $this->extensions = [];
}
/**
@@ -149,13 +191,10 @@ class ActivityManager implements IManager {
*/
function getNotificationTypes($languageCode) {
$notificationTypes = array();
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $result = $c->getNotificationTypes($languageCode);
- if (is_array($result)) {
- $notificationTypes = array_merge($notificationTypes, $result);
- }
+ foreach ($this->getExtensions() as $c) {
+ $result = $c->getNotificationTypes($languageCode);
+ if (is_array($result)) {
+ $notificationTypes = array_merge($notificationTypes, $result);
}
}
@@ -168,13 +207,10 @@ class ActivityManager implements IManager {
*/
function getDefaultTypes($method) {
$defaultTypes = array();
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $types = $c->getDefaultTypes($method);
- if (is_array($types)) {
- $defaultTypes = array_merge($types, $defaultTypes);
- }
+ foreach ($this->getExtensions() as $c) {
+ $types = $c->getDefaultTypes($method);
+ if (is_array($types)) {
+ $defaultTypes = array_merge($types, $defaultTypes);
}
}
return $defaultTypes;
@@ -189,14 +225,11 @@ class ActivityManager implements IManager {
return $this->typeIcons[$type];
}
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $icon = $c->getTypeIcon($type);
- if (is_string($icon)) {
- $this->typeIcons[$type] = $icon;
- return $icon;
- }
+ foreach ($this->getExtensions() as $c) {
+ $icon = $c->getTypeIcon($type);
+ if (is_string($icon)) {
+ $this->typeIcons[$type] = $icon;
+ return $icon;
}
}
@@ -214,13 +247,10 @@ class ActivityManager implements IManager {
* @return string|false
*/
function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) {
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $translation = $c->translate($app, $text, $params, $stripPath, $highlightParams, $languageCode);
- if (is_string($translation)) {
- return $translation;
- }
+ foreach ($this->getExtensions() as $c) {
+ $translation = $c->translate($app, $text, $params, $stripPath, $highlightParams, $languageCode);
+ if (is_string($translation)) {
+ return $translation;
}
}
@@ -241,14 +271,11 @@ class ActivityManager implements IManager {
$this->specialParameters[$app] = array();
}
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $specialParameter = $c->getSpecialParameterList($app, $text);
- if (is_array($specialParameter)) {
- $this->specialParameters[$app][$text] = $specialParameter;
- return $specialParameter;
- }
+ foreach ($this->getExtensions() as $c) {
+ $specialParameter = $c->getSpecialParameterList($app, $text);
+ if (is_array($specialParameter)) {
+ $this->specialParameters[$app][$text] = $specialParameter;
+ return $specialParameter;
}
}
@@ -261,13 +288,10 @@ class ActivityManager implements IManager {
* @return integer|false
*/
function getGroupParameter($activity) {
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $parameter = $c->getGroupParameter($activity);
- if ($parameter !== false) {
- return $parameter;
- }
+ foreach ($this->getExtensions() as $c) {
+ $parameter = $c->getGroupParameter($activity);
+ if ($parameter !== false) {
+ return $parameter;
}
}
@@ -282,14 +306,11 @@ class ActivityManager implements IManager {
'apps' => array(),
'top' => array(),
);
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $additionalEntries = $c->getNavigation();
- if (is_array($additionalEntries)) {
- $entries['apps'] = array_merge($entries['apps'], $additionalEntries['apps']);
- $entries['top'] = array_merge($entries['top'], $additionalEntries['top']);
- }
+ foreach ($this->getExtensions() as $c) {
+ $additionalEntries = $c->getNavigation();
+ if (is_array($additionalEntries)) {
+ $entries['apps'] = array_merge($entries['apps'], $additionalEntries['apps']);
+ $entries['top'] = array_merge($entries['top'], $additionalEntries['top']);
}
}
@@ -305,13 +326,10 @@ class ActivityManager implements IManager {
return $this->validFilters[$filterValue];
}
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- if ($c->isFilterValid($filterValue) === true) {
- $this->validFilters[$filterValue] = true;
- return true;
- }
+ foreach ($this->getExtensions() as $c) {
+ if ($c->isFilterValid($filterValue) === true) {
+ $this->validFilters[$filterValue] = true;
+ return true;
}
}
@@ -329,13 +347,10 @@ class ActivityManager implements IManager {
return $types;
}
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $result = $c->filterNotificationTypes($types, $filter);
- if (is_array($result)) {
- $types = $result;
- }
+ foreach ($this->getExtensions() as $c) {
+ $result = $c->filterNotificationTypes($types, $filter);
+ if (is_array($result)) {
+ $types = $result;
}
}
return $types;
@@ -353,16 +368,13 @@ class ActivityManager implements IManager {
$conditions = array();
$parameters = array();
- foreach($this->extensions as $extension) {
- $c = $extension();
- if ($c instanceof IExtension) {
- $result = $c->getQueryForFilter($filter);
- if (is_array($result)) {
- list($condition, $parameter) = $result;
- if ($condition && is_array($parameter)) {
- $conditions[] = $condition;
- $parameters = array_merge($parameters, $parameter);
- }
+ foreach ($this->getExtensions() as $c) {
+ $result = $c->getQueryForFilter($filter);
+ if (is_array($result)) {
+ list($condition, $parameter) = $result;
+ if ($condition && is_array($parameter)) {
+ $conditions[] = $condition;
+ $parameters = array_merge($parameters, $parameter);
}
}
}
diff --git a/lib/private/api.php b/lib/private/api.php
index 8e483b7efe9..3c599de25a9 100644
--- a/lib/private/api.php
+++ b/lib/private/api.php
@@ -1,4 +1,7 @@
<?php
+use OCP\API;
+use OCP\AppFramework\Http;
+
/**
* @author Bart Visscher <bartv@thisnet.nl>
* @author Bernhard Posselt <dev@bernhard-posselt.com>
@@ -82,7 +85,7 @@ class OC_API {
* @param array $requirements
*/
public static function register($method, $url, $action, $app,
- $authLevel = \OCP\API::USER_AUTH,
+ $authLevel = API::USER_AUTH,
$defaults = array(),
$requirements = array()) {
$name = strtolower($method).$url;
@@ -123,7 +126,7 @@ class OC_API {
if(!self::isAuthorised($action)) {
$responses[] = array(
'app' => $action['app'],
- 'response' => new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED, 'Unauthorised'),
+ 'response' => new OC_OCS_Result(null, API::RESPOND_UNAUTHORISED, 'Unauthorised'),
'shipped' => OC_App::isShipped($action['app']),
);
continue;
@@ -131,7 +134,7 @@ class OC_API {
if(!is_callable($action['action'])) {
$responses[] = array(
'app' => $action['app'],
- 'response' => new OC_OCS_Result(null, \OCP\API::RESPOND_NOT_FOUND, 'Api method not found'),
+ 'response' => new OC_OCS_Result(null, API::RESPOND_NOT_FOUND, 'Api method not found'),
'shipped' => OC_App::isShipped($action['app']),
);
continue;
@@ -252,15 +255,15 @@ class OC_API {
private static function isAuthorised($action) {
$level = $action['authlevel'];
switch($level) {
- case \OCP\API::GUEST_AUTH:
+ case API::GUEST_AUTH:
// Anyone can access
return true;
break;
- case \OCP\API::USER_AUTH:
+ case API::USER_AUTH:
// User required
return self::loginUser();
break;
- case \OCP\API::SUBADMIN_AUTH:
+ case API::SUBADMIN_AUTH:
// Check for subadmin
$user = self::loginUser();
if(!$user) {
@@ -275,7 +278,7 @@ class OC_API {
}
}
break;
- case \OCP\API::ADMIN_AUTH:
+ case API::ADMIN_AUTH:
// Check for admin
$user = self::loginUser();
if(!$user) {
@@ -342,28 +345,28 @@ class OC_API {
*/
public static function respond($result, $format='xml') {
// Send 401 headers if unauthorised
- if($result->getStatusCode() === \OCP\API::RESPOND_UNAUTHORISED) {
+ if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) {
header('WWW-Authenticate: Basic realm="Authorisation Required"');
header('HTTP/1.0 401 Unauthorized');
}
- $response = array(
- 'ocs' => array(
- 'meta' => $result->getMeta(),
- 'data' => $result->getData(),
- ),
- );
- if ($format == 'json') {
- OC_JSON::encodedPrint($response);
- } else if ($format == 'xml') {
- header('Content-type: text/xml; charset=UTF-8');
- $writer = new XMLWriter();
- $writer->openMemory();
- $writer->setIndent( true );
- $writer->startDocument();
- self::toXML($response, $writer);
- $writer->endDocument();
- echo $writer->outputMemory(true);
+
+ foreach($result->getHeaders() as $name => $value) {
+ header($name . ': ' . $value);
}
+
+ $meta = $result->getMeta();
+ $data = $result->getData();
+ if (self::isV2()) {
+ $statusCode = self::mapStatusCodes($result->getStatusCode());
+ if (!is_null($statusCode)) {
+ $meta['statuscode'] = $statusCode;
+ OC_Response::setStatus($statusCode);
+ }
+ }
+
+ self::setContentType($format);
+ $body = self::renderResult($format, $meta, $data);
+ echo $body;
}
/**
@@ -400,8 +403,8 @@ class OC_API {
/**
* Based on the requested format the response content type is set
*/
- public static function setContentType() {
- $format = self::requestedFormat();
+ public static function setContentType($format = null) {
+ $format = is_null($format) ? self::requestedFormat() : $format;
if ($format === 'xml') {
header('Content-type: text/xml; charset=UTF-8');
return;
@@ -415,5 +418,63 @@ class OC_API {
header('Content-Type: application/octet-stream; charset=utf-8');
}
+ /**
+ * @return boolean
+ */
+ private static function isV2() {
+ $request = \OC::$server->getRequest();
+ $script = $request->getScriptName();
+
+ return $script === '/ocs/v2.php';
+ }
+ /**
+ * @param integer $sc
+ * @return int
+ */
+ public static function mapStatusCodes($sc) {
+ switch ($sc) {
+ case API::RESPOND_NOT_FOUND:
+ return Http::STATUS_NOT_FOUND;
+ case API::RESPOND_SERVER_ERROR:
+ return Http::STATUS_INTERNAL_SERVER_ERROR;
+ case API::RESPOND_UNKNOWN_ERROR:
+ return Http::STATUS_INTERNAL_SERVER_ERROR;
+ case API::RESPOND_UNAUTHORISED:
+ // already handled for v1
+ return null;
+ case 100:
+ return Http::STATUS_OK;
+ }
+ // any 2xx, 4xx and 5xx will be used as is
+ if ($sc >= 200 && $sc < 600) {
+ return $sc;
+ }
+
+ return Http::STATUS_BAD_REQUEST;
+ }
+
+ /**
+ * @param string $format
+ * @return string
+ */
+ public static function renderResult($format, $meta, $data) {
+ $response = array(
+ 'ocs' => array(
+ 'meta' => $meta,
+ 'data' => $data,
+ ),
+ );
+ if ($format == 'json') {
+ return OC_JSON::encode($response);
+ }
+
+ $writer = new XMLWriter();
+ $writer->openMemory();
+ $writer->setIndent(true);
+ $writer->startDocument();
+ self::toXML($response, $writer);
+ $writer->endDocument();
+ return $writer->outputMemory(true);
+ }
}
diff --git a/lib/private/app.php b/lib/private/app.php
index 1e49fdc6010..9de1c66ee55 100644
--- a/lib/private/app.php
+++ b/lib/private/app.php
@@ -74,6 +74,16 @@ class OC_App {
}
/**
+ * Check if an app is loaded
+ *
+ * @param string $app
+ * @return bool
+ */
+ public static function isAppLoaded($app) {
+ return in_array($app, self::$loadedApps, true);
+ }
+
+ /**
* loads all apps
*
* @param array $types
@@ -779,7 +789,7 @@ class OC_App {
if (is_resource($dh)) {
while (($file = readdir($dh)) !== false) {
- if ($file[0] != '.' and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
+ if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
$apps[] = $file;
@@ -1162,9 +1172,7 @@ class OC_App {
OC_DB::updateDbFromStructure(self::getAppPath($appId) . '/appinfo/database.xml');
}
unset(self::$appVersion[$appId]);
- if (!self::isEnabled($appId)) {
- return false;
- }
+ // run upgrade code
if (file_exists(self::getAppPath($appId) . '/appinfo/update.php')) {
self::loadApp($appId, false);
include self::getAppPath($appId) . '/appinfo/update.php';
@@ -1173,15 +1181,15 @@ class OC_App {
//set remote/public handlers
$appData = self::getAppInfo($appId);
if (array_key_exists('ocsid', $appData)) {
- \OC::$server->getAppConfig()->setValue($appId, 'ocsid', $appData['ocsid']);
- } elseif(\OC::$server->getAppConfig()->getValue($appId, 'ocsid', null) !== null) {
- \OC::$server->getAppConfig()->deleteKey($appId, 'ocsid');
+ \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
+ } elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
+ \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
}
foreach ($appData['remote'] as $name => $path) {
- OCP\CONFIG::setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
+ \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
}
foreach ($appData['public'] as $name => $path) {
- OCP\CONFIG::setAppValue('core', 'public_' . $name, $appId . '/' . $path);
+ \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
}
self::setAppTypes($appId);
diff --git a/lib/private/app/codechecker/abstractcheck.php b/lib/private/app/codechecker/abstractcheck.php
new file mode 100644
index 00000000000..c1c6524e42f
--- /dev/null
+++ b/lib/private/app/codechecker/abstractcheck.php
@@ -0,0 +1,139 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\App\CodeChecker;
+
+abstract class AbstractCheck implements ICheck {
+ /** @var ICheck */
+ protected $check;
+
+ /**
+ * @param ICheck $check
+ */
+ public function __construct(ICheck $check) {
+ $this->check = $check;
+ }
+
+ /**
+ * @param int $errorCode
+ * @param string $errorObject
+ * @return string
+ */
+ public function getDescription($errorCode, $errorObject) {
+ switch ($errorCode) {
+ case CodeChecker::STATIC_CALL_NOT_ALLOWED:
+ $functions = $this->getLocalFunctions();
+ $functions = array_change_key_case($functions, CASE_LOWER);
+ if (isset($functions[$errorObject])) {
+ return $this->getLocalDescription();
+ }
+ // no break;
+ case CodeChecker::CLASS_EXTENDS_NOT_ALLOWED:
+ case CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED:
+ case CodeChecker::CLASS_NEW_NOT_ALLOWED:
+ case CodeChecker::CLASS_USE_NOT_ALLOWED:
+ $classes = $this->getLocalClasses();
+ $classes = array_change_key_case($classes, CASE_LOWER);
+ if (isset($classes[$errorObject])) {
+ return $this->getLocalDescription();
+ }
+ break;
+
+ case CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED:
+ $constants = $this->getLocalConstants();
+ $constants = array_change_key_case($constants, CASE_LOWER);
+ if (isset($constants[$errorObject])) {
+ return $this->getLocalDescription();
+ }
+ break;
+
+ case CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED:
+ $methods = $this->getLocalMethods();
+ $methods = array_change_key_case($methods, CASE_LOWER);
+ if (isset($methods[$errorObject])) {
+ return $this->getLocalDescription();
+ }
+ break;
+ }
+
+ return $this->check->getDescription($errorCode, $errorObject);
+ }
+
+ /**
+ * @return string
+ */
+ abstract protected function getLocalDescription();
+
+ /**
+ * @return array
+ */
+ abstract protected function getLocalClasses();
+
+ /**
+ * @return array
+ */
+ abstract protected function getLocalConstants();
+
+ /**
+ * @return array
+ */
+ abstract protected function getLocalFunctions();
+
+ /**
+ * @return array
+ */
+ abstract protected function getLocalMethods();
+
+ /**
+ * @return array E.g.: `'ClassName' => 'oc version',`
+ */
+ public function getClasses() {
+ return array_merge($this->getLocalClasses(), $this->check->getClasses());
+ }
+
+ /**
+ * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',`
+ */
+ public function getConstants() {
+ return array_merge($this->getLocalConstants(), $this->check->getConstants());
+ }
+
+ /**
+ * @return array E.g.: `'functionName' => 'oc version',`
+ */
+ public function getFunctions() {
+ return array_merge($this->getLocalFunctions(), $this->check->getFunctions());
+ }
+
+ /**
+ * @return array E.g.: `'ClassName::methodName' => 'oc version',`
+ */
+ public function getMethods() {
+ return array_merge($this->getLocalMethods(), $this->check->getMethods());
+ }
+
+ /**
+ * @return bool
+ */
+ public function checkStrongComparisons() {
+ return $this->check->checkStrongComparisons();
+ }
+}
diff --git a/lib/private/app/codechecker.php b/lib/private/app/codechecker/codechecker.php
index 3f6cd19d6b4..ef7dc7f3e4d 100644
--- a/lib/private/app/codechecker.php
+++ b/lib/private/app/codechecker/codechecker.php
@@ -21,14 +21,13 @@
*
*/
-namespace OC\App;
+namespace OC\App\CodeChecker;
use OC\Hooks\BasicEmitter;
use PhpParser\Lexer;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\NodeTraverser;
-use PhpParser\NodeVisitorAbstract;
use PhpParser\Parser;
use RecursiveCallbackFilterIterator;
use RecursiveDirectoryIterator;
@@ -42,43 +41,20 @@ class CodeChecker extends BasicEmitter {
const CLASS_IMPLEMENTS_NOT_ALLOWED = 1001;
const STATIC_CALL_NOT_ALLOWED = 1002;
const CLASS_CONST_FETCH_NOT_ALLOWED = 1003;
- const CLASS_NEW_FETCH_NOT_ALLOWED = 1004;
+ const CLASS_NEW_NOT_ALLOWED = 1004;
const OP_OPERATOR_USAGE_DISCOURAGED = 1005;
+ const CLASS_USE_NOT_ALLOWED = 1006;
+ const CLASS_METHOD_CALL_NOT_ALLOWED = 1007;
/** @var Parser */
private $parser;
- /** @var string[] */
- private $blackListedClassNames;
+ /** @var ICheck */
+ protected $checkList;
- public function __construct() {
+ public function __construct(ICheck $checkList) {
+ $this->checkList = $checkList;
$this->parser = new Parser(new Lexer);
- $this->blackListedClassNames = [
- // classes replaced by the public api
- 'OC_API',
- 'OC_App',
- 'OC_AppConfig',
- 'OC_Avatar',
- 'OC_BackgroundJob',
- 'OC_Config',
- 'OC_DB',
- 'OC_Files',
- 'OC_Helper',
- 'OC_Hook',
- 'OC_Image',
- 'OC_JSON',
- 'OC_L10N',
- 'OC_Log',
- 'OC_Mail',
- 'OC_Preferences',
- 'OC_Search_Provider',
- 'OC_Search_Result',
- 'OC_Request',
- 'OC_Response',
- 'OC_Template',
- 'OC_User',
- 'OC_Util',
- ];
}
/**
@@ -138,7 +114,7 @@ class CodeChecker extends BasicEmitter {
$code = file_get_contents($file);
$statements = $this->parser->parse($code);
- $visitor = new CodeCheckVisitor($this->blackListedClassNames);
+ $visitor = new NodeVisitor($this->checkList);
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
diff --git a/lib/private/app/codechecker/deprecationcheck.php b/lib/private/app/codechecker/deprecationcheck.php
new file mode 100644
index 00000000000..3b6dc968bb5
--- /dev/null
+++ b/lib/private/app/codechecker/deprecationcheck.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\App\CodeChecker;
+
+class DeprecationCheck extends AbstractCheck implements ICheck {
+ /**
+ * @return string
+ */
+ protected function getLocalDescription() {
+ return 'deprecated';
+ }
+
+ /**
+ * @return array E.g.: `'ClassName' => 'oc version',`
+ */
+ protected function getLocalClasses() {
+ return [
+ 'OCP\Config' => '8.0.0',
+ 'OCP\Contacts' => '8.1.0',
+ 'OCP\DB' => '8.1.0',
+ 'OCP\IHelper' => '8.1.0',
+ 'OCP\JSON' => '8.1.0',
+ 'OCP\Response' => '8.1.0',
+ 'OCP\AppFramework\IApi' => '8.0.0',
+ ];
+ }
+
+ /**
+ * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',`
+ */
+ protected function getLocalConstants() {
+ return [
+ 'OCP::PERMISSION_CREATE' => '8.0.0',
+ 'OCP::PERMISSION_READ' => '8.0.0',
+ 'OCP::PERMISSION_UPDATE' => '8.0.0',
+ 'OCP::PERMISSION_DELETE' => '8.0.0',
+ 'OCP::PERMISSION_SHARE' => '8.0.0',
+ 'OCP::PERMISSION_ALL' => '8.0.0',
+ 'OCP::FILENAME_INVALID_CHARS' => '8.0.0',
+ ];
+ }
+
+ /**
+ * @return array E.g.: `'functionName' => 'oc version',`
+ */
+ protected function getLocalFunctions() {
+ return [
+ 'OCP::image_path' => '8.0.0',
+ 'OCP::mimetype_icon' => '8.0.0',
+ 'OCP::preview_icon' => '8.0.0',
+ 'OCP::publicPreview_icon' => '8.0.0',
+ 'OCP::human_file_size' => '8.0.0',
+ 'OCP::relative_modified_date' => '8.0.0',
+ 'OCP::simple_file_size' => '8.0.0',
+ 'OCP::html_select_options' => '8.0.0',
+ ];
+ }
+
+ /**
+ * @return array E.g.: `'ClassName::methodName' => 'oc version',`
+ */
+ protected function getLocalMethods() {
+ return [
+ 'OCP\App::register' => '8.1.0',
+ 'OCP\App::addNavigationEntry' => '8.1.0',
+ 'OCP\App::setActiveNavigationEntry' => '8.1.0',
+
+ 'OCP\AppFramework\Controller::params' => '7.0.0',
+ 'OCP\AppFramework\Controller::getParams' => '7.0.0',
+ 'OCP\AppFramework\Controller::method' => '7.0.0',
+ 'OCP\AppFramework\Controller::getUploadedFile' => '7.0.0',
+ 'OCP\AppFramework\Controller::env' => '7.0.0',
+ 'OCP\AppFramework\Controller::cookie' => '7.0.0',
+ 'OCP\AppFramework\Controller::render' => '7.0.0',
+
+ 'OCP\AppFramework\IAppContainer::getCoreApi' => '8.0.0',
+ 'OCP\AppFramework\IAppContainer::isLoggedIn' => '8.0.0',
+ 'OCP\AppFramework\IAppContainer::isAdminUser' => '8.0.0',
+ 'OCP\AppFramework\IAppContainer::log' => '8.0.0',
+
+ 'OCP\BackgroundJob::addQueuedTask' => '6.0.0',
+ 'OCP\BackgroundJob::addRegularTask' => '6.0.0',
+ 'OCP\BackgroundJob::allQueuedTasks' => '6.0.0',
+ 'OCP\BackgroundJob::allRegularTasks' => '6.0.0',
+ 'OCP\BackgroundJob::deleteQueuedTask' => '6.0.0',
+ 'OCP\BackgroundJob::findQueuedTask' => '6.0.0',
+ 'OCP\BackgroundJob::queuedTaskWhereAppIs' => '6.0.0',
+ 'OCP\BackgroundJob::registerJob' => '8.1.0',
+
+ 'OCP\Files::tmpFile' => '8.1.0',
+ 'OCP\Files::tmpFolder' => '8.1.0',
+
+ 'OCP\IAppConfig::getValue' => '8.0.0',
+ 'OCP\IAppConfig::deleteKey' => '8.0.0',
+ 'OCP\IAppConfig::getKeys' => '8.0.0',
+ 'OCP\IAppConfig::setValue' => '8.0.0',
+ 'OCP\IAppConfig::deleteApp' => '8.0.0',
+
+ 'OCP\ISearch::search' => '8.0.0',
+
+ 'OCP\IServerContainer::getDb' => '8.1.0',
+ 'OCP\IServerContainer::getHTTPHelper' => '8.1.0',
+
+ 'OCP\User::getUser' => '8.0.0',
+ 'OCP\User::getUsers' => '8.1.0',
+ 'OCP\User::getDisplayName' => '8.1.0',
+ 'OCP\User::getDisplayNames' => '8.1.0',
+ 'OCP\User::userExists' => '8.1.0',
+ 'OCP\User::logout' => '8.1.0',
+ 'OCP\User::checkPassword' => '8.1.0',
+
+ 'OCP\Util::sendMail' => '8.1.0',
+ 'OCP\Util::formatDate' => '8.0.0',
+ 'OCP\Util::encryptedFiles' => '8.1.0',
+ 'OCP\Util::linkToRoute' => '8.1.0',
+ 'OCP\Util::linkTo' => '8.1.0',
+ 'OCP\Util::getServerHost' => '8.1.0',
+ 'OCP\Util::getServerProtocol' => '8.1.0',
+ 'OCP\Util::getRequestUri' => '8.1.0',
+ 'OCP\Util::getScriptName' => '8.1.0',
+ 'OCP\Util::imagePath' => '8.1.0',
+ 'OCP\Util::isValidFileName' => '8.1.0',
+ 'OCP\Util::generateRandomBytes' => '8.1.0',
+ 'OCP\Util::mb_str_replace' => '8.2.0',
+ 'OCP\Util::mb_substr_replace' => '8.2.0',
+ ];
+ }
+}
diff --git a/lib/private/app/codechecker/emptycheck.php b/lib/private/app/codechecker/emptycheck.php
new file mode 100644
index 00000000000..0e5df55d090
--- /dev/null
+++ b/lib/private/app/codechecker/emptycheck.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\App\CodeChecker;
+
+class EmptyCheck implements ICheck {
+ /**
+ * @param int $errorCode
+ * @param string $errorObject
+ * @return string
+ */
+ public function getDescription($errorCode, $errorObject) {
+ return '';
+ }
+
+ /**
+ * @return array E.g.: `'ClassName' => 'oc version',`
+ */
+ public function getClasses() {
+ return [];
+ }
+
+ /**
+ * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',`
+ */
+ public function getConstants() {
+ return [];
+ }
+
+ /**
+ * @return array E.g.: `'functionName' => 'oc version',`
+ */
+ public function getFunctions() {
+ return [];
+ }
+
+ /**
+ * @return array E.g.: `'ClassName::methodName' => 'oc version',`
+ */
+ public function getMethods() {
+ return [];
+ }
+
+ /**
+ * @return bool
+ */
+ public function checkStrongComparisons() {
+ return false;
+ }
+}
diff --git a/lib/private/app/codechecker/icheck.php b/lib/private/app/codechecker/icheck.php
new file mode 100644
index 00000000000..a00e0d8fa13
--- /dev/null
+++ b/lib/private/app/codechecker/icheck.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\App\CodeChecker;
+
+interface ICheck {
+ /**
+ * @param int $errorCode
+ * @param string $errorObject
+ * @return string
+ */
+ public function getDescription($errorCode, $errorObject);
+
+ /**
+ * @return array E.g.: `'ClassName' => 'oc version',`
+ */
+ public function getClasses();
+
+ /**
+ * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',`
+ */
+ public function getConstants();
+
+ /**
+ * @return array E.g.: `'functionName' => 'oc version',`
+ */
+ public function getFunctions();
+
+ /**
+ * @return array E.g.: `'ClassName::methodName' => 'oc version',`
+ */
+ public function getMethods();
+
+ /**
+ * @return bool
+ */
+ public function checkStrongComparisons();
+}
diff --git a/lib/private/app/codechecker/nodevisitor.php b/lib/private/app/codechecker/nodevisitor.php
new file mode 100644
index 00000000000..a22f852f36a
--- /dev/null
+++ b/lib/private/app/codechecker/nodevisitor.php
@@ -0,0 +1,306 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\App\CodeChecker;
+
+use PhpParser\Node;
+use PhpParser\Node\Name;
+use PhpParser\NodeVisitorAbstract;
+
+class NodeVisitor extends NodeVisitorAbstract {
+ /** @var ICheck */
+ protected $list;
+
+ /** @var string */
+ protected $blackListDescription;
+ /** @var string[] */
+ protected $blackListedClassNames;
+ /** @var string[] */
+ protected $blackListedConstants;
+ /** @var string[] */
+ protected $blackListedFunctions;
+ /** @var string[] */
+ protected $blackListedMethods;
+ /** @var bool */
+ protected $checkEqualOperatorUsage;
+ /** @var string[] */
+ protected $errorMessages;
+
+ /**
+ * @param ICheck $list
+ */
+ public function __construct(ICheck $list) {
+ $this->list = $list;
+
+ $this->blackListedClassNames = [];
+ foreach ($list->getClasses() as $class => $blackListInfo) {
+ if (is_numeric($class) && is_string($blackListInfo)) {
+ $class = $blackListInfo;
+ $blackListInfo = null;
+ }
+
+ $class = strtolower($class);
+ $this->blackListedClassNames[$class] = $class;
+ }
+
+ $this->blackListedConstants = [];
+ foreach ($list->getConstants() as $constantName => $blackListInfo) {
+ $constantName = strtolower($constantName);
+ $this->blackListedConstants[$constantName] = $constantName;
+ }
+
+ $this->blackListedFunctions = [];
+ foreach ($list->getFunctions() as $functionName => $blackListInfo) {
+ $functionName = strtolower($functionName);
+ $this->blackListedFunctions[$functionName] = $functionName;
+ }
+
+ $this->blackListedMethods = [];
+ foreach ($list->getMethods() as $functionName => $blackListInfo) {
+ $functionName = strtolower($functionName);
+ $this->blackListedMethods[$functionName] = $functionName;
+ }
+
+ $this->checkEqualOperatorUsage = $list->checkStrongComparisons();
+
+ $this->errorMessages = [
+ CodeChecker::CLASS_EXTENDS_NOT_ALLOWED => "%s class must not be extended",
+ CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED => "%s interface must not be implemented",
+ CodeChecker::STATIC_CALL_NOT_ALLOWED => "Static method of %s class must not be called",
+ CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED => "Constant of %s class must not not be fetched",
+ CodeChecker::CLASS_NEW_NOT_ALLOWED => "%s class must not be instantiated",
+ CodeChecker::CLASS_USE_NOT_ALLOWED => "%s class must not be imported with a use statement",
+ CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED => "Method of %s class must not be called",
+
+ CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED => "is discouraged",
+ ];
+ }
+
+ /** @var array */
+ public $errors = [];
+
+ public function enterNode(Node $node) {
+ if ($this->checkEqualOperatorUsage && $node instanceof Node\Expr\BinaryOp\Equal) {
+ $this->errors[]= [
+ 'disallowedToken' => '==',
+ 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED,
+ 'line' => $node->getLine(),
+ 'reason' => $this->buildReason('==', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED)
+ ];
+ }
+ if ($this->checkEqualOperatorUsage && $node instanceof Node\Expr\BinaryOp\NotEqual) {
+ $this->errors[]= [
+ 'disallowedToken' => '!=',
+ 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED,
+ 'line' => $node->getLine(),
+ 'reason' => $this->buildReason('!=', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED)
+ ];
+ }
+ if ($node instanceof Node\Stmt\Class_) {
+ if (!is_null($node->extends)) {
+ $this->checkBlackList($node->extends->toString(), CodeChecker::CLASS_EXTENDS_NOT_ALLOWED, $node);
+ }
+ foreach ($node->implements as $implements) {
+ $this->checkBlackList($implements->toString(), CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED, $node);
+ }
+ }
+ if ($node instanceof Node\Expr\StaticCall) {
+ if (!is_null($node->class)) {
+ if ($node->class instanceof Name) {
+ $this->checkBlackList($node->class->toString(), CodeChecker::STATIC_CALL_NOT_ALLOWED, $node);
+
+ $this->checkBlackListFunction($node->class->toString(), $node->name, $node);
+ $this->checkBlackListMethod($node->class->toString(), $node->name, $node);
+ }
+
+ if ($node->class instanceof Node\Expr\Variable) {
+ /**
+ * TODO: find a way to detect something like this:
+ * $c = "OC_API";
+ * $n = $c::call();
+ */
+ // $this->checkBlackListMethod($node->class->..., $node->name, $node);
+ }
+ }
+ }
+ if ($node instanceof Node\Expr\MethodCall) {
+ if (!is_null($node->var)) {
+ if ($node->var instanceof Node\Expr\Variable) {
+ /**
+ * TODO: find a way to detect something like this:
+ * $c = new OC_API();
+ * $n = $c::call();
+ * $n = $c->call();
+ */
+ // $this->checkBlackListMethod($node->var->..., $node->name, $node);
+ }
+ }
+ }
+ if ($node instanceof Node\Expr\ClassConstFetch) {
+ if (!is_null($node->class)) {
+ if ($node->class instanceof Name) {
+ $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED, $node);
+ }
+ if ($node->class instanceof Node\Expr\Variable) {
+ /**
+ * TODO: find a way to detect something like this:
+ * $c = "OC_API";
+ * $n = $i::ADMIN_AUTH;
+ */
+ }
+
+ $this->checkBlackListConstant($node->class->toString(), $node->name, $node);
+ }
+ }
+ if ($node instanceof Node\Expr\New_) {
+ if (!is_null($node->class)) {
+ if ($node->class instanceof Name) {
+ $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_NEW_NOT_ALLOWED, $node);
+ }
+ if ($node->class instanceof Node\Expr\Variable) {
+ /**
+ * TODO: find a way to detect something like this:
+ * $c = "OC_API";
+ * $n = new $i;
+ */
+ }
+ }
+ }
+ if ($node instanceof Node\Stmt\UseUse) {
+ $this->checkBlackList($node->name->toString(), CodeChecker::CLASS_USE_NOT_ALLOWED, $node);
+ if ($node->alias) {
+ $this->addUseNameToBlackList($node->name->toString(), $node->alias);
+ } else {
+ $this->addUseNameToBlackList($node->name->toString(), $node->name->getLast());
+ }
+ }
+ }
+
+ /**
+ * Check whether an alias was introduced for a namespace of a blacklisted class
+ *
+ * Example:
+ * - Blacklist entry: OCP\AppFramework\IApi
+ * - Name: OCP\AppFramework
+ * - Alias: OAF
+ * => new blacklist entry: OAF\IApi
+ *
+ * @param string $name
+ * @param string $alias
+ */
+ private function addUseNameToBlackList($name, $alias) {
+ $name = strtolower($name);
+ $alias = strtolower($alias);
+
+ foreach ($this->blackListedClassNames as $blackListedAlias => $blackListedClassName) {
+ if (strpos($blackListedClassName, $name . '\\') === 0) {
+ $aliasedClassName = str_replace($name, $alias, $blackListedClassName);
+ $this->blackListedClassNames[$aliasedClassName] = $blackListedClassName;
+ }
+ }
+
+ foreach ($this->blackListedConstants as $blackListedAlias => $blackListedConstant) {
+ if (strpos($blackListedConstant, $name . '\\') === 0 || strpos($blackListedConstant, $name . '::') === 0) {
+ $aliasedConstantName = str_replace($name, $alias, $blackListedConstant);
+ $this->blackListedConstants[$aliasedConstantName] = $blackListedConstant;
+ }
+ }
+
+ foreach ($this->blackListedFunctions as $blackListedAlias => $blackListedFunction) {
+ if (strpos($blackListedFunction, $name . '\\') === 0 || strpos($blackListedFunction, $name . '::') === 0) {
+ $aliasedFunctionName = str_replace($name, $alias, $blackListedFunction);
+ $this->blackListedFunctions[$aliasedFunctionName] = $blackListedFunction;
+ }
+ }
+
+ foreach ($this->blackListedMethods as $blackListedAlias => $blackListedMethod) {
+ if (strpos($blackListedMethod, $name . '\\') === 0 || strpos($blackListedMethod, $name . '::') === 0) {
+ $aliasedMethodName = str_replace($name, $alias, $blackListedMethod);
+ $this->blackListedMethods[$aliasedMethodName] = $blackListedMethod;
+ }
+ }
+ }
+
+ private function checkBlackList($name, $errorCode, Node $node) {
+ $lowerName = strtolower($name);
+
+ if (isset($this->blackListedClassNames[$lowerName])) {
+ $this->errors[]= [
+ 'disallowedToken' => $name,
+ 'errorCode' => $errorCode,
+ 'line' => $node->getLine(),
+ 'reason' => $this->buildReason($this->blackListedClassNames[$lowerName], $errorCode)
+ ];
+ }
+ }
+
+ private function checkBlackListConstant($class, $constantName, Node $node) {
+ $name = $class . '::' . $constantName;
+ $lowerName = strtolower($name);
+
+ if (isset($this->blackListedConstants[$lowerName])) {
+ $this->errors[]= [
+ 'disallowedToken' => $name,
+ 'errorCode' => CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED,
+ 'line' => $node->getLine(),
+ 'reason' => $this->buildReason($this->blackListedConstants[$lowerName], CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED)
+ ];
+ }
+ }
+
+ private function checkBlackListFunction($class, $functionName, Node $node) {
+ $name = $class . '::' . $functionName;
+ $lowerName = strtolower($name);
+
+ if (isset($this->blackListedFunctions[$lowerName])) {
+ $this->errors[]= [
+ 'disallowedToken' => $name,
+ 'errorCode' => CodeChecker::STATIC_CALL_NOT_ALLOWED,
+ 'line' => $node->getLine(),
+ 'reason' => $this->buildReason($this->blackListedFunctions[$lowerName], CodeChecker::STATIC_CALL_NOT_ALLOWED)
+ ];
+ }
+ }
+
+ private function checkBlackListMethod($class, $functionName, Node $node) {
+ $name = $class . '::' . $functionName;
+ $lowerName = strtolower($name);
+
+ if (isset($this->blackListedMethods[$lowerName])) {
+ $this->errors[]= [
+ 'disallowedToken' => $name,
+ 'errorCode' => CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED,
+ 'line' => $node->getLine(),
+ 'reason' => $this->buildReason($this->blackListedMethods[$lowerName], CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED)
+ ];
+ }
+ }
+
+ private function buildReason($name, $errorCode) {
+ if (isset($this->errorMessages[$errorCode])) {
+ $desc = $this->list->getDescription($errorCode, $name);
+ return sprintf($this->errorMessages[$errorCode], $desc);
+ }
+
+ return "$name usage not allowed - error: $errorCode";
+ }
+}
diff --git a/lib/private/app/codechecker/privatecheck.php b/lib/private/app/codechecker/privatecheck.php
new file mode 100644
index 00000000000..d6f4eb06981
--- /dev/null
+++ b/lib/private/app/codechecker/privatecheck.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\App\CodeChecker;
+
+class PrivateCheck extends AbstractCheck implements ICheck {
+ /**
+ * @return string
+ */
+ protected function getLocalDescription() {
+ return 'private';
+ }
+
+ /**
+ * @return array
+ */
+ public function getLocalClasses() {
+ return [
+ // classes replaced by the public api
+ 'OC_API' => '6.0.0',
+ 'OC_App' => '6.0.0',
+ 'OC_AppConfig' => '6.0.0',
+ 'OC_Avatar' => '6.0.0',
+ 'OC_BackgroundJob' => '6.0.0',
+ 'OC_Config' => '6.0.0',
+ 'OC_DB' => '6.0.0',
+ 'OC_Files' => '6.0.0',
+ 'OC_Helper' => '6.0.0',
+ 'OC_Hook' => '6.0.0',
+ 'OC_Image' => '6.0.0',
+ 'OC_JSON' => '6.0.0',
+ 'OC_L10N' => '6.0.0',
+ 'OC_Log' => '6.0.0',
+ 'OC_Mail' => '6.0.0',
+ 'OC_Preferences' => '6.0.0',
+ 'OC_Search_Provider' => '6.0.0',
+ 'OC_Search_Result' => '6.0.0',
+ 'OC_Request' => '6.0.0',
+ 'OC_Response' => '6.0.0',
+ 'OC_Template' => '6.0.0',
+ 'OC_User' => '6.0.0',
+ 'OC_Util' => '6.0.0',
+ ];
+ }
+
+ /**
+ * @return array
+ */
+ public function getLocalConstants() {
+ return [];
+ }
+
+ /**
+ * @return array
+ */
+ public function getLocalFunctions() {
+ return [];
+ }
+
+ /**
+ * @return array
+ */
+ public function getLocalMethods() {
+ return [];
+ }
+}
diff --git a/lib/private/app/codechecker/strongcomparisoncheck.php b/lib/private/app/codechecker/strongcomparisoncheck.php
new file mode 100644
index 00000000000..7de0fe3e5c3
--- /dev/null
+++ b/lib/private/app/codechecker/strongcomparisoncheck.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\App\CodeChecker;
+
+class StrongComparisonCheck implements ICheck {
+ /** @var ICheck */
+ protected $check;
+
+ /**
+ * @param ICheck $check
+ */
+ public function __construct(ICheck $check) {
+ $this->check = $check;
+ }
+
+ /**
+ * @param int $errorCode
+ * @param string $errorObject
+ * @return string
+ */
+ public function getDescription($errorCode, $errorObject) {
+ return $this->check->getDescription($errorCode, $errorObject);
+ }
+
+ /**
+ * @return array
+ */
+ public function getClasses() {
+ return $this->check->getClasses();
+ }
+
+ /**
+ * @return array
+ */
+ public function getConstants() {
+ return $this->check->getConstants();
+ }
+
+ /**
+ * @return array
+ */
+ public function getFunctions() {
+ return $this->check->getFunctions();
+ }
+
+ /**
+ * @return array
+ */
+ public function getMethods() {
+ return $this->check->getMethods();
+ }
+
+ /**
+ * @return bool
+ */
+ public function checkStrongComparisons() {
+ return true;
+ }
+}
diff --git a/lib/private/app/codecheckvisitor.php b/lib/private/app/codecheckvisitor.php
deleted file mode 100644
index e983bd8630b..00000000000
--- a/lib/private/app/codecheckvisitor.php
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-/**
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @copyright Copyright (c) 2015, ownCloud, Inc.
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OC\App;
-
-use PhpParser\Node;
-use PhpParser\Node\Name;
-use PhpParser\NodeVisitorAbstract;
-
-class CodeCheckVisitor extends NodeVisitorAbstract {
-
- public function __construct($blackListedClassNames) {
- $this->blackListedClassNames = array_map('strtolower', $blackListedClassNames);
- }
-
- public $errors = [];
-
- public function enterNode(Node $node) {
- if ($node instanceof Node\Expr\BinaryOp\Equal) {
- $this->errors[]= [
- 'disallowedToken' => '==',
- 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED,
- 'line' => $node->getLine(),
- 'reason' => $this->buildReason('==', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED)
- ];
- }
- if ($node instanceof Node\Expr\BinaryOp\NotEqual) {
- $this->errors[]= [
- 'disallowedToken' => '!=',
- 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED,
- 'line' => $node->getLine(),
- 'reason' => $this->buildReason('!=', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED)
- ];
- }
- if ($node instanceof Node\Stmt\Class_) {
- if (!is_null($node->extends)) {
- $this->checkBlackList($node->extends->toString(), CodeChecker::CLASS_EXTENDS_NOT_ALLOWED, $node);
- }
- foreach ($node->implements as $implements) {
- $this->checkBlackList($implements->toString(), CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED, $node);
- }
- }
- if ($node instanceof Node\Expr\StaticCall) {
- if (!is_null($node->class)) {
- if ($node->class instanceof Name) {
- $this->checkBlackList($node->class->toString(), CodeChecker::STATIC_CALL_NOT_ALLOWED, $node);
- }
- if ($node->class instanceof Node\Expr\Variable) {
- /**
- * TODO: find a way to detect something like this:
- * $c = "OC_API";
- * $n = $i::call();
- */
- }
- }
- }
- if ($node instanceof Node\Expr\ClassConstFetch) {
- if (!is_null($node->class)) {
- if ($node->class instanceof Name) {
- $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED, $node);
- }
- if ($node->class instanceof Node\Expr\Variable) {
- /**
- * TODO: find a way to detect something like this:
- * $c = "OC_API";
- * $n = $i::ADMIN_AUTH;
- */
- }
- }
- }
- if ($node instanceof Node\Expr\New_) {
- if (!is_null($node->class)) {
- if ($node->class instanceof Name) {
- $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_NEW_FETCH_NOT_ALLOWED, $node);
- }
- if ($node->class instanceof Node\Expr\Variable) {
- /**
- * TODO: find a way to detect something like this:
- * $c = "OC_API";
- * $n = new $i;
- */
- }
- }
- }
- }
-
- private function checkBlackList($name, $errorCode, Node $node) {
- if (in_array(strtolower($name), $this->blackListedClassNames)) {
- $this->errors[]= [
- 'disallowedToken' => $name,
- 'errorCode' => $errorCode,
- 'line' => $node->getLine(),
- 'reason' => $this->buildReason($name, $errorCode)
- ];
- }
- }
-
- private function buildReason($name, $errorCode) {
- static $errorMessages= [
- CodeChecker::CLASS_EXTENDS_NOT_ALLOWED => "used as base class",
- CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED => "used as interface",
- CodeChecker::STATIC_CALL_NOT_ALLOWED => "static method call on private class",
- CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED => "used to fetch a const from",
- CodeChecker::CLASS_NEW_FETCH_NOT_ALLOWED => "is instanciated",
- CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED => "is discouraged"
- ];
-
- if (isset($errorMessages[$errorCode])) {
- return $errorMessages[$errorCode];
- }
-
- return "$name usage not allowed - error: $errorCode";
- }
-}
diff --git a/lib/private/appframework/db/db.php b/lib/private/appframework/db/db.php
index d05cd44b54c..cde85831687 100644
--- a/lib/private/appframework/db/db.php
+++ b/lib/private/appframework/db/db.php
@@ -46,6 +46,15 @@ class Db implements IDb {
}
/**
+ * Gets the ExpressionBuilder for the connection.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder
+ */
+ public function getQueryBuilder() {
+ return $this->connection->getQueryBuilder();
+ }
+
+ /**
* Used to abstract the ownCloud database access away
*
* @param string $sql the sql query with ? placeholder for params
diff --git a/lib/private/appframework/dependencyinjection/dicontainer.php b/lib/private/appframework/dependencyinjection/dicontainer.php
index a11e4ee05b3..544da74a010 100644
--- a/lib/private/appframework/dependencyinjection/dicontainer.php
+++ b/lib/private/appframework/dependencyinjection/dicontainer.php
@@ -39,8 +39,6 @@ use OC\AppFramework\Middleware\Security\SecurityMiddleware;
use OC\AppFramework\Middleware\Security\CORSMiddleware;
use OC\AppFramework\Middleware\SessionMiddleware;
use OC\AppFramework\Utility\SimpleContainer;
-use OC\AppFramework\Utility\TimeFactory;
-use OC\AppFramework\Utility\ControllerMethodReflector;
use OCP\AppFramework\IApi;
use OCP\AppFramework\IAppContainer;
use OCP\AppFramework\Middleware;
@@ -59,19 +57,14 @@ class DIContainer extends SimpleContainer implements IAppContainer {
* @param string $appName the name of the app
*/
public function __construct($appName, $urlParams = array()){
+ parent::__construct();
$this['AppName'] = $appName;
$this['urlParams'] = $urlParams;
// aliases
- $this->registerService('appName', function($c) {
- return $c->query('AppName');
- });
- $this->registerService('webRoot', function($c) {
- return $c->query('WebRoot');
- });
- $this->registerService('userId', function($c) {
- return $c->query('UserId');
- });
+ $this->registerAlias('appName', 'AppName');
+ $this->registerAlias('webRoot', 'WebRoot');
+ $this->registerAlias('userId', 'UserId');
/**
* Core services
@@ -104,6 +97,10 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getMemCacheFactory();
});
+ $this->registerService('OC\\CapabilitiesManager', function($c) {
+ return $this->getServer()->getCapabilitiesManager();
+ });
+
$this->registerService('OCP\\IConfig', function($c) {
return $this->getServer()->getConfig();
});
@@ -156,9 +153,8 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getJobList();
});
- $this->registerService('OCP\\AppFramework\\Utility\\IControllerMethodReflector', function($c) {
- return $c->query('ControllerMethodReflector');
- });
+ $this->registerAlias('OCP\\AppFramework\\Utility\\IControllerMethodReflector', 'OC\AppFramework\Utility\ControllerMethodReflector');
+ $this->registerAlias('ControllerMethodReflector', 'OCP\\AppFramework\\Utility\\IControllerMethodReflector');
$this->registerService('OCP\\INavigationManager', function($c) {
return $this->getServer()->getNavigationManager();
@@ -168,9 +164,10 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getPreviewManager();
});
- $this->registerService('OCP\\IRequest', function($c) {
- return $c->query('Request');
+ $this->registerService('OCP\\IRequest', function () {
+ return $this->getServer()->getRequest();
});
+ $this->registerAlias('Request', 'OCP\\IRequest');
$this->registerService('OCP\\ITagManager', function($c) {
return $this->getServer()->getTagManager();
@@ -180,9 +177,9 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getTempManager();
});
- $this->registerService('OCP\\AppFramework\\Utility\\ITimeFactory', function($c) {
- return $c->query('TimeFactory');
- });
+ $this->registerAlias('OCP\\AppFramework\\Utility\\ITimeFactory', 'OC\AppFramework\Utility\TimeFactory');
+ $this->registerAlias('TimeFactory', 'OCP\\AppFramework\\Utility\\ITimeFactory');
+
$this->registerService('OCP\\Route\\IRouter', function($c) {
return $this->getServer()->getRouter();
@@ -220,6 +217,10 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getUserSession();
});
+ $this->registerService('OCP\\ISession', function($c) {
+ return $this->getServer()->getSession();
+ });
+
$this->registerService('ServerContainer', function ($c) {
return $this->getServer();
});
@@ -245,14 +246,6 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return new API($c['AppName']);
});
- $this->registerService('Request', function($c) {
- /** @var $c SimpleContainer */
- /** @var $server SimpleContainer */
- $server = $c->query('ServerContainer');
- /** @var $server IServerContainer */
- return $server->getRequest();
- });
-
$this->registerService('Protocol', function($c){
if(isset($_SERVER['SERVER_PROTOCOL'])) {
return new Http($_SERVER, $_SERVER['SERVER_PROTOCOL']);
@@ -318,18 +311,6 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $dispatcher;
});
-
- /**
- * Utilities
- */
- $this->registerService('TimeFactory', function($c){
- return new TimeFactory();
- });
-
- $this->registerService('ControllerMethodReflector', function($c) {
- return new ControllerMethodReflector();
- });
-
}
@@ -414,5 +395,15 @@ class DIContainer extends SimpleContainer implements IAppContainer {
\OCP\Util::writeLog($this->getAppName(), $message, $level);
}
+ /**
+ * Register a capability
+ *
+ * @param string $serviceName e.g. 'OCA\Files\Capabilities'
+ */
+ public function registerCapability($serviceName) {
+ $this->query('OC\CapabilitiesManager')->registerCapability(function() use ($serviceName) {
+ return $this->query($serviceName);
+ });
+ }
}
diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php
index f826ef45bb5..aaad286e843 100644
--- a/lib/private/appframework/http/request.php
+++ b/lib/private/appframework/http/request.php
@@ -416,12 +416,10 @@ class Request implements \ArrayAccess, \Countable, IRequest {
}
// Check if the token is valid
- if($token !== $this->items['requesttoken']) {
- // Not valid
- return false;
- } else {
- // Valid token
+ if(\OCP\Security\StringUtils::equals($token, $this->items['requesttoken'])) {
return true;
+ } else {
+ return false;
}
}
@@ -454,7 +452,10 @@ class Request implements \ArrayAccess, \Countable, IRequest {
$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
- $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', []);
+ $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
+ 'HTTP_X_FORWARDED_FOR'
+ // only have one default, so we cannot ship an insecure product out of the box
+ ]);
foreach($forwardedForHeaders as $header) {
if(isset($this->server[$header])) {
@@ -478,7 +479,8 @@ class Request implements \ArrayAccess, \Countable, IRequest {
*/
private function isOverwriteCondition($type = '') {
$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '') . '/';
- return $regex === '//' || preg_match($regex, $this->server['REMOTE_ADDR']) === 1
+ $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
+ return $regex === '//' || preg_match($regex, $remoteAddr) === 1
|| $type !== 'protocol';
}
diff --git a/lib/private/appframework/middleware/security/corsmiddleware.php b/lib/private/appframework/middleware/security/corsmiddleware.php
index 600eb2318cf..d7c42cd9b13 100644
--- a/lib/private/appframework/middleware/security/corsmiddleware.php
+++ b/lib/private/appframework/middleware/security/corsmiddleware.php
@@ -2,6 +2,7 @@
/**
* @author Bernhard Posselt <dev@bernhard-posselt.com>
* @author Morris Jobke <hey@morrisjobke.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
@@ -23,6 +24,9 @@
namespace OC\AppFramework\Middleware\Security;
use OC\AppFramework\Utility\ControllerMethodReflector;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUserSession;
use OCP\AppFramework\Http\Response;
@@ -57,8 +61,8 @@ class CORSMiddleware extends Middleware {
* @param IUserSession $session
*/
public function __construct(IRequest $request,
- ControllerMethodReflector $reflector,
- IUserSession $session) {
+ ControllerMethodReflector $reflector,
+ IUserSession $session) {
$this->request = $request;
$this->reflector = $reflector;
$this->session = $session;
@@ -71,6 +75,7 @@ class CORSMiddleware extends Middleware {
* @param Controller $controller the controller that is being called
* @param string $methodName the name of the method that will be called on
* the controller
+ * @throws SecurityException
* @since 6.0.0
*/
public function beforeController($controller, $methodName){
@@ -83,7 +88,7 @@ class CORSMiddleware extends Middleware {
$this->session->logout();
if(!$this->session->login($user, $pass)) {
- throw new SecurityException('CORS requires basic auth');
+ throw new SecurityException('CORS requires basic auth', Http::STATUS_UNAUTHORIZED);
}
}
}
@@ -97,6 +102,7 @@ class CORSMiddleware extends Middleware {
* the controller
* @param Response $response the generated response from the controller
* @return Response a Response object
+ * @throws SecurityException
*/
public function afterController($controller, $methodName, Response $response){
// only react if its a CORS request and if the request sends origin and
@@ -106,7 +112,7 @@ class CORSMiddleware extends Middleware {
// allow credentials headers must not be true or CSRF is possible
// otherwise
- foreach($response->getHeaders() as $header => $value ) {
+ foreach($response->getHeaders() as $header => $value) {
if(strtolower($header) === 'access-control-allow-credentials' &&
strtolower(trim($value)) === 'true') {
$msg = 'Access-Control-Allow-Credentials must not be '.
@@ -121,5 +127,28 @@ class CORSMiddleware extends Middleware {
return $response;
}
+ /**
+ * If an SecurityException is being caught return a JSON error response
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ * @param \Exception $exception the thrown exception
+ * @throws \Exception the passed in exception if it cant handle it
+ * @return Response a Response object or null in case that the exception could not be handled
+ */
+ public function afterException($controller, $methodName, \Exception $exception){
+ if($exception instanceof SecurityException){
+ $response = new JSONResponse(['message' => $exception->getMessage()]);
+ if($exception->getCode() !== 0) {
+ $response->setStatus($exception->getCode());
+ } else {
+ $response->setStatus(Http::STATUS_INTERNAL_SERVER_ERROR);
+ }
+ return $response;
+ }
+
+ throw $exception;
+ }
}
diff --git a/lib/private/appframework/middleware/security/securitymiddleware.php b/lib/private/appframework/middleware/security/securitymiddleware.php
index 715fb2457d2..34c626ce8be 100644
--- a/lib/private/appframework/middleware/security/securitymiddleware.php
+++ b/lib/private/appframework/middleware/security/securitymiddleware.php
@@ -69,13 +69,13 @@ class SecurityMiddleware extends Middleware {
* @param bool $isAdminUser
*/
public function __construct(IRequest $request,
- ControllerMethodReflector $reflector,
- INavigationManager $navigationManager,
- IURLGenerator $urlGenerator,
- ILogger $logger,
- $appName,
- $isLoggedIn,
- $isAdminUser){
+ ControllerMethodReflector $reflector,
+ INavigationManager $navigationManager,
+ IURLGenerator $urlGenerator,
+ ILogger $logger,
+ $appName,
+ $isLoggedIn,
+ $isAdminUser){
$this->navigationManager = $navigationManager;
$this->request = $request;
$this->reflector = $reflector;
diff --git a/lib/private/appframework/utility/simplecontainer.php b/lib/private/appframework/utility/simplecontainer.php
index 5a69d3dbbd2..83a08acde26 100644
--- a/lib/private/appframework/utility/simplecontainer.php
+++ b/lib/private/appframework/utility/simplecontainer.php
@@ -26,21 +26,28 @@
namespace OC\AppFramework\Utility;
-use \OCP\AppFramework\QueryException;
+use ReflectionClass;
+use ReflectionException;
+use Closure;
+
+use Pimple\Container;
+
+use OCP\AppFramework\QueryException;
+use OCP\IContainer;
/**
* Class SimpleContainer
*
- * SimpleContainer is a simple implementation of IContainer on basis of \Pimple
+ * SimpleContainer is a simple implementation of IContainer on basis of Pimple
*/
-class SimpleContainer extends \Pimple\Container implements \OCP\IContainer {
+class SimpleContainer extends Container implements IContainer {
/**
- * @param \ReflectionClass $class the class to instantiate
- * @return \stdClass the created class
+ * @param ReflectionClass $class the class to instantiate
+ * @return stdClass the created class
*/
- private function buildClass(\ReflectionClass $class) {
+ private function buildClass(ReflectionClass $class) {
$constructor = $class->getConstructor();
if ($constructor === null) {
return $class->newInstance();
@@ -67,20 +74,20 @@ class SimpleContainer extends \Pimple\Container implements \OCP\IContainer {
* If a parameter is not registered in the container try to instantiate it
* by using reflection to find out how to build the class
* @param string $name the class name to resolve
- * @return \stdClass
+ * @return stdClass
* @throws QueryException if the class could not be found or instantiated
*/
private function resolve($name) {
$baseMsg = 'Could not resolve ' . $name . '!';
try {
- $class = new \ReflectionClass($name);
+ $class = new ReflectionClass($name);
if ($class->isInstantiable()) {
return $this->buildClass($class);
} else {
throw new QueryException($baseMsg .
' Class can not be instantiated');
}
- } catch(\ReflectionException $e) {
+ } catch(ReflectionException $e) {
throw new QueryException($baseMsg . ' ' . $e->getMessage());
}
}
@@ -92,6 +99,7 @@ class SimpleContainer extends \Pimple\Container implements \OCP\IContainer {
* @throws QueryException if the query could not be resolved
*/
public function query($name) {
+ $name = $this->sanitizeName($name);
if ($this->offsetExists($name)) {
return $this->offsetGet($name);
} else {
@@ -117,10 +125,11 @@ class SimpleContainer extends \Pimple\Container implements \OCP\IContainer {
* Created instance will be cached in case $shared is true.
*
* @param string $name name of the service to register another backend for
- * @param \Closure $closure the closure to be called on service creation
+ * @param Closure $closure the closure to be called on service creation
* @param bool $shared
*/
- public function registerService($name, \Closure $closure, $shared = true) {
+ public function registerService($name, Closure $closure, $shared = true) {
+ $name = $this->sanitizeName($name);
if (isset($this[$name])) {
unset($this[$name]);
}
@@ -131,5 +140,25 @@ class SimpleContainer extends \Pimple\Container implements \OCP\IContainer {
}
}
+ /**
+ * Shortcut for returning a service from a service under a different key,
+ * e.g. to tell the container to return a class when queried for an
+ * interface
+ * @param string $alias the alias that should be registered
+ * @param string $target the target that should be resolved instead
+ */
+ public function registerAlias($alias, $target) {
+ $this->registerService($alias, function (IContainer $container) use ($target) {
+ return $container->query($target);
+ }, false);
+ }
+
+ /*
+ * @param string $name
+ * @return string
+ */
+ protected function sanitizeName($name) {
+ return ltrim($name, '\\');
+ }
}
diff --git a/lib/private/capabilitiesmanager.php b/lib/private/capabilitiesmanager.php
new file mode 100644
index 00000000000..74154f2c631
--- /dev/null
+++ b/lib/private/capabilitiesmanager.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC;
+
+
+use OCP\Capabilities\ICapability;
+
+class CapabilitiesManager {
+
+ /**
+ * @var \Closure[]
+ */
+ private $capabilities = array();
+
+ /**
+ * Get an array of al the capabilities that are registered at this manager
+ *
+ * @throws \InvalidArgumentException
+ * @return array
+ */
+ public function getCapabilities() {
+ $capabilities = [];
+ foreach($this->capabilities as $capability) {
+ $c = $capability();
+ if ($c instanceof ICapability) {
+ $capabilities = array_replace_recursive($capabilities, $c->getCapabilities());
+ } else {
+ throw new \InvalidArgumentException('The given Capability (' . get_class($c) . ') does not implement the ICapability interface');
+ }
+ }
+
+ return $capabilities;
+ }
+
+ /**
+ * In order to improve lazy loading a closure can be registered which will be called in case
+ * capabilities are actually requested
+ *
+ * $callable has to return an instance of OCP\Capabilities\ICapability
+ *
+ * @param \Closure $callable
+ */
+ public function registerCapability(\Closure $callable) {
+ array_push($this->capabilities, $callable);
+ }
+}
diff --git a/lib/private/connector/sabre/exceptionloggerplugin.php b/lib/private/connector/sabre/exceptionloggerplugin.php
index 0b89ae4aef6..53a1f738ea6 100644
--- a/lib/private/connector/sabre/exceptionloggerplugin.php
+++ b/lib/private/connector/sabre/exceptionloggerplugin.php
@@ -28,7 +28,7 @@ use Sabre\DAV\Exception;
use Sabre\HTTP\Response;
class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin {
- private $nonFatalExceptions = array(
+ protected $nonFatalExceptions = array(
'Sabre\DAV\Exception\NotAuthenticated' => true,
// the sync client uses this to find out whether files exist,
// so it is not always an error, log it as debug
@@ -95,6 +95,7 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin {
$exception = [
'Message' => $message,
+ 'Exception' => $exceptionClass,
'Code' => $ex->getCode(),
'Trace' => $ex->getTraceAsString(),
'File' => $ex->getFile(),
diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php
index 18bd3b8d91d..b7d0c547f24 100644
--- a/lib/private/connector/sabre/file.php
+++ b/lib/private/connector/sabre/file.php
@@ -208,10 +208,9 @@ class File extends Node implements IFile {
}
// since we skipped the view we need to scan and emit the hooks ourselves
- $partStorage->getScanner()->scanFile($internalPath);
+ $this->fileView->getUpdater()->update($this->path);
if ($view) {
- $this->fileView->getUpdater()->propagate($hookPath);
if (!$exists) {
\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array(
\OC\Files\Filesystem::signal_param_path => $hookPath
@@ -333,7 +332,7 @@ class File extends Node implements IFile {
$info = \OC_FileChunking::decodeName($name);
if (empty($info)) {
- throw new NotImplemented();
+ throw new NotImplemented('Invalid chunk name');
}
$chunk_handler = new \OC_FileChunking($info);
$bytesWritten = $chunk_handler->store($info['index'], $data);
diff --git a/lib/private/connector/sabre/filesplugin.php b/lib/private/connector/sabre/filesplugin.php
index 608e8cd9017..84620f454aa 100644
--- a/lib/private/connector/sabre/filesplugin.php
+++ b/lib/private/connector/sabre/filesplugin.php
@@ -56,10 +56,19 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
private $tree;
/**
+ * Whether this is public webdav.
+ * If true, some returned information will be stripped off.
+ *
+ * @var bool
+ */
+ private $isPublic;
+
+ /**
* @param \Sabre\DAV\Tree $tree
*/
- public function __construct(\Sabre\DAV\Tree $tree) {
+ public function __construct(\Sabre\DAV\Tree $tree, $isPublic = false) {
$this->tree = $tree;
+ $this->isPublic = $isPublic;
}
/**
@@ -129,7 +138,12 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
});
$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
- return $node->getDavPermissions();
+ $perms = $node->getDavPermissions();
+ if ($this->isPublic) {
+ // remove mount information
+ $perms = str_replace(['S', 'M'], '', $perms);
+ }
+ return $perms;
});
$propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php
index 1e9b9ba59e2..18d3c1dcf23 100644
--- a/lib/private/connector/sabre/objecttree.php
+++ b/lib/private/connector/sabre/objecttree.php
@@ -41,7 +41,7 @@ class ObjectTree extends \Sabre\DAV\Tree {
protected $fileView;
/**
- * @var \OC\Files\Mount\Manager
+ * @var \OCP\Files\Mount\IMountManager
*/
protected $mountManager;
@@ -54,9 +54,9 @@ class ObjectTree extends \Sabre\DAV\Tree {
/**
* @param \Sabre\DAV\INode $rootNode
* @param \OC\Files\View $view
- * @param \OC\Files\Mount\Manager $mountManager
+ * @param \OCP\Files\Mount\IMountManager $mountManager
*/
- public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OC\Files\Mount\Manager $mountManager) {
+ public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) {
$this->rootNode = $rootNode;
$this->fileView = $view;
$this->mountManager = $mountManager;
diff --git a/lib/private/connector/sabre/serverfactory.php b/lib/private/connector/sabre/serverfactory.php
new file mode 100644
index 00000000000..525ff0104cd
--- /dev/null
+++ b/lib/private/connector/sabre/serverfactory.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Connector\Sabre;
+
+use OCP\Files\Mount\IMountManager;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\ILogger;
+use OCP\ITagManager;
+use OCP\IUserSession;
+use Sabre\DAV\Auth\Backend\BackendInterface;
+
+class ServerFactory {
+ public function __construct(
+ IConfig $config,
+ ILogger $logger,
+ IDBConnection $databaseConnection,
+ IUserSession $userSession,
+ IMountManager $mountManager,
+ ITagManager $tagManager
+ ) {
+ $this->config = $config;
+ $this->logger = $logger;
+ $this->databaseConnection = $databaseConnection;
+ $this->userSession = $userSession;
+ $this->mountManager = $mountManager;
+ $this->tagManager = $tagManager;
+ }
+
+ /**
+ * @param string $baseUri
+ * @param string $requestUri
+ * @param BackendInterface $authBackend
+ * @param callable $viewCallBack callback that should return the view for the dav endpoint
+ * @return Server
+ */
+ public function createServer($baseUri, $requestUri, BackendInterface $authBackend, callable $viewCallBack) {
+ // Fire up server
+ $objectTree = new \OC\Connector\Sabre\ObjectTree();
+ $server = new \OC\Connector\Sabre\Server($objectTree);
+ // Set URL explicitly due to reverse-proxy situations
+ $server->httpRequest->setUrl($requestUri);
+ $server->setBaseUri($baseUri);
+
+ // Load plugins
+ $defaults = new \OC_Defaults();
+ $server->addPlugin(new \OC\Connector\Sabre\BlockLegacyClientPlugin($this->config));
+ $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName()));
+ // FIXME: The following line is a workaround for legacy components relying on being able to send a GET to /
+ $server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin());
+ $server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree));
+ $server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin($this->config));
+ $server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger));
+
+ // wait with registering these until auth is handled and the filesystem is setup
+ $server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) {
+ /** @var \OC\Files\View $view */
+ $view = $viewCallBack();
+ $rootInfo = $view->getFileInfo('');
+
+ // Create ownCloud Dir
+ if ($rootInfo->getType() === 'dir') {
+ $root = new \OC\Connector\Sabre\Directory($view, $rootInfo);
+ } else {
+ $root = new \OC\Connector\Sabre\File($view, $rootInfo);
+ }
+ $objectTree->init($root, $view, $this->mountManager);
+
+ $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view));
+
+ if($this->userSession->isLoggedIn()) {
+ $server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager));
+ // custom properties plugin must be the last one
+ $server->addPlugin(
+ new \Sabre\DAV\PropertyStorage\Plugin(
+ new \OC\Connector\Sabre\CustomPropertiesBackend(
+ $objectTree,
+ $this->databaseConnection,
+ $this->userSession->getUser()
+ )
+ )
+ );
+ }
+ $server->addPlugin(new \OC\Connector\Sabre\CopyEtagHeaderPlugin());
+ }, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
+ return $server;
+ }
+}
diff --git a/lib/private/db.php b/lib/private/db.php
index 1e93eb1892e..a4a7b7d17d4 100644
--- a/lib/private/db.php
+++ b/lib/private/db.php
@@ -121,7 +121,7 @@ class OC_DB {
if (is_string($stmt)) {
// convert to an array with 'sql'
if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT
- // TODO try to convert LIMIT OFFSET notation to parameters, see fixLimitClauseForMSSQL
+ // TODO try to convert LIMIT OFFSET notation to parameters
$message = 'LIMIT and OFFSET are forbidden for portability reasons,'
. ' pass an array with \'limit\' and \'offset\' instead';
throw new \OC\DatabaseException($message);
diff --git a/lib/private/db/connection.php b/lib/private/db/connection.php
index a36bd2649df..def3f2fd120 100644
--- a/lib/private/db/connection.php
+++ b/lib/private/db/connection.php
@@ -29,6 +29,8 @@ use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\Common\EventManager;
+use OC\DB\QueryBuilder\ExpressionBuilder;
+use OC\DB\QueryBuilder\QueryBuilder;
use OCP\IDBConnection;
class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
@@ -52,6 +54,56 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
}
/**
+ * Returns a QueryBuilder for the connection.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder
+ */
+ public function getQueryBuilder() {
+ return new QueryBuilder($this);
+ }
+
+ /**
+ * Gets the QueryBuilder for the connection.
+ *
+ * @return \Doctrine\DBAL\Query\QueryBuilder
+ * @deprecated please use $this->getQueryBuilder() instead
+ */
+ public function createQueryBuilder() {
+ $backtrace = $this->getCallerBacktrace();
+ \OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
+ return parent::createQueryBuilder();
+ }
+
+ /**
+ * Gets the ExpressionBuilder for the connection.
+ *
+ * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
+ * @deprecated please use $this->getQueryBuilder()->expr() instead
+ */
+ public function getExpressionBuilder() {
+ $backtrace = $this->getCallerBacktrace();
+ \OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
+ return parent::getExpressionBuilder();
+ }
+
+ /**
+ * Get the file and line that called the method where `getCallerBacktrace()` was used
+ *
+ * @return string
+ */
+ protected function getCallerBacktrace() {
+ $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+
+ // 0 is the method where we use `getCallerBacktrace`
+ // 1 is the target method which uses the method we want to log
+ if (isset($traces[1])) {
+ return $traces[1]['file'] . ':' . $traces[1]['line'];
+ }
+
+ return '';
+ }
+
+ /**
* @return string
*/
public function getPrefix() {
diff --git a/lib/private/db/connectionfactory.php b/lib/private/db/connectionfactory.php
index 83a59cddd7f..b6c3396e147 100644
--- a/lib/private/db/connectionfactory.php
+++ b/lib/private/db/connectionfactory.php
@@ -39,12 +39,6 @@ class ConnectionFactory {
* \Doctrine\DBAL\DriverManager::getConnection().
*/
protected $defaultConnectionParams = array(
- 'mssql' => array(
- 'adapter' => '\OC\DB\AdapterSQLSrv',
- 'charset' => 'UTF8',
- 'driver' => 'pdo_sqlsrv',
- 'wrapperClass' => 'OC\DB\Connection',
- ),
'mysql' => array(
'adapter' => '\OC\DB\AdapterMySQL',
'charset' => 'UTF8',
diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php
index 6b9888d361b..aef485ed686 100644
--- a/lib/private/db/mdb2schemamanager.php
+++ b/lib/private/db/mdb2schemamanager.php
@@ -32,7 +32,6 @@ use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
-use Doctrine\DBAL\Platforms\SQLServerPlatform;
class MDB2SchemaManager {
/**
@@ -85,8 +84,6 @@ class MDB2SchemaManager {
return new OracleMigrator($this->conn, $random, $config);
} else if ($platform instanceof MySqlPlatform) {
return new MySQLMigrator($this->conn, $random, $config);
- } else if ($platform instanceof SQLServerPlatform) {
- return new MsSqlMigrator($this->conn, $random, $config);
} else if ($platform instanceof PostgreSqlPlatform) {
return new Migrator($this->conn, $random, $config);
} else {
diff --git a/lib/private/db/querybuilder/compositeexpression.php b/lib/private/db/querybuilder/compositeexpression.php
new file mode 100644
index 00000000000..523f397e235
--- /dev/null
+++ b/lib/private/db/querybuilder/compositeexpression.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\DB\QueryBuilder;
+
+use OCP\DB\QueryBuilder\ICompositeExpression;
+
+class CompositeExpression implements ICompositeExpression, \Countable {
+ /** @var \Doctrine\DBAL\Query\Expression\CompositeExpression */
+ protected $compositeExpression;
+
+ /**
+ * Constructor.
+ *
+ * @param \Doctrine\DBAL\Query\Expression\CompositeExpression $compositeExpression
+ */
+ public function __construct(\Doctrine\DBAL\Query\Expression\CompositeExpression $compositeExpression) {
+ $this->compositeExpression = $compositeExpression;
+ }
+
+ /**
+ * Adds multiple parts to composite expression.
+ *
+ * @param array $parts
+ *
+ * @return \OCP\DB\QueryBuilder\ICompositeExpression
+ */
+ public function addMultiple(array $parts = array()) {
+ $this->compositeExpression->addMultiple($parts);
+
+ return $this;
+ }
+
+ /**
+ * Adds an expression to composite expression.
+ *
+ * @param mixed $part
+ *
+ * @return \OCP\DB\QueryBuilder\ICompositeExpression
+ */
+ public function add($part) {
+ $this->compositeExpression->add($part);
+
+ return $this;
+ }
+
+ /**
+ * Retrieves the amount of expressions on composite expression.
+ *
+ * @return integer
+ */
+ public function count() {
+ return $this->compositeExpression->count();
+ }
+
+ /**
+ * Returns the type of this composite expression (AND/OR).
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->compositeExpression->getType();
+ }
+
+ /**
+ * Retrieves the string representation of this composite expression.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->compositeExpression;
+ }
+}
diff --git a/lib/private/db/querybuilder/expressionbuilder.php b/lib/private/db/querybuilder/expressionbuilder.php
new file mode 100644
index 00000000000..cd5b7fbfc54
--- /dev/null
+++ b/lib/private/db/querybuilder/expressionbuilder.php
@@ -0,0 +1,312 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\DB\QueryBuilder;
+
+use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder;
+use OCP\DB\QueryBuilder\IExpressionBuilder;
+use OCP\IDBConnection;
+
+class ExpressionBuilder implements IExpressionBuilder {
+ /** @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder */
+ private $expressionBuilder;
+
+ /** @var QuoteHelper */
+ private $helper;
+
+ /**
+ * Initializes a new <tt>ExpressionBuilder</tt>.
+ *
+ * @param \OCP\IDBConnection $connection
+ */
+ public function __construct(IDBConnection $connection) {
+ $this->helper = new QuoteHelper();
+ $this->expressionBuilder = new DoctrineExpressionBuilder($connection);
+ }
+
+ /**
+ * Creates a conjunction of the given boolean expressions.
+ *
+ * Example:
+ *
+ * [php]
+ * // (u.type = ?) AND (u.role = ?)
+ * $expr->andX('u.type = ?', 'u.role = ?'));
+ *
+ * @param mixed $x Optional clause. Defaults = null, but requires
+ * at least one defined when converting to string.
+ *
+ * @return \OCP\DB\QueryBuilder\ICompositeExpression
+ */
+ public function andX($x = null) {
+ $arguments = func_get_args();
+ $compositeExpression = call_user_func_array([$this->expressionBuilder, 'andX'], $arguments);
+ return new CompositeExpression($compositeExpression);
+ }
+
+ /**
+ * Creates a disjunction of the given boolean expressions.
+ *
+ * Example:
+ *
+ * [php]
+ * // (u.type = ?) OR (u.role = ?)
+ * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?'));
+ *
+ * @param mixed $x Optional clause. Defaults = null, but requires
+ * at least one defined when converting to string.
+ *
+ * @return \OCP\DB\QueryBuilder\ICompositeExpression
+ */
+ public function orX($x = null) {
+ $arguments = func_get_args();
+ $compositeExpression = call_user_func_array([$this->expressionBuilder, 'orX'], $arguments);
+ return new CompositeExpression($compositeExpression);
+ }
+
+ /**
+ * Creates a comparison expression.
+ *
+ * @param mixed $x The left expression.
+ * @param string $operator One of the ExpressionBuilder::* constants.
+ * @param mixed $y The right expression.
+ *
+ * @return string
+ */
+ public function comparison($x, $operator, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->comparison($x, $operator, $y);
+ }
+
+ /**
+ * Creates an equality comparison expression with the given arguments.
+ *
+ * First argument is considered the left expression and the second is the right expression.
+ * When converted to string, it will generated a <left expr> = <right expr>. Example:
+ *
+ * [php]
+ * // u.id = ?
+ * $expr->eq('u.id', '?');
+ *
+ * @param mixed $x The left expression.
+ * @param mixed $y The right expression.
+ *
+ * @return string
+ */
+ public function eq($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->eq($x, $y);
+ }
+
+ /**
+ * Creates a non equality comparison expression with the given arguments.
+ * First argument is considered the left expression and the second is the right expression.
+ * When converted to string, it will generated a <left expr> <> <right expr>. Example:
+ *
+ * [php]
+ * // u.id <> 1
+ * $q->where($q->expr()->neq('u.id', '1'));
+ *
+ * @param mixed $x The left expression.
+ * @param mixed $y The right expression.
+ *
+ * @return string
+ */
+ public function neq($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->neq($x, $y);
+ }
+
+ /**
+ * Creates a lower-than comparison expression with the given arguments.
+ * First argument is considered the left expression and the second is the right expression.
+ * When converted to string, it will generated a <left expr> < <right expr>. Example:
+ *
+ * [php]
+ * // u.id < ?
+ * $q->where($q->expr()->lt('u.id', '?'));
+ *
+ * @param mixed $x The left expression.
+ * @param mixed $y The right expression.
+ *
+ * @return string
+ */
+ public function lt($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->lt($x, $y);
+ }
+
+ /**
+ * Creates a lower-than-equal comparison expression with the given arguments.
+ * First argument is considered the left expression and the second is the right expression.
+ * When converted to string, it will generated a <left expr> <= <right expr>. Example:
+ *
+ * [php]
+ * // u.id <= ?
+ * $q->where($q->expr()->lte('u.id', '?'));
+ *
+ * @param mixed $x The left expression.
+ * @param mixed $y The right expression.
+ *
+ * @return string
+ */
+ public function lte($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->lte($x, $y);
+ }
+
+ /**
+ * Creates a greater-than comparison expression with the given arguments.
+ * First argument is considered the left expression and the second is the right expression.
+ * When converted to string, it will generated a <left expr> > <right expr>. Example:
+ *
+ * [php]
+ * // u.id > ?
+ * $q->where($q->expr()->gt('u.id', '?'));
+ *
+ * @param mixed $x The left expression.
+ * @param mixed $y The right expression.
+ *
+ * @return string
+ */
+ public function gt($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->gt($x, $y);
+ }
+
+ /**
+ * Creates a greater-than-equal comparison expression with the given arguments.
+ * First argument is considered the left expression and the second is the right expression.
+ * When converted to string, it will generated a <left expr> >= <right expr>. Example:
+ *
+ * [php]
+ * // u.id >= ?
+ * $q->where($q->expr()->gte('u.id', '?'));
+ *
+ * @param mixed $x The left expression.
+ * @param mixed $y The right expression.
+ *
+ * @return string
+ */
+ public function gte($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->gte($x, $y);
+ }
+
+ /**
+ * Creates an IS NULL expression with the given arguments.
+ *
+ * @param string $x The field in string format to be restricted by IS NULL.
+ *
+ * @return string
+ */
+ public function isNull($x) {
+ $x = $this->helper->quoteColumnName($x);
+ return $this->expressionBuilder->isNull($x);
+ }
+
+ /**
+ * Creates an IS NOT NULL expression with the given arguments.
+ *
+ * @param string $x The field in string format to be restricted by IS NOT NULL.
+ *
+ * @return string
+ */
+ public function isNotNull($x) {
+ $x = $this->helper->quoteColumnName($x);
+ return $this->expressionBuilder->isNotNull($x);
+ }
+
+ /**
+ * Creates a LIKE() comparison expression with the given arguments.
+ *
+ * @param string $x Field in string format to be inspected by LIKE() comparison.
+ * @param mixed $y Argument to be used in LIKE() comparison.
+ *
+ * @return string
+ */
+ public function like($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->like($x, $y);
+ }
+
+ /**
+ * Creates a NOT LIKE() comparison expression with the given arguments.
+ *
+ * @param string $x Field in string format to be inspected by NOT LIKE() comparison.
+ * @param mixed $y Argument to be used in NOT LIKE() comparison.
+ *
+ * @return string
+ */
+ public function notLike($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnName($y);
+ return $this->expressionBuilder->notLike($x, $y);
+ }
+
+ /**
+ * Creates a IN () comparison expression with the given arguments.
+ *
+ * @param string $x The field in string format to be inspected by IN() comparison.
+ * @param string|array $y The placeholder or the array of values to be used by IN() comparison.
+ *
+ * @return string
+ */
+ public function in($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnNames($y);
+ return $this->expressionBuilder->in($x, $y);
+ }
+
+ /**
+ * Creates a NOT IN () comparison expression with the given arguments.
+ *
+ * @param string $x The field in string format to be inspected by NOT IN() comparison.
+ * @param string|array $y The placeholder or the array of values to be used by NOT IN() comparison.
+ *
+ * @return string
+ */
+ public function notIn($x, $y) {
+ $x = $this->helper->quoteColumnName($x);
+ $y = $this->helper->quoteColumnNames($y);
+ return $this->expressionBuilder->notIn($x, $y);
+ }
+
+ /**
+ * Quotes a given input parameter.
+ *
+ * @param mixed $input The parameter to be quoted.
+ * @param string|null $type The type of the parameter.
+ *
+ * @return Literal
+ */
+ public function literal($input, $type = null) {
+ return new Literal($this->expressionBuilder->literal($input, $type));
+ }
+}
diff --git a/lib/private/db/querybuilder/literal.php b/lib/private/db/querybuilder/literal.php
new file mode 100644
index 00000000000..361b5ccf3b8
--- /dev/null
+++ b/lib/private/db/querybuilder/literal.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\DB\QueryBuilder;
+
+use OCP\DB\QueryBuilder\ILiteral;
+
+class Literal implements ILiteral{
+ /** @var mixed */
+ protected $literal;
+
+ public function __construct($literal) {
+ $this->literal = $literal;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString() {
+ return (string) $this->literal;
+ }
+}
diff --git a/lib/private/db/querybuilder/parameter.php b/lib/private/db/querybuilder/parameter.php
new file mode 100644
index 00000000000..c14b4e21925
--- /dev/null
+++ b/lib/private/db/querybuilder/parameter.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\DB\QueryBuilder;
+
+use OCP\DB\QueryBuilder\IParameter;
+
+class Parameter implements IParameter {
+ /** @var mixed */
+ protected $name;
+
+ public function __construct($name) {
+ $this->name = $name;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString() {
+ return (string) $this->name;
+ }
+}
diff --git a/lib/private/db/querybuilder/querybuilder.php b/lib/private/db/querybuilder/querybuilder.php
new file mode 100644
index 00000000000..1d97faf77cc
--- /dev/null
+++ b/lib/private/db/querybuilder/querybuilder.php
@@ -0,0 +1,1013 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\DB\QueryBuilder;
+
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\QueryBuilder\IQueryFunction;
+use OCP\DB\QueryBuilder\IParameter;
+use OCP\IDBConnection;
+
+class QueryBuilder implements IQueryBuilder {
+
+ /** @var \OCP\IDBConnection */
+ private $connection;
+
+ /** @var \Doctrine\DBAL\Query\QueryBuilder */
+ private $queryBuilder;
+
+ /** @var QuoteHelper */
+ private $helper;
+
+ /** @var bool */
+ private $automaticTablePrefix = true;
+
+ /**
+ * Initializes a new QueryBuilder.
+ *
+ * @param \OCP\IDBConnection $connection
+ */
+ public function __construct(IDBConnection $connection) {
+ $this->connection = $connection;
+ $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection);
+ $this->helper = new QuoteHelper();
+ }
+
+ /**
+ * Enable/disable automatic prefixing of table names with the oc_ prefix
+ *
+ * @param bool $enabled If set to true table names will be prefixed with the
+ * owncloud database prefix automatically.
+ * @since 8.2.0
+ */
+ public function automaticTablePrefix($enabled) {
+ $this->automaticTablePrefix = (bool) $enabled;
+ }
+
+ /**
+ * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
+ * This producer method is intended for convenient inline usage. Example:
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u')
+ * ->from('users', 'u')
+ * ->where($qb->expr()->eq('u.id', 1));
+ * </code>
+ *
+ * For more complex expression construction, consider storing the expression
+ * builder object in a local variable.
+ *
+ * @return \OCP\DB\QueryBuilder\IExpressionBuilder
+ */
+ public function expr() {
+ return new ExpressionBuilder($this->connection);
+ }
+
+ /**
+ * Gets the type of the currently built query.
+ *
+ * @return integer
+ */
+ public function getType() {
+ return $this->queryBuilder->getType();
+ }
+
+ /**
+ * Gets the associated DBAL Connection for this query builder.
+ *
+ * @return \OCP\IDBConnection
+ */
+ public function getConnection() {
+ return $this->connection;
+ }
+
+ /**
+ * Gets the state of this query builder instance.
+ *
+ * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
+ */
+ public function getState() {
+ return $this->queryBuilder->getState();
+ }
+
+ /**
+ * Executes this query using the bound parameters and their types.
+ *
+ * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
+ * for insert, update and delete statements.
+ *
+ * @return \Doctrine\DBAL\Driver\Statement|int
+ */
+ public function execute() {
+ return $this->queryBuilder->execute();
+ }
+
+ /**
+ * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u')
+ * ->from('User', 'u')
+ * echo $qb->getSQL(); // SELECT u FROM User u
+ * </code>
+ *
+ * @return string The SQL query string.
+ */
+ public function getSQL() {
+ return $this->queryBuilder->getSQL();
+ }
+
+ /**
+ * Sets a query parameter for the query being constructed.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u')
+ * ->from('users', 'u')
+ * ->where('u.id = :user_id')
+ * ->setParameter(':user_id', 1);
+ * </code>
+ *
+ * @param string|integer $key The parameter position or name.
+ * @param mixed $value The parameter value.
+ * @param string|null $type One of the PDO::PARAM_* constants.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function setParameter($key, $value, $type = null) {
+ $this->queryBuilder->setParameter($key, $value, $type);
+
+ return $this;
+ }
+
+ /**
+ * Sets a collection of query parameters for the query being constructed.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u')
+ * ->from('users', 'u')
+ * ->where('u.id = :user_id1 OR u.id = :user_id2')
+ * ->setParameters(array(
+ * ':user_id1' => 1,
+ * ':user_id2' => 2
+ * ));
+ * </code>
+ *
+ * @param array $params The query parameters to set.
+ * @param array $types The query parameters types to set.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function setParameters(array $params, array $types = array()) {
+ $this->queryBuilder->setParameters($params, $types);
+
+ return $this;
+ }
+
+ /**
+ * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
+ *
+ * @return array The currently defined query parameters indexed by parameter index or name.
+ */
+ public function getParameters() {
+ return $this->queryBuilder->getParameters();
+ }
+
+ /**
+ * Gets a (previously set) query parameter of the query being constructed.
+ *
+ * @param mixed $key The key (index or name) of the bound parameter.
+ *
+ * @return mixed The value of the bound parameter.
+ */
+ public function getParameter($key) {
+ return $this->queryBuilder->getParameter($key);
+ }
+
+ /**
+ * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
+ *
+ * @return array The currently defined query parameter types indexed by parameter index or name.
+ */
+ public function getParameterTypes() {
+ return $this->queryBuilder->getParameterTypes();
+ }
+
+ /**
+ * Gets a (previously set) query parameter type of the query being constructed.
+ *
+ * @param mixed $key The key (index or name) of the bound parameter type.
+ *
+ * @return mixed The value of the bound parameter type.
+ */
+ public function getParameterType($key) {
+ return $this->queryBuilder->getParameterType($key);
+ }
+
+ /**
+ * Sets the position of the first result to retrieve (the "offset").
+ *
+ * @param integer $firstResult The first result to return.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function setFirstResult($firstResult) {
+ $this->queryBuilder->setFirstResult($firstResult);
+
+ return $this;
+ }
+
+ /**
+ * Gets the position of the first result the query object was set to retrieve (the "offset").
+ * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
+ *
+ * @return integer The position of the first result.
+ */
+ public function getFirstResult() {
+ return $this->queryBuilder->getFirstResult();
+ }
+
+ /**
+ * Sets the maximum number of results to retrieve (the "limit").
+ *
+ * NOTE: Setting max results to "0" will cause mixed behaviour. While most
+ * of the databases will just return an empty result set, Oracle will return
+ * all entries.
+ *
+ * @param integer $maxResults The maximum number of results to retrieve.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function setMaxResults($maxResults) {
+ $this->queryBuilder->setMaxResults($maxResults);
+
+ return $this;
+ }
+
+ /**
+ * Gets the maximum number of results the query object was set to retrieve (the "limit").
+ * Returns NULL if {@link setMaxResults} was not applied to this query builder.
+ *
+ * @return integer The maximum number of results.
+ */
+ public function getMaxResults() {
+ return $this->queryBuilder->getMaxResults();
+ }
+
+ /**
+ * Specifies an item that is to be returned in the query result.
+ * Replaces any previously specified selections, if any.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.id', 'p.id')
+ * ->from('users', 'u')
+ * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
+ * </code>
+ *
+ * @param mixed $select The selection expressions.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function select($select = null) {
+ $selects = is_array($select) ? $select : func_get_args();
+
+ $this->queryBuilder->select(
+ $this->helper->quoteColumnNames($selects)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Adds an item that is to be returned in the query result.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.id')
+ * ->addSelect('p.id')
+ * ->from('users', 'u')
+ * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
+ * </code>
+ *
+ * @param mixed $select The selection expression.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function addSelect($select = null) {
+ $selects = is_array($select) ? $select : func_get_args();
+
+ $this->queryBuilder->addSelect(
+ $this->helper->quoteColumnNames($selects)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Turns the query being built into a bulk delete query that ranges over
+ * a certain table.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->delete('users', 'u')
+ * ->where('u.id = :user_id');
+ * ->setParameter(':user_id', 1);
+ * </code>
+ *
+ * @param string $delete The table whose rows are subject to the deletion.
+ * @param string $alias The table alias used in the constructed query.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function delete($delete = null, $alias = null) {
+ $this->queryBuilder->delete(
+ $this->getTableName($delete),
+ $alias
+ );
+
+ return $this;
+ }
+
+ /**
+ * Turns the query being built into a bulk update query that ranges over
+ * a certain table
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->update('users', 'u')
+ * ->set('u.password', md5('password'))
+ * ->where('u.id = ?');
+ * </code>
+ *
+ * @param string $update The table whose rows are subject to the update.
+ * @param string $alias The table alias used in the constructed query.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function update($update = null, $alias = null) {
+ $this->queryBuilder->update(
+ $this->getTableName($update),
+ $alias
+ );
+
+ return $this;
+ }
+
+ /**
+ * Turns the query being built into an insert query that inserts into
+ * a certain table
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->insert('users')
+ * ->values(
+ * array(
+ * 'name' => '?',
+ * 'password' => '?'
+ * )
+ * );
+ * </code>
+ *
+ * @param string $insert The table into which the rows should be inserted.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function insert($insert = null) {
+ $this->queryBuilder->insert(
+ $this->getTableName($insert)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Creates and adds a query root corresponding to the table identified by the
+ * given alias, forming a cartesian product with any existing query roots.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.id')
+ * ->from('users', 'u')
+ * </code>
+ *
+ * @param string $from The table.
+ * @param string|null $alias The alias of the table.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function from($from, $alias = null) {
+ $this->queryBuilder->from(
+ $this->getTableName($from),
+ $alias
+ );
+
+ return $this;
+ }
+
+ /**
+ * Creates and adds a join to the query.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.name')
+ * ->from('users', 'u')
+ * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
+ * </code>
+ *
+ * @param string $fromAlias The alias that points to a from clause.
+ * @param string $join The table name to join.
+ * @param string $alias The alias of the join table.
+ * @param string $condition The condition for the join.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function join($fromAlias, $join, $alias, $condition = null) {
+ $this->queryBuilder->join(
+ $fromAlias,
+ $this->getTableName($join),
+ $alias,
+ $condition
+ );
+
+ return $this;
+ }
+
+ /**
+ * Creates and adds a join to the query.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.name')
+ * ->from('users', 'u')
+ * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
+ * </code>
+ *
+ * @param string $fromAlias The alias that points to a from clause.
+ * @param string $join The table name to join.
+ * @param string $alias The alias of the join table.
+ * @param string $condition The condition for the join.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function innerJoin($fromAlias, $join, $alias, $condition = null) {
+ $this->queryBuilder->innerJoin(
+ $fromAlias,
+ $this->getTableName($join),
+ $alias,
+ $condition
+ );
+
+ return $this;
+ }
+
+ /**
+ * Creates and adds a left join to the query.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.name')
+ * ->from('users', 'u')
+ * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
+ * </code>
+ *
+ * @param string $fromAlias The alias that points to a from clause.
+ * @param string $join The table name to join.
+ * @param string $alias The alias of the join table.
+ * @param string $condition The condition for the join.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function leftJoin($fromAlias, $join, $alias, $condition = null) {
+ $this->queryBuilder->leftJoin(
+ $fromAlias,
+ $this->getTableName($join),
+ $alias,
+ $condition
+ );
+
+ return $this;
+ }
+
+ /**
+ * Creates and adds a right join to the query.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.name')
+ * ->from('users', 'u')
+ * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
+ * </code>
+ *
+ * @param string $fromAlias The alias that points to a from clause.
+ * @param string $join The table name to join.
+ * @param string $alias The alias of the join table.
+ * @param string $condition The condition for the join.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function rightJoin($fromAlias, $join, $alias, $condition = null) {
+ $this->queryBuilder->rightJoin(
+ $fromAlias,
+ $this->getTableName($join),
+ $alias,
+ $condition
+ );
+
+ return $this;
+ }
+
+ /**
+ * Sets a new value for a column in a bulk update query.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->update('users', 'u')
+ * ->set('u.password', md5('password'))
+ * ->where('u.id = ?');
+ * </code>
+ *
+ * @param string $key The column to set.
+ * @param string $value The value, expression, placeholder, etc.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function set($key, $value) {
+ $this->queryBuilder->set(
+ $this->helper->quoteColumnName($key),
+ $this->helper->quoteColumnName($value)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Specifies one or more restrictions to the query result.
+ * Replaces any previously specified restrictions, if any.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.name')
+ * ->from('users', 'u')
+ * ->where('u.id = ?');
+ *
+ * // You can optionally programatically build and/or expressions
+ * $qb = $conn->getQueryBuilder();
+ *
+ * $or = $qb->expr()->orx();
+ * $or->add($qb->expr()->eq('u.id', 1));
+ * $or->add($qb->expr()->eq('u.id', 2));
+ *
+ * $qb->update('users', 'u')
+ * ->set('u.password', md5('password'))
+ * ->where($or);
+ * </code>
+ *
+ * @param mixed $predicates The restriction predicates.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function where($predicates) {
+ call_user_func_array(
+ [$this->queryBuilder, 'where'],
+ func_get_args()
+ );
+
+ return $this;
+ }
+
+ /**
+ * Adds one or more restrictions to the query results, forming a logical
+ * conjunction with any previously specified restrictions.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u')
+ * ->from('users', 'u')
+ * ->where('u.username LIKE ?')
+ * ->andWhere('u.is_active = 1');
+ * </code>
+ *
+ * @param mixed $where The query restrictions.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ *
+ * @see where()
+ */
+ public function andWhere($where) {
+ call_user_func_array(
+ [$this->queryBuilder, 'andWhere'],
+ func_get_args()
+ );
+
+ return $this;
+ }
+
+ /**
+ * Adds one or more restrictions to the query results, forming a logical
+ * disjunction with any previously specified restrictions.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.name')
+ * ->from('users', 'u')
+ * ->where('u.id = 1')
+ * ->orWhere('u.id = 2');
+ * </code>
+ *
+ * @param mixed $where The WHERE statement.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ *
+ * @see where()
+ */
+ public function orWhere($where) {
+ call_user_func_array(
+ [$this->queryBuilder, 'orWhere'],
+ func_get_args()
+ );
+
+ return $this;
+ }
+
+ /**
+ * Specifies a grouping over the results of the query.
+ * Replaces any previously specified groupings, if any.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.name')
+ * ->from('users', 'u')
+ * ->groupBy('u.id');
+ * </code>
+ *
+ * @param mixed $groupBy The grouping expression.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function groupBy($groupBy) {
+ $groupBys = is_array($groupBy) ? $groupBy : func_get_args();
+
+ call_user_func_array(
+ [$this->queryBuilder, 'groupBy'],
+ $this->helper->quoteColumnNames($groupBys)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Adds a grouping expression to the query.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->select('u.name')
+ * ->from('users', 'u')
+ * ->groupBy('u.lastLogin');
+ * ->addGroupBy('u.createdAt')
+ * </code>
+ *
+ * @param mixed $groupBy The grouping expression.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function addGroupBy($groupBy) {
+ $groupBys = is_array($groupBy) ? $groupBy : func_get_args();
+
+ call_user_func_array(
+ [$this->queryBuilder, 'addGroupBy'],
+ $this->helper->quoteColumnNames($groupBys)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Sets a value for a column in an insert query.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->insert('users')
+ * ->values(
+ * array(
+ * 'name' => '?'
+ * )
+ * )
+ * ->setValue('password', '?');
+ * </code>
+ *
+ * @param string $column The column into which the value should be inserted.
+ * @param string $value The value that should be inserted into the column.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function setValue($column, $value) {
+ $this->queryBuilder->setValue(
+ $this->helper->quoteColumnName($column),
+ $value
+ );
+
+ return $this;
+ }
+
+ /**
+ * Specifies values for an insert query indexed by column names.
+ * Replaces any previous values, if any.
+ *
+ * <code>
+ * $qb = $conn->getQueryBuilder()
+ * ->insert('users')
+ * ->values(
+ * array(
+ * 'name' => '?',
+ * 'password' => '?'
+ * )
+ * );
+ * </code>
+ *
+ * @param array $values The values to specify for the insert query indexed by column names.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function values(array $values) {
+ $quotedValues = [];
+ foreach ($values as $key => $value) {
+ $quotedValues[$this->helper->quoteColumnName($key)] = $value;
+ }
+
+ $this->queryBuilder->values($quotedValues);
+
+ return $this;
+ }
+
+ /**
+ * Specifies a restriction over the groups of the query.
+ * Replaces any previous having restrictions, if any.
+ *
+ * @param mixed $having The restriction over the groups.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function having($having) {
+ call_user_func_array(
+ [$this->queryBuilder, 'having'],
+ func_get_args()
+ );
+
+ return $this;
+ }
+
+ /**
+ * Adds a restriction over the groups of the query, forming a logical
+ * conjunction with any existing having restrictions.
+ *
+ * @param mixed $having The restriction to append.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function andHaving($having) {
+ call_user_func_array(
+ [$this->queryBuilder, 'andHaving'],
+ func_get_args()
+ );
+
+ return $this;
+ }
+
+ /**
+ * Adds a restriction over the groups of the query, forming a logical
+ * disjunction with any existing having restrictions.
+ *
+ * @param mixed $having The restriction to add.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function orHaving($having) {
+ call_user_func_array(
+ [$this->queryBuilder, 'orHaving'],
+ func_get_args()
+ );
+
+ return $this;
+ }
+
+ /**
+ * Specifies an ordering for the query results.
+ * Replaces any previously specified orderings, if any.
+ *
+ * @param string $sort The ordering expression.
+ * @param string $order The ordering direction.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function orderBy($sort, $order = null) {
+ $this->queryBuilder->orderBy(
+ $this->helper->quoteColumnName($sort),
+ $order
+ );
+
+ return $this;
+ }
+
+ /**
+ * Adds an ordering to the query results.
+ *
+ * @param string $sort The ordering expression.
+ * @param string $order The ordering direction.
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function addOrderBy($sort, $order = null) {
+ $this->queryBuilder->addOrderBy(
+ $this->helper->quoteColumnName($sort),
+ $order
+ );
+
+ return $this;
+ }
+
+ /**
+ * Gets a query part by its name.
+ *
+ * @param string $queryPartName
+ *
+ * @return mixed
+ */
+ public function getQueryPart($queryPartName) {
+ return $this->queryBuilder->getQueryPart($queryPartName);
+ }
+
+ /**
+ * Gets all query parts.
+ *
+ * @return array
+ */
+ public function getQueryParts() {
+ return $this->queryBuilder->getQueryParts();
+ }
+
+ /**
+ * Resets SQL parts.
+ *
+ * @param array|null $queryPartNames
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function resetQueryParts($queryPartNames = null) {
+ $this->queryBuilder->resetQueryParts($queryPartNames);
+
+ return $this;
+ }
+
+ /**
+ * Resets a single SQL part.
+ *
+ * @param string $queryPartName
+ *
+ * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance.
+ */
+ public function resetQueryPart($queryPartName) {
+ $this->queryBuilder->resetQueryPart($queryPartName);
+
+ return $this;
+ }
+
+ /**
+ * Creates a new named parameter and bind the value $value to it.
+ *
+ * This method provides a shortcut for PDOStatement::bindValue
+ * when using prepared statements.
+ *
+ * The parameter $value specifies the value that you want to bind. If
+ * $placeholder is not provided bindValue() will automatically create a
+ * placeholder for you. An automatic placeholder will be of the name
+ * ':dcValue1', ':dcValue2' etc.
+ *
+ * For more information see {@link http://php.net/pdostatement-bindparam}
+ *
+ * Example:
+ * <code>
+ * $value = 2;
+ * $q->eq( 'id', $q->bindValue( $value ) );
+ * $stmt = $q->executeQuery(); // executed with 'id = 2'
+ * </code>
+ *
+ * @license New BSD License
+ * @link http://www.zetacomponents.org
+ *
+ * @param mixed $value
+ * @param mixed $type
+ * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
+ *
+ * @return IParameter the placeholder name used.
+ */
+ public function createNamedParameter($value, $type = \PDO::PARAM_STR, $placeHolder = null) {
+ return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder));
+ }
+
+ /**
+ * Creates a new positional parameter and bind the given value to it.
+ *
+ * Attention: If you are using positional parameters with the query builder you have
+ * to be very careful to bind all parameters in the order they appear in the SQL
+ * statement , otherwise they get bound in the wrong order which can lead to serious
+ * bugs in your code.
+ *
+ * Example:
+ * <code>
+ * $qb = $conn->getQueryBuilder();
+ * $qb->select('u.*')
+ * ->from('users', 'u')
+ * ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR))
+ * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR))
+ * </code>
+ *
+ * @param mixed $value
+ * @param integer $type
+ *
+ * @return IParameter
+ */
+ public function createPositionalParameter($value, $type = \PDO::PARAM_STR) {
+ return new Parameter($this->queryBuilder->createPositionalParameter($value, $type));
+ }
+
+ /**
+ * Creates a new parameter
+ *
+ * Example:
+ * <code>
+ * $qb = $conn->getQueryBuilder();
+ * $qb->select('u.*')
+ * ->from('users', 'u')
+ * ->where('u.username = ' . $qb->createParameter('name'))
+ * ->setParameter('name', 'Bar', PDO::PARAM_STR))
+ * </code>
+ *
+ * @param string $name
+ *
+ * @return IParameter
+ */
+ public function createParameter($name) {
+ return new Parameter(':' . $name);
+ }
+
+ /**
+ * Creates a new function
+ *
+ * Attention: Column names inside the call have to be quoted before hand
+ *
+ * Example:
+ * <code>
+ * $qb = $conn->getQueryBuilder();
+ * $qb->select($qb->createFunction('COUNT(*)'))
+ * ->from('users', 'u')
+ * echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u
+ * </code>
+ * <code>
+ * $qb = $conn->getQueryBuilder();
+ * $qb->select($qb->createFunction('COUNT(`column`)'))
+ * ->from('users', 'u')
+ * echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u
+ * </code>
+ *
+ * @param string $call
+ *
+ * @return IQueryFunction
+ */
+ public function createFunction($call) {
+ return new QueryFunction($call);
+ }
+
+ /**
+ * @param string $table
+ * @return string
+ */
+ private function getTableName($table) {
+ if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) {
+ return $this->helper->quoteColumnName($table);
+ }
+
+ return $this->helper->quoteColumnName('*PREFIX*' . $table);
+ }
+}
diff --git a/lib/private/db/querybuilder/queryfunction.php b/lib/private/db/querybuilder/queryfunction.php
new file mode 100644
index 00000000000..2a47d83b917
--- /dev/null
+++ b/lib/private/db/querybuilder/queryfunction.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\DB\QueryBuilder;
+
+use OCP\DB\QueryBuilder\IQueryFunction;
+
+class QueryFunction implements IQueryFunction {
+ /** @var string */
+ protected $function;
+
+ public function __construct($function) {
+ $this->function = $function;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString() {
+ return (string) $this->function;
+ }
+}
diff --git a/lib/private/db/querybuilder/quotehelper.php b/lib/private/db/querybuilder/quotehelper.php
new file mode 100644
index 00000000000..0735f313abc
--- /dev/null
+++ b/lib/private/db/querybuilder/quotehelper.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\DB\QueryBuilder;
+
+use OCP\DB\QueryBuilder\ILiteral;
+use OCP\DB\QueryBuilder\IParameter;
+use OCP\DB\QueryBuilder\IQueryFunction;
+
+class QuoteHelper {
+ /**
+ * @param array|string|ILiteral|IParameter|IQueryFunction $strings string, Literal or Parameter
+ * @return array|string
+ */
+ public function quoteColumnNames($strings) {
+ if (!is_array($strings)) {
+ return $this->quoteColumnName($strings);
+ }
+
+ $return = [];
+ foreach ($strings as $string) {
+ $return[] = $this->quoteColumnName($string);
+ }
+
+ return $return;
+ }
+
+ /**
+ * @param string|ILiteral|IParameter|IQueryFunction $string string, Literal or Parameter
+ * @return string
+ */
+ public function quoteColumnName($string) {
+ if ($string instanceof IParameter || $string instanceof ILiteral || $string instanceof IQueryFunction) {
+ return (string) $string;
+ }
+
+ if ($string === null || $string === '*') {
+ return $string;
+ }
+
+ if (!is_string($string)) {
+ throw new \InvalidArgumentException('Only strings, Literals and Parameters are allowed');
+ }
+
+ if (substr_count($string, '.')) {
+ list($alias, $columnName) = explode('.', $string);
+
+ if ($columnName === '*') {
+ return $string;
+ }
+
+ return $alias . '.`' . $columnName . '`';
+ }
+
+ return '`' . $string . '`';
+ }
+}
diff --git a/lib/private/encryption/util.php b/lib/private/encryption/util.php
index 8bff65428d3..d0733941a35 100644
--- a/lib/private/encryption/util.php
+++ b/lib/private/encryption/util.php
@@ -128,35 +128,6 @@ class Util {
}
/**
- * read header into array
- *
- * @param string $header
- * @return array
- */
- public function readHeader($header) {
-
- $result = array();
-
- if (substr($header, 0, strlen(self::HEADER_START)) === self::HEADER_START) {
- $endAt = strpos($header, self::HEADER_END);
- if ($endAt !== false) {
- $header = substr($header, 0, $endAt + strlen(self::HEADER_END));
-
- // +1 to not start with an ':' which would result in empty element at the beginning
- $exploded = explode(':', substr($header, strlen(self::HEADER_START)+1));
-
- $element = array_shift($exploded);
- while ($element !== self::HEADER_END) {
- $result[$element] = array_shift($exploded);
- $element = array_shift($exploded);
- }
- }
- }
-
- return $result;
- }
-
- /**
* create header for encrypted file
*
* @param array $headerData
diff --git a/lib/private/files.php b/lib/private/files.php
index b61d09d8a0c..6268bf8a129 100644
--- a/lib/private/files.php
+++ b/lib/private/files.php
@@ -21,6 +21,7 @@
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Valerio Ponte <valerio.ponte@gmail.com>
* @author Vincent Petry <pvince81@owncloud.com>
+ * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
@@ -268,58 +269,80 @@ class OC_Files {
* set the maximum upload size limit for apache hosts using .htaccess
*
* @param int $size file size in bytes
+ * @param array $files override '.htaccess' and '.user.ini' locations
* @return bool false on failure, size on success
*/
- static function setUploadLimit($size) {
+ public static function setUploadLimit($size, $files = []) {
//don't allow user to break his config
- if ($size > PHP_INT_MAX) {
- //max size is always 1 byte lower than computerFileSize returns
- if ($size > PHP_INT_MAX + 1)
- return false;
- $size -= 1;
- }
+ $size = intval($size);
if ($size < self::UPLOAD_MIN_LIMIT_BYTES) {
return false;
}
$size = OC_Helper::phpFileSize($size);
- //don't allow user to break his config -- broken or malicious size input
- if (intval($size) === 0) {
- return false;
- }
-
- //suppress errors in case we don't have permissions for
- $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess');
- if (!$htaccess) {
- return false;
- }
-
$phpValueKeys = array(
'upload_max_filesize',
'post_max_size'
);
- foreach ($phpValueKeys as $key) {
- $pattern = '/php_value ' . $key . ' (\S)*/';
- $setting = 'php_value ' . $key . ' ' . $size;
- $hasReplaced = 0;
- $content = preg_replace($pattern, $setting, $htaccess, 1, $hasReplaced);
- if ($content !== null) {
- $htaccess = $content;
+ // default locations if not overridden by $files
+ $files = array_merge([
+ '.htaccess' => OC::$SERVERROOT . '/.htaccess',
+ '.user.ini' => OC::$SERVERROOT . '/.user.ini'
+ ], $files);
+
+ $updateFiles = [
+ $files['.htaccess'] => [
+ 'pattern' => '/php_value %1$s (\S)*/',
+ 'setting' => 'php_value %1$s %2$s'
+ ],
+ $files['.user.ini'] => [
+ 'pattern' => '/%1$s=(\S)*/',
+ 'setting' => '%1$s=%2$s'
+ ]
+ ];
+
+ $success = true;
+
+ foreach ($updateFiles as $filename => $patternMap) {
+ // suppress warnings from fopen()
+ $handle = @fopen($filename, 'r+');
+ if (!$handle) {
+ \OCP\Util::writeLog('files',
+ 'Can\'t write upload limit to ' . $filename . '. Please check the file permissions',
+ \OCP\Util::WARN);
+ $success = false;
+ continue; // try to update as many files as possible
}
- if ($hasReplaced === 0) {
- $htaccess .= "\n" . $setting;
+
+ $content = '';
+ while (!feof($handle)) {
+ $content .= fread($handle, 1000);
}
+
+ foreach ($phpValueKeys as $key) {
+ $pattern = vsprintf($patternMap['pattern'], [$key]);
+ $setting = vsprintf($patternMap['setting'], [$key, $size]);
+ $hasReplaced = 0;
+ $newContent = preg_replace($pattern, $setting, $content, 1, $hasReplaced);
+ if ($newContent !== null) {
+ $content = $newContent;
+ }
+ if ($hasReplaced === 0) {
+ $content .= "\n" . $setting;
+ }
+ }
+
+ // write file back
+ ftruncate($handle, 0);
+ rewind($handle);
+ fwrite($handle, $content);
+
+ fclose($handle);
}
- //check for write permissions
- if (is_writable(OC::$SERVERROOT . '/.htaccess')) {
- file_put_contents(OC::$SERVERROOT . '/.htaccess', $htaccess);
+ if ($success) {
return OC_Helper::computerFileSize($size);
- } else {
- \OCP\Util::writeLog('files',
- 'Can\'t write upload limit to ' . OC::$SERVERROOT . '/.htaccess. Please check the file permissions',
- \OCP\Util::WARN);
}
return false;
}
diff --git a/lib/private/files/cache/storage.php b/lib/private/files/cache/storage.php
index ebef245f399..338d8308281 100644
--- a/lib/private/files/cache/storage.php
+++ b/lib/private/files/cache/storage.php
@@ -43,9 +43,10 @@ class Storage {
/**
* @param \OC\Files\Storage\Storage|string $storage
+ * @param bool $isAvailable
* @throws \RuntimeException
*/
- public function __construct($storage) {
+ public function __construct($storage, $isAvailable = true) {
if ($storage instanceof \OC\Files\Storage\Storage) {
$this->storageId = $storage->getId();
} else {
@@ -53,17 +54,14 @@ class Storage {
}
$this->storageId = self::adjustStorageId($this->storageId);
- $sql = 'SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?';
- $result = \OC_DB::executeAudited($sql, array($this->storageId));
- if ($row = $result->fetchRow()) {
+ if ($row = self::getStorageById($this->storageId)) {
$this->numericId = $row['numeric_id'];
} else {
$connection = \OC_DB::getConnection();
- if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId])) {
+ if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $isAvailable])) {
$this->numericId = \OC_DB::insertid('*PREFIX*storages');
} else {
- $result = \OC_DB::executeAudited($sql, array($this->storageId));
- if ($row = $result->fetchRow()) {
+ if ($row = self::getStorageById($this->storageId)) {
$this->numericId = $row['numeric_id'];
} else {
throw new \RuntimeException('Storage could neither be inserted nor be selected from the database');
@@ -73,6 +71,16 @@ class Storage {
}
/**
+ * @param string $storageId
+ * @return array|null
+ */
+ public static function getStorageById($storageId) {
+ $sql = 'SELECT * FROM `*PREFIX*storages` WHERE `id` = ?';
+ $result = \OC_DB::executeAudited($sql, array($storageId));
+ return $result->fetchRow();
+ }
+
+ /**
* Adjusts the storage id to use md5 if too long
* @param string $storageId storage id
* @return string unchanged $storageId if its length is less than 64 characters,
@@ -120,9 +128,7 @@ class Storage {
public static function getNumericStorageId($storageId) {
$storageId = self::adjustStorageId($storageId);
- $sql = 'SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?';
- $result = \OC_DB::executeAudited($sql, array($storageId));
- if ($row = $result->fetchRow()) {
+ if ($row = self::getStorageById($storageId)) {
return $row['numeric_id'];
} else {
return null;
@@ -130,6 +136,28 @@ class Storage {
}
/**
+ * @return array|null [ available, last_checked ]
+ */
+ public function getAvailability() {
+ if ($row = self::getStorageById($this->storageId)) {
+ return [
+ 'available' => $row['available'],
+ 'last_checked' => $row['last_checked']
+ ];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @param bool $isAvailable
+ */
+ public function setAvailability($isAvailable) {
+ $sql = 'UPDATE `*PREFIX*storages` SET `available` = ?, `last_checked` = ? WHERE `id` = ?';
+ \OC_DB::executeAudited($sql, array($isAvailable, time(), $this->storageId));
+ }
+
+ /**
* Check if a string storage id is known
*
* @param string $storageId
diff --git a/lib/private/files/node/root.php b/lib/private/files/node/root.php
index 7ffb3674a8f..4df926748de 100644
--- a/lib/private/files/node/root.php
+++ b/lib/private/files/node/root.php
@@ -323,4 +323,33 @@ class Root extends Folder implements IRootFolder {
public function getName() {
return '';
}
+
+ /**
+ * Returns a view to user's files folder
+ *
+ * @param String $userId user ID
+ * @return \OCP\Files\Folder
+ */
+ public function getUserFolder($userId) {
+ \OC\Files\Filesystem::initMountPoints($userId);
+ $dir = '/' . $userId;
+ $folder = null;
+
+ if (!$this->nodeExists($dir)) {
+ $folder = $this->newFolder($dir);
+ } else {
+ $folder = $this->get($dir);
+ }
+
+ $dir = '/files';
+ if (!$folder->nodeExists($dir)) {
+ $folder = $folder->newFolder($dir);
+ \OC_Util::copySkeleton($userId, $folder);
+ } else {
+ $folder = $folder->get($dir);
+ }
+
+ return $folder;
+
+ }
}
diff --git a/lib/private/files/objectstore/objectstorestorage.php b/lib/private/files/objectstore/objectstorestorage.php
index 24398649727..a85553186ae 100644
--- a/lib/private/files/objectstore/objectstorestorage.php
+++ b/lib/private/files/objectstore/objectstorestorage.php
@@ -24,6 +24,7 @@
namespace OC\Files\ObjectStore;
+use Icewind\Streams\IteratorDirectory;
use OCP\Files\ObjectStore\IObjectStore;
class ObjectStoreStorage extends \OC\Files\Storage\Common {
@@ -216,9 +217,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
$files[] = $file['name'];
}
- \OC\Files\Stream\Dir::register('objectstore' . $path . '/', $files);
-
- return opendir('fakedir://objectstore' . $path . '/');
+ return IteratorDirectory::wrap($files);
} catch (\Exception $e) {
\OCP\Util::writeLog('objectstore', $e->getMessage(), \OCP\Util::ERROR);
return false;
diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php
index 847cb8492fe..a5ed5fd3996 100644
--- a/lib/private/files/storage/common.php
+++ b/lib/private/files/storage/common.php
@@ -338,7 +338,7 @@ abstract class Common implements Storage {
}
if (!isset($this->watcher)) {
$this->watcher = new Watcher($storage);
- $globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_ONCE);
+ $globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
}
return $this->watcher;
@@ -404,6 +404,11 @@ abstract class Common implements Storage {
return implode('/', $output);
}
+ /**
+ * Test a storage for availability
+ *
+ * @return bool
+ */
public function test() {
if ($this->stat('')) {
return true;
@@ -650,4 +655,18 @@ abstract class Common implements Storage {
public function changeLock($path, $type, ILockingProvider $provider) {
$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
}
+
+ /**
+ * @return array [ available, last_checked ]
+ */
+ public function getAvailability() {
+ return $this->getStorageCache()->getAvailability();
+ }
+
+ /**
+ * @param bool $isAvailable
+ */
+ public function setAvailability($isAvailable) {
+ $this->getStorageCache()->setAvailability($isAvailable);
+ }
}
diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php
index d67e6b9f97e..6e89dcccbcd 100644
--- a/lib/private/files/storage/dav.php
+++ b/lib/private/files/storage/dav.php
@@ -38,7 +38,7 @@ namespace OC\Files\Storage;
use Exception;
use OC\Files\Filesystem;
use OC\Files\Stream\Close;
-use OC\Files\Stream\Dir;
+use Icewind\Streams\IteratorDirectory;
use OC\MemCache\ArrayCache;
use OCP\Constants;
use OCP\Files;
@@ -211,17 +211,16 @@ class DAV extends Common {
$file = basename($file);
$content[] = $file;
}
- Dir::register($id, $content);
- return opendir('fakedir://' . $id);
+ return IteratorDirectory::wrap($content);
} catch (ClientHttpException $e) {
if ($e->getHttpStatus() === 404) {
$this->statCache->clear($path . '/');
$this->statCache->set($path, false);
return false;
}
- $this->convertException($e);
+ $this->convertException($e, $path);
} catch (\Exception $e) {
- $this->convertException($e);
+ $this->convertException($e, $path);
}
return false;
}
@@ -286,9 +285,9 @@ class DAV extends Common {
if ($e->getHttpStatus() === 404) {
return false;
}
- $this->convertException($e);
+ $this->convertException($e, $path);
} catch (\Exception $e) {
- $this->convertException($e);
+ $this->convertException($e, $path);
}
return false;
}
@@ -311,9 +310,9 @@ class DAV extends Common {
if ($e->getHttpStatus() === 404) {
return false;
}
- $this->convertException($e);
+ $this->convertException($e, $path);
} catch (\Exception $e) {
- $this->convertException($e);
+ $this->convertException($e, $path);
}
return false;
}
@@ -363,6 +362,9 @@ class DAV extends Common {
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($statusCode !== 200) {
Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, Util::ERROR);
+ if ($statusCode === 423) {
+ throw new \OCP\Lock\LockedException($path);
+ }
}
curl_close($curl);
rewind($fp);
@@ -446,10 +448,10 @@ class DAV extends Common {
if ($e->getHttpStatus() === 501) {
return false;
}
- $this->convertException($e);
+ $this->convertException($e, $path);
return false;
} catch (\Exception $e) {
- $this->convertException($e);
+ $this->convertException($e, $path);
return false;
}
} else {
@@ -502,6 +504,9 @@ class DAV extends Common {
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($statusCode !== 200) {
Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, Util::ERROR);
+ if ($statusCode === 423) {
+ throw new \OCP\Lock\LockedException($path);
+ }
}
curl_close($curl);
fclose($source);
@@ -564,9 +569,9 @@ class DAV extends Common {
if ($e->getHttpStatus() === 404) {
return array();
}
- $this->convertException($e);
+ $this->convertException($e, $path);
} catch (\Exception $e) {
- $this->convertException($e);
+ $this->convertException($e, $path);
}
return array();
}
@@ -591,9 +596,9 @@ class DAV extends Common {
if ($e->getHttpStatus() === 404) {
return false;
}
- $this->convertException($e);
+ $this->convertException($e, $path);
} catch (\Exception $e) {
- $this->convertException($e);
+ $this->convertException($e, $path);
}
return false;
}
@@ -643,9 +648,9 @@ class DAV extends Common {
return false;
}
- $this->convertException($e);
+ $this->convertException($e, $path);
} catch (\Exception $e) {
- $this->convertException($e);
+ $this->convertException($e, $path);
}
return false;
}
@@ -760,13 +765,17 @@ class DAV extends Common {
return $remoteMtime > $time;
}
} catch (ClientHttpException $e) {
- if ($e->getHttpStatus() === 404) {
+ if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
+ if ($path === '') {
+ // if root is gone it means the storage is not available
+ throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
+ }
return false;
}
- $this->convertException($e);
+ $this->convertException($e, $path);
return false;
} catch (\Exception $e) {
- $this->convertException($e);
+ $this->convertException($e, $path);
return false;
}
}
@@ -778,15 +787,19 @@ class DAV extends Common {
* or do nothing.
*
* @param Exception $e sabre exception
+ * @param string $path optional path from the operation
*
* @throws StorageInvalidException if the storage is invalid, for example
* when the authentication expired or is invalid
* @throws StorageNotAvailableException if the storage is not available,
* which might be temporary
*/
- private function convertException(Exception $e) {
+ private function convertException(Exception $e, $path = '') {
Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
if ($e instanceof ClientHttpException) {
+ if ($e->getHttpStatus() === 423) {
+ throw new \OCP\Lock\LockedException($path);
+ }
if ($e->getHttpStatus() === 401) {
// either password was changed or was invalid all along
throw new StorageInvalidException(get_class($e).': '.$e->getMessage());
diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php
index b7272b7d1f0..3676fe69131 100644
--- a/lib/private/files/storage/local.php
+++ b/lib/private/files/storage/local.php
@@ -35,7 +35,6 @@
*/
namespace OC\Files\Storage;
-
/**
* for local filestore, we only have to map the paths
*/
diff --git a/lib/private/files/storage/wrapper/availability.php b/lib/private/files/storage/wrapper/availability.php
new file mode 100644
index 00000000000..37319a8f7d1
--- /dev/null
+++ b/lib/private/files/storage/wrapper/availability.php
@@ -0,0 +1,462 @@
+<?php
+/**
+ * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Files\Storage\Wrapper;
+
+/**
+ * Availability checker for storages
+ *
+ * Throws a StorageNotAvailableException for storages with known failures
+ */
+class Availability extends Wrapper {
+ const RECHECK_TTL_SEC = 600; // 10 minutes
+
+ /**
+ * @return bool
+ */
+ private function updateAvailability() {
+ try {
+ $result = $this->test();
+ } catch (\Exception $e) {
+ $result = false;
+ }
+ $this->setAvailability($result);
+ return $result;
+ }
+
+ /**
+ * @return bool
+ */
+ private function isAvailable() {
+ $availability = $this->getAvailability();
+ if (!$availability['available']) {
+ // trigger a recheck if TTL reached
+ if ((time() - $availability['last_checked']) > self::RECHECK_TTL_SEC) {
+ return $this->updateAvailability();
+ }
+ }
+ return $availability['available'];
+ }
+
+ /**
+ * @throws \OCP\Files\StorageNotAvailableException
+ */
+ private function checkAvailability() {
+ if (!$this->isAvailable()) {
+ throw new \OCP\Files\StorageNotAvailableException();
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function mkdir($path) {
+ $this->checkAvailability();
+ try {
+ return parent::mkdir($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function rmdir($path) {
+ $this->checkAvailability();
+ try {
+ return parent::rmdir($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function opendir($path) {
+ $this->checkAvailability();
+ try {
+ return parent::opendir($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function is_dir($path) {
+ $this->checkAvailability();
+ try {
+ return parent::is_dir($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function is_file($path) {
+ $this->checkAvailability();
+ try {
+ return parent::is_file($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function stat($path) {
+ $this->checkAvailability();
+ try {
+ return parent::stat($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function filetype($path) {
+ $this->checkAvailability();
+ try {
+ return parent::filetype($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function filesize($path) {
+ $this->checkAvailability();
+ try {
+ return parent::filesize($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function isCreatable($path) {
+ $this->checkAvailability();
+ try {
+ return parent::isCreatable($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function isReadable($path) {
+ $this->checkAvailability();
+ try {
+ return parent::isReadable($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function isUpdatable($path) {
+ $this->checkAvailability();
+ try {
+ return parent::isUpdatable($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function isDeletable($path) {
+ $this->checkAvailability();
+ try {
+ return parent::isDeletable($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function isSharable($path) {
+ $this->checkAvailability();
+ try {
+ return parent::isSharable($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function getPermissions($path) {
+ $this->checkAvailability();
+ try {
+ return parent::getPermissions($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function file_exists($path) {
+ $this->checkAvailability();
+ try {
+ return parent::file_exists($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function filemtime($path) {
+ $this->checkAvailability();
+ try {
+ return parent::filemtime($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function file_get_contents($path) {
+ $this->checkAvailability();
+ try {
+ return parent::file_get_contents($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function file_put_contents($path, $data) {
+ $this->checkAvailability();
+ try {
+ return parent::file_put_contents($path, $data);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function unlink($path) {
+ $this->checkAvailability();
+ try {
+ return parent::unlink($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function rename($path1, $path2) {
+ $this->checkAvailability();
+ try {
+ return parent::rename($path1, $path2);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function copy($path1, $path2) {
+ $this->checkAvailability();
+ try {
+ return parent::copy($path1, $path2);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function fopen($path, $mode) {
+ $this->checkAvailability();
+ try {
+ return parent::fopen($path, $mode);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function getMimeType($path) {
+ $this->checkAvailability();
+ try {
+ return parent::getMimeType($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function hash($type, $path, $raw = false) {
+ $this->checkAvailability();
+ try {
+ return parent::hash($type, $path, $raw);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function free_space($path) {
+ $this->checkAvailability();
+ try {
+ return parent::free_space($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function search($query) {
+ $this->checkAvailability();
+ try {
+ return parent::search($query);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function touch($path, $mtime = null) {
+ $this->checkAvailability();
+ try {
+ return parent::touch($path, $mtime);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function getLocalFile($path) {
+ $this->checkAvailability();
+ try {
+ return parent::getLocalFile($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function getLocalFolder($path) {
+ $this->checkAvailability();
+ try {
+ return parent::getLocalFolder($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function hasUpdated($path, $time) {
+ $this->checkAvailability();
+ try {
+ return parent::hasUpdated($path, $time);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function getOwner($path) {
+ $this->checkAvailability();
+ try {
+ return parent::getOwner($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function getETag($path) {
+ $this->checkAvailability();
+ try {
+ return parent::getETag($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function getDirectDownload($path) {
+ $this->checkAvailability();
+ try {
+ return parent::getDirectDownload($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
+ $this->checkAvailability();
+ try {
+ return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
+ $this->checkAvailability();
+ try {
+ return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+
+ /** {@inheritdoc} */
+ public function getMetaData($path) {
+ $this->checkAvailability();
+ try {
+ return parent::getMetaData($path);
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ $this->setAvailability(false);
+ throw $e;
+ }
+ }
+}
diff --git a/lib/private/files/storage/wrapper/encryption.php b/lib/private/files/storage/wrapper/encryption.php
index 8818b822fa7..4ba9b21ddb4 100644
--- a/lib/private/files/storage/wrapper/encryption.php
+++ b/lib/private/files/storage/wrapper/encryption.php
@@ -31,6 +31,7 @@ use OC\Encryption\Util;
use OC\Files\Filesystem;
use OC\Files\Mount\Manager;
use OC\Files\Storage\LocalTempFileTrait;
+use OCP\Encryption\Exceptions\GenericEncryptionException;
use OCP\Encryption\IFile;
use OCP\Encryption\IManager;
use OCP\Encryption\Keys\IStorage;
@@ -126,12 +127,11 @@ class Encryption extends Wrapper {
$info = $this->getCache()->get($path);
if (isset($this->unencryptedSize[$fullPath])) {
$size = $this->unencryptedSize[$fullPath];
+ // update file cache
+ $info['encrypted'] = true;
+ $info['size'] = $size;
+ $this->getCache()->put($path, $info);
- if (isset($info['fileid'])) {
- $info['encrypted'] = true;
- $info['size'] = $size;
- $this->getCache()->put($path, $info);
- }
return $size;
}
@@ -174,9 +174,8 @@ class Encryption extends Wrapper {
public function file_get_contents($path) {
$encryptionModule = $this->getEncryptionModule($path);
- $info = $this->getCache()->get($path);
- if ($encryptionModule || $info['encrypted'] === true) {
+ if ($encryptionModule) {
$handle = $this->fopen($path, "r");
if (!$handle) {
return false;
@@ -338,14 +337,15 @@ class Encryption extends Wrapper {
* @param string $path
* @param string $mode
* @return resource
+ * @throws GenericEncryptionException
+ * @throws ModuleDoesNotExistsException
*/
public function fopen($path, $mode) {
$encryptionEnabled = $this->encryptionManager->isEnabled();
$shouldEncrypt = false;
$encryptionModule = null;
- $rawHeader = $this->getHeader($path);
- $header = $this->util->readHeader($rawHeader);
+ $header = $this->getHeader($path);
$fullPath = $this->getFullPath($path);
$encryptionModuleId = $this->util->getEncryptionModuleId($header);
@@ -380,6 +380,10 @@ class Encryption extends Wrapper {
|| $mode === 'wb'
|| $mode === 'wb+'
) {
+ // don't overwrite encrypted files if encyption is not enabled
+ if ($targetIsEncrypted && $encryptionEnabled === false) {
+ throw new GenericEncryptionException('Tried to access encrypted file but encryption is not enabled');
+ }
if ($encryptionEnabled) {
// if $encryptionModuleId is empty, the default module will be used
$encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId);
@@ -398,6 +402,7 @@ class Encryption extends Wrapper {
// OC_DEFAULT_MODULE to read the file
$encryptionModule = $this->encryptionManager->getEncryptionModule('OC_DEFAULT_MODULE');
$shouldEncrypt = true;
+ $targetIsEncrypted = true;
}
}
} catch (ModuleDoesNotExistsException $e) {
@@ -413,10 +418,11 @@ class Encryption extends Wrapper {
}
if ($shouldEncrypt === true && $encryptionModule !== null) {
+ $headerSize = $this->getHeaderSize($path);
$source = $this->storage->fopen($path, $mode);
$handle = \OC\Files\Stream\Encryption::wrap($source, $path, $fullPath, $header,
$this->uid, $encryptionModule, $this->storage, $this, $this->util, $this->fileHelper, $mode,
- $size, $unencryptedSize, strlen($rawHeader));
+ $size, $unencryptedSize, $headerSize);
return $handle;
}
@@ -515,7 +521,7 @@ class Encryption extends Wrapper {
if ($preserveMtime) {
$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
}
- $isEncrypted = $this->mount->getOption('encrypt', true) ? 1 : 0;
+ $isEncrypted = $this->encryptionManager->isEnabled() && $this->mount->getOption('encrypt', true) ? 1 : 0;
// in case of a rename we need to manipulate the source cache because
// this information will be kept for the new target
@@ -606,27 +612,101 @@ class Encryption extends Wrapper {
}
/**
+ * read first block of encrypted file, typically this will contain the
+ * encryption header
+ *
+ * @param string $path
+ * @return string
+ */
+ protected function readFirstBlock($path) {
+ $firstBlock = '';
+ if ($this->storage->file_exists($path)) {
+ $handle = $this->storage->fopen($path, 'r');
+ $firstBlock = fread($handle, $this->util->getHeaderSize());
+ fclose($handle);
+ }
+ return $firstBlock;
+ }
+
+ /**
+ * return header size of given file
+ *
+ * @param string $path
+ * @return int
+ */
+ protected function getHeaderSize($path) {
+ $headerSize = 0;
+ $realFile = $this->util->stripPartialFileExtension($path);
+ if ($this->storage->file_exists($realFile)) {
+ $path = $realFile;
+ }
+ $firstBlock = $this->readFirstBlock($path);
+
+ if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) {
+ $headerSize = strlen($firstBlock);
+ }
+
+ return $headerSize;
+ }
+
+ /**
+ * parse raw header to array
+ *
+ * @param string $rawHeader
+ * @return array
+ */
+ protected function parseRawHeader($rawHeader) {
+ $result = array();
+ if (substr($rawHeader, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) {
+ $header = $rawHeader;
+ $endAt = strpos($header, Util::HEADER_END);
+ if ($endAt !== false) {
+ $header = substr($header, 0, $endAt + strlen(Util::HEADER_END));
+
+ // +1 to not start with an ':' which would result in empty element at the beginning
+ $exploded = explode(':', substr($header, strlen(Util::HEADER_START)+1));
+
+ $element = array_shift($exploded);
+ while ($element !== Util::HEADER_END) {
+ $result[$element] = array_shift($exploded);
+ $element = array_shift($exploded);
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
* read header from file
*
* @param string $path
* @return array
*/
protected function getHeader($path) {
- $header = '';
$realFile = $this->util->stripPartialFileExtension($path);
if ($this->storage->file_exists($realFile)) {
$path = $realFile;
}
- if ($this->storage->file_exists($path)) {
- $handle = $this->storage->fopen($path, 'r');
- $firstBlock = fread($handle, $this->util->getHeaderSize());
- fclose($handle);
- if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) {
- $header = $firstBlock;
+ $firstBlock = $this->readFirstBlock($path);
+ $result = $this->parseRawHeader($firstBlock);
+
+ // if the header doesn't contain a encryption module we check if it is a
+ // legacy file. If true, we add the default encryption module
+ if (!isset($result[Util::HEADER_ENCRYPTION_MODULE_KEY])) {
+ if (!empty($result)) {
+ $result[Util::HEADER_ENCRYPTION_MODULE_KEY] = 'OC_DEFAULT_MODULE';
+ } else {
+ // if the header was empty we have to check first if it is a encrypted file at all
+ $info = $this->getCache()->get($path);
+ if (isset($info['encrypted']) && $info['encrypted'] === true) {
+ $result[Util::HEADER_ENCRYPTION_MODULE_KEY] = 'OC_DEFAULT_MODULE';
+ }
}
}
- return $header;
+
+ return $result;
}
/**
@@ -639,8 +719,7 @@ class Encryption extends Wrapper {
*/
protected function getEncryptionModule($path) {
$encryptionModule = null;
- $rawHeader = $this->getHeader($path);
- $header = $this->util->readHeader($rawHeader);
+ $header = $this->getHeader($path);
$encryptionModuleId = $this->util->getEncryptionModuleId($header);
if (!empty($encryptionModuleId)) {
try {
@@ -675,4 +754,5 @@ class Encryption extends Wrapper {
return false;
}
+
}
diff --git a/lib/private/files/storage/wrapper/permissionsmask.php b/lib/private/files/storage/wrapper/permissionsmask.php
index 993936321d0..50c3f2a6268 100644
--- a/lib/private/files/storage/wrapper/permissionsmask.php
+++ b/lib/private/files/storage/wrapper/permissionsmask.php
@@ -65,6 +65,10 @@ class PermissionsMask extends Wrapper {
return $this->checkMask(Constants::PERMISSION_DELETE) and parent::isDeletable($path);
}
+ public function isSharable($path) {
+ return $this->checkMask(Constants::PERMISSION_SHARE) and parent::isSharable($parm);
+ }
+
public function getPermissions($path) {
return $this->storage->getPermissions($path) & $this->mask;
}
diff --git a/lib/private/files/storage/wrapper/wrapper.php b/lib/private/files/storage/wrapper/wrapper.php
index d1414880beb..b43dd4fe142 100644
--- a/lib/private/files/storage/wrapper/wrapper.php
+++ b/lib/private/files/storage/wrapper/wrapper.php
@@ -498,6 +498,24 @@ class Wrapper implements \OC\Files\Storage\Storage {
}
/**
+ * Get availability of the storage
+ *
+ * @return array [ available, last_checked ]
+ */
+ public function getAvailability() {
+ return $this->storage->getAvailability();
+ }
+
+ /**
+ * Set availability of the storage
+ *
+ * @param bool $isAvailable
+ */
+ public function setAvailability($isAvailable) {
+ $this->storage->setAvailability($isAvailable);
+ }
+
+ /**
* @param string $path the path of the target folder
* @param string $fileName the name of the file itself
* @return void
diff --git a/lib/private/files/type/detection.php b/lib/private/files/type/detection.php
index 61ca32a5ed1..ba286637df3 100644
--- a/lib/private/files/type/detection.php
+++ b/lib/private/files/type/detection.php
@@ -4,6 +4,7 @@
* @author Jens-Christian Fischer <jens-christian.fischer@switch.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <icewind@owncloud.com>
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Tanghus <thomas@tanghus.net>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
@@ -25,6 +26,9 @@
namespace OC\Files\Type;
+use OCP\Files\IMimeTypeDetector;
+use OCP\IURLGenerator;
+
/**
* Class Detection
*
@@ -32,9 +36,28 @@ namespace OC\Files\Type;
*
* @package OC\Files\Type
*/
-class Detection {
- protected $mimetypes = array();
- protected $secureMimeTypes = array();
+class Detection implements IMimeTypeDetector {
+ protected $mimetypes = [];
+ protected $secureMimeTypes = [];
+
+ protected $mimetypeIcons = [];
+ /** @var string[] */
+ protected $mimeTypeAlias = [];
+
+ /** @var IURLGenerator */
+ private $urlGenerator;
+
+ /** @var string */
+ private $configDir;
+
+ /**
+ * @param IURLGenerator $urlGenerator
+ * @param string $configDir
+ */
+ public function __construct(IURLGenerator $urlGenerator, $configDir) {
+ $this->urlGenerator = $urlGenerator;
+ $this->configDir = $configDir;
+ }
/**
* Add an extension -> mimetype mapping
@@ -66,17 +89,57 @@ class Detection {
// Update the alternative mimetypes to avoid having to look them up each time.
foreach ($this->mimetypes as $mimeType) {
- $this->secureMimeTypes[$mimeType[0]] = $mimeType[1] ?: $mimeType[0];
+ $this->secureMimeTypes[$mimeType[0]] = isset($mimeType[1]) ? $mimeType[1]: $mimeType[0];
}
}
/**
+ * Add the mimetype aliases if they are not yet present
+ */
+ private function loadAliases() {
+ if (!empty($this->mimeTypeAlias)) {
+ return;
+ }
+
+ $file = file_get_contents($this->configDir . '/mimetypealiases.dist.json');
+ $this->mimeTypeAlias = get_object_vars(json_decode($file));
+
+ if (file_exists($this->configDir . '/mimetypealiases.json')) {
+ $custom = get_object_vars(json_decode(file_get_contents($this->configDir . '/mimetypealiases.json')));
+ $this->mimeTypeAlias = array_merge($this->mimeTypeAlias, $custom);
+ }
+ }
+
+ /**
+ * Add mimetype mappings if they are not yet present
+ */
+ private function loadMappings() {
+ if (!empty($this->mimetypes)) {
+ return;
+ }
+
+ $dist = file_get_contents($this->configDir . '/mimetypemapping.dist.json');
+ $mimetypemapping = get_object_vars(json_decode($dist));
+
+ //Check if need to load custom mappings
+ if (file_exists($this->configDir . '/mimetypemapping.json')) {
+ $custom = file_get_contents($this->configDir . '/mimetypemapping.json');
+ $custom_mapping = get_object_vars(json_decode($custom));
+ $mimetypemapping = array_merge($mimetypemapping, $custom_mapping);
+ }
+
+ $this->registerTypeArray($mimetypemapping);
+ }
+
+ /**
* detect mimetype only based on filename, content of file is not used
*
* @param string $path
* @return string
*/
public function detectPath($path) {
+ $this->loadMappings();
+
if (strpos($path, '.')) {
//try to guess the type by the file extension
$extension = strtolower(strrchr(basename($path), "."));
@@ -96,6 +159,8 @@ class Detection {
* @return string
*/
public function detect($path) {
+ $this->loadMappings();
+
if (@is_dir($path)) {
// directories are easy
return "httpd/unix-directory";
@@ -166,8 +231,64 @@ class Detection {
* @return string
*/
public function getSecureMimeType($mimeType) {
+ $this->loadMappings();
+
return isset($this->secureMimeTypes[$mimeType])
? $this->secureMimeTypes[$mimeType]
: 'application/octet-stream';
}
+
+ /**
+ * Get path to the icon of a file type
+ * @param string $mimeType the MIME type
+ * @return string the url
+ */
+ public function mimeTypeIcon($mimetype) {
+ $this->loadAliases();
+
+ if (isset($this->mimeTypeAlias[$mimetype])) {
+ $mimetype = $this->mimeTypeAlias[$mimetype];
+ }
+ if (isset($this->mimetypeIcons[$mimetype])) {
+ return $this->mimetypeIcons[$mimetype];
+ }
+
+ // Replace slash and backslash with a minus
+ $icon = str_replace('/', '-', $mimetype);
+ $icon = str_replace('\\', '-', $icon);
+
+ // Is it a dir?
+ if ($mimetype === 'dir') {
+ $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/folder.png');
+ return $this->mimetypeIcons[$mimetype];
+ }
+ if ($mimetype === 'dir-shared') {
+ $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/folder-shared.png');
+ return $this->mimetypeIcons[$mimetype];
+ }
+ if ($mimetype === 'dir-external') {
+ $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/folder-external.png');
+ return $this->mimetypeIcons[$mimetype];
+ }
+
+ // Icon exists?
+ try {
+ $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/' . $icon . '.png');
+ return $this->mimetypeIcons[$mimetype];
+ } catch (\RuntimeException $e) {
+ // Specified image not found
+ }
+
+ // Try only the first part of the filetype
+ $mimePart = substr($icon, 0, strpos($icon, '-'));
+ try {
+ $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/' . $mimePart . '.png');
+ return $this->mimetypeIcons[$mimetype];
+ } catch (\RuntimeException $e) {
+ // Image for the first part of the mimetype not found
+ }
+
+ $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/file.png');
+ return $this->mimetypeIcons[$mimetype];
+ }
}
diff --git a/lib/private/files/utils/scanner.php b/lib/private/files/utils/scanner.php
index 3d68eb530a2..c70f4beb31d 100644
--- a/lib/private/files/utils/scanner.php
+++ b/lib/private/files/utils/scanner.php
@@ -131,6 +131,9 @@ class Scanner extends PublicEmitter {
* @throws \OC\ForbiddenException
*/
public function scan($dir = '') {
+ if (!Filesystem::isValidPath($dir)) {
+ throw new \InvalidArgumentException('Invalid path to scan');
+ }
$mounts = $this->getMounts($dir);
foreach ($mounts as $mount) {
if (is_null($mount->getStorage())) {
diff --git a/lib/private/files/view.php b/lib/private/files/view.php
index cb3c05d2bca..9afa9d40b20 100644
--- a/lib/private/files/view.php
+++ b/lib/private/files/view.php
@@ -642,10 +642,10 @@ class View {
}
$run = true;
- if ($this->shouldEmitHooks() && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
+ if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
// if it was a rename from a part file to a regular file it was a write and not a rename operation
$this->emit_file_hooks_pre($exists, $path2, $run);
- } elseif ($this->shouldEmitHooks()) {
+ } elseif ($this->shouldEmitHooks($path1)) {
\OC_Hook::emit(
Filesystem::CLASSNAME, Filesystem::signal_rename,
array(
@@ -1087,6 +1087,11 @@ class View {
return true;
}
$fullPath = $this->getAbsolutePath($path);
+
+ if ($fullPath === $defaultRoot) {
+ return true;
+ }
+
return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
}
@@ -1354,7 +1359,7 @@ class View {
// if sharing was disabled for the user we remove the share permissions
if (\OCP\Util::isSharingDisabledForUser()) {
- $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
+ $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
}
$files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount);
@@ -1656,11 +1661,11 @@ class View {
}
// verify database - e.g. mysql only 3-byte chars
- if (preg_match('%^(?:
+ if (preg_match('%(?:
\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
-)*$%xs', $fileName)) {
+)%xs', $fileName)) {
throw new InvalidPathException($l10n->t('4-byte characters are not supported in file names'));
}
diff --git a/lib/private/helper.php b/lib/private/helper.php
index 4c2f1f509c8..b8e4b451835 100644
--- a/lib/private/helper.php
+++ b/lib/private/helper.php
@@ -50,81 +50,7 @@ use Symfony\Component\Process\ExecutableFinder;
* Collection of useful functions
*/
class OC_Helper {
- private static $mimetypeIcons = array();
- private static $mimetypeDetector;
private static $templateManager;
- /** @var string[] */
- private static $mimeTypeAlias = array(
- 'application/octet-stream' => 'file', // use file icon as fallback
-
- 'application/illustrator' => 'image/vector',
- 'application/postscript' => 'image/vector',
- 'image/svg+xml' => 'image/vector',
-
- 'application/coreldraw' => 'image',
- 'application/x-gimp' => 'image',
- 'application/x-photoshop' => 'image',
- 'application/x-dcraw' => 'image',
-
- 'application/font-sfnt' => 'font',
- 'application/x-font' => 'font',
- 'application/font-woff' => 'font',
- 'application/vnd.ms-fontobject' => 'font',
-
- 'application/json' => 'text/code',
- 'application/x-perl' => 'text/code',
- 'application/x-php' => 'text/code',
- 'text/x-shellscript' => 'text/code',
- 'application/yaml' => 'text/code',
- 'application/xml' => 'text/html',
- 'text/css' => 'text/code',
- 'application/x-tex' => 'text',
-
- 'application/x-compressed' => 'package/x-generic',
- 'application/x-7z-compressed' => 'package/x-generic',
- 'application/x-deb' => 'package/x-generic',
- 'application/x-gzip' => 'package/x-generic',
- 'application/x-rar-compressed' => 'package/x-generic',
- 'application/x-tar' => 'package/x-generic',
- 'application/vnd.android.package-archive' => 'package/x-generic',
- 'application/zip' => 'package/x-generic',
-
- 'application/msword' => 'x-office/document',
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'x-office/document',
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'x-office/document',
- 'application/vnd.ms-word.document.macroEnabled.12' => 'x-office/document',
- 'application/vnd.ms-word.template.macroEnabled.12' => 'x-office/document',
- 'application/vnd.oasis.opendocument.text' => 'x-office/document',
- 'application/vnd.oasis.opendocument.text-template' => 'x-office/document',
- 'application/vnd.oasis.opendocument.text-web' => 'x-office/document',
- 'application/vnd.oasis.opendocument.text-master' => 'x-office/document',
-
- 'application/mspowerpoint' => 'x-office/presentation',
- 'application/vnd.ms-powerpoint' => 'x-office/presentation',
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'x-office/presentation',
- 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'x-office/presentation',
- 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'x-office/presentation',
- 'application/vnd.ms-powerpoint.addin.macroEnabled.12' => 'x-office/presentation',
- 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => 'x-office/presentation',
- 'application/vnd.ms-powerpoint.template.macroEnabled.12' => 'x-office/presentation',
- 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => 'x-office/presentation',
- 'application/vnd.oasis.opendocument.presentation' => 'x-office/presentation',
- 'application/vnd.oasis.opendocument.presentation-template' => 'x-office/presentation',
-
- 'application/msexcel' => 'x-office/spreadsheet',
- 'application/vnd.ms-excel' => 'x-office/spreadsheet',
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'x-office/spreadsheet',
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'x-office/spreadsheet',
- 'application/vnd.ms-excel.sheet.macroEnabled.12' => 'x-office/spreadsheet',
- 'application/vnd.ms-excel.template.macroEnabled.12' => 'x-office/spreadsheet',
- 'application/vnd.ms-excel.addin.macroEnabled.12' => 'x-office/spreadsheet',
- 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => 'x-office/spreadsheet',
- 'application/vnd.oasis.opendocument.spreadsheet' => 'x-office/spreadsheet',
- 'application/vnd.oasis.opendocument.spreadsheet-template' => 'x-office/spreadsheet',
- 'text/csv' => 'x-office/spreadsheet',
-
- 'application/msaccess' => 'database',
- );
/**
* Creates an url using a defined route
@@ -253,48 +179,10 @@ class OC_Helper {
* @return string the url
*
* Returns the path to the image of this file type.
+ * @deprecated 8.2.0 Use \OC::$server->getMimeTypeDetector()->mimeTypeIcon($mimetype)
*/
public static function mimetypeIcon($mimetype) {
-
- if (isset(self::$mimeTypeAlias[$mimetype])) {
- $mimetype = self::$mimeTypeAlias[$mimetype];
- }
- if (isset(self::$mimetypeIcons[$mimetype])) {
- return self::$mimetypeIcons[$mimetype];
- }
- // Replace slash and backslash with a minus
- $icon = str_replace('/', '-', $mimetype);
- $icon = str_replace('\\', '-', $icon);
-
- // Is it a dir?
- if ($mimetype === 'dir') {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder.png';
- return OC::$WEBROOT . '/core/img/filetypes/folder.png';
- }
- if ($mimetype === 'dir-shared') {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder-shared.png';
- return OC::$WEBROOT . '/core/img/filetypes/folder-shared.png';
- }
- if ($mimetype === 'dir-external') {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder-external.png';
- return OC::$WEBROOT . '/core/img/filetypes/folder-external.png';
- }
-
- // Icon exists?
- if (file_exists(OC::$SERVERROOT . '/core/img/filetypes/' . $icon . '.png')) {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/' . $icon . '.png';
- return OC::$WEBROOT . '/core/img/filetypes/' . $icon . '.png';
- }
-
- // Try only the first part of the filetype
- $mimePart = substr($icon, 0, strpos($icon, '-'));
- if (file_exists(OC::$SERVERROOT . '/core/img/filetypes/' . $mimePart . '.png')) {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/' . $mimePart . '.png';
- return OC::$WEBROOT . '/core/img/filetypes/' . $mimePart . '.png';
- } else {
- self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/file.png';
- return OC::$WEBROOT . '/core/img/filetypes/file.png';
- }
+ return \OC::$server->getMimeTypeDetector()->mimeTypeIcon($mimetype);
}
/**
@@ -485,13 +373,10 @@ class OC_Helper {
/**
* @return \OC\Files\Type\Detection
+ * @deprecated 8.2.0 use \OC::$server->getMimeTypeDetector()
*/
static public function getMimetypeDetector() {
- if (!self::$mimetypeDetector) {
- self::$mimetypeDetector = new \OC\Files\Type\Detection();
- self::$mimetypeDetector->registerTypeArray(include 'mimetypes.list.php');
- }
- return self::$mimetypeDetector;
+ return \OC::$server->getMimeTypeDetector();
}
/**
@@ -509,9 +394,10 @@ class OC_Helper {
*
* @param string $path
* @return string
+ * @deprecated 8.2.0 Use \OC::$server->getMimeTypeDetector()->detectPath($path)
*/
static public function getFileNameMimeType($path) {
- return self::getMimetypeDetector()->detectPath($path);
+ return \OC::$server->getMimeTypeDetector()->detectPath($path);
}
/**
@@ -520,9 +406,10 @@ class OC_Helper {
* @param string $path
* @return string
* does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
+ * @deprecated 8.2.0 Use \OC::$server->getMimeTypeDetector()->detect($path)
*/
static function getMimeType($path) {
- return self::getMimetypeDetector()->detect($path);
+ return \OC::$server->getMimeTypeDetector()->detect($path);
}
/**
@@ -530,9 +417,10 @@ class OC_Helper {
*
* @param string $mimeType
* @return string
+ * @deprecated 8.2.0 Use \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType)
*/
static function getSecureMimeType($mimeType) {
- return self::getMimetypeDetector()->getSecureMimeType($mimeType);
+ return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType);
}
/**
@@ -540,20 +428,13 @@ class OC_Helper {
*
* @param string $data
* @return string
+ * @deprecated 8.2.0 Use \OC::$server->getMimeTypeDetector()->detectString($data)
*/
static function getStringMimeType($data) {
- return self::getMimetypeDetector()->detectString($data);
+ return \OC::$server->getMimeTypeDetector()->detectString($data);
}
/**
- * Checks $_REQUEST contains a var for the $s key. If so, returns the html-escaped value of this var; otherwise returns the default value provided by $d.
- * @param string $s name of the var to escape, if set.
- * @param string $d default value.
- * @return string the print-safe value.
- *
- */
-
- /**
* detect if a given program is found in the search PATH
*
* @param string $name
@@ -770,17 +651,11 @@ class OC_Helper {
* @param int $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string.
* @param int $length Length of the part to be replaced
* @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
- * @internal param string $input The input string. .Opposite to the PHP build-in function does not accept an array.
* @return string
+ * @deprecated 8.2.0 Use substr_replace() instead.
*/
- public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') {
- $start = intval($start);
- $length = intval($length);
- $string = mb_substr($string, 0, $start, $encoding) .
- $replacement .
- mb_substr($string, $start + $length, mb_strlen($string, 'UTF-8') - $start, $encoding);
-
- return $string;
+ public static function mb_substr_replace($string, $replacement, $start, $length = 0, $encoding = 'UTF-8') {
+ return substr_replace($string, $replacement, $start, $length);
}
/**
@@ -792,17 +667,11 @@ class OC_Helper {
* @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
* @param int $count If passed, this will be set to the number of replacements performed.
* @return string
+ * @deprecated 8.2.0 Use str_replace() instead.
*
*/
public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) {
- $offset = -1;
- $length = mb_strlen($search, $encoding);
- while (($i = mb_strrpos($subject, $search, $offset, $encoding)) !== false) {
- $subject = OC_Helper::mb_substr_replace($subject, $replace, $i, $length);
- $offset = $i - mb_strlen($subject, $encoding);
- $count++;
- }
- return $subject;
+ return str_replace($search, $replace, $subject, $count);
}
/**
diff --git a/lib/private/installer.php b/lib/private/installer.php
index 37af8d0edcb..392dc1c0817 100644
--- a/lib/private/installer.php
+++ b/lib/private/installer.php
@@ -107,6 +107,10 @@ class OC_Installer{
}
$extractDir .= '/' . $info['id'];
+ if(!file_exists($extractDir)) {
+ OC_Helper::rmdirr($basedir);
+ throw new \Exception($l->t("Archive does not contain a directory named %s", $info['id']));
+ }
OC_Helper::copyr($extractDir, $basedir);
//remove temporary files
diff --git a/lib/private/memcache/factory.php b/lib/private/memcache/factory.php
index 5bb7e42c808..fe82558e731 100644
--- a/lib/private/memcache/factory.php
+++ b/lib/private/memcache/factory.php
@@ -29,6 +29,7 @@
namespace OC\Memcache;
use \OCP\ICacheFactory;
+use \OCP\ILogger;
class Factory implements ICacheFactory {
const NULL_CACHE = '\\OC\\Memcache\\NullCache';
@@ -39,6 +40,11 @@ class Factory implements ICacheFactory {
private $globalPrefix;
/**
+ * @var ILogger $logger
+ */
+ private $logger;
+
+ /**
* @var string $localCacheClass
*/
private $localCacheClass;
@@ -55,13 +61,15 @@ class Factory implements ICacheFactory {
/**
* @param string $globalPrefix
+ * @param ILogger $logger
* @param string|null $localCacheClass
* @param string|null $distributedCacheClass
* @param string|null $lockingCacheClass
*/
- public function __construct($globalPrefix,
+ public function __construct($globalPrefix, ILogger $logger,
$localCacheClass = null, $distributedCacheClass = null, $lockingCacheClass = null)
{
+ $this->logger = $logger;
$this->globalPrefix = $globalPrefix;
if (!$localCacheClass) {
@@ -71,22 +79,43 @@ class Factory implements ICacheFactory {
$distributedCacheClass = $localCacheClass;
}
+ $missingCacheMessage = 'Memcache {class} not available for {use} cache';
+ $missingCacheHint = 'Is the matching PHP module installed and enabled?';
if (!$localCacheClass::isAvailable()) {
- throw new \OC\HintException(
- 'Missing memcache class ' . $localCacheClass . ' for local cache',
- 'Is the matching PHP module installed and enabled ?'
- );
+ if (\OC::$CLI && !defined('PHPUNIT_RUN')) {
+ // CLI should not hard-fail on broken memcache
+ $this->logger->info($missingCacheMessage, [
+ 'class' => $localCacheClass,
+ 'use' => 'local',
+ 'app' => 'cli'
+ ]);
+ $localCacheClass = self::NULL_CACHE;
+ } else {
+ throw new \OC\HintException(strtr($missingCacheMessage, [
+ '{class}' => $localCacheClass, '{use}' => 'local'
+ ]), $missingCacheHint);
+ }
}
if (!$distributedCacheClass::isAvailable()) {
- throw new \OC\HintException(
- 'Missing memcache class ' . $distributedCacheClass . ' for distributed cache',
- 'Is the matching PHP module installed and enabled ?'
- );
+ if (\OC::$CLI && !defined('PHPUNIT_RUN')) {
+ // CLI should not hard-fail on broken memcache
+ $this->logger->info($missingCacheMessage, [
+ 'class' => $distributedCacheClass,
+ 'use' => 'distributed',
+ 'app' => 'cli'
+ ]);
+ $distributedCacheClass = self::NULL_CACHE;
+ } else {
+ throw new \OC\HintException(strtr($missingCacheMessage, [
+ '{class}' => $distributedCacheClass, '{use}' => 'distributed'
+ ]), $missingCacheHint);
+ }
}
if (!($lockingCacheClass && $lockingCacheClass::isAvailable())) {
// dont fallback since the fallback might not be suitable for storing lock
- $lockingCacheClass = '\OC\Memcache\NullCache';
+ $lockingCacheClass = self::NULL_CACHE;
}
+
$this->localCacheClass = $localCacheClass;
$this->distributedCacheClass = $distributedCacheClass;
$this->lockingCacheClass = $lockingCacheClass;
diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php
deleted file mode 100644
index efb8089420e..00000000000
--- a/lib/private/mimetypes.list.php
+++ /dev/null
@@ -1,200 +0,0 @@
-<?php
-/**
- * @author Aidan Amavi <github@aidanamavi.com>
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Normal Ra <normalraw@gmail.com>
- * @author Olivier Paroz <github@oparoz.com>
- * @author Robin Appelman <icewind@owncloud.com>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- * @author Thomas Tanghus <thomas@tanghus.net>
- * @author tiezdne <oswald.84@t-online.de>
- * @author Victor Dubiniuk <dubiniuk@owncloud.com>
- *
- * @copyright Copyright (c) 2015, ownCloud, Inc.
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-/**
- * Array mapping file extensions to mimetypes (in alphabetical order).
- *
- * The first index in the mime type array is the assumed correct mimetype
- * and the second is either a secure alternative or null if the correct
- * is considered secure.
- */
-return array(
- '3gp' => array('video/3gpp', null),
- '7z' => array('application/x-7z-compressed', null),
- 'accdb' => array('application/msaccess', null),
- 'ai' => array('application/illustrator', null),
- 'apk' => array('application/vnd.android.package-archive', null),
- 'arw' => array('image/x-dcraw', null),
- 'avi' => array('video/x-msvideo', null),
- 'bash' => array('text/x-shellscript', null),
- 'blend' => array('application/x-blender', null),
- 'bin' => array('application/x-bin', null),
- 'bmp' => array('image/bmp', null),
- 'bpg' => array('image/bpg', null),
- 'cb7' => array('application/x-cbr', null),
- 'cba' => array('application/x-cbr', null),
- 'cbr' => array('application/x-cbr', null),
- 'cbt' => array('application/x-cbr', null),
- 'cbtc' => array('application/x-cbr', null),
- 'cbz' => array('application/x-cbr', null),
- 'cc' => array('text/x-c', null),
- 'cdr' => array('application/coreldraw', null),
- 'cnf' => array('text/plain', null),
- 'conf' => array('text/plain', null),
- 'cpp' => array('text/x-c++src', null),
- 'cr2' => array('image/x-dcraw', null),
- 'css' => array('text/css', null),
- 'csv' => array('text/csv', null),
- 'cvbdl' => array('application/x-cbr', null),
- 'c' => array('text/x-c', null),
- 'c++' => array('text/x-c++src', null),
- 'dcr' => array('image/x-dcraw', null),
- 'deb' => array('application/x-deb', null),
- 'dng' => array('image/x-dcraw', null),
- 'doc' => array('application/msword', null),
- 'docm' => array('application/vnd.ms-word.document.macroEnabled.12', null),
- 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', null),
- 'dot' => array('application/msword', null),
- 'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.template', null),
- 'dv' => array('video/dv', null),
- 'eot' => array('application/vnd.ms-fontobject', null),
- 'epub' => array('application/epub+zip', null),
- 'eps' => array('application/postscript', null),
- 'erf' => array('image/x-dcraw', null),
- 'exe' => array('application/x-ms-dos-executable', null),
- 'flac' => array('audio/flac', null),
- 'flv' => array('video/x-flv', null),
- 'gif' => array('image/gif', null),
- 'gz' => array('application/x-gzip', null),
- 'gzip' => array('application/x-gzip', null),
- 'h' => array('text/x-h', null),
- 'hh' => array('text/x-h', null),
- 'html' => array('text/html', 'text/plain'),
- 'htm' => array('text/html', 'text/plain'),
- 'ical' => array('text/calendar', null),
- 'ics' => array('text/calendar', null),
- 'iiq' => array('image/x-dcraw', null),
- 'impress' => array('text/impress', null),
- 'jpeg' => array('image/jpeg', null),
- 'jpg' => array('image/jpeg', null),
- 'jps' => array('image/jpeg', null),
- 'js' => array('application/javascript', 'text/plain'),
- 'json' => array('application/json', 'text/plain'),
- 'k25' => array('image/x-dcraw', null),
- 'kdc' => array('image/x-dcraw', null),
- 'key' => array('application/x-iwork-keynote-sffkey', null),
- 'keynote' => array('application/x-iwork-keynote-sffkey', null),
- 'kra' => array('application/x-krita', null),
- 'm2t' => array('video/mp2t', null),
- 'm4v' => array('video/mp4', null),
- 'markdown' => array('text/markdown', null),
- 'mdown' => array('text/markdown', null),
- 'md' => array('text/markdown', null),
- 'mdb' => array('application/msaccess', null),
- 'mdwn' => array('text/markdown', null),
- 'mkd' => array('text/markdown', null),
- 'mef' => array('image/x-dcraw', null),
- 'mkv' => array('video/x-matroska', null),
- 'mobi' => array('application/x-mobipocket-ebook', null),
- 'mov' => array('video/quicktime', null),
- 'mp3' => array('audio/mpeg', null),
- 'mp4' => array('video/mp4', null),
- 'mpeg' => array('video/mpeg', null),
- 'mpg' => array('video/mpeg', null),
- 'mpo' => array('image/jpeg', null),
- 'msi' => array('application/x-msi', null),
- 'mts' => ['video/MP2T', null],
- 'mt2s' => ['video/MP2T', null],
- 'nef' => array('image/x-dcraw', null),
- 'numbers' => array('application/x-iwork-numbers-sffnumbers', null),
- 'odf' => array('application/vnd.oasis.opendocument.formula', null),
- 'odg' => array('application/vnd.oasis.opendocument.graphics', null),
- 'odp' => array('application/vnd.oasis.opendocument.presentation', null),
- 'ods' => array('application/vnd.oasis.opendocument.spreadsheet', null),
- 'odt' => array('application/vnd.oasis.opendocument.text', null),
- 'oga' => array('audio/ogg', null),
- 'ogg' => array('audio/ogg', null),
- 'ogv' => array('video/ogg', null),
- 'opus' => array('audio/ogg', null),
- 'orf' => array('image/x-dcraw', null),
- 'otf' => array('application/font-sfnt', null),
- 'pages' => array('application/x-iwork-pages-sffpages', null),
- 'pdf' => array('application/pdf', null),
- 'pfb' => array('application/x-font', null),
- 'pef' => array('image/x-dcraw', null),
- 'php' => array('application/x-php', null),
- 'pl' => array('application/x-perl', null),
- 'png' => array('image/png', null),
- 'pot' => array('application/vnd.ms-powerpoint', null),
- 'potm' => array('application/vnd.ms-powerpoint.template.macroEnabled.12', null),
- 'potx' => array('application/vnd.openxmlformats-officedocument.presentationml.template', null),
- 'ppa' => array('application/vnd.ms-powerpoint', null),
- 'ppam' => array('application/vnd.ms-powerpoint.addin.macroEnabled.12', null),
- 'pps' => array('application/vnd.ms-powerpoint', null),
- 'ppsm' => array('application/vnd.ms-powerpoint.slideshow.macroEnabled.12', null),
- 'ppsx' => array('application/vnd.openxmlformats-officedocument.presentationml.slideshow', null),
- 'ppt' => array('application/vnd.ms-powerpoint', null),
- 'pptm' => array('application/vnd.ms-powerpoint.presentation.macroEnabled.12', null),
- 'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', null),
- 'ps' => array('application/postscript', null),
- 'psd' => array('application/x-photoshop', null),
- 'py' => array('text/x-python', null),
- 'raf' => array('image/x-dcraw', null),
- 'rar' => array('application/x-rar-compressed', null),
- 'reveal' => array('text/reveal', null),
- 'rw2' => array('image/x-dcraw', null),
- 'sgf' => array('application/sgf', null),
- 'sh-lib' => array('text/x-shellscript', null),
- 'sh' => array('text/x-shellscript', null),
- 'srf' => array('image/x-dcraw', null),
- 'sr2' => array('image/x-dcraw', null),
- 'svg' => array('image/svg+xml', 'text/plain'),
- 'swf' => array('application/x-shockwave-flash', 'application/octet-stream'),
- 'tar' => array('application/x-tar', null),
- 'tar.gz' => array('application/x-compressed', null),
- 'tex' => array('application/x-tex', null),
- 'tgz' => array('application/x-compressed', null),
- 'tiff' => array('image/tiff', null),
- 'tif' => array('image/tiff', null),
- 'ttf' => array('application/font-sfnt', null),
- 'txt' => array('text/plain', null),
- 'vcard' => array('text/vcard', null),
- 'vcf' => array('text/vcard', null),
- 'vob' => array('video/dvd', null),
- 'wav' => array('audio/wav', null),
- 'webm' => array('video/webm', null),
- 'woff' => array('application/font-woff', null),
- 'wmv' => array('video/x-ms-wmv', null),
- 'xcf' => array('application/x-gimp', null),
- 'xla' => array('application/vnd.ms-excel', null),
- 'xlam' => array('application/vnd.ms-excel.addin.macroEnabled.12', null),
- 'xls' => array('application/vnd.ms-excel', null),
- 'xlsb' => array('application/vnd.ms-excel.sheet.binary.macroEnabled.12', null),
- 'xlsm' => array('application/vnd.ms-excel.sheet.macroEnabled.12', null),
- 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null),
- 'xlt' => array('application/vnd.ms-excel', null),
- 'xltm' => array('application/vnd.ms-excel.template.macroEnabled.12', null),
- 'xltx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.template', null),
- 'xml' => array('application/xml', 'text/plain'),
- 'xrf' => array('image/x-dcraw', null),
- 'yaml' => array('application/yaml', 'text/plain'),
- 'yml' => array('application/yaml', 'text/plain'),
- 'zip' => array('application/zip', null),
-);
diff --git a/lib/private/ocs.php b/lib/private/ocs.php
index 6d166f8adb0..c5133bf9ee8 100644
--- a/lib/private/ocs.php
+++ b/lib/private/ocs.php
@@ -28,212 +28,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+use OCP\API;
/**
* Class to handle open collaboration services API requests
- *
*/
class OC_OCS {
-
/**
- * reads input data from get/post and converts the date to a special data-type
- *
- * @param string $method HTTP method to read the key from
- * @param string $key Parameter to read
- * @param string $type Variable type to format data
- * @param string $default Default value to return if the key is not found
- * @return string Data or if the key is not found and no default is set it will exit with a 400 Bad request
- */
- public static function readData($method, $key, $type = 'raw', $default = null) {
- $data = false;
- if ($method == 'get') {
- if (isset($_GET[$key])) {
- $data = $_GET[$key];
- } else if (isset($default)) {
- return $default;
- } else {
- $data = false;
- }
- } else if ($method == 'post') {
- if (isset($_POST[$key])) {
- $data = $_POST[$key];
- } else if (isset($default)) {
- return $default;
- } else {
- $data = false;
- }
- }
- if ($data === false) {
- echo self::generateXml('', 'fail', 400, 'Bad request. Please provide a valid '.$key);
- exit();
- } else {
- // NOTE: Is the raw type necessary? It might be a little risky without sanitization
- if ($type == 'raw') return $data;
- elseif ($type == 'text') return OC_Util::sanitizeHTML($data);
- elseif ($type == 'int') return (int) $data;
- elseif ($type == 'float') return (float) $data;
- elseif ($type == 'array') return OC_Util::sanitizeHTML($data);
- else return OC_Util::sanitizeHTML($data);
- }
- }
-
+ * Called when a not existing OCS endpoint has been called
+ */
public static function notFound() {
- if($_SERVER['REQUEST_METHOD'] == 'GET') {
- $method='get';
- }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') {
- $method='put';
- }elseif($_SERVER['REQUEST_METHOD'] == 'POST') {
- $method='post';
- }else{
- echo('internal server error: method not supported');
- exit();
- }
-
- $format = self::readData($method, 'format', 'text', '');
+ $format = \OC::$server->getRequest()->getParam('format', 'xml');
$txt='Invalid query, please check the syntax. API specifications are here:'
- .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n";
- $txt.=OC_OCS::getDebugOutput();
- echo(OC_OCS::generateXml($format, 'failed', 999, $txt));
-
- }
-
- /**
- * generated some debug information to make it easier to find failed API calls
- * @return string data
- */
- private static function getDebugOutput() {
- $txt='';
- $txt.="debug output:\n";
- if(isset($_SERVER['REQUEST_METHOD'])) $txt.='http request method: '.$_SERVER['REQUEST_METHOD']."\n";
- if(isset($_SERVER['REQUEST_URI'])) $txt.='http request uri: '.$_SERVER['REQUEST_URI']."\n";
- if(isset($_GET)) foreach($_GET as $key=>$value) $txt.='get parameter: '.$key.'->'.$value."\n";
- if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n";
- return($txt);
+ .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n";
+ OC_API::respond(new OC_OCS_Result(null, API::RESPOND_UNKNOWN_ERROR, $txt), $format);
}
-
- /**
- * generates the xml or json response for the API call from an multidimenional data array.
- * @param string $format
- * @param string $status
- * @param string $statuscode
- * @param string $message
- * @param array $data
- * @param string $tag
- * @param string $tagattribute
- * @param int $dimension
- * @param int|string $itemscount
- * @param int|string $itemsperpage
- * @return string xml/json
- */
- public static function generateXml($format, $status, $statuscode,
- $message, $data=array(), $tag='', $tagattribute='', $dimension=-1, $itemscount='', $itemsperpage='') {
- if($format=='json') {
- $json=array();
- $json['status']=$status;
- $json['statuscode']=$statuscode;
- $json['message']=$message;
- $json['totalitems']=$itemscount;
- $json['itemsperpage']=$itemsperpage;
- $json['data']=$data;
- return(json_encode($json));
- }else{
- $txt='';
- $writer = xmlwriter_open_memory();
- xmlwriter_set_indent( $writer, 2 );
- xmlwriter_start_document($writer );
- xmlwriter_start_element($writer, 'ocs');
- xmlwriter_start_element($writer, 'meta');
- xmlwriter_write_element($writer, 'status', $status);
- xmlwriter_write_element($writer, 'statuscode', $statuscode);
- xmlwriter_write_element($writer, 'message', $message);
- if($itemscount<>'') xmlwriter_write_element($writer, 'totalitems', $itemscount);
- if(!empty($itemsperpage)) xmlwriter_write_element($writer, 'itemsperpage', $itemsperpage);
- xmlwriter_end_element($writer);
- if($dimension=='0') {
- // 0 dimensions
- xmlwriter_write_element($writer, 'data', $data);
-
- }elseif($dimension=='1') {
- xmlwriter_start_element($writer, 'data');
- foreach($data as $key=>$entry) {
- xmlwriter_write_element($writer, $key, $entry);
- }
- xmlwriter_end_element($writer);
-
- }elseif($dimension=='2') {
- xmlwriter_start_element($writer, 'data');
- foreach($data as $entry) {
- xmlwriter_start_element($writer, $tag);
- if(!empty($tagattribute)) {
- xmlwriter_write_attribute($writer, 'details', $tagattribute);
- }
- foreach($entry as $key=>$value) {
- if(is_array($value)) {
- foreach($value as $k=>$v) {
- xmlwriter_write_element($writer, $k, $v);
- }
- } else {
- xmlwriter_write_element($writer, $key, $value);
- }
- }
- xmlwriter_end_element($writer);
- }
- xmlwriter_end_element($writer);
-
- }elseif($dimension=='3') {
- xmlwriter_start_element($writer, 'data');
- foreach($data as $entrykey=>$entry) {
- xmlwriter_start_element($writer, $tag);
- if(!empty($tagattribute)) {
- xmlwriter_write_attribute($writer, 'details', $tagattribute);
- }
- foreach($entry as $key=>$value) {
- if(is_array($value)) {
- xmlwriter_start_element($writer, $entrykey);
- foreach($value as $k=>$v) {
- xmlwriter_write_element($writer, $k, $v);
- }
- xmlwriter_end_element($writer);
- } else {
- xmlwriter_write_element($writer, $key, $value);
- }
- }
- xmlwriter_end_element($writer);
- }
- xmlwriter_end_element($writer);
- }elseif($dimension=='dynamic') {
- xmlwriter_start_element($writer, 'data');
- OC_OCS::toxml($writer, $data, 'comment');
- xmlwriter_end_element($writer);
- }
-
- xmlwriter_end_element($writer);
-
- xmlwriter_end_document( $writer );
- $txt.=xmlwriter_output_memory( $writer );
- unset($writer);
- return($txt);
- }
- }
-
- /**
- * @param resource $writer
- * @param array $data
- * @param string $node
- */
- public static function toXml($writer, $data, $node) {
- foreach($data as $key => $value) {
- if (is_numeric($key)) {
- $key = $node;
- }
- if (is_array($value)) {
- xmlwriter_start_element($writer, $key);
- OC_OCS::toxml($writer, $value, $node);
- xmlwriter_end_element($writer);
- }else{
- xmlwriter_write_element($writer, $key, $value);
- }
- }
- }
}
diff --git a/lib/private/ocs/cloud.php b/lib/private/ocs/cloud.php
index f662bde2858..8f4f1769e9c 100644
--- a/lib/private/ocs/cloud.php
+++ b/lib/private/ocs/cloud.php
@@ -3,6 +3,7 @@
* @author Bart Visscher <bartv@thisnet.nl>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Tom Needham <tom@owncloud.com>
*
@@ -36,12 +37,8 @@ class OC_OCS_Cloud {
'edition' => OC_Util::getEditionString(),
);
- $result['capabilities'] = array(
- 'core' => array(
- 'pollinterval' => OC_Config::getValue('pollinterval', 60),
- ),
- );
-
+ $result['capabilities'] = \OC::$server->getCapabilitiesManager()->getCapabilities();
+
return new OC_OCS_Result($result);
}
diff --git a/lib/private/ocs/corecapabilities.php b/lib/private/ocs/corecapabilities.php
new file mode 100644
index 00000000000..6b620ab0a84
--- /dev/null
+++ b/lib/private/ocs/corecapabilities.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\OCS;
+
+use OCP\Capabilities\ICapability;
+use OCP\IConfig;
+
+/**
+ * Class Capabilities
+ *
+ * @package OC\OCS
+ */
+class CoreCapabilities implements ICapability {
+
+ /** @var IConfig */
+ private $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ }
+
+ /**
+ * Return this classes capabilities
+ *
+ * @return array
+ */
+ public function getCapabilities() {
+ return [
+ 'core' => [
+ 'pollinterval' => $this->config->getSystemValue('pollinterval', 60)
+ ]
+ ];
+ }
+}
diff --git a/lib/private/db/mssqlmigrator.php b/lib/private/ocs/exception.php
index bedb5bac6c4..93bee773771 100644
--- a/lib/private/db/mssqlmigrator.php
+++ b/lib/private/ocs/exception.php
@@ -1,6 +1,5 @@
<?php
/**
- * @author Morris Jobke <hey@morrisjobke.de>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
@@ -20,18 +19,16 @@
*
*/
-namespace OC\DB;
+namespace OC\OCS;
-use Doctrine\DBAL\Schema\Schema;
+class Exception extends \Exception {
-class MsSqlMigrator extends Migrator {
+ public function __construct(\OC_OCS_Result $result) {
+ $this->result = $result;
+ }
- /**
- * @param \Doctrine\DBAL\Schema\Schema $targetSchema
- */
- public function migrate(Schema $targetSchema) {
- throw new MigrationException('',
- 'Database migration is required to continue operation. This feature is provided within the Enterprise Edition.');
+ public function getResult() {
+ return $this->result;
}
}
diff --git a/lib/private/ocs/privatedata.php b/lib/private/ocs/privatedata.php
index 0fa50ad0c67..249c17b3792 100644
--- a/lib/private/ocs/privatedata.php
+++ b/lib/private/ocs/privatedata.php
@@ -69,7 +69,7 @@ class OC_OCS_Privatedata {
$user = OC_User::getUser();
$app = addslashes(strip_tags($parameters['app']));
$key = addslashes(strip_tags($parameters['key']));
- $value = OC_OCS::readData('post', 'value', 'text');
+ $value = (string)$_POST['value'];
// update in DB
$query = \OCP\DB::prepare('UPDATE `*PREFIX*privatedata` SET `value` = ? WHERE `user` = ? AND `app` = ? AND `key` = ?');
diff --git a/lib/private/ocs/result.php b/lib/private/ocs/result.php
index 1ee2982ac4a..42b6166b823 100644
--- a/lib/private/ocs/result.php
+++ b/lib/private/ocs/result.php
@@ -27,7 +27,23 @@
class OC_OCS_Result{
- protected $data, $message, $statusCode, $items, $perPage;
+ /** @var array */
+ protected $data;
+
+ /** @var null|string */
+ protected $message;
+
+ /** @var int */
+ protected $statusCode;
+
+ /** @var integer */
+ protected $items;
+
+ /** @var integer */
+ protected $perPage;
+
+ /** @var array */
+ private $headers = [];
/**
* create the OCS_Result object
@@ -77,7 +93,7 @@ class OC_OCS_Result{
*/
public function getMeta() {
$meta = array();
- $meta['status'] = ($this->statusCode === 100) ? 'ok' : 'failure';
+ $meta['status'] = $this->succeeded() ? 'ok' : 'failure';
$meta['statuscode'] = $this->statusCode;
$meta['message'] = $this->message;
if(isset($this->items)) {
@@ -106,5 +122,32 @@ class OC_OCS_Result{
return ($this->statusCode == 100);
}
+ /**
+ * Adds a new header to the response
+ * @param string $name The name of the HTTP header
+ * @param string $value The value, null will delete it
+ * @return $this
+ */
+ public function addHeader($name, $value) {
+ $name = trim($name); // always remove leading and trailing whitespace
+ // to be able to reliably check for security
+ // headers
+
+ if(is_null($value)) {
+ unset($this->headers[$name]);
+ } else {
+ $this->headers[$name] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the set headers
+ * @return array the headers
+ */
+ public function getHeaders() {
+ return $this->headers;
+ }
}
diff --git a/lib/private/ocsclient.php b/lib/private/ocsclient.php
index d6593e5089a..78df3b79bb6 100644
--- a/lib/private/ocsclient.php
+++ b/lib/private/ocsclient.php
@@ -272,7 +272,7 @@ class OCSClient {
$tmp = $data->data->content;
if (is_null($tmp)) {
- \OCP\Util::writeLog('core', 'No update found at the ownCloud appstore for app ' . $id, \OCP\Util::INFO);
+ \OCP\Util::writeLog('core', 'No update found at the ownCloud appstore for app ' . $id, \OCP\Util::DEBUG);
return null;
}
diff --git a/lib/private/repair.php b/lib/private/repair.php
index 166efd3eb8f..bf385af2c2b 100644
--- a/lib/private/repair.php
+++ b/lib/private/repair.php
@@ -103,14 +103,14 @@ class Repair extends BasicEmitter {
public static function getRepairSteps() {
return array(
new RepairMimeTypes(),
- new RepairLegacyStorages(\OC::$server->getConfig(), \OC_DB::getConnection()),
+ new RepairLegacyStorages(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()),
new RepairConfig(),
new AssetCache(),
- new FillETags(\OC_DB::getConnection()),
- new CleanTags(\OC_DB::getConnection()),
- new DropOldTables(\OC_DB::getConnection()),
+ new FillETags(\OC::$server->getDatabaseConnection()),
+ new CleanTags(\OC::$server->getDatabaseConnection()),
+ new DropOldTables(\OC::$server->getDatabaseConnection()),
new DropOldJobs(\OC::$server->getJobList()),
- new RemoveGetETagEntries(\OC_DB::getConnection()),
+ new RemoveGetETagEntries(\OC::$server->getDatabaseConnection()),
);
}
diff --git a/lib/private/route/router.php b/lib/private/route/router.php
index 48992366092..33669452f2d 100644
--- a/lib/private/route/router.php
+++ b/lib/private/route/router.php
@@ -150,6 +150,12 @@ class Router implements IRouter {
\OC::$server->getEventLogger()->start('loadroutes' . $requestedApp, 'Loading Routes');
foreach ($routingFiles as $app => $file) {
if (!isset($this->loadedApps[$app])) {
+ if (!\OC_App::isAppLoaded($app)) {
+ // app MUST be loaded before app routes
+ // try again next time loadRoutes() is called
+ $this->loaded = false;
+ continue;
+ }
$this->loadedApps[$app] = true;
$this->useCollection($app);
$this->requireRouteFile($file, $app);
diff --git a/lib/private/security/crypto.php b/lib/private/security/crypto.php
index bca0f08090d..9bae1d6992c 100644
--- a/lib/private/security/crypto.php
+++ b/lib/private/security/crypto.php
@@ -23,11 +23,10 @@
namespace OC\Security;
-use Crypt_AES;
-use Crypt_Hash;
+use phpseclib\Crypt\AES;
+use phpseclib\Crypt\Hash;
use OCP\Security\ICrypto;
use OCP\Security\ISecureRandom;
-use OCP\Security\StringUtils;
use OCP\IConfig;
/**
@@ -41,7 +40,7 @@ use OCP\IConfig;
* @package OC\Security
*/
class Crypto implements ICrypto {
- /** @var Crypt_AES $cipher */
+ /** @var AES $cipher */
private $cipher;
/** @var int */
private $ivLength = 16;
@@ -50,8 +49,12 @@ class Crypto implements ICrypto {
/** @var ISecureRandom */
private $random;
+ /**
+ * @param IConfig $config
+ * @param ISecureRandom $random
+ */
function __construct(IConfig $config, ISecureRandom $random) {
- $this->cipher = new Crypt_AES();
+ $this->cipher = new AES();
$this->config = $config;
$this->random = $random;
}
@@ -69,7 +72,7 @@ class Crypto implements ICrypto {
// Append an "a" behind the password and hash it to prevent reusing the same password as for encryption
$password = hash('sha512', $password . 'a');
- $hash = new Crypt_Hash('sha512');
+ $hash = new Hash('sha512');
$hash->setKey($password);
return $hash->hash($message);
}
@@ -119,7 +122,7 @@ class Crypto implements ICrypto {
$this->cipher->setIV($iv);
- if(!StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) {
+ if(!\OCP\Security\StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) {
throw new \Exception('HMAC does not match.');
}
diff --git a/lib/private/server.php b/lib/private/server.php
index af137ce36aa..89001567219 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -12,6 +12,7 @@
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <icewind@owncloud.com>
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Sander <brantje@gmail.com>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Thomas Tanghus <thomas@tanghus.net>
@@ -50,8 +51,6 @@ use OC\Http\Client\ClientService;
use OC\Lock\MemcacheLockingProvider;
use OC\Lock\NoopLockingProvider;
use OC\Mail\Mailer;
-use OC\Memcache\ArrayCache;
-use OC\Memcache\NullCache;
use OC\Security\CertificateManager;
use OC\Security\Crypto;
use OC\Security\Hasher;
@@ -59,6 +58,8 @@ use OC\Security\SecureRandom;
use OC\Security\TrustedDomainHelper;
use OC\Tagging\TagMapper;
use OCP\IServerContainer;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Class Server
@@ -75,6 +76,7 @@ class Server extends SimpleContainer implements IServerContainer {
* @param string $webRoot
*/
public function __construct($webRoot) {
+ parent::__construct();
$this->webRoot = $webRoot;
$this->registerService('ContactsManager', function ($c) {
@@ -234,17 +236,17 @@ class Server extends SimpleContainer implements IServerContainer {
$instanceId = \OC_Util::getInstanceId();
$path = \OC::$SERVERROOT;
$prefix = md5($instanceId.'-'.$version.'-'.$path);
- return new \OC\Memcache\Factory($prefix,
+ return new \OC\Memcache\Factory($prefix, $c->getLogger(),
$config->getSystemValue('memcache.local', null),
$config->getSystemValue('memcache.distributed', null),
$config->getSystemValue('memcache.locking', null)
);
}
- return new \OC\Memcache\Factory('',
- new ArrayCache(),
- new ArrayCache(),
- new ArrayCache()
+ return new \OC\Memcache\Factory('', $c->getLogger(),
+ '\\OC\\Memcache\\ArrayCache',
+ '\\OC\\Memcache\\ArrayCache',
+ '\\OC\\Memcache\\ArrayCache'
);
});
$this->registerService('ActivityManager', function (Server $c) {
@@ -444,6 +446,21 @@ class Server extends SimpleContainer implements IServerContainer {
$this->registerService('MountManager', function () {
return new \OC\Files\Mount\Manager();
});
+ $this->registerService('MimeTypeDetector', function(Server $c) {
+ return new \OC\Files\Type\Detection(
+ $c->getURLGenerator(),
+ \OC::$configDir);
+ });
+ $this->registerService('CapabilitiesManager', function (Server $c) {
+ $manager = new \OC\CapabilitiesManager();
+ $manager->registerCapability(function() use ($c) {
+ return new \OC\OCS\CoreCapabilities($c->getConfig());
+ });
+ return $manager;
+ });
+ $this->registerService('EventDispatcher', function() {
+ return new EventDispatcher();
+ });
}
/**
@@ -535,29 +552,9 @@ class Server extends SimpleContainer implements IServerContainer {
return null;
}
$userId = $user->getUID();
- } else {
- $user = $this->getUserManager()->get($userId);
}
- \OC\Files\Filesystem::initMountPoints($userId);
- $dir = '/' . $userId;
$root = $this->getRootFolder();
- $folder = null;
-
- if (!$root->nodeExists($dir)) {
- $folder = $root->newFolder($dir);
- } else {
- $folder = $root->get($dir);
- }
-
- $dir = '/files';
- if (!$folder->nodeExists($dir)) {
- $folder = $folder->newFolder($dir);
- \OC_Util::copySkeleton($user, $folder);
- } else {
- $folder = $folder->get($dir);
- }
-
- return $folder;
+ return $root->getUserFolder($userId);
}
/**
@@ -951,4 +948,32 @@ class Server extends SimpleContainer implements IServerContainer {
function getMountManager() {
return $this->query('MountManager');
}
+
+ /*
+ * Get the MimeTypeDetector
+ *
+ * @return \OCP\Files\IMimeTypeDetector
+ */
+ public function getMimeTypeDetector() {
+ return $this->query('MimeTypeDetector');
+ }
+
+ /**
+ * Get the manager of all the capabilities
+ *
+ * @return \OC\CapabilitiesManager
+ */
+ public function getCapabilitiesManager() {
+ return $this->query('CapabilitiesManager');
+ }
+
+ /**
+ * Get the EventDispatcher
+ *
+ * @return EventDispatcherInterface
+ * @since 8.2.0
+ */
+ public function getEventDispatcher() {
+ return $this->query('EventDispatcher');
+ }
}
diff --git a/lib/private/setup.php b/lib/private/setup.php
index 50bf0dceafc..8f1ae389e45 100644
--- a/lib/private/setup.php
+++ b/lib/private/setup.php
@@ -39,6 +39,8 @@ use bantu\IniGetWrapper\IniGetWrapper;
use Exception;
use OCP\IConfig;
use OCP\IL10N;
+use OCP\ILogger;
+use OCP\Security\ISecureRandom;
class Setup {
/** @var \OCP\IConfig */
@@ -49,6 +51,10 @@ class Setup {
protected $l10n;
/** @var \OC_Defaults */
protected $defaults;
+ /** @var ILogger */
+ protected $logger;
+ /** @var ISecureRandom */
+ protected $random;
/**
* @param IConfig $config
@@ -58,18 +64,22 @@ class Setup {
function __construct(IConfig $config,
IniGetWrapper $iniWrapper,
IL10N $l10n,
- \OC_Defaults $defaults) {
+ \OC_Defaults $defaults,
+ ILogger $logger,
+ ISecureRandom $random
+ ) {
$this->config = $config;
$this->iniWrapper = $iniWrapper;
$this->l10n = $l10n;
$this->defaults = $defaults;
+ $this->logger = $logger;
+ $this->random = $random;
}
static $dbSetupClasses = array(
'mysql' => '\OC\Setup\MySQL',
'pgsql' => '\OC\Setup\PostgreSQL',
'oci' => '\OC\Setup\OCI',
- 'mssql' => '\OC\Setup\MSSQL',
'sqlite' => '\OC\Setup\Sqlite',
'sqlite3' => '\OC\Setup\Sqlite',
);
@@ -79,7 +89,7 @@ class Setup {
* @param string $name
* @return bool
*/
- public function class_exists($name) {
+ protected function class_exists($name) {
return class_exists($name);
}
@@ -88,11 +98,20 @@ class Setup {
* @param string $name
* @return bool
*/
- public function is_callable($name) {
+ protected function is_callable($name) {
return is_callable($name);
}
/**
+ * Wrapper around \PDO::getAvailableDrivers
+ *
+ * @return array
+ */
+ protected function getAvailableDbDriversForPdo() {
+ return \PDO::getAvailableDrivers();
+ }
+
+ /**
* Get the available and supported databases of this instance
*
* @param bool $allowAllDatabases
@@ -107,8 +126,8 @@ class Setup {
'name' => 'SQLite'
),
'mysql' => array(
- 'type' => 'function',
- 'call' => 'mysql_connect',
+ 'type' => 'pdo',
+ 'call' => 'mysql',
'name' => 'MySQL/MariaDB'
),
'pgsql' => array(
@@ -120,11 +139,6 @@ class Setup {
'type' => 'function',
'call' => 'oci_connect',
'name' => 'Oracle'
- ),
- 'mssql' => array(
- 'type' => 'function',
- 'call' => 'sqlsrv_connect',
- 'name' => 'MS SQL'
)
);
if ($allowAllDatabases) {
@@ -142,10 +156,15 @@ class Setup {
foreach($configuredDatabases as $database) {
if(array_key_exists($database, $availableDatabases)) {
$working = false;
- if($availableDatabases[$database]['type'] === 'class') {
- $working = $this->class_exists($availableDatabases[$database]['call']);
- } elseif ($availableDatabases[$database]['type'] === 'function') {
- $working = $this->is_callable($availableDatabases[$database]['call']);
+ $type = $availableDatabases[$database]['type'];
+ $call = $availableDatabases[$database]['call'];
+
+ if($type === 'class') {
+ $working = $this->class_exists($call);
+ } elseif ($type === 'function') {
+ $working = $this->is_callable($call);
+ } elseif($type === 'pdo') {
+ $working = in_array($call, $this->getAvailableDbDriversForPdo(), TRUE);
}
if($working) {
$supportedDatabases[$database] = $availableDatabases[$database]['name'];
@@ -218,7 +237,6 @@ class Setup {
'hasMySQL' => isset($databases['mysql']),
'hasPostgreSQL' => isset($databases['pgsql']),
'hasOracle' => isset($databases['oci']),
- 'hasMSSQL' => isset($databases['mssql']),
'databases' => $databases,
'directory' => $dataDir,
'htaccessWorking' => $htAccessWorking,
@@ -256,7 +274,8 @@ class Setup {
$class = self::$dbSetupClasses[$dbType];
/** @var \OC\Setup\AbstractDatabase $dbSetup */
- $dbSetup = new $class($l, 'db_structure.xml');
+ $dbSetup = new $class($l, 'db_structure.xml', $this->config,
+ $this->logger, $this->random);
$error = array_merge($error, $dbSetup->validate($options));
// validate the data directory
@@ -291,9 +310,9 @@ class Setup {
}
//generate a random salt that is used to salt the local user passwords
- $salt = \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(30);
+ $salt = $this->random->getLowStrengthGenerator()->generate(30);
// generate a secret
- $secret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(48);
+ $secret = $this->random->getMediumStrengthGenerator()->generate(48);
//write the config file
$this->config->setSystemValues([
@@ -358,7 +377,7 @@ class Setup {
//try to write logtimezone
if (date_default_timezone_get()) {
- \OC_Config::setValue('logtimezone', date_default_timezone_get());
+ $config->setSystemValue('logtimezone', date_default_timezone_get());
}
//and we are done
@@ -396,7 +415,9 @@ class Setup {
* @throws \OC\HintException If .htaccess does not include the current version
*/
public static function updateHtaccess() {
- $setupHelper = new \OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), new \OC_Defaults());
+ $setupHelper = new \OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(),
+ \OC::$server->getL10N('lib'), new \OC_Defaults(), \OC::$server->getLogger(),
+ \OC::$server->getSecureRandom());
if(!$setupHelper->isCurrentHtaccess()) {
throw new \OC\HintException('.htaccess file has the wrong version. Please upload the correct version. Maybe you forgot to replace it after updating?');
}
diff --git a/lib/private/setup/abstractdatabase.php b/lib/private/setup/abstractdatabase.php
index 13daf1782fc..1ec853c3b02 100644
--- a/lib/private/setup/abstractdatabase.php
+++ b/lib/private/setup/abstractdatabase.php
@@ -22,22 +22,39 @@
*/
namespace OC\Setup;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\Security\ISecureRandom;
+
abstract class AbstractDatabase {
- /**
- * @var \OC_L10N
- */
+ /** @var \OC_L10N */
protected $trans;
+ /** @var string */
protected $dbDefinitionFile;
- protected $dbuser;
- protected $dbpassword;
- protected $dbname;
- protected $dbhost;
- protected $tableprefix;
+ /** @var string */
+ protected $dbUser;
+ /** @var string */
+ protected $dbPassword;
+ /** @var string */
+ protected $dbName;
+ /** @var string */
+ protected $dbHost;
+ /** @var string */
+ protected $tablePrefix;
+ /** @var IConfig */
+ protected $config;
+ /** @var ILogger */
+ protected $logger;
+ /** @var ISecureRandom */
+ protected $random;
- public function __construct($trans, $dbDefinitionFile) {
+ public function __construct($trans, $dbDefinitionFile, IConfig $config, ILogger $logger, ISecureRandom $random) {
$this->trans = $trans;
$this->dbDefinitionFile = $dbDefinitionFile;
+ $this->config = $config;
+ $this->logger = $logger;
+ $this->random = $random;
}
public function validate($config) {
@@ -61,17 +78,17 @@ abstract class AbstractDatabase {
$dbHost = !empty($config['dbhost']) ? $config['dbhost'] : 'localhost';
$dbTablePrefix = isset($config['dbtableprefix']) ? $config['dbtableprefix'] : 'oc_';
- \OC_Config::setValues([
+ $this->config->setSystemValues([
'dbname' => $dbName,
'dbhost' => $dbHost,
'dbtableprefix' => $dbTablePrefix,
]);
- $this->dbuser = $dbUser;
- $this->dbpassword = $dbPass;
- $this->dbname = $dbName;
- $this->dbhost = $dbHost;
- $this->tableprefix = $dbTablePrefix;
+ $this->dbUser = $dbUser;
+ $this->dbPassword = $dbPass;
+ $this->dbName = $dbName;
+ $this->dbHost = $dbHost;
+ $this->tablePrefix = $dbTablePrefix;
}
abstract public function setupDatabase($userName);
diff --git a/lib/private/setup/mssql.php b/lib/private/setup/mssql.php
deleted file mode 100644
index 0ae02d6cbcc..00000000000
--- a/lib/private/setup/mssql.php
+++ /dev/null
@@ -1,203 +0,0 @@
-<?php
-/**
- * @author Bart Visscher <bartv@thisnet.nl>
- * @author Joas Schilling <nickvergessen@owncloud.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @copyright Copyright (c) 2015, ownCloud, Inc.
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-namespace OC\Setup;
-
-class MSSQL extends AbstractDatabase {
- public $dbprettyname = 'MS SQL Server';
-
- public function setupDatabase($username) {
- //check if the database user has admin right
- $masterConnectionInfo = array( "Database" => "master", "UID" => $this->dbuser, "PWD" => $this->dbpassword);
-
- $masterConnection = @sqlsrv_connect($this->dbhost, $masterConnectionInfo);
- if(!$masterConnection) {
- $entry = '';
- if( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- }
- throw new \OC\DatabaseSetupException($this->trans->t('MS SQL username and/or password not valid: %s', array($entry)),
- $this->trans->t('You need to enter either an existing account or the administrator.'));
- }
-
- \OC_Config::setValues([
- 'dbuser' => $this->dbuser,
- 'dbpassword' => $this->dbpassword,
- ]);
-
- $this->createDBLogin($masterConnection);
-
- $this->createDatabase($masterConnection);
-
- $this->createDBUser($masterConnection);
-
- sqlsrv_close($masterConnection);
-
- $this->createDatabaseStructure();
- }
-
- private function createDBLogin($connection) {
- $query = "SELECT * FROM master.sys.server_principals WHERE name = '".$this->dbuser."';";
- $result = sqlsrv_query($connection, $query);
- if ($result === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- } else {
- $row = sqlsrv_fetch_array($result);
-
- if ($row === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- } else {
- if ($row == null) {
- $query = "CREATE LOGIN [".$this->dbuser."] WITH PASSWORD = '".$this->dbpassword."';";
- $result = sqlsrv_query($connection, $query);
- if (!$result or $result === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- }
- }
- }
- }
- }
-
- private function createDBUser($connection) {
- $query = "SELECT * FROM [".$this->dbname."].sys.database_principals WHERE name = '".$this->dbuser."';";
- $result = sqlsrv_query($connection, $query);
- if ($result === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- } else {
- $row = sqlsrv_fetch_array($result);
-
- if ($row === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- } else {
- if ($row == null) {
- $query = "USE [".$this->dbname."]; CREATE USER [".$this->dbuser."] FOR LOGIN [".$this->dbuser."];";
- $result = sqlsrv_query($connection, $query);
- if (!$result || $result === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry = 'DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- }
- }
-
- $query = "USE [".$this->dbname."]; EXEC sp_addrolemember 'db_owner', '".$this->dbuser."';";
- $result = sqlsrv_query($connection, $query);
- if (!$result || $result === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- }
- }
- }
- }
-
- private function createDatabase($connection) {
- $query = "CREATE DATABASE [".$this->dbname."];";
- $result = sqlsrv_query($connection, $query);
- if (!$result || $result === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- }
- }
-
- private function createDatabaseStructure() {
- $connectionInfo = array( "Database" => $this->dbname, "UID" => $this->dbuser, "PWD" => $this->dbpassword);
-
- $connection = @sqlsrv_connect($this->dbhost, $connectionInfo);
-
- //fill the database if needed
- $query = "SELECT * FROM INFORMATION_SCHEMA.TABLES"
- ." WHERE TABLE_SCHEMA = '".$this->dbname."'"
- ." AND TABLE_NAME = '".$this->tableprefix."users'";
- $result = sqlsrv_query($connection, $query);
- if ($result === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- } else {
- $row = sqlsrv_fetch_array($result);
-
- if ($row === false) {
- if ( ($errors = sqlsrv_errors() ) != null) {
- $entry='DB Error: "'.print_r(sqlsrv_errors()).'"<br />';
- } else {
- $entry = '';
- }
- $entry.='Offending command was: '.$query.'<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- } else {
- if ($row == null) {
- \OC_DB::createDbFromStructure($this->dbDefinitionFile);
- }
- }
- }
-
- sqlsrv_close($connection);
- }
-}
diff --git a/lib/private/setup/mysql.php b/lib/private/setup/mysql.php
index c01ff724b80..5597592f21e 100644
--- a/lib/private/setup/mysql.php
+++ b/lib/private/setup/mysql.php
@@ -23,110 +23,139 @@
*/
namespace OC\Setup;
+use OC\DB\ConnectionFactory;
+use OCP\IDBConnection;
+
class MySQL extends AbstractDatabase {
public $dbprettyname = 'MySQL/MariaDB';
public function setupDatabase($username) {
//check if the database user has admin right
- $connection = @mysql_connect($this->dbhost, $this->dbuser, $this->dbpassword);
- if(!$connection) {
- throw new \OC\DatabaseSetupException($this->trans->t('MySQL/MariaDB username and/or password not valid'),
- $this->trans->t('You need to enter either an existing account or the administrator.'));
- }
- //user already specified in config
- $oldUser=\OC_Config::getValue('dbuser', false);
-
- //we don't have a dbuser specified in config
- if($this->dbuser!=$oldUser) {
- //add prefix to the admin username to prevent collisions
- $adminUser=substr('oc_'.$username, 0, 16);
-
- $i = 1;
- while(true) {
- //this should be enough to check for admin rights in mysql
- $query="SELECT user FROM mysql.user WHERE user='$adminUser'";
-
- $result = mysql_query($query, $connection);
+ $connection = $this->connect();
- //current dbuser has admin rights
- if($result) {
- //new dbuser does not exist
- if(mysql_num_rows($result) === 0) {
- //use the admin login data for the new database user
- $this->dbuser=$adminUser;
-
- //create a random password so we don't need to store the admin password in the config file
- $this->dbpassword=\OC_Util::generateRandomBytes(30);
-
- $this->createDBUser($connection);
-
- break;
- } else {
- //repeat with different username
- $length=strlen((string)$i);
- $adminUser=substr('oc_'.$username, 0, 16 - $length).$i;
- $i++;
- }
- } else {
- break;
- }
- };
-
- \OC_Config::setValues([
- 'dbuser' => $this->dbuser,
- 'dbpassword' => $this->dbpassword,
- ]);
- }
+ $this->createSpecificUser($username, $connection);
//create the database
$this->createDatabase($connection);
//fill the database if needed
- $query='select count(*) from information_schema.tables'
- ." where table_schema='".$this->dbname."' AND table_name = '".$this->tableprefix."users';";
- $result = mysql_query($query, $connection);
- if($result) {
- $row=mysql_fetch_row($result);
- }
+ $query='select count(*) from information_schema.tables where table_schema=? AND table_name = ?';
+ $result = $connection->executeQuery($query, [$this->dbName, $this->tablePrefix.'users']);
+ $row = $result->fetch();
if(!$result or $row[0]==0) {
\OC_DB::createDbFromStructure($this->dbDefinitionFile);
}
- mysql_close($connection);
}
+ /**
+ * @param \OC\DB\Connection $connection
+ */
private function createDatabase($connection) {
- $name = $this->dbname;
- $user = $this->dbuser;
- //we cant use OC_BD functions here because we need to connect as the administrative user.
- $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET utf8 COLLATE utf8_bin;";
- $result = mysql_query($query, $connection);
- if(!$result) {
- $entry = $this->trans->t('DB Error: "%s"', array(mysql_error($connection))) . '<br />';
- $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />';
- \OCP\Util::writeLog('setup.mssql', $entry, \OCP\Util::WARN);
- }
- $query="GRANT ALL PRIVILEGES ON `$name` . * TO '$user'";
+ try{
+ $name = $this->dbName;
+ $user = $this->dbUser;
+ //we cant use OC_BD functions here because we need to connect as the administrative user.
+ $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET utf8 COLLATE utf8_bin;";
+ $connection->executeUpdate($query);
- //this query will fail if there aren't the right permissions, ignore the error
- mysql_query($query, $connection);
+ //this query will fail if there aren't the right permissions, ignore the error
+ $query="GRANT ALL PRIVILEGES ON `$name` . * TO '$user'";
+ $connection->executeUpdate($query);
+ } catch (\Exception $ex) {
+ $this->logger->error('Database creation failed: {error}', [
+ 'app' => 'mysql.setup',
+ 'error' => $ex->getMessage()
+ ]);
+ }
}
+ /**
+ * @param IDbConnection $connection
+ * @throws \OC\DatabaseSetupException
+ */
private function createDBUser($connection) {
- $name = $this->dbuser;
- $password = $this->dbpassword;
+ $name = $this->dbUser;
+ $password = $this->dbPassword;
// we need to create 2 accounts, one for global use and one for local user. if we don't specify the local one,
// the anonymous user would take precedence when there is one.
$query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'";
- $result = mysql_query($query, $connection);
- if (!$result) {
- throw new \OC\DatabaseSetupException($this->trans->t("MySQL/MariaDB user '%s'@'localhost' exists already.", array($name)),
- $this->trans->t("Drop this user from MySQL/MariaDB", array($name)));
- }
+ $connection->executeUpdate($query);
$query = "CREATE USER '$name'@'%' IDENTIFIED BY '$password'";
- $result = mysql_query($query, $connection);
- if (!$result) {
- throw new \OC\DatabaseSetupException($this->trans->t("MySQL/MariaDB user '%s'@'%%' already exists", array($name)),
- $this->trans->t("Drop this user from MySQL/MariaDB."));
+ $connection->executeUpdate($query);
+ }
+
+ /**
+ * @return \OC\DB\Connection
+ * @throws \OC\DatabaseSetupException
+ */
+ private function connect() {
+ $type = 'mysql';
+ $connectionParams = array(
+ 'host' => $this->dbHost,
+ 'user' => $this->dbUser,
+ 'password' => $this->dbPassword,
+ 'tablePrefix' => $this->tablePrefix,
+ );
+ $cf = new ConnectionFactory();
+ return $cf->getConnection($type, $connectionParams);
+ }
+
+ /**
+ * @param $username
+ * @param IDBConnection $connection
+ * @return array
+ */
+ private function createSpecificUser($username, $connection) {
+ try {
+ //user already specified in config
+ $oldUser = $this->config->getSystemValue('dbuser', false);
+
+ //we don't have a dbuser specified in config
+ if ($this->dbUser !== $oldUser) {
+ //add prefix to the admin username to prevent collisions
+ $adminUser = substr('oc_' . $username, 0, 16);
+
+ $i = 1;
+ while (true) {
+ //this should be enough to check for admin rights in mysql
+ $query = 'SELECT user FROM mysql.user WHERE user=?';
+ $result = $connection->executeQuery($query, [$adminUser]);
+
+ //current dbuser has admin rights
+ if ($result) {
+ $data = $result->fetchAll();
+ //new dbuser does not exist
+ if (count($data) === 0) {
+ //use the admin login data for the new database user
+ $this->dbUser = $adminUser;
+
+ //create a random password so we don't need to store the admin password in the config file
+ $this->dbPassword = $this->random->getMediumStrengthGenerator()->generate(30);
+
+ $this->createDBUser($connection);
+
+ break;
+ } else {
+ //repeat with different username
+ $length = strlen((string)$i);
+ $adminUser = substr('oc_' . $username, 0, 16 - $length) . $i;
+ $i++;
+ }
+ } else {
+ break;
+ }
+ };
+ }
+ } catch (\Exception $ex) {
+ $this->logger->error('Specific user creation failed: {error}', [
+ 'app' => 'mysql.setup',
+ 'error' => $ex->getMessage()
+ ]);
}
+
+ $this->config->setSystemValues([
+ 'dbuser' => $this->dbUser,
+ 'dbpassword' => $this->dbPassword,
+ ]);
}
}
diff --git a/lib/private/setup/oci.php b/lib/private/setup/oci.php
index d46d5529da0..1e1eb1ff54e 100644
--- a/lib/private/setup/oci.php
+++ b/lib/private/setup/oci.php
@@ -38,10 +38,10 @@ class OCI extends AbstractDatabase {
$this->dbtablespace = 'USERS';
}
// allow empty hostname for oracle
- $this->dbhost = $config['dbhost'];
+ $this->dbHost = $config['dbhost'];
\OC_Config::setValues([
- 'dbhost' => $this->dbhost,
+ 'dbhost' => $this->dbHost,
'dbtablespace' => $this->dbtablespace,
]);
}
@@ -58,8 +58,8 @@ class OCI extends AbstractDatabase {
}
public function setupDatabase($username) {
- $e_host = addslashes($this->dbhost);
- $e_dbname = addslashes($this->dbname);
+ $e_host = addslashes($this->dbHost);
+ $e_dbname = addslashes($this->dbName);
//check if the database user has admin right
if ($e_host == '') {
$easy_connect_string = $e_dbname; // use dbname as easy connect name
@@ -67,7 +67,7 @@ class OCI extends AbstractDatabase {
$easy_connect_string = '//'.$e_host.'/'.$e_dbname;
}
\OCP\Util::writeLog('setup oracle', 'connect string: ' . $easy_connect_string, \OCP\Util::DEBUG);
- $connection = @oci_connect($this->dbuser, $this->dbpassword, $easy_connect_string);
+ $connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string);
if(!$connection) {
$errorMessage = $this->getLastError();
if ($errorMessage) {
@@ -103,23 +103,23 @@ class OCI extends AbstractDatabase {
//use the admin login data for the new database user
//add prefix to the oracle user name to prevent collisions
- $this->dbuser='oc_'.$username;
+ $this->dbUser='oc_'.$username;
//create a new password so we don't need to store the admin config in the config file
- $this->dbpassword=\OC_Util::generateRandomBytes(30);
+ $this->dbPassword=\OC_Util::generateRandomBytes(30);
//oracle passwords are treated as identifiers:
// must start with alphanumeric char
// needs to be shortened to 30 bytes, as the two " needed to escape the identifier count towards the identifier length.
- $this->dbpassword=substr($this->dbpassword, 0, 30);
+ $this->dbPassword=substr($this->dbPassword, 0, 30);
$this->createDBUser($connection);
}
}
\OC_Config::setValues([
- 'dbuser' => $this->dbuser,
- 'dbname' => $this->dbname,
- 'dbpassword' => $this->dbpassword,
+ 'dbuser' => $this->dbUser,
+ 'dbname' => $this->dbName,
+ 'dbpassword' => $this->dbPassword,
]);
//create the database not necessary, oracle implies user = schema
@@ -131,26 +131,26 @@ class OCI extends AbstractDatabase {
oci_close($connection);
// connect to the oracle database (schema=$this->dbuser) an check if the schema needs to be filled
- $this->dbuser = \OC_Config::getValue('dbuser');
+ $this->dbUser = \OC_Config::getValue('dbuser');
//$this->dbname = \OC_Config::getValue('dbname');
- $this->dbpassword = \OC_Config::getValue('dbpassword');
+ $this->dbPassword = \OC_Config::getValue('dbpassword');
- $e_host = addslashes($this->dbhost);
- $e_dbname = addslashes($this->dbname);
+ $e_host = addslashes($this->dbHost);
+ $e_dbname = addslashes($this->dbName);
if ($e_host == '') {
$easy_connect_string = $e_dbname; // use dbname as easy connect name
} else {
$easy_connect_string = '//'.$e_host.'/'.$e_dbname;
}
- $connection = @oci_connect($this->dbuser, $this->dbpassword, $easy_connect_string);
+ $connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string);
if(!$connection) {
throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'),
$this->trans->t('You need to enter either an existing account or the administrator.'));
}
$query = "SELECT count(*) FROM user_tables WHERE table_name = :un";
$stmt = oci_parse($connection, $query);
- $un = $this->tableprefix.'users';
+ $un = $this->tablePrefix.'users';
oci_bind_by_name($stmt, ':un', $un);
if (!$stmt) {
$entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />';
@@ -171,8 +171,8 @@ class OCI extends AbstractDatabase {
* @param resource $connection
*/
private function createDBUser($connection) {
- $name = $this->dbuser;
- $password = $this->dbpassword;
+ $name = $this->dbUser;
+ $password = $this->dbPassword;
$query = "SELECT * FROM all_users WHERE USERNAME = :un";
$stmt = oci_parse($connection, $query);
if (!$stmt) {
diff --git a/lib/private/setup/postgresql.php b/lib/private/setup/postgresql.php
index c8fd3b98fe4..319b6676ef8 100644
--- a/lib/private/setup/postgresql.php
+++ b/lib/private/setup/postgresql.php
@@ -27,9 +27,9 @@ class PostgreSQL extends AbstractDatabase {
public $dbprettyname = 'PostgreSQL';
public function setupDatabase($username) {
- $e_host = addslashes($this->dbhost);
- $e_user = addslashes($this->dbuser);
- $e_password = addslashes($this->dbpassword);
+ $e_host = addslashes($this->dbHost);
+ $e_user = addslashes($this->dbUser);
+ $e_password = addslashes($this->dbPassword);
// Fix database with port connection
if(strpos($e_host, ':')) {
@@ -43,7 +43,7 @@ class PostgreSQL extends AbstractDatabase {
$connection = @pg_connect($connection_string);
if(!$connection) {
// Try if we can connect to the DB with the specified name
- $e_dbname = addslashes($this->dbname);
+ $e_dbname = addslashes($this->dbName);
$connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' port='$port' password='$e_password'";
$connection = @pg_connect($connection_string);
@@ -51,7 +51,7 @@ class PostgreSQL extends AbstractDatabase {
throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'),
$this->trans->t('You need to enter either an existing account or the administrator.'));
}
- $e_user = pg_escape_string($this->dbuser);
+ $e_user = pg_escape_string($this->dbUser);
//check for roles creation rights in postgresql
$query="SELECT 1 FROM pg_roles WHERE rolcreaterole=TRUE AND rolname='$e_user'";
$result = pg_query($connection, $query);
@@ -59,16 +59,16 @@ class PostgreSQL extends AbstractDatabase {
//use the admin login data for the new database user
//add prefix to the postgresql user name to prevent collisions
- $this->dbuser='oc_'.$username;
+ $this->dbUser='oc_'.$username;
//create a new password so we don't need to store the admin config in the config file
- $this->dbpassword=\OC_Util::generateRandomBytes(30);
+ $this->dbPassword=\OC_Util::generateRandomBytes(30);
$this->createDBUser($connection);
}
\OC_Config::setValues([
- 'dbuser' => $this->dbuser,
- 'dbpassword' => $this->dbpassword,
+ 'dbuser' => $this->dbUser,
+ 'dbpassword' => $this->dbPassword,
]);
//create the database
@@ -78,13 +78,13 @@ class PostgreSQL extends AbstractDatabase {
pg_close($connection);
// connect to the ownCloud database (dbname=$this->dbname) and check if it needs to be filled
- $this->dbuser = \OC_Config::getValue('dbuser');
- $this->dbpassword = \OC_Config::getValue('dbpassword');
+ $this->dbUser = \OC_Config::getValue('dbuser');
+ $this->dbPassword = \OC_Config::getValue('dbpassword');
- $e_host = addslashes($this->dbhost);
- $e_dbname = addslashes($this->dbname);
- $e_user = addslashes($this->dbuser);
- $e_password = addslashes($this->dbpassword);
+ $e_host = addslashes($this->dbHost);
+ $e_dbname = addslashes($this->dbName);
+ $e_user = addslashes($this->dbUser);
+ $e_password = addslashes($this->dbPassword);
// Fix database with port connection
if(strpos($e_host, ':')) {
@@ -99,7 +99,7 @@ class PostgreSQL extends AbstractDatabase {
throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'),
$this->trans->t('You need to enter either an existing account or the administrator.'));
}
- $query = "select count(*) FROM pg_class WHERE relname='".$this->tableprefix."users' limit 1";
+ $query = "select count(*) FROM pg_class WHERE relname='".$this->tablePrefix."users' limit 1";
$result = pg_query($connection, $query);
if($result) {
$row = pg_fetch_row($result);
@@ -111,8 +111,8 @@ class PostgreSQL extends AbstractDatabase {
private function createDatabase($connection) {
//we cant use OC_BD functions here because we need to connect as the administrative user.
- $e_name = pg_escape_string($this->dbname);
- $e_user = pg_escape_string($this->dbuser);
+ $e_name = pg_escape_string($this->dbName);
+ $e_user = pg_escape_string($this->dbUser);
$query = "select datname from pg_database where datname = '$e_name'";
$result = pg_query($connection, $query);
if(!$result) {
@@ -137,8 +137,8 @@ class PostgreSQL extends AbstractDatabase {
}
private function createDBUser($connection) {
- $e_name = pg_escape_string($this->dbuser);
- $e_password = pg_escape_string($this->dbpassword);
+ $e_name = pg_escape_string($this->dbUser);
+ $e_password = pg_escape_string($this->dbPassword);
$query = "select * from pg_roles where rolname='$e_name';";
$result = pg_query($connection, $query);
if(!$result) {
diff --git a/lib/private/share/hooks.php b/lib/private/share/hooks.php
index b0d4f0677f5..98143124e82 100644
--- a/lib/private/share/hooks.php
+++ b/lib/private/share/hooks.php
@@ -24,6 +24,13 @@
namespace OC\Share;
class Hooks extends \OC\Share\Constants {
+
+ /**
+ * remember which targets need to be updated in the post addToGroup Hook
+ * @var array
+ */
+ private static $updateTargets = array();
+
/**
* Function that is called after a user is deleted. Cleans up the shares of that user.
* @param array $arguments
@@ -41,46 +48,98 @@ class Hooks extends \OC\Share\Constants {
}
}
+
/**
- * Function that is called after a user is added to a group.
- * TODO what does it do?
+ * Function that is called before a user is added to a group.
+ * check if we need to create a unique target for the user
* @param array $arguments
*/
- public static function post_addToGroup($arguments) {
+ public static function pre_addToGroup($arguments) {
+ /** @var \OC\DB\Connection $db */
+ $db = \OC::$server->getDatabaseConnection();
+
+ $insert = $db->createQueryBuilder();
+ $select = $db->createQueryBuilder();
// 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()) {
+ $select->select('*')
+ ->from('`*PREFIX*share`')
+ ->where($select->expr()->andX(
+ $select->expr()->eq('`share_type`', ':shareType'),
+ $select->expr()->eq('`share_with`', ':shareWith')
+ ))
+ ->setParameter('shareType', self::SHARE_TYPE_GROUP)
+ ->setParameter('shareWith', $arguments['gid']);
- $sourceExists = \OC\Share\Share::getItemSharedWithBySource($item['item_type'], $item['item_source'], self::FORMAT_NONE, null, true, $arguments['uid']);
+ $result = $select->execute();
- if ($sourceExists) {
- $fileTarget = $sourceExists['file_target'];
- $itemTarget = $sourceExists['item_target'];
+ while ($item = $result->fetch()) {
+
+ $itemTarget = Helper::generateTarget(
+ $item['item_type'],
+ $item['item_source'],
+ self::SHARE_TYPE_USER,
+ $arguments['uid'],
+ $item['uid_owner'],
+ null,
+ $item['parent']
+ );
+
+ if ($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
+ $fileTarget = Helper::generateTarget(
+ $item['item_type'],
+ $item['file_target'],
+ self::SHARE_TYPE_USER,
+ $arguments['uid'],
+ $item['uid_owner'],
+ null,
+ $item['parent']
+ );
} else {
- $itemTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, $arguments['uid'],
- $item['uid_owner'], null, $item['parent']);
-
- // do we also need a file target
- if ($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
- $fileTarget = Helper::generateTarget('file', $item['file_target'], self::SHARE_TYPE_USER, $arguments['uid'],
- $item['uid_owner'], null, $item['parent']);
- } else {
- $fileTarget = null;
- }
+ $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');
+ if (
+ ($fileTarget === null && $itemTarget != $item['item_target'])
+ || ($fileTarget !== null && $fileTarget !== $item['file_target'])
+ ) {
+ self::$updateTargets[$arguments['gid']][] = [
+ '`item_type`' => $insert->expr()->literal($item['item_type']),
+ '`item_source`' => $insert->expr()->literal($item['item_source']),
+ '`item_target`' => $insert->expr()->literal($itemTarget),
+ '`file_target`' => $insert->expr()->literal($fileTarget),
+ '`parent`' => $insert->expr()->literal($item['id']),
+ '`share_type`' => $insert->expr()->literal(self::$shareTypeGroupUserUnique),
+ '`share_with`' => $insert->expr()->literal($arguments['uid']),
+ '`uid_owner`' => $insert->expr()->literal($item['uid_owner']),
+ '`permissions`' => $insert->expr()->literal($item['permissions']),
+ '`stime`' => $insert->expr()->literal($item['stime']),
+ '`file_source`' => $insert->expr()->literal($item['file_source']),
+ ];
+ }
+ }
+ }
+
+ /**
+ * Function that is called after a user is added to a group.
+ * add unique target for the user if needed
+ * @param array $arguments
+ */
+ public static function post_addToGroup($arguments) {
+ /** @var \OC\DB\Connection $db */
+ $db = \OC::$server->getDatabaseConnection();
+
+ $insert = $db->createQueryBuilder();
+ $insert->insert('`*PREFIX*share`');
+
+ if (isset(self::$updateTargets[$arguments['gid']])) {
+ foreach (self::$updateTargets[$arguments['gid']] as $newTarget) {
+ $insert->values($newTarget);
+ $insert->execute();
}
+ unset(self::$updateTargets[$arguments['gid']]);
}
}
diff --git a/lib/private/share/share.php b/lib/private/share/share.php
index 14909c6f8ce..9aea4677b5b 100644
--- a/lib/private/share/share.php
+++ b/lib/private/share/share.php
@@ -38,7 +38,7 @@
namespace OC\Share;
use OCP\IUserSession;
-use OC\DB\Connection;
+use OCP\IDBConnection;
use OCP\IConfig;
/**
@@ -142,15 +142,25 @@ class Share extends Constants {
while ($source !== -1) {
// Fetch all shares with another user
- $query = \OC_DB::prepare(
- 'SELECT `share_with`, `file_source`, `file_target`
+ if (!$returnUserPaths) {
+ $query = \OC_DB::prepare(
+ 'SELECT `share_with`, `file_source`, `file_target`
+ FROM
+ `*PREFIX*share`
+ WHERE
+ `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
+ );
+ $result = $query->execute(array($source, self::SHARE_TYPE_USER));
+ } else {
+ $query = \OC_DB::prepare(
+ 'SELECT `share_with`, `file_source`, `file_target`
FROM
`*PREFIX*share`
WHERE
- `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
- );
-
- $result = $query->execute(array($source, self::SHARE_TYPE_USER));
+ `item_source` = ? AND `share_type` IN (?, ?) AND `item_type` IN (\'file\', \'folder\')'
+ );
+ $result = $query->execute(array($source, self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
+ }
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
@@ -182,7 +192,12 @@ class Share extends Constants {
$shares = array_merge($shares, $usersInGroup);
if ($returnUserPaths) {
foreach ($usersInGroup as $user) {
- $fileTargets[(int) $row['file_source']][$user] = $row;
+ if (!isset($fileTargets[(int) $row['file_source']][$user])) {
+ // When the user already has an entry for this file source
+ // the file is either shared directly with him as well, or
+ // he has an exception entry (because of naming conflict).
+ $fileTargets[(int) $row['file_source']][$user] = $row;
+ }
}
}
}
@@ -238,9 +253,6 @@ class Share extends Constants {
// Include owner in list of users, if requested
if ($includeOwner) {
$shares[] = $ownerUser;
- if ($returnUserPaths) {
- $sharePaths[$ownerUser] = $path;
- }
}
if ($returnUserPaths) {
@@ -268,6 +280,12 @@ class Share extends Constants {
}
}
+ if ($includeOwner) {
+ $sharePaths[$ownerUser] = $path;
+ } else {
+ unset($sharePaths[$ownerUser]);
+ }
+
return $sharePaths;
}
@@ -719,6 +737,7 @@ class Share extends Constants {
// Generate hash of password - same method as user passwords
if (!empty($shareWith)) {
+ self::verifyPassword($shareWith);
$shareWith = \OC::$server->getHasher()->hash($shareWith);
} else {
// reuse the already set password, but only if we change permissions
@@ -763,6 +782,19 @@ class Share extends Constants {
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::ERROR);
throw new \Exception($message_t);
} else if ($shareType === self::SHARE_TYPE_REMOTE) {
+
+ /*
+ * Check if file is not already shared with the remote user
+ */
+ if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_REMOTE,
+ $shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true, true)) {
+ $message = 'Sharing %s failed, because this item is already shared with %s';
+ $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
+ \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::ERROR);
+ throw new \Exception($message_t);
+ }
+
+
$token = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
\OCP\Security\ISecureRandom::CHAR_DIGITS);
@@ -1177,17 +1209,17 @@ class Share extends Constants {
/**
* Retrieve the owner of a connection
*
- * @param Connection $connection
+ * @param IDBConnection $connection
* @param int $shareId
* @throws \Exception
* @return string uid of share owner
*/
- private static function getShareOwner(Connection $connection, $shareId) {
- $qb = $connection->createQueryBuilder();
+ private static function getShareOwner(IDBConnection $connection, $shareId) {
+ $qb = $connection->getQueryBuilder();
- $qb->select('`uid_owner`')
- ->from('`*PREFIX*share`')
- ->where('`id` = :shareId')
+ $qb->select('uid_owner')
+ ->from('share')
+ ->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
->setParameter(':shareId', $shareId);
$result = $qb->execute();
$result = $result->fetch();
@@ -1200,18 +1232,18 @@ class Share extends Constants {
}
/**
- * Set expiration date for a share
+ * Set password for a public link share
*
* @param IUserSession $userSession
- * @param Connection $connection
+ * @param IDBConnection $connection
* @param IConfig $config
* @param int $shareId
* @param string $password
* @throws \Exception
* @return boolean
*/
- public static function setPassword(IUserSession $userSession,
- Connection $connection,
+ public static function setPassword(IUserSession $userSession,
+ IDBConnection $connection,
IConfig $config,
$shareId, $password) {
$user = $userSession->getUser();
@@ -1234,10 +1266,12 @@ class Share extends Constants {
throw new \Exception('Cannot remove password');
}
- $qb = $connection->createQueryBuilder();
- $qb->update('`*PREFIX*share`')
- ->set('`share_with`', ':pass')
- ->where('`id` = :shareId')
+ self::verifyPassword($password);
+
+ $qb = $connection->getQueryBuilder();
+ $qb->update('share')
+ ->set('share_with', $qb->createParameter('pass'))
+ ->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password))
->setParameter(':shareId', $shareId);
@@ -2586,4 +2620,23 @@ class Share extends Constants {
$result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]);
return $result->fetchAll();
}
+
+ /**
+ * @param string $password
+ * @throws \Exception
+ */
+ private static function verifyPassword($password) {
+
+ $accepted = true;
+ $message = '';
+ \OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
+ 'password' => $password,
+ 'accepted' => &$accepted,
+ 'message' => &$message
+ ]);
+
+ if (!$accepted) {
+ throw new \Exception($message);
+ }
+ }
}
diff --git a/lib/private/systemconfig.php b/lib/private/systemconfig.php
index 451ccd85e2f..13b0959768a 100644
--- a/lib/private/systemconfig.php
+++ b/lib/private/systemconfig.php
@@ -29,6 +29,14 @@ namespace OC;
*/
class SystemConfig {
/**
+ * Lists all available config keys
+ * @return array an array of key names
+ */
+ public function getKeys() {
+ return \OC_Config::getKeys();
+ }
+
+ /**
* Sets a new system wide value
*
* @param string $key the key of the value, under which will be saved
diff --git a/lib/private/template.php b/lib/private/template.php
index ca689729f80..e7acc778de3 100644
--- a/lib/private/template.php
+++ b/lib/private/template.php
@@ -222,9 +222,9 @@ class OC_Template extends \OC\Template\Base {
/**
* print error page using Exception details
- * @param Exception $exception
+ * @param Exception|Error $exception
*/
- public static function printExceptionErrorPage(Exception $exception) {
+ public static function printExceptionErrorPage($exception) {
$request = \OC::$server->getRequest();
$content = new \OC_Template('', 'exception', 'error', false);
$content->assign('errorClass', get_class($exception));
diff --git a/lib/private/updater.php b/lib/private/updater.php
index cba87434165..f73fa8ff655 100644
--- a/lib/private/updater.php
+++ b/lib/private/updater.php
@@ -271,6 +271,13 @@ class Updater extends BasicEmitter {
throw new \Exception($e->getMessage());
}
+ // FIXME: Some users do not upload the new ca-bundle.crt, let's catch this
+ // in the update. For a newer release we shall use an integrity check after
+ // the update.
+ if(!file_exists(\OC::$configDir .'/ca-bundle.crt')) {
+ throw new \Exception('Please upload the ca-bundle.crt file into the \'config\' directory.');
+ }
+
// 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)
diff --git a/lib/private/user.php b/lib/private/user.php
index 1d8b89e273f..92807246144 100644
--- a/lib/private/user.php
+++ b/lib/private/user.php
@@ -405,7 +405,7 @@ class OC_User {
return $backend->getLogoutAttribute();
}
- return 'href="' . link_to('', 'index.php') . '?logout=true&requesttoken=' . urlencode(OC_Util::callRegister()) . '"';
+ return 'href="' . link_to('', 'index.php') . '?logout=true&amp;requesttoken=' . urlencode(OC_Util::callRegister()) . '"';
}
/**
diff --git a/lib/private/user/manager.php b/lib/private/user/manager.php
index 09400d8365c..6a57aa3f7ae 100644
--- a/lib/private/user/manager.php
+++ b/lib/private/user/manager.php
@@ -182,7 +182,7 @@ class Manager extends PublicEmitter implements IUserManager {
}
}
- \OC::$server->getLogger()->warning('Login failed: \''. $loginname .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). ')', ['app' => 'core']);
+ \OC::$server->getLogger()->warning('Login failed: \''. $loginname .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
return false;
}
diff --git a/lib/private/util.php b/lib/private/util.php
index 51815a0ae3d..501dbf5c4c5 100644
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -143,6 +143,14 @@ class OC_Util {
return $storage;
});
+ // install storage availability wrapper, before most other wrappers
+ \OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
+ if (!$storage->isLocal()) {
+ return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
+ }
+ return $storage;
+ });
+
\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
// set up quota for home storages, even for other users
// which can happen when using sharing
@@ -267,17 +275,17 @@ class OC_Util {
/**
* copies the skeleton to the users /files
*
- * @param \OC\User\User $user
+ * @param String $userId
* @param \OCP\Files\Folder $userDirectory
*/
- public static function copySkeleton(\OC\User\User $user, \OCP\Files\Folder $userDirectory) {
+ public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
$skeletonDirectory = \OCP\Config::getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
if (!empty($skeletonDirectory)) {
\OCP\Util::writeLog(
'files_skeleton',
- 'copying skeleton for '.$user->getUID().' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
+ 'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
\OCP\Util::DEBUG
);
self::copyr($skeletonDirectory, $userDirectory);
@@ -567,7 +575,8 @@ class OC_Util {
}
$webServerRestart = false;
- $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), new \OC_Defaults());
+ $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
+ new \OC_Defaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
$availableDatabases = $setup->getSupportedDatabases();
if (empty($availableDatabases)) {
$errors[] = array(