summaryrefslogtreecommitdiffstats
path: root/lib/private/legacy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/legacy')
-rw-r--r--lib/private/legacy/api.php533
-rw-r--r--lib/private/legacy/app.php1284
-rw-r--r--lib/private/legacy/archive.php158
-rw-r--r--lib/private/legacy/db.php258
-rw-r--r--lib/private/legacy/defaults.php286
-rw-r--r--lib/private/legacy/eventsource.php125
-rw-r--r--lib/private/legacy/filechunking.php175
-rw-r--r--lib/private/legacy/files.php316
-rw-r--r--lib/private/legacy/group.php300
-rw-r--r--lib/private/legacy/helper.php703
-rw-r--r--lib/private/legacy/hook.php145
-rw-r--r--lib/private/legacy/image.php1147
-rw-r--r--lib/private/legacy/installer.php638
-rw-r--r--lib/private/legacy/json.php179
-rw-r--r--lib/private/legacy/ocs.php42
-rw-r--r--lib/private/legacy/response.php268
-rw-r--r--lib/private/legacy/template.php433
-rw-r--r--lib/private/legacy/user.php639
-rw-r--r--lib/private/legacy/util.php1450
19 files changed, 9079 insertions, 0 deletions
diff --git a/lib/private/legacy/api.php b/lib/private/legacy/api.php
new file mode 100644
index 00000000000..bab879c95f8
--- /dev/null
+++ b/lib/private/legacy/api.php
@@ -0,0 +1,533 @@
+<?php
+/**
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Michael Gapczynski <GapczynskiM@gmail.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Tom Needham <tom@owncloud.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+use OCP\API;
+use OCP\AppFramework\Http;
+
+/**
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Michael Gapczynski <GapczynskiM@gmail.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Tom Needham <tom@owncloud.com>
+ * @author Vincent Petry <pvince81@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/>
+ *
+ */
+
+class OC_API {
+
+ /**
+ * API authentication levels
+ */
+
+ /** @deprecated Use \OCP\API::GUEST_AUTH instead */
+ const GUEST_AUTH = 0;
+
+ /** @deprecated Use \OCP\API::USER_AUTH instead */
+ const USER_AUTH = 1;
+
+ /** @deprecated Use \OCP\API::SUBADMIN_AUTH instead */
+ const SUBADMIN_AUTH = 2;
+
+ /** @deprecated Use \OCP\API::ADMIN_AUTH instead */
+ const ADMIN_AUTH = 3;
+
+ /**
+ * API Response Codes
+ */
+
+ /** @deprecated Use \OCP\API::RESPOND_UNAUTHORISED instead */
+ const RESPOND_UNAUTHORISED = 997;
+
+ /** @deprecated Use \OCP\API::RESPOND_SERVER_ERROR instead */
+ const RESPOND_SERVER_ERROR = 996;
+
+ /** @deprecated Use \OCP\API::RESPOND_NOT_FOUND instead */
+ const RESPOND_NOT_FOUND = 998;
+
+ /** @deprecated Use \OCP\API::RESPOND_UNKNOWN_ERROR instead */
+ const RESPOND_UNKNOWN_ERROR = 999;
+
+ /**
+ * api actions
+ */
+ protected static $actions = array();
+ private static $logoutRequired = false;
+ private static $isLoggedIn = false;
+
+ /**
+ * registers an api call
+ * @param string $method the http method
+ * @param string $url the url to match
+ * @param callable $action the function to run
+ * @param string $app the id of the app registering the call
+ * @param int $authLevel the level of authentication required for the call
+ * @param array $defaults
+ * @param array $requirements
+ */
+ public static function register($method, $url, $action, $app,
+ $authLevel = API::USER_AUTH,
+ $defaults = array(),
+ $requirements = array()) {
+ $name = strtolower($method).$url;
+ $name = str_replace(array('/', '{', '}'), '_', $name);
+ if(!isset(self::$actions[$name])) {
+ $oldCollection = OC::$server->getRouter()->getCurrentCollection();
+ OC::$server->getRouter()->useCollection('ocs');
+ OC::$server->getRouter()->create($name, $url)
+ ->method($method)
+ ->defaults($defaults)
+ ->requirements($requirements)
+ ->action('OC_API', 'call');
+ self::$actions[$name] = array();
+ OC::$server->getRouter()->useCollection($oldCollection);
+ }
+ self::$actions[$name][] = array('app' => $app, 'action' => $action, 'authlevel' => $authLevel);
+ }
+
+ /**
+ * handles an api call
+ * @param array $parameters
+ */
+ public static function call($parameters) {
+ $request = \OC::$server->getRequest();
+ $method = $request->getMethod();
+
+ // Prepare the request variables
+ if($method === 'PUT') {
+ $parameters['_put'] = $request->getParams();
+ } else if($method === 'DELETE') {
+ $parameters['_delete'] = $request->getParams();
+ }
+ $name = $parameters['_route'];
+ // Foreach registered action
+ $responses = array();
+ foreach(self::$actions[$name] as $action) {
+ // Check authentication and availability
+ if(!self::isAuthorised($action)) {
+ $responses[] = array(
+ 'app' => $action['app'],
+ 'response' => new OC_OCS_Result(null, API::RESPOND_UNAUTHORISED, 'Unauthorised'),
+ 'shipped' => OC_App::isShipped($action['app']),
+ );
+ continue;
+ }
+ if(!is_callable($action['action'])) {
+ $responses[] = array(
+ 'app' => $action['app'],
+ 'response' => new OC_OCS_Result(null, API::RESPOND_NOT_FOUND, 'Api method not found'),
+ 'shipped' => OC_App::isShipped($action['app']),
+ );
+ continue;
+ }
+ // Run the action
+ $responses[] = array(
+ 'app' => $action['app'],
+ 'response' => call_user_func($action['action'], $parameters),
+ 'shipped' => OC_App::isShipped($action['app']),
+ );
+ }
+ $response = self::mergeResponses($responses);
+ $format = self::requestedFormat();
+ if (self::$logoutRequired) {
+ \OC::$server->getUserSession()->logout();
+ }
+
+ self::respond($response, $format);
+ }
+
+ /**
+ * merge the returned result objects into one response
+ * @param array $responses
+ * @return OC_OCS_Result
+ */
+ public static function mergeResponses($responses) {
+ // Sort into shipped and third-party
+ $shipped = array(
+ 'succeeded' => array(),
+ 'failed' => array(),
+ );
+ $thirdparty = array(
+ 'succeeded' => array(),
+ 'failed' => array(),
+ );
+
+ foreach($responses as $response) {
+ if($response['shipped'] || ($response['app'] === 'core')) {
+ if($response['response']->succeeded()) {
+ $shipped['succeeded'][$response['app']] = $response;
+ } else {
+ $shipped['failed'][$response['app']] = $response;
+ }
+ } else {
+ if($response['response']->succeeded()) {
+ $thirdparty['succeeded'][$response['app']] = $response;
+ } else {
+ $thirdparty['failed'][$response['app']] = $response;
+ }
+ }
+ }
+
+ // Remove any error responses if there is one shipped response that succeeded
+ if(!empty($shipped['failed'])) {
+ // Which shipped response do we use if they all failed?
+ // They may have failed for different reasons (different status codes)
+ // Which response code should we return?
+ // Maybe any that are not \OCP\API::RESPOND_SERVER_ERROR
+ // Merge failed responses if more than one
+ $data = array();
+ foreach($shipped['failed'] as $failure) {
+ $data = array_merge_recursive($data, $failure['response']->getData());
+ }
+ $picked = reset($shipped['failed']);
+ $code = $picked['response']->getStatusCode();
+ $meta = $picked['response']->getMeta();
+ $headers = $picked['response']->getHeaders();
+ $response = new OC_OCS_Result($data, $code, $meta['message'], $headers);
+ return $response;
+ } elseif(!empty($shipped['succeeded'])) {
+ $responses = array_merge($shipped['succeeded'], $thirdparty['succeeded']);
+ } elseif(!empty($thirdparty['failed'])) {
+ // Merge failed responses if more than one
+ $data = array();
+ foreach($thirdparty['failed'] as $failure) {
+ $data = array_merge_recursive($data, $failure['response']->getData());
+ }
+ $picked = reset($thirdparty['failed']);
+ $code = $picked['response']->getStatusCode();
+ $meta = $picked['response']->getMeta();
+ $headers = $picked['response']->getHeaders();
+ $response = new OC_OCS_Result($data, $code, $meta['message'], $headers);
+ return $response;
+ } else {
+ $responses = $thirdparty['succeeded'];
+ }
+ // Merge the successful responses
+ $data = [];
+ $codes = [];
+ $header = [];
+
+ foreach($responses as $response) {
+ if($response['shipped']) {
+ $data = array_merge_recursive($response['response']->getData(), $data);
+ } else {
+ $data = array_merge_recursive($data, $response['response']->getData());
+ }
+ $header = array_merge_recursive($header, $response['response']->getHeaders());
+ $codes[] = ['code' => $response['response']->getStatusCode(),
+ 'meta' => $response['response']->getMeta()];
+ }
+
+ // Use any non 100 status codes
+ $statusCode = 100;
+ $statusMessage = null;
+ foreach($codes as $code) {
+ if($code['code'] != 100) {
+ $statusCode = $code['code'];
+ $statusMessage = $code['meta']['message'];
+ break;
+ }
+ }
+
+ return new OC_OCS_Result($data, $statusCode, $statusMessage, $header);
+ }
+
+ /**
+ * authenticate the api call
+ * @param array $action the action details as supplied to OC_API::register()
+ * @return bool
+ */
+ private static function isAuthorised($action) {
+ $level = $action['authlevel'];
+ switch($level) {
+ case API::GUEST_AUTH:
+ // Anyone can access
+ return true;
+ case API::USER_AUTH:
+ // User required
+ return self::loginUser();
+ case API::SUBADMIN_AUTH:
+ // Check for subadmin
+ $user = self::loginUser();
+ if(!$user) {
+ return false;
+ } else {
+ $userObject = \OC::$server->getUserSession()->getUser();
+ if($userObject === null) {
+ return false;
+ }
+ $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
+ $admin = OC_User::isAdminUser($user);
+ if($isSubAdmin || $admin) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ case API::ADMIN_AUTH:
+ // Check for admin
+ $user = self::loginUser();
+ if(!$user) {
+ return false;
+ } else {
+ return OC_User::isAdminUser($user);
+ }
+ default:
+ // oops looks like invalid level supplied
+ return false;
+ }
+ }
+
+ /**
+ * http basic auth
+ * @return string|false (username, or false on failure)
+ */
+ private static function loginUser() {
+ if(self::$isLoggedIn === true) {
+ return \OC_User::getUser();
+ }
+
+ // reuse existing login
+ $loggedIn = OC_User::isLoggedIn();
+ if ($loggedIn === true) {
+ $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false;
+ if ($ocsApiRequest) {
+
+ // initialize the user's filesystem
+ \OC_Util::setUpFS(\OC_User::getUser());
+ self::$isLoggedIn = true;
+
+ return OC_User::getUser();
+ }
+ return false;
+ }
+
+ // basic auth - because OC_User::login will create a new session we shall only try to login
+ // if user and pass are set
+ if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) ) {
+ $authUser = $_SERVER['PHP_AUTH_USER'];
+ $authPw = $_SERVER['PHP_AUTH_PW'];
+ $return = OC_User::login($authUser, $authPw);
+ if ($return === true) {
+ self::$logoutRequired = true;
+
+ // initialize the user's filesystem
+ \OC_Util::setUpFS(\OC_User::getUser());
+ self::$isLoggedIn = true;
+
+ /**
+ * Add DAV authenticated. This should in an ideal world not be
+ * necessary but the iOS App reads cookies from anywhere instead
+ * only the DAV endpoint.
+ * This makes sure that the cookies will be valid for the whole scope
+ * @see https://github.com/owncloud/core/issues/22893
+ */
+ \OC::$server->getSession()->set(
+ \OCA\DAV\Connector\Sabre\Auth::DAV_AUTHENTICATED,
+ \OC::$server->getUserSession()->getUser()->getUID()
+ );
+
+ return \OC_User::getUser();
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * respond to a call
+ * @param OC_OCS_Result $result
+ * @param string $format the format xml|json
+ */
+ public static function respond($result, $format='xml') {
+ $request = \OC::$server->getRequest();
+
+ // Send 401 headers if unauthorised
+ if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) {
+ // If request comes from JS return dummy auth request
+ if($request->getHeader('X-Requested-With') === 'XMLHttpRequest') {
+ header('WWW-Authenticate: DummyBasic realm="Authorisation Required"');
+ } else {
+ header('WWW-Authenticate: Basic realm="Authorisation Required"');
+ }
+ header('HTTP/1.0 401 Unauthorized');
+ }
+
+ foreach($result->getHeaders() as $name => $value) {
+ header($name . ': ' . $value);
+ }
+
+ $meta = $result->getMeta();
+ $data = $result->getData();
+ if (self::isV2($request)) {
+ $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;
+ }
+
+ /**
+ * @param XMLWriter $writer
+ */
+ private static function toXML($array, $writer) {
+ foreach($array as $k => $v) {
+ if ($k[0] === '@') {
+ $writer->writeAttribute(substr($k, 1), $v);
+ continue;
+ } else if (is_numeric($k)) {
+ $k = 'element';
+ }
+ if(is_array($v)) {
+ $writer->startElement($k);
+ self::toXML($v, $writer);
+ $writer->endElement();
+ } else {
+ $writer->writeElement($k, $v);
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public static function requestedFormat() {
+ $formats = array('json', 'xml');
+
+ $format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml';
+ return $format;
+ }
+
+ /**
+ * Based on the requested format the response content type is set
+ * @param string $format
+ */
+ 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;
+ }
+
+ if ($format === 'json') {
+ header('Content-Type: application/json; charset=utf-8');
+ return;
+ }
+
+ header('Content-Type: application/octet-stream; charset=utf-8');
+ }
+
+ /**
+ * @param \OCP\IRequest $request
+ * @return bool
+ */
+ protected static function isV2(\OCP\IRequest $request) {
+ $script = $request->getScriptName();
+
+ return substr($script, -11) === '/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/legacy/app.php b/lib/private/legacy/app.php
new file mode 100644
index 00000000000..246bf97ee91
--- /dev/null
+++ b/lib/private/legacy/app.php
@@ -0,0 +1,1284 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Borjan Tchakaloff <borjan@tchakaloff.fr>
+ * @author Brice Maron <brice@bmaron.net>
+ * @author Christopher Schรคpers <kondou@ts.unde.re>
+ * @author Felix Moeller <mail@felixmoeller.de>
+ * @author Frank Karlitschek <frank@owncloud.org>
+ * @author Georg Ehrke <georg@owncloud.com>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Kamil Domanski <kdomanski@kdemail.net>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Markus Goetz <markus@woboq.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author RealRancor <Fisch.666@gmx.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author Sam Tuke <mail@samtuke.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Thomas Tanghus <thomas@tanghus.net>
+ * @author Tom Needham <tom@owncloud.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+use OC\App\DependencyAnalyzer;
+use OC\App\Platform;
+use OC\OCSClient;
+use OC\Repair;
+
+/**
+ * This class manages the apps. It allows them to register and integrate in the
+ * ownCloud ecosystem. Furthermore, this class is responsible for installing,
+ * upgrading and removing apps.
+ */
+class OC_App {
+ static private $appVersion = [];
+ static private $adminForms = array();
+ static private $personalForms = array();
+ static private $appInfo = array();
+ static private $appTypes = array();
+ static private $loadedApps = array();
+ static private $altLogin = array();
+ const officialApp = 200;
+
+ /**
+ * clean the appId
+ *
+ * @param string|boolean $app AppId that needs to be cleaned
+ * @return string
+ */
+ public static function cleanAppId($app) {
+ return str_replace(array('\0', '/', '\\', '..'), '', $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 string[] | string | null $types
+ * @return bool
+ *
+ * This function walks through the ownCloud directory and loads all apps
+ * it can find. A directory contains an app if the file /appinfo/info.xml
+ * exists.
+ *
+ * if $types is set, only apps of those types will be loaded
+ */
+ public static function loadApps($types = null) {
+ if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
+ return false;
+ }
+ // Load the enabled apps here
+ $apps = self::getEnabledApps();
+
+ // Add each apps' folder as allowed class path
+ foreach($apps as $app) {
+ $path = self::getAppPath($app);
+ if($path !== false) {
+ \OC::$loader->addValidRoot($path);
+ }
+ }
+
+ // prevent app.php from printing output
+ ob_start();
+ foreach ($apps as $app) {
+ if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
+ self::loadApp($app);
+ }
+ }
+ ob_end_clean();
+
+ return true;
+ }
+
+ /**
+ * load a single app
+ *
+ * @param string $app
+ * @param bool $checkUpgrade whether an upgrade check should be done
+ * @throws \OC\NeedsUpdateException
+ */
+ public static function loadApp($app, $checkUpgrade = true) {
+ self::$loadedApps[] = $app;
+ $appPath = self::getAppPath($app);
+ if($appPath === false) {
+ return;
+ }
+ \OC::$loader->addValidRoot($appPath); // in case someone calls loadApp() directly
+ if (is_file($appPath . '/appinfo/app.php')) {
+ \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
+ if ($checkUpgrade and self::shouldUpgrade($app)) {
+ throw new \OC\NeedsUpdateException();
+ }
+ self::requireAppFile($app);
+ if (self::isType($app, array('authentication'))) {
+ // since authentication apps affect the "is app enabled for group" check,
+ // the enabled apps cache needs to be cleared to make sure that the
+ // next time getEnableApps() is called it will also include apps that were
+ // enabled for groups
+ self::$enabledAppsCache = array();
+ }
+ \OC::$server->getEventLogger()->end('load_app_' . $app);
+ }
+ }
+
+ /**
+ * Load app.php from the given app
+ *
+ * @param string $app app name
+ */
+ private static function requireAppFile($app) {
+ try {
+ // encapsulated here to avoid variable scope conflicts
+ require_once $app . '/appinfo/app.php';
+ } catch (Error $ex) {
+ \OC::$server->getLogger()->logException($ex);
+ $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
+ if (!in_array($app, $blacklist)) {
+ self::disable($app);
+ }
+ }
+ }
+
+ /**
+ * check if an app is of a specific type
+ *
+ * @param string $app
+ * @param string|array $types
+ * @return bool
+ */
+ public static function isType($app, $types) {
+ if (is_string($types)) {
+ $types = array($types);
+ }
+ $appTypes = self::getAppTypes($app);
+ foreach ($types as $type) {
+ if (array_search($type, $appTypes) !== false) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * get the types of an app
+ *
+ * @param string $app
+ * @return array
+ */
+ private static function getAppTypes($app) {
+ //load the cache
+ if (count(self::$appTypes) == 0) {
+ self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
+ }
+
+ if (isset(self::$appTypes[$app])) {
+ return explode(',', self::$appTypes[$app]);
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * read app types from info.xml and cache them in the database
+ */
+ public static function setAppTypes($app) {
+ $appData = self::getAppInfo($app);
+ if(!is_array($appData)) {
+ return;
+ }
+
+ if (isset($appData['types'])) {
+ $appTypes = implode(',', $appData['types']);
+ } else {
+ $appTypes = '';
+ }
+
+ \OC::$server->getAppConfig()->setValue($app, 'types', $appTypes);
+ }
+
+ /**
+ * check if app is shipped
+ *
+ * @param string $appId the id of the app to check
+ * @return bool
+ *
+ * Check if an app that is installed is a shipped app or installed from the appstore.
+ */
+ public static function isShipped($appId) {
+ return \OC::$server->getAppManager()->isShipped($appId);
+ }
+
+ /**
+ * get all enabled apps
+ */
+ protected static $enabledAppsCache = array();
+
+ /**
+ * Returns apps enabled for the current user.
+ *
+ * @param bool $forceRefresh whether to refresh the cache
+ * @param bool $all whether to return apps for all users, not only the
+ * currently logged in one
+ * @return string[]
+ */
+ public static function getEnabledApps($forceRefresh = false, $all = false) {
+ if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
+ return array();
+ }
+ // in incognito mode or when logged out, $user will be false,
+ // which is also the case during an upgrade
+ $appManager = \OC::$server->getAppManager();
+ if ($all) {
+ $user = null;
+ } else {
+ $user = \OC::$server->getUserSession()->getUser();
+ }
+
+ if (is_null($user)) {
+ $apps = $appManager->getInstalledApps();
+ } else {
+ $apps = $appManager->getEnabledAppsForUser($user);
+ }
+ $apps = array_filter($apps, function ($app) {
+ return $app !== 'files';//we add this manually
+ });
+ sort($apps);
+ array_unshift($apps, 'files');
+ return $apps;
+ }
+
+ /**
+ * checks whether or not an app is enabled
+ *
+ * @param string $app app
+ * @return bool
+ *
+ * This function checks whether or not an app is enabled.
+ */
+ public static function isEnabled($app) {
+ return \OC::$server->getAppManager()->isEnabledForUser($app);
+ }
+
+ /**
+ * enables an app
+ *
+ * @param mixed $app app
+ * @param array $groups (optional) when set, only these groups will have access to the app
+ * @throws \Exception
+ * @return void
+ *
+ * This function set an app as enabled in appconfig.
+ */
+ public static function enable($app, $groups = null) {
+ self::$enabledAppsCache = array(); // flush
+ if (!OC_Installer::isInstalled($app)) {
+ $app = self::installApp($app);
+ }
+
+ $appManager = \OC::$server->getAppManager();
+ if (!is_null($groups)) {
+ $groupManager = \OC::$server->getGroupManager();
+ $groupsList = [];
+ foreach ($groups as $group) {
+ $groupItem = $groupManager->get($group);
+ if ($groupItem instanceof \OCP\IGroup) {
+ $groupsList[] = $groupManager->get($group);
+ }
+ }
+ $appManager->enableAppForGroups($app, $groupsList);
+ } else {
+ $appManager->enableApp($app);
+ }
+ }
+
+ /**
+ * @param string $app
+ * @return int
+ */
+ private static function downloadApp($app) {
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($app, \OCP\Util::getVersion());
+ $download = $ocsClient->getApplicationDownload($app, \OCP\Util::getVersion());
+ if(isset($download['downloadlink']) and $download['downloadlink']!='') {
+ // Replace spaces in download link without encoding entire URL
+ $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
+ $info = array('source' => 'http', 'href' => $download['downloadlink'], 'appdata' => $appData);
+ $app = OC_Installer::installApp($info);
+ }
+ return $app;
+ }
+
+ /**
+ * @param string $app
+ * @return bool
+ */
+ public static function removeApp($app) {
+ if (self::isShipped($app)) {
+ return false;
+ }
+
+ return OC_Installer::removeApp($app);
+ }
+
+ /**
+ * This function set an app as disabled in appconfig.
+ *
+ * @param string $app app
+ * @throws Exception
+ */
+ public static function disable($app) {
+ // Convert OCS ID to regular application identifier
+ if(self::getInternalAppIdByOcs($app) !== false) {
+ $app = self::getInternalAppIdByOcs($app);
+ }
+
+ self::$enabledAppsCache = array(); // flush
+ // check if app is a shipped app or not. if not delete
+ \OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
+ $appManager = \OC::$server->getAppManager();
+ $appManager->disableApp($app);
+ }
+
+ /**
+ * Returns the Settings Navigation
+ *
+ * @return string[]
+ *
+ * This function returns an array containing all settings pages added. The
+ * entries are sorted by the key 'order' ascending.
+ */
+ public static function getSettingsNavigation() {
+ $l = \OC::$server->getL10N('lib');
+ $urlGenerator = \OC::$server->getURLGenerator();
+
+ $settings = array();
+ // by default, settings only contain the help menu
+ if (OC_Util::getEditionString() === '' &&
+ \OC::$server->getSystemConfig()->getValue('knowledgebaseenabled', true) == true
+ ) {
+ $settings = array(
+ array(
+ "id" => "help",
+ "order" => 1000,
+ "href" => $urlGenerator->linkToRoute('settings_help'),
+ "name" => $l->t("Help"),
+ "icon" => $urlGenerator->imagePath("settings", "help.svg")
+ )
+ );
+ }
+
+ // if the user is logged-in
+ if (OC_User::isLoggedIn()) {
+ // personal menu
+ $settings[] = array(
+ "id" => "personal",
+ "order" => 1,
+ "href" => $urlGenerator->linkToRoute('settings_personal'),
+ "name" => $l->t("Personal"),
+ "icon" => $urlGenerator->imagePath("settings", "personal.svg")
+ );
+
+ //SubAdmins are also allowed to access user management
+ $userObject = \OC::$server->getUserSession()->getUser();
+ $isSubAdmin = false;
+ if($userObject !== null) {
+ $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
+ }
+ if ($isSubAdmin) {
+ // admin users menu
+ $settings[] = array(
+ "id" => "core_users",
+ "order" => 2,
+ "href" => $urlGenerator->linkToRoute('settings_users'),
+ "name" => $l->t("Users"),
+ "icon" => $urlGenerator->imagePath("settings", "users.svg")
+ );
+ }
+
+ // if the user is an admin
+ if (OC_User::isAdminUser(OC_User::getUser())) {
+ // admin settings
+ $settings[] = array(
+ "id" => "admin",
+ "order" => 1000,
+ "href" => $urlGenerator->linkToRoute('settings_admin'),
+ "name" => $l->t("Admin"),
+ "icon" => $urlGenerator->imagePath("settings", "admin.svg")
+ );
+ }
+ }
+
+ $navigation = self::proceedNavigation($settings);
+ return $navigation;
+ }
+
+ // This is private as well. It simply works, so don't ask for more details
+ private static function proceedNavigation($list) {
+ $activeApp = OC::$server->getNavigationManager()->getActiveEntry();
+ foreach ($list as &$navEntry) {
+ if ($navEntry['id'] == $activeApp) {
+ $navEntry['active'] = true;
+ } else {
+ $navEntry['active'] = false;
+ }
+ }
+ unset($navEntry);
+
+ usort($list, create_function('$a, $b', 'if( $a["order"] == $b["order"] ) {return 0;}elseif( $a["order"] < $b["order"] ) {return -1;}else{return 1;}'));
+
+ return $list;
+ }
+
+ /**
+ * Get the path where to install apps
+ *
+ * @return string|false
+ */
+ public static function getInstallPath() {
+ if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
+ return false;
+ }
+
+ foreach (OC::$APPSROOTS as $dir) {
+ if (isset($dir['writable']) && $dir['writable'] === true) {
+ return $dir['path'];
+ }
+ }
+
+ \OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
+ return null;
+ }
+
+
+ /**
+ * search for an app in all app-directories
+ *
+ * @param string $appId
+ * @return false|string
+ */
+ protected static function findAppInDirectories($appId) {
+ $sanitizedAppId = self::cleanAppId($appId);
+ if($sanitizedAppId !== $appId) {
+ return false;
+ }
+ static $app_dir = array();
+
+ if (isset($app_dir[$appId])) {
+ return $app_dir[$appId];
+ }
+
+ $possibleApps = array();
+ foreach (OC::$APPSROOTS as $dir) {
+ if (file_exists($dir['path'] . '/' . $appId)) {
+ $possibleApps[] = $dir;
+ }
+ }
+
+ if (empty($possibleApps)) {
+ return false;
+ } elseif (count($possibleApps) === 1) {
+ $dir = array_shift($possibleApps);
+ $app_dir[$appId] = $dir;
+ return $dir;
+ } else {
+ $versionToLoad = array();
+ foreach ($possibleApps as $possibleApp) {
+ $version = self::getAppVersionByPath($possibleApp['path']);
+ if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
+ $versionToLoad = array(
+ 'dir' => $possibleApp,
+ 'version' => $version,
+ );
+ }
+ }
+ $app_dir[$appId] = $versionToLoad['dir'];
+ return $versionToLoad['dir'];
+ //TODO - write test
+ }
+ }
+
+ /**
+ * Get the directory for the given app.
+ * If the app is defined in multiple directories, the first one is taken. (false if not found)
+ *
+ * @param string $appId
+ * @return string|false
+ */
+ public static function getAppPath($appId) {
+ if ($appId === null || trim($appId) === '') {
+ return false;
+ }
+
+ if (($dir = self::findAppInDirectories($appId)) != false) {
+ return $dir['path'] . '/' . $appId;
+ }
+ return false;
+ }
+
+
+ /**
+ * check if an app's directory is writable
+ *
+ * @param string $appId
+ * @return bool
+ */
+ public static function isAppDirWritable($appId) {
+ $path = self::getAppPath($appId);
+ return ($path !== false) ? is_writable($path) : false;
+ }
+
+ /**
+ * Get the path for the given app on the access
+ * If the app is defined in multiple directories, the first one is taken. (false if not found)
+ *
+ * @param string $appId
+ * @return string|false
+ */
+ public static function getAppWebPath($appId) {
+ if (($dir = self::findAppInDirectories($appId)) != false) {
+ return OC::$WEBROOT . $dir['url'] . '/' . $appId;
+ }
+ return false;
+ }
+
+ /**
+ * get the last version of the app from appinfo/info.xml
+ *
+ * @param string $appId
+ * @return string
+ */
+ public static function getAppVersion($appId) {
+ if (!isset(self::$appVersion[$appId])) {
+ $file = self::getAppPath($appId);
+ self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0';
+ }
+ return self::$appVersion[$appId];
+ }
+
+ /**
+ * get app's version based on it's path
+ *
+ * @param string $path
+ * @return string
+ */
+ public static function getAppVersionByPath($path) {
+ $infoFile = $path . '/appinfo/info.xml';
+ $appData = self::getAppInfo($infoFile, true);
+ return isset($appData['version']) ? $appData['version'] : '';
+ }
+
+
+ /**
+ * Read all app metadata from the info.xml file
+ *
+ * @param string $appId id of the app or the path of the info.xml file
+ * @param boolean $path (optional)
+ * @return array|null
+ * @note all data is read from info.xml, not just pre-defined fields
+ */
+ public static function getAppInfo($appId, $path = false) {
+ if ($path) {
+ $file = $appId;
+ } else {
+ if (isset(self::$appInfo[$appId])) {
+ return self::$appInfo[$appId];
+ }
+ $appPath = self::getAppPath($appId);
+ if($appPath === false) {
+ return null;
+ }
+ $file = $appPath . '/appinfo/info.xml';
+ }
+
+ $parser = new \OC\App\InfoParser(\OC::$server->getURLGenerator());
+ $data = $parser->parse($file);
+
+ if (is_array($data)) {
+ $data = OC_App::parseAppInfo($data);
+ }
+ if(isset($data['ocsid'])) {
+ $storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
+ if($storedId !== '' && $storedId !== $data['ocsid']) {
+ $data['ocsid'] = $storedId;
+ }
+ }
+
+ self::$appInfo[$appId] = $data;
+
+ return $data;
+ }
+
+ /**
+ * Returns the navigation
+ *
+ * @return array
+ *
+ * This function returns an array containing all entries added. The
+ * entries are sorted by the key 'order' ascending. Additional to the keys
+ * given for each app the following keys exist:
+ * - active: boolean, signals if the user is on this navigation entry
+ */
+ public static function getNavigation() {
+ $entries = OC::$server->getNavigationManager()->getAll();
+ $navigation = self::proceedNavigation($entries);
+ return $navigation;
+ }
+
+ /**
+ * get the id of loaded app
+ *
+ * @return string
+ */
+ public static function getCurrentApp() {
+ $request = \OC::$server->getRequest();
+ $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
+ $topFolder = substr($script, 0, strpos($script, '/'));
+ if (empty($topFolder)) {
+ $path_info = $request->getPathInfo();
+ if ($path_info) {
+ $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
+ }
+ }
+ if ($topFolder == 'apps') {
+ $length = strlen($topFolder);
+ return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
+ } else {
+ return $topFolder;
+ }
+ }
+
+ /**
+ * @param string $type
+ * @return array
+ */
+ public static function getForms($type) {
+ $forms = array();
+ switch ($type) {
+ case 'admin':
+ $source = self::$adminForms;
+ break;
+ case 'personal':
+ $source = self::$personalForms;
+ break;
+ default:
+ return array();
+ }
+ foreach ($source as $form) {
+ $forms[] = include $form;
+ }
+ return $forms;
+ }
+
+ /**
+ * register an admin form to be shown
+ *
+ * @param string $app
+ * @param string $page
+ */
+ public static function registerAdmin($app, $page) {
+ self::$adminForms[] = $app . '/' . $page . '.php';
+ }
+
+ /**
+ * register a personal form to be shown
+ * @param string $app
+ * @param string $page
+ */
+ public static function registerPersonal($app, $page) {
+ self::$personalForms[] = $app . '/' . $page . '.php';
+ }
+
+ /**
+ * @param array $entry
+ */
+ public static function registerLogIn(array $entry) {
+ self::$altLogin[] = $entry;
+ }
+
+ /**
+ * @return array
+ */
+ public static function getAlternativeLogIns() {
+ return self::$altLogin;
+ }
+
+ /**
+ * get a list of all apps in the apps folder
+ *
+ * @return array an array of app names (string IDs)
+ * @todo: change the name of this method to getInstalledApps, which is more accurate
+ */
+ public static function getAllApps() {
+
+ $apps = array();
+
+ foreach (OC::$APPSROOTS as $apps_dir) {
+ if (!is_readable($apps_dir['path'])) {
+ \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
+ continue;
+ }
+ $dh = opendir($apps_dir['path']);
+
+ if (is_resource($dh)) {
+ while (($file = readdir($dh)) !== false) {
+
+ if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
+
+ $apps[] = $file;
+ }
+ }
+ }
+ }
+
+ return $apps;
+ }
+
+ /**
+ * List all apps, this is used in apps.php
+ *
+ * @param bool $onlyLocal
+ * @param bool $includeUpdateInfo Should we check whether there is an update
+ * in the app store?
+ * @param OCSClient $ocsClient
+ * @return array
+ */
+ public static function listAllApps($onlyLocal = false,
+ $includeUpdateInfo = true,
+ OCSClient $ocsClient) {
+ $installedApps = OC_App::getAllApps();
+
+ //TODO which apps do we want to blacklist and how do we integrate
+ // blacklisting with the multi apps folder feature?
+
+ //we don't want to show configuration for these
+ $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
+ $appList = array();
+
+ foreach ($installedApps as $app) {
+ if (array_search($app, $blacklist) === false) {
+
+ $info = OC_App::getAppInfo($app);
+ if (!is_array($info)) {
+ \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
+ continue;
+ }
+
+ if (!isset($info['name'])) {
+ \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
+ continue;
+ }
+
+ $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no');
+ $info['groups'] = null;
+ if ($enabled === 'yes') {
+ $active = true;
+ } else if ($enabled === 'no') {
+ $active = false;
+ } else {
+ $active = true;
+ $info['groups'] = $enabled;
+ }
+
+ $info['active'] = $active;
+
+ if (self::isShipped($app)) {
+ $info['internal'] = true;
+ $info['level'] = self::officialApp;
+ $info['removable'] = false;
+ } else {
+ $info['internal'] = false;
+ $info['removable'] = true;
+ }
+
+ $info['update'] = ($includeUpdateInfo) ? OC_Installer::isUpdateAvailable($app) : null;
+
+ $appPath = self::getAppPath($app);
+ if($appPath !== false) {
+ $appIcon = $appPath . '/img/' . $app . '.svg';
+ if (file_exists($appIcon)) {
+ $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app . '.svg');
+ $info['previewAsIcon'] = true;
+ } else {
+ $appIcon = $appPath . '/img/app.svg';
+ if (file_exists($appIcon)) {
+ $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, 'app.svg');
+ $info['previewAsIcon'] = true;
+ }
+ }
+ }
+ $info['version'] = OC_App::getAppVersion($app);
+ $appList[] = $info;
+ }
+ }
+ if ($onlyLocal) {
+ $remoteApps = [];
+ } else {
+ $remoteApps = OC_App::getAppstoreApps('approved', null, $ocsClient);
+ }
+ if ($remoteApps) {
+ // Remove duplicates
+ foreach ($appList as $app) {
+ foreach ($remoteApps AS $key => $remote) {
+ if ($app['name'] === $remote['name'] ||
+ (isset($app['ocsid']) &&
+ $app['ocsid'] === $remote['id'])
+ ) {
+ unset($remoteApps[$key]);
+ }
+ }
+ }
+ $combinedApps = array_merge($appList, $remoteApps);
+ } else {
+ $combinedApps = $appList;
+ }
+
+ return $combinedApps;
+ }
+
+ /**
+ * Returns the internal app ID or false
+ * @param string $ocsID
+ * @return string|false
+ */
+ public static function getInternalAppIdByOcs($ocsID) {
+ if(is_numeric($ocsID)) {
+ $idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid');
+ if(array_search($ocsID, $idArray)) {
+ return array_search($ocsID, $idArray);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get a list of all apps on the appstore
+ * @param string $filter
+ * @param string|null $category
+ * @param OCSClient $ocsClient
+ * @return array|bool multi-dimensional array of apps.
+ * Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
+ */
+ public static function getAppstoreApps($filter = 'approved',
+ $category = null,
+ OCSClient $ocsClient) {
+ $categories = [$category];
+
+ if (is_null($category)) {
+ $categoryNames = $ocsClient->getCategories(\OCP\Util::getVersion());
+ if (is_array($categoryNames)) {
+ // Check that categories of apps were retrieved correctly
+ if (!$categories = array_keys($categoryNames)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ $page = 0;
+ $remoteApps = $ocsClient->getApplications($categories, $page, $filter, \OCP\Util::getVersion());
+ $apps = [];
+ $i = 0;
+ $l = \OC::$server->getL10N('core');
+ foreach ($remoteApps as $app) {
+ $potentialCleanId = self::getInternalAppIdByOcs($app['id']);
+ // enhance app info (for example the description)
+ $apps[$i] = OC_App::parseAppInfo($app);
+ $apps[$i]['author'] = $app['personid'];
+ $apps[$i]['ocs_id'] = $app['id'];
+ $apps[$i]['internal'] = 0;
+ $apps[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false;
+ $apps[$i]['update'] = false;
+ $apps[$i]['groups'] = false;
+ $apps[$i]['score'] = $app['score'];
+ $apps[$i]['removable'] = false;
+ if ($app['label'] == 'recommended') {
+ $apps[$i]['internallabel'] = (string)$l->t('Recommended');
+ $apps[$i]['internalclass'] = 'recommendedapp';
+ }
+
+ // Apps from the appstore are always assumed to be compatible with the
+ // the current release as the initial filtering is done on the appstore
+ $apps[$i]['dependencies']['owncloud']['@attributes']['min-version'] = implode('.', \OCP\Util::getVersion());
+ $apps[$i]['dependencies']['owncloud']['@attributes']['max-version'] = implode('.', \OCP\Util::getVersion());
+
+ $i++;
+ }
+
+
+
+ if (empty($apps)) {
+ return false;
+ } else {
+ return $apps;
+ }
+ }
+
+ public static function shouldUpgrade($app) {
+ $versions = self::getAppVersions();
+ $currentVersion = OC_App::getAppVersion($app);
+ if ($currentVersion && isset($versions[$app])) {
+ $installedVersion = $versions[$app];
+ if (version_compare($currentVersion, $installedVersion, '>')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adjust the number of version parts of $version1 to match
+ * the number of version parts of $version2.
+ *
+ * @param string $version1 version to adjust
+ * @param string $version2 version to take the number of parts from
+ * @return string shortened $version1
+ */
+ private static function adjustVersionParts($version1, $version2) {
+ $version1 = explode('.', $version1);
+ $version2 = explode('.', $version2);
+ // reduce $version1 to match the number of parts in $version2
+ while (count($version1) > count($version2)) {
+ array_pop($version1);
+ }
+ // if $version1 does not have enough parts, add some
+ while (count($version1) < count($version2)) {
+ $version1[] = '0';
+ }
+ return implode('.', $version1);
+ }
+
+ /**
+ * Check whether the current ownCloud version matches the given
+ * application's version requirements.
+ *
+ * The comparison is made based on the number of parts that the
+ * app info version has. For example for ownCloud 6.0.3 if the
+ * app info version is expecting version 6.0, the comparison is
+ * made on the first two parts of the ownCloud version.
+ * This means that it's possible to specify "requiremin" => 6
+ * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
+ *
+ * @param string $ocVersion ownCloud version to check against
+ * @param array $appInfo app info (from xml)
+ *
+ * @return boolean true if compatible, otherwise false
+ */
+ public static function isAppCompatible($ocVersion, $appInfo) {
+ $requireMin = '';
+ $requireMax = '';
+ if (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
+ $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
+ } else if (isset($appInfo['requiremin'])) {
+ $requireMin = $appInfo['requiremin'];
+ } else if (isset($appInfo['require'])) {
+ $requireMin = $appInfo['require'];
+ }
+
+ if (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
+ $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
+ } else if (isset($appInfo['requiremax'])) {
+ $requireMax = $appInfo['requiremax'];
+ }
+
+ if (is_array($ocVersion)) {
+ $ocVersion = implode('.', $ocVersion);
+ }
+
+ if (!empty($requireMin)
+ && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
+ ) {
+
+ return false;
+ }
+
+ if (!empty($requireMax)
+ && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * get the installed version of all apps
+ */
+ public static function getAppVersions() {
+ static $versions;
+
+ if(!$versions) {
+ $appConfig = \OC::$server->getAppConfig();
+ $versions = $appConfig->getValues(false, 'installed_version');
+ }
+ return $versions;
+ }
+
+ /**
+ * @param string $app
+ * @return bool
+ * @throws Exception if app is not compatible with this version of ownCloud
+ * @throws Exception if no app-name was specified
+ */
+ public static function installApp($app) {
+ $l = \OC::$server->getL10N('core');
+ $config = \OC::$server->getConfig();
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ $config,
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($app, \OCP\Util::getVersion());
+
+ // check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string
+ if (!is_numeric($app)) {
+ $shippedVersion = self::getAppVersion($app);
+ if ($appData && version_compare($shippedVersion, $appData['version'], '<')) {
+ $app = self::downloadApp($app);
+ } else {
+ $app = OC_Installer::installShippedApp($app);
+ }
+ } else {
+ // Maybe the app is already installed - compare the version in this
+ // case and use the local already installed one.
+ // FIXME: This is a horrible hack. I feel sad. The god of code cleanness may forgive me.
+ $internalAppId = self::getInternalAppIdByOcs($app);
+ if($internalAppId !== false) {
+ if($appData && version_compare(\OC_App::getAppVersion($internalAppId), $appData['version'], '<')) {
+ $app = self::downloadApp($app);
+ } else {
+ self::enable($internalAppId);
+ $app = $internalAppId;
+ }
+ } else {
+ $app = self::downloadApp($app);
+ }
+ }
+
+ if ($app !== false) {
+ // check if the app is compatible with this version of ownCloud
+ $info = self::getAppInfo($app);
+ if(!is_array($info)) {
+ throw new \Exception(
+ $l->t('App "%s" cannot be installed because appinfo file cannot be read.',
+ [$info['name']]
+ )
+ );
+ }
+
+ $version = \OCP\Util::getVersion();
+ if (!self::isAppCompatible($version, $info)) {
+ throw new \Exception(
+ $l->t('App "%s" cannot be installed because it is not compatible with this version of ownCloud.',
+ array($info['name'])
+ )
+ );
+ }
+
+ // check for required dependencies
+ $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
+ $missing = $dependencyAnalyzer->analyze($info);
+ if (!empty($missing)) {
+ $missingMsg = join(PHP_EOL, $missing);
+ throw new \Exception(
+ $l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
+ array($info['name'], $missingMsg)
+ )
+ );
+ }
+
+ $config->setAppValue($app, 'enabled', 'yes');
+ if (isset($appData['id'])) {
+ $config->setAppValue($app, 'ocsid', $appData['id']);
+ }
+ \OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
+ } else {
+ throw new \Exception($l->t("No app name specified"));
+ }
+
+ return $app;
+ }
+
+ /**
+ * update the database for the app and call the update script
+ *
+ * @param string $appId
+ * @return bool
+ */
+ public static function updateApp($appId) {
+ $appPath = self::getAppPath($appId);
+ if($appPath === false) {
+ return false;
+ }
+ $appData = self::getAppInfo($appId);
+ self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
+ if (file_exists($appPath . '/appinfo/database.xml')) {
+ OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
+ }
+ self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
+ self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
+ unset(self::$appVersion[$appId]);
+ // run upgrade code
+ if (file_exists($appPath . '/appinfo/update.php')) {
+ self::loadApp($appId, false);
+ include $appPath . '/appinfo/update.php';
+ }
+
+ //set remote/public handlers
+ if (array_key_exists('ocsid', $appData)) {
+ \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) {
+ \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
+ }
+ foreach ($appData['public'] as $name => $path) {
+ \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
+ }
+
+ self::setAppTypes($appId);
+
+ $version = \OC_App::getAppVersion($appId);
+ \OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version);
+
+ return true;
+ }
+
+ /**
+ * @param string $appId
+ * @param string[] $steps
+ * @throws \OC\NeedsUpdateException
+ */
+ private static function executeRepairSteps($appId, array $steps) {
+ if (empty($steps)) {
+ return;
+ }
+ // load the app
+ self::loadApp($appId, false);
+
+ $dispatcher = OC::$server->getEventDispatcher();
+
+ // load the steps
+ $r = new Repair([], $dispatcher);
+ foreach ($steps as $step) {
+ try {
+ $r->addStep($step);
+ } catch (Exception $ex) {
+ $r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
+ \OC::$server->getLogger()->logException($ex);
+ }
+ }
+ // run the steps
+ $r->run();
+ }
+
+ /**
+ * @param string $appId
+ * @param string[] $steps
+ */
+ private static function setupLiveMigrations($appId, array $steps) {
+ $queue = \OC::$server->getJobList();
+ foreach ($steps as $step) {
+ $queue->add('OC\Migration\BackgroundRepair', [
+ 'app' => $appId,
+ 'step' => $step]);
+ }
+ }
+
+ /**
+ * @param string $appId
+ * @return \OC\Files\View|false
+ */
+ public static function getStorage($appId) {
+ if (OC_App::isEnabled($appId)) { //sanity check
+ if (OC_User::isLoggedIn()) {
+ $view = new \OC\Files\View('/' . OC_User::getUser());
+ if (!$view->file_exists($appId)) {
+ $view->mkdir($appId);
+ }
+ return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
+ } else {
+ \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
+ return false;
+ }
+ } else {
+ \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
+ return false;
+ }
+ }
+
+ /**
+ * parses the app data array and enhanced the 'description' value
+ *
+ * @param array $data the app data
+ * @return array improved app data
+ */
+ public static function parseAppInfo(array $data) {
+
+ // just modify the description if it is available
+ // otherwise this will create a $data element with an empty 'description'
+ if (isset($data['description'])) {
+ if (is_string($data['description'])) {
+ // sometimes the description contains line breaks and they are then also
+ // shown in this way in the app management which isn't wanted as HTML
+ // manages line breaks itself
+
+ // first of all we split on empty lines
+ $paragraphs = preg_split("!\n[[:space:]]*\n!mu", $data['description']);
+
+ $result = [];
+ foreach ($paragraphs as $value) {
+ // replace multiple whitespace (tabs, space, newlines) inside a paragraph
+ // with a single space - also trims whitespace
+ $result[] = trim(preg_replace('![[:space:]]+!mu', ' ', $value));
+ }
+
+ // join the single paragraphs with a empty line in between
+ $data['description'] = implode("\n\n", $result);
+
+ } else {
+ $data['description'] = '';
+ }
+ }
+
+ return $data;
+ }
+}
diff --git a/lib/private/legacy/archive.php b/lib/private/legacy/archive.php
new file mode 100644
index 00000000000..62512d1448a
--- /dev/null
+++ b/lib/private/legacy/archive.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Christopher Schรคpers <kondou@ts.unde.re>
+ * @author Felix Moeller <mail@felixmoeller.de>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+abstract class OC_Archive{
+ /**
+ * Open any of the supported archive types
+ *
+ * @param string $path
+ * @return OC_Archive|void
+ */
+ public static function open($path) {
+ $mime = \OC::$server->getMimeTypeDetector()->detect($path);
+
+ switch($mime) {
+ case 'application/zip':
+ return new OC_Archive_ZIP($path);
+ case 'application/x-gzip':
+ return new OC_Archive_TAR($path);
+ case 'application/x-bzip2':
+ return new OC_Archive_TAR($path);
+ }
+ }
+
+ /**
+ * @param $source
+ */
+ abstract function __construct($source);
+ /**
+ * add an empty folder to the archive
+ * @param string $path
+ * @return bool
+ */
+ abstract function addFolder($path);
+ /**
+ * add a file to the archive
+ * @param string $path
+ * @param string $source either a local file or string data
+ * @return bool
+ */
+ abstract function addFile($path, $source='');
+ /**
+ * rename a file or folder in the archive
+ * @param string $source
+ * @param string $dest
+ * @return bool
+ */
+ abstract function rename($source, $dest);
+ /**
+ * get the uncompressed size of a file in the archive
+ * @param string $path
+ * @return int
+ */
+ abstract function filesize($path);
+ /**
+ * get the last modified time of a file in the archive
+ * @param string $path
+ * @return int
+ */
+ abstract function mtime($path);
+ /**
+ * get the files in a folder
+ * @param string $path
+ * @return array
+ */
+ abstract function getFolder($path);
+ /**
+ * get all files in the archive
+ * @return array
+ */
+ abstract function getFiles();
+ /**
+ * get the content of a file
+ * @param string $path
+ * @return string
+ */
+ abstract function getFile($path);
+ /**
+ * extract a single file from the archive
+ * @param string $path
+ * @param string $dest
+ * @return bool
+ */
+ abstract function extractFile($path, $dest);
+ /**
+ * extract the archive
+ * @param string $dest
+ * @return bool
+ */
+ abstract function extract($dest);
+ /**
+ * check if a file or folder exists in the archive
+ * @param string $path
+ * @return bool
+ */
+ abstract function fileExists($path);
+ /**
+ * remove a file or folder from the archive
+ * @param string $path
+ * @return bool
+ */
+ abstract function remove($path);
+ /**
+ * get a file handler
+ * @param string $path
+ * @param string $mode
+ * @return resource
+ */
+ abstract function getStream($path, $mode);
+ /**
+ * add a folder and all its content
+ * @param string $path
+ * @param string $source
+ * @return boolean|null
+ */
+ function addRecursive($path, $source) {
+ $dh = opendir($source);
+ if(is_resource($dh)) {
+ $this->addFolder($path);
+ while (($file = readdir($dh)) !== false) {
+ if($file=='.' or $file=='..') {
+ continue;
+ }
+ if(is_dir($source.'/'.$file)) {
+ $this->addRecursive($path.'/'.$file, $source.'/'.$file);
+ }else{
+ $this->addFile($path.'/'.$file, $source.'/'.$file);
+ }
+ }
+ }
+ }
+}
diff --git a/lib/private/legacy/db.php b/lib/private/legacy/db.php
new file mode 100644
index 00000000000..5d612312919
--- /dev/null
+++ b/lib/private/legacy/db.php
@@ -0,0 +1,258 @@
+<?php
+/**
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+/**
+ * This class manages the access to the database. It basically is a wrapper for
+ * Doctrine with some adaptions.
+ */
+class OC_DB {
+
+ /**
+ * get MDB2 schema manager
+ *
+ * @return \OC\DB\MDB2SchemaManager
+ */
+ private static function getMDB2SchemaManager() {
+ return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection());
+ }
+
+ /**
+ * Prepare a SQL query
+ * @param string $query Query string
+ * @param int $limit
+ * @param int $offset
+ * @param bool $isManipulation
+ * @throws \OC\DatabaseException
+ * @return OC_DB_StatementWrapper prepared SQL query
+ *
+ * SQL query via Doctrine prepare(), needs to be execute()'d!
+ */
+ static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) {
+ $connection = \OC::$server->getDatabaseConnection();
+
+ if ($isManipulation === null) {
+ //try to guess, so we return the number of rows on manipulations
+ $isManipulation = self::isManipulation($query);
+ }
+
+ // return the result
+ try {
+ $result =$connection->prepare($query, $limit, $offset);
+ } catch (\Doctrine\DBAL\DBALException $e) {
+ throw new \OC\DatabaseException($e->getMessage(), $query);
+ }
+ // differentiate between query and manipulation
+ $result = new OC_DB_StatementWrapper($result, $isManipulation);
+ return $result;
+ }
+
+ /**
+ * tries to guess the type of statement based on the first 10 characters
+ * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements
+ *
+ * @param string $sql
+ * @return bool
+ */
+ static public function isManipulation( $sql ) {
+ $selectOccurrence = stripos($sql, 'SELECT');
+ if ($selectOccurrence !== false && $selectOccurrence < 10) {
+ return false;
+ }
+ $insertOccurrence = stripos($sql, 'INSERT');
+ if ($insertOccurrence !== false && $insertOccurrence < 10) {
+ return true;
+ }
+ $updateOccurrence = stripos($sql, 'UPDATE');
+ if ($updateOccurrence !== false && $updateOccurrence < 10) {
+ return true;
+ }
+ $deleteOccurrence = stripos($sql, 'DELETE');
+ if ($deleteOccurrence !== false && $deleteOccurrence < 10) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * execute a prepared statement, on error write log and throw exception
+ * @param mixed $stmt OC_DB_StatementWrapper,
+ * an array with 'sql' and optionally 'limit' and 'offset' keys
+ * .. or a simple sql query string
+ * @param array $parameters
+ * @return OC_DB_StatementWrapper
+ * @throws \OC\DatabaseException
+ */
+ static public function executeAudited( $stmt, array $parameters = null) {
+ if (is_string($stmt)) {
+ // convert to an array with 'sql'
+ if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT
+ // 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);
+ }
+ $stmt = array('sql' => $stmt, 'limit' => null, 'offset' => null);
+ }
+ if (is_array($stmt)) {
+ // convert to prepared statement
+ if ( ! array_key_exists('sql', $stmt) ) {
+ $message = 'statement array must at least contain key \'sql\'';
+ throw new \OC\DatabaseException($message);
+ }
+ if ( ! array_key_exists('limit', $stmt) ) {
+ $stmt['limit'] = null;
+ }
+ if ( ! array_key_exists('limit', $stmt) ) {
+ $stmt['offset'] = null;
+ }
+ $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']);
+ }
+ self::raiseExceptionOnError($stmt, 'Could not prepare statement');
+ if ($stmt instanceof OC_DB_StatementWrapper) {
+ $result = $stmt->execute($parameters);
+ self::raiseExceptionOnError($result, 'Could not execute statement');
+ } else {
+ if (is_object($stmt)) {
+ $message = 'Expected a prepared statement or array got ' . get_class($stmt);
+ } else {
+ $message = 'Expected a prepared statement or array got ' . gettype($stmt);
+ }
+ throw new \OC\DatabaseException($message);
+ }
+ return $result;
+ }
+
+ /**
+ * saves database schema to xml file
+ * @param string $file name of file
+ * @param int $mode
+ * @return bool
+ *
+ * TODO: write more documentation
+ */
+ public static function getDbStructure($file) {
+ $schemaManager = self::getMDB2SchemaManager();
+ return $schemaManager->getDbStructure($file);
+ }
+
+ /**
+ * Creates tables from XML file
+ * @param string $file file to read structure from
+ * @return bool
+ *
+ * TODO: write more documentation
+ */
+ public static function createDbFromStructure( $file ) {
+ $schemaManager = self::getMDB2SchemaManager();
+ $result = $schemaManager->createDbFromStructure($file);
+ return $result;
+ }
+
+ /**
+ * update the database schema
+ * @param string $file file to read structure from
+ * @throws Exception
+ * @return string|boolean
+ */
+ public static function updateDbFromStructure($file) {
+ $schemaManager = self::getMDB2SchemaManager();
+ try {
+ $result = $schemaManager->updateDbFromStructure($file);
+ } catch (Exception $e) {
+ \OCP\Util::writeLog('core', 'Failed to update database structure ('.$e.')', \OCP\Util::FATAL);
+ throw $e;
+ }
+ return $result;
+ }
+
+ /**
+ * simulate the database schema update
+ * @param string $file file to read structure from
+ * @throws Exception
+ * @return string|boolean
+ */
+ public static function simulateUpdateDbFromStructure($file) {
+ $schemaManager = self::getMDB2SchemaManager();
+ try {
+ $result = $schemaManager->simulateUpdateDbFromStructure($file);
+ } catch (Exception $e) {
+ \OCP\Util::writeLog('core', 'Simulated database structure update failed ('.$e.')', \OCP\Util::FATAL);
+ throw $e;
+ }
+ return $result;
+ }
+
+ /**
+ * remove all tables defined in a database structure xml file
+ * @param string $file the xml file describing the tables
+ */
+ public static function removeDBStructure($file) {
+ $schemaManager = self::getMDB2SchemaManager();
+ $schemaManager->removeDBStructure($file);
+ }
+
+ /**
+ * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException
+ * @param mixed $result
+ * @param string $message
+ * @return void
+ * @throws \OC\DatabaseException
+ */
+ public static function raiseExceptionOnError($result, $message = null) {
+ if($result === false) {
+ if ($message === null) {
+ $message = self::getErrorMessage();
+ } else {
+ $message .= ', Root cause:' . self::getErrorMessage();
+ }
+ throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode());
+ }
+ }
+
+ /**
+ * returns the error code and message as a string for logging
+ * works with DoctrineException
+ * @return string
+ */
+ public static function getErrorMessage() {
+ $connection = \OC::$server->getDatabaseConnection();
+ return $connection->getError();
+ }
+
+ /**
+ * Checks if a table exists in the database - the database prefix will be prepended
+ *
+ * @param string $table
+ * @return bool
+ * @throws \OC\DatabaseException
+ */
+ public static function tableExists($table) {
+ $connection = \OC::$server->getDatabaseConnection();
+ return $connection->tableExists($table);
+ }
+}
diff --git a/lib/private/legacy/defaults.php b/lib/private/legacy/defaults.php
new file mode 100644
index 00000000000..43e8c8082cc
--- /dev/null
+++ b/lib/private/legacy/defaults.php
@@ -0,0 +1,286 @@
+<?php
+/**
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Pascal de Bruijn <pmjdebruijn@pcode.nl>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author scolebrook <scolebrook@mac.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Volkan Gezer <volkangezer@gmail.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+class OC_Defaults {
+
+ private $theme;
+ private $l;
+
+ private $defaultEntity;
+ private $defaultName;
+ private $defaultTitle;
+ private $defaultBaseUrl;
+ private $defaultSyncClientUrl;
+ private $defaultiOSClientUrl;
+ private $defaultiTunesAppId;
+ private $defaultAndroidClientUrl;
+ private $defaultDocBaseUrl;
+ private $defaultDocVersion;
+ private $defaultSlogan;
+ private $defaultLogoClaim;
+ private $defaultMailHeaderColor;
+
+ function __construct() {
+ $this->l = \OC::$server->getL10N('lib');
+ $version = \OCP\Util::getVersion();
+
+ $this->defaultEntity = 'ownCloud'; /* e.g. company name, used for footers and copyright notices */
+ $this->defaultName = 'ownCloud'; /* short name, used when referring to the software */
+ $this->defaultTitle = 'ownCloud'; /* can be a longer name, for titles */
+ $this->defaultBaseUrl = 'https://owncloud.org';
+ $this->defaultSyncClientUrl = 'https://owncloud.org/sync-clients/';
+ $this->defaultiOSClientUrl = 'https://itunes.apple.com/us/app/owncloud/id543672169?mt=8';
+ $this->defaultiTunesAppId = '543672169';
+ $this->defaultAndroidClientUrl = 'https://play.google.com/store/apps/details?id=com.owncloud.android';
+ $this->defaultDocBaseUrl = 'https://doc.owncloud.org';
+ $this->defaultDocVersion = $version[0] . '.' . $version[1]; // used to generate doc links
+ $this->defaultSlogan = $this->l->t('web services under your control');
+ $this->defaultLogoClaim = '';
+ $this->defaultMailHeaderColor = '#1d2d44'; /* header color of mail notifications */
+
+ $themePath = OC::$SERVERROOT . '/themes/' . OC_Util::getTheme() . '/defaults.php';
+ if (file_exists($themePath)) {
+ // prevent defaults.php from printing output
+ ob_start();
+ require_once $themePath;
+ ob_end_clean();
+ if (class_exists('OC_Theme')) {
+ $this->theme = new OC_Theme();
+ }
+ }
+ }
+
+ /**
+ * @param string $method
+ */
+ private function themeExist($method) {
+ if (isset($this->theme) && method_exists($this->theme, $method)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the base URL
+ * @return string URL
+ */
+ public function getBaseUrl() {
+ if ($this->themeExist('getBaseUrl')) {
+ return $this->theme->getBaseUrl();
+ } else {
+ return $this->defaultBaseUrl;
+ }
+ }
+
+ /**
+ * Returns the URL where the sync clients are listed
+ * @return string URL
+ */
+ public function getSyncClientUrl() {
+ if ($this->themeExist('getSyncClientUrl')) {
+ return $this->theme->getSyncClientUrl();
+ } else {
+ return $this->defaultSyncClientUrl;
+ }
+ }
+
+ /**
+ * Returns the URL to the App Store for the iOS Client
+ * @return string URL
+ */
+ public function getiOSClientUrl() {
+ if ($this->themeExist('getiOSClientUrl')) {
+ return $this->theme->getiOSClientUrl();
+ } else {
+ return $this->defaultiOSClientUrl;
+ }
+ }
+
+ /**
+ * Returns the AppId for the App Store for the iOS Client
+ * @return string AppId
+ */
+ public function getiTunesAppId() {
+ if ($this->themeExist('getiTunesAppId')) {
+ return $this->theme->getiTunesAppId();
+ } else {
+ return $this->defaultiTunesAppId;
+ }
+ }
+
+ /**
+ * Returns the URL to Google Play for the Android Client
+ * @return string URL
+ */
+ public function getAndroidClientUrl() {
+ if ($this->themeExist('getAndroidClientUrl')) {
+ return $this->theme->getAndroidClientUrl();
+ } else {
+ return $this->defaultAndroidClientUrl;
+ }
+ }
+
+ /**
+ * Returns the documentation URL
+ * @return string URL
+ */
+ public function getDocBaseUrl() {
+ if ($this->themeExist('getDocBaseUrl')) {
+ return $this->theme->getDocBaseUrl();
+ } else {
+ return $this->defaultDocBaseUrl;
+ }
+ }
+
+ /**
+ * Returns the title
+ * @return string title
+ */
+ public function getTitle() {
+ if ($this->themeExist('getTitle')) {
+ return $this->theme->getTitle();
+ } else {
+ return $this->defaultTitle;
+ }
+ }
+
+ /**
+ * Returns the short name of the software
+ * @return string title
+ */
+ public function getName() {
+ if ($this->themeExist('getName')) {
+ return $this->theme->getName();
+ } else {
+ return $this->defaultName;
+ }
+ }
+
+ /**
+ * Returns the short name of the software containing HTML strings
+ * @return string title
+ */
+ public function getHTMLName() {
+ if ($this->themeExist('getHTMLName')) {
+ return $this->theme->getHTMLName();
+ } else {
+ return $this->defaultName;
+ }
+ }
+
+ /**
+ * Returns entity (e.g. company name) - used for footer, copyright
+ * @return string entity name
+ */
+ public function getEntity() {
+ if ($this->themeExist('getEntity')) {
+ return $this->theme->getEntity();
+ } else {
+ return $this->defaultEntity;
+ }
+ }
+
+ /**
+ * Returns slogan
+ * @return string slogan
+ */
+ public function getSlogan() {
+ if ($this->themeExist('getSlogan')) {
+ return $this->theme->getSlogan();
+ } else {
+ return $this->defaultSlogan;
+ }
+ }
+
+ /**
+ * Returns logo claim
+ * @return string logo claim
+ */
+ public function getLogoClaim() {
+ if ($this->themeExist('getLogoClaim')) {
+ return $this->theme->getLogoClaim();
+ } else {
+ return $this->defaultLogoClaim;
+ }
+ }
+
+ /**
+ * Returns short version of the footer
+ * @return string short footer
+ */
+ public function getShortFooter() {
+ if ($this->themeExist('getShortFooter')) {
+ $footer = $this->theme->getShortFooter();
+ } else {
+ $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' .
+ ' rel="noreferrer">' .$this->getEntity() . '</a>'.
+ ' โ€“ ' . $this->getSlogan();
+ }
+
+ return $footer;
+ }
+
+ /**
+ * Returns long version of the footer
+ * @return string long footer
+ */
+ public function getLongFooter() {
+ if ($this->themeExist('getLongFooter')) {
+ $footer = $this->theme->getLongFooter();
+ } else {
+ $footer = $this->getShortFooter();
+ }
+
+ return $footer;
+ }
+
+ /**
+ * @param string $key
+ */
+ public function buildDocLinkToKey($key) {
+ if ($this->themeExist('buildDocLinkToKey')) {
+ return $this->theme->buildDocLinkToKey($key);
+ }
+ return $this->getDocBaseUrl() . '/server/' . $this->defaultDocVersion . '/go.php?to=' . $key;
+ }
+
+ /**
+ * Returns mail header color
+ * @return string
+ */
+ public function getMailHeaderColor() {
+ if ($this->themeExist('getMailHeaderColor')) {
+ return $this->theme->getMailHeaderColor();
+ } else {
+ return $this->defaultMailHeaderColor;
+ }
+ }
+
+}
diff --git a/lib/private/legacy/eventsource.php b/lib/private/legacy/eventsource.php
new file mode 100644
index 00000000000..f567d1e6ca5
--- /dev/null
+++ b/lib/private/legacy/eventsource.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Felix Moeller <mail@felixmoeller.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+/**
+ * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events)
+ * includes a fallback for older browsers and IE
+ *
+ * use server side events with caution, to many open requests can hang the server
+ */
+class OC_EventSource implements \OCP\IEventSource {
+ /**
+ * @var bool
+ */
+ private $fallback;
+
+ /**
+ * @var int
+ */
+ private $fallBackId = 0;
+
+ /**
+ * @var bool
+ */
+ private $started = false;
+
+ protected function init() {
+ if ($this->started) {
+ return;
+ }
+ $this->started = true;
+
+ // prevent php output buffering, caching and nginx buffering
+ OC_Util::obEnd();
+ header('Cache-Control: no-cache');
+ header('X-Accel-Buffering: no');
+ $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true';
+ if ($this->fallback) {
+ $this->fallBackId = (int)$_GET['fallback_id'];
+ /**
+ * FIXME: The default content-security-policy of ownCloud forbids inline
+ * JavaScript for security reasons. IE starting on Windows 10 will
+ * however also obey the CSP which will break the event source fallback.
+ *
+ * As a workaround thus we set a custom policy which allows the execution
+ * of inline JavaScript.
+ *
+ * @link https://github.com/owncloud/core/issues/14286
+ */
+ header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'");
+ header("Content-Type: text/html");
+ echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy
+ } else {
+ header("Content-Type: text/event-stream");
+ }
+ if (!(\OC::$server->getRequest()->passesCSRFCheck())) {
+ $this->send('error', 'Possible CSRF attack. Connection will be closed.');
+ $this->close();
+ exit();
+ }
+ flush();
+ }
+
+ /**
+ * send a message to the client
+ *
+ * @param string $type
+ * @param mixed $data
+ *
+ * @throws \BadMethodCallException
+ * if only one parameter is given, a typeless message will be send with that parameter as data
+ */
+ public function send($type, $data = null) {
+ if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) {
+ throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')');
+ }
+ $this->init();
+ if (is_null($data)) {
+ $data = $type;
+ $type = null;
+ }
+ if ($this->fallback) {
+ $response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
+ . $this->fallBackId . ',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL;
+ echo $response;
+ } else {
+ if ($type) {
+ echo 'event: ' . $type . PHP_EOL;
+ }
+ echo 'data: ' . OCP\JSON::encode($data) . PHP_EOL;
+ }
+ echo PHP_EOL;
+ flush();
+ }
+
+ /**
+ * close the connection of the event source
+ */
+ public function close() {
+ $this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it
+ }
+}
diff --git a/lib/private/legacy/filechunking.php b/lib/private/legacy/filechunking.php
new file mode 100644
index 00000000000..f2cef275458
--- /dev/null
+++ b/lib/private/legacy/filechunking.php
@@ -0,0 +1,175 @@
+<?php
+/**
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Felix Moeller <mail@felixmoeller.de>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Thomas Tanghus <thomas@tanghus.net>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+
+class OC_FileChunking {
+ protected $info;
+ protected $cache;
+
+ static public function decodeName($name) {
+ preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches);
+ return $matches;
+ }
+
+ /**
+ * @param string[] $info
+ */
+ public function __construct($info) {
+ $this->info = $info;
+ }
+
+ public function getPrefix() {
+ $name = $this->info['name'];
+ $transferid = $this->info['transferid'];
+
+ return $name.'-chunking-'.$transferid.'-';
+ }
+
+ protected function getCache() {
+ if (!isset($this->cache)) {
+ $this->cache = new \OC\Cache\File();
+ }
+ return $this->cache;
+ }
+
+ /**
+ * Stores the given $data under the given $key - the number of stored bytes is returned
+ *
+ * @param string $index
+ * @param resource $data
+ * @return int
+ */
+ public function store($index, $data) {
+ $cache = $this->getCache();
+ $name = $this->getPrefix().$index;
+ $cache->set($name, $data);
+
+ return $cache->size($name);
+ }
+
+ public function isComplete() {
+ $prefix = $this->getPrefix();
+ $cache = $this->getCache();
+ $chunkcount = (int)$this->info['chunkcount'];
+
+ for($i=($chunkcount-1); $i >= 0; $i--) {
+ if (!$cache->hasKey($prefix.$i)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Assembles the chunks into the file specified by the path.
+ * Chunks are deleted afterwards.
+ *
+ * @param resource $f target path
+ *
+ * @return integer assembled file size
+ *
+ * @throws \OC\InsufficientStorageException when file could not be fully
+ * assembled due to lack of free space
+ */
+ public function assemble($f) {
+ $cache = $this->getCache();
+ $prefix = $this->getPrefix();
+ $count = 0;
+ for ($i = 0; $i < $this->info['chunkcount']; $i++) {
+ $chunk = $cache->get($prefix.$i);
+ // remove after reading to directly save space
+ $cache->remove($prefix.$i);
+ $count += fwrite($f, $chunk);
+ // let php release the memory to work around memory exhausted error with php 5.6
+ $chunk = null;
+ }
+
+ return $count;
+ }
+
+ /**
+ * Returns the size of the chunks already present
+ * @return integer size in bytes
+ */
+ public function getCurrentSize() {
+ $cache = $this->getCache();
+ $prefix = $this->getPrefix();
+ $total = 0;
+ for ($i = 0; $i < $this->info['chunkcount']; $i++) {
+ $total += $cache->size($prefix.$i);
+ }
+ return $total;
+ }
+
+ /**
+ * Removes all chunks which belong to this transmission
+ */
+ public function cleanup() {
+ $cache = $this->getCache();
+ $prefix = $this->getPrefix();
+ for($i=0; $i < $this->info['chunkcount']; $i++) {
+ $cache->remove($prefix.$i);
+ }
+ }
+
+ /**
+ * Removes one specific chunk
+ * @param string $index
+ */
+ public function remove($index) {
+ $cache = $this->getCache();
+ $prefix = $this->getPrefix();
+ $cache->remove($prefix.$index);
+ }
+
+ /**
+ * Assembles the chunks into the file specified by the path.
+ * Also triggers the relevant hooks and proxies.
+ *
+ * @param \OC\Files\Storage\Storage $storage storage
+ * @param string $path target path relative to the storage
+ * @return bool true on success or false if file could not be created
+ *
+ * @throws \OC\ServerNotAvailableException
+ */
+ public function file_assemble($storage, $path) {
+ // use file_put_contents as method because that best matches what this function does
+ if (\OC\Files\Filesystem::isValidPath($path)) {
+ $target = $storage->fopen($path, 'w');
+ if ($target) {
+ $count = $this->assemble($target);
+ fclose($target);
+ return $count > 0;
+ } else {
+ return false;
+ }
+ }
+ return false;
+ }
+}
diff --git a/lib/private/legacy/files.php b/lib/private/legacy/files.php
new file mode 100644
index 00000000000..9b6a1a4465f
--- /dev/null
+++ b/lib/private/legacy/files.php
@@ -0,0 +1,316 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Frank Karlitschek <frank@owncloud.org>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Michael Gapczynski <GapczynskiM@gmail.com>
+ * @author Nicolai Ehemann <en@enlightened.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Thibaut GRIDEL <tgridel@free.fr>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Victor Dubiniuk <dubiniuk@owncloud.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+use OC\Files\View;
+use OC\Streamer;
+use OCP\Lock\ILockingProvider;
+
+/**
+ * Class for file server access
+ *
+ */
+class OC_Files {
+ const FILE = 1;
+ const ZIP_FILES = 2;
+ const ZIP_DIR = 3;
+
+ const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB
+
+ /**
+ * @param string $filename
+ * @param string $name
+ */
+ private static function sendHeaders($filename, $name) {
+ OC_Response::setContentDispositionHeader($name, 'attachment');
+ header('Content-Transfer-Encoding: binary');
+ OC_Response::disableCaching();
+ $fileSize = \OC\Files\Filesystem::filesize($filename);
+ $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
+ header('Content-Type: '.$type);
+ if ($fileSize > -1) {
+ OC_Response::setContentLengthHeader($fileSize);
+ }
+ }
+
+ /**
+ * return the content of a file or return a zip file containing multiple files
+ *
+ * @param string $dir
+ * @param string $files ; separated list of files to download
+ * @param boolean $onlyHeader ; boolean to only send header of the request
+ */
+ public static function get($dir, $files, $onlyHeader = false) {
+
+ $view = \OC\Files\Filesystem::getView();
+ $getType = self::FILE;
+ $filename = $dir;
+ try {
+
+ if (is_array($files) && count($files) === 1) {
+ $files = $files[0];
+ }
+
+ if (!is_array($files)) {
+ $filename = $dir . '/' . $files;
+ if (!$view->is_dir($filename)) {
+ self::getSingleFile($view, $dir, $files, $onlyHeader);
+ return;
+ }
+ }
+
+ $name = 'download';
+ if (is_array($files)) {
+ $getType = self::ZIP_FILES;
+ $basename = basename($dir);
+ if ($basename) {
+ $name = $basename;
+ }
+
+ $filename = $dir . '/' . $name;
+ } else {
+ $filename = $dir . '/' . $files;
+ $getType = self::ZIP_DIR;
+ // downloading root ?
+ if ($files !== '') {
+ $name = $files;
+ }
+ }
+
+ $streamer = new Streamer();
+ OC_Util::obEnd();
+
+ self::lockFiles($view, $dir, $files);
+
+ $streamer->sendHeaders($name);
+ $executionTime = intval(OC::$server->getIniWrapper()->getNumeric('max_execution_time'));
+ set_time_limit(0);
+ if ($getType === self::ZIP_FILES) {
+ foreach ($files as $file) {
+ $file = $dir . '/' . $file;
+ if (\OC\Files\Filesystem::is_file($file)) {
+ $fileSize = \OC\Files\Filesystem::filesize($file);
+ $fh = \OC\Files\Filesystem::fopen($file, 'r');
+ $streamer->addFileFromStream($fh, basename($file), $fileSize);
+ fclose($fh);
+ } elseif (\OC\Files\Filesystem::is_dir($file)) {
+ $streamer->addDirRecursive($file);
+ }
+ }
+ } elseif ($getType === self::ZIP_DIR) {
+ $file = $dir . '/' . $files;
+ $streamer->addDirRecursive($file);
+ }
+ $streamer->finalize();
+ set_time_limit($executionTime);
+ self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
+ } catch (\OCP\Lock\LockedException $ex) {
+ self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
+ OC::$server->getLogger()->logException($ex);
+ $l = \OC::$server->getL10N('core');
+ $hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
+ \OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint);
+ } catch (\OCP\Files\ForbiddenException $ex) {
+ self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
+ OC::$server->getLogger()->logException($ex);
+ $l = \OC::$server->getL10N('core');
+ \OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage());
+ } catch (\Exception $ex) {
+ self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
+ OC::$server->getLogger()->logException($ex);
+ $l = \OC::$server->getL10N('core');
+ $hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
+ \OC_Template::printErrorPage($l->t('Can\'t read file'), $hint);
+ }
+ }
+
+ /**
+ * @param View $view
+ * @param string $name
+ * @param string $dir
+ * @param boolean $onlyHeader
+ */
+ private static function getSingleFile($view, $dir, $name, $onlyHeader) {
+ $filename = $dir . '/' . $name;
+ OC_Util::obEnd();
+ $view->lockFile($filename, ILockingProvider::LOCK_SHARED);
+
+ if (\OC\Files\Filesystem::isReadable($filename)) {
+ self::sendHeaders($filename, $name);
+ } elseif (!\OC\Files\Filesystem::file_exists($filename)) {
+ header("HTTP/1.0 404 Not Found");
+ $tmpl = new OC_Template('', '404', 'guest');
+ $tmpl->printPage();
+ exit();
+ } else {
+ header("HTTP/1.0 403 Forbidden");
+ die('403 Forbidden');
+ }
+ if ($onlyHeader) {
+ return;
+ }
+ $view->readfile($filename);
+ }
+
+ /**
+ * @param View $view
+ * @param string $dir
+ * @param string[]|string $files
+ */
+ public static function lockFiles($view, $dir, $files) {
+ if (!is_array($files)) {
+ $file = $dir . '/' . $files;
+ $files = [$file];
+ }
+ foreach ($files as $file) {
+ $file = $dir . '/' . $file;
+ $view->lockFile($file, ILockingProvider::LOCK_SHARED);
+ if ($view->is_dir($file)) {
+ $contents = $view->getDirectoryContent($file);
+ $contents = array_map(function($fileInfo) use ($file) {
+ /** @var \OCP\Files\FileInfo $fileInfo */
+ return $file . '/' . $fileInfo->getName();
+ }, $contents);
+ self::lockFiles($view, $dir, $contents);
+ }
+ }
+ }
+
+ /**
+ * 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
+ */
+ public static function setUploadLimit($size, $files = []) {
+ //don't allow user to break his config
+ $size = intval($size);
+ if ($size < self::UPLOAD_MIN_LIMIT_BYTES) {
+ return false;
+ }
+ $size = OC_Helper::phpFileSize($size);
+
+ $phpValueKeys = array(
+ 'upload_max_filesize',
+ 'post_max_size'
+ );
+
+ // 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
+ }
+
+ $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, 2, $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);
+ }
+
+ if ($success) {
+ return OC_Helper::computerFileSize($size);
+ }
+ return false;
+ }
+
+ /**
+ * @param string $dir
+ * @param $files
+ * @param integer $getType
+ * @param View $view
+ * @param string $filename
+ */
+ private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) {
+ if ($getType === self::FILE) {
+ $view->unlockFile($filename, ILockingProvider::LOCK_SHARED);
+ }
+ if ($getType === self::ZIP_FILES) {
+ foreach ($files as $file) {
+ $file = $dir . '/' . $file;
+ $view->unlockFile($file, ILockingProvider::LOCK_SHARED);
+ }
+ }
+ if ($getType === self::ZIP_DIR) {
+ $file = $dir . '/' . $files;
+ $view->unlockFile($file, ILockingProvider::LOCK_SHARED);
+ }
+ }
+
+}
diff --git a/lib/private/legacy/group.php b/lib/private/legacy/group.php
new file mode 100644
index 00000000000..f1b84069a38
--- /dev/null
+++ b/lib/private/legacy/group.php
@@ -0,0 +1,300 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Georg Ehrke <georg@owncloud.com>
+ * @author goodkiller <markopraakli@gmail.com>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author macjohnny <estebanmarin@gmx.ch>
+ * @author Michael Gapczynski <GapczynskiM@gmail.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Qingping Hou <dave2008713@gmail.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+/**
+ * This class provides all methods needed for managing groups.
+ *
+ * Note that &run is deprecated and won't work anymore.
+ * Hooks provided:
+ * pre_createGroup(&run, gid)
+ * post_createGroup(gid)
+ * pre_deleteGroup(&run, gid)
+ * post_deleteGroup(gid)
+ * pre_addToGroup(&run, uid, gid)
+ * post_addToGroup(uid, gid)
+ * pre_removeFromGroup(&run, uid, gid)
+ * post_removeFromGroup(uid, gid)
+ */
+class OC_Group {
+
+ /**
+ * @return \OC\Group\Manager
+ * @deprecated Use \OC::$server->getGroupManager();
+ */
+ public static function getManager() {
+ return \OC::$server->getGroupManager();
+ }
+
+ /**
+ * @return \OC\User\Manager
+ * @deprecated Use \OC::$server->getUserManager()
+ */
+ private static function getUserManager() {
+ return \OC::$server->getUserManager();
+ }
+
+ /**
+ * set the group backend
+ * @param \OC_Group_Backend $backend The backend to use for user management
+ * @return bool
+ */
+ public static function useBackend($backend) {
+ self::getManager()->addBackend($backend);
+ return true;
+ }
+
+ /**
+ * remove all used backends
+ */
+ public static function clearBackends() {
+ self::getManager()->clearBackends();
+ }
+
+ /**
+ * Try to create a new group
+ * @param string $gid The name of the group to create
+ * @return bool
+ *
+ * Tries to create a new group. If the group name already exists, false will
+ * be returned. Basic checking of Group name
+ * @deprecated Use \OC::$server->getGroupManager()->createGroup() instead
+ */
+ public static function createGroup($gid) {
+ if (self::getManager()->createGroup($gid)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * delete a group
+ * @param string $gid gid of the group to delete
+ * @return bool
+ *
+ * Deletes a group and removes it from the group_user-table
+ * @deprecated Use \OC::$server->getGroupManager()->delete() instead
+ */
+ public static function deleteGroup($gid) {
+ $group = self::getManager()->get($gid);
+ if ($group) {
+ if ($group->delete()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * is user in group?
+ * @param string $uid uid of the user
+ * @param string $gid gid of the group
+ * @return bool
+ *
+ * Checks whether the user is member of a group or not.
+ * @deprecated Use \OC::$server->getGroupManager->inGroup($user);
+ */
+ public static function inGroup($uid, $gid) {
+ $group = self::getManager()->get($gid);
+ $user = self::getUserManager()->get($uid);
+ if ($group and $user) {
+ return $group->inGroup($user);
+ }
+ return false;
+ }
+
+ /**
+ * Add a user to a group
+ * @param string $uid Name of the user to add to group
+ * @param string $gid Name of the group in which add the user
+ * @return bool
+ *
+ * Adds a user to a group.
+ * @deprecated Use \OC::$server->getGroupManager->addUser();
+ */
+ public static function addToGroup($uid, $gid) {
+ $group = self::getManager()->get($gid);
+ $user = self::getUserManager()->get($uid);
+ if ($group and $user) {
+ $group->addUser($user);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Removes a user from a group
+ * @param string $uid Name of the user to remove from group
+ * @param string $gid Name of the group from which remove the user
+ * @return bool
+ *
+ * removes the user from a group.
+ */
+ public static function removeFromGroup($uid, $gid) {
+ $group = self::getManager()->get($gid);
+ $user = self::getUserManager()->get($uid);
+ if ($group and $user) {
+ OC_Hook::emit("OC_Group", "pre_removeFromGroup", array("run" => true, "uid" => $uid, "gid" => $gid));
+ $group->removeUser($user);
+ OC_Hook::emit("OC_User", "post_removeFromGroup", array("uid" => $uid, "gid" => $gid));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get all groups a user belongs to
+ * @param string $uid Name of the user
+ * @return array an array of group names
+ *
+ * This function fetches all groups a user belongs to. It does not check
+ * if the user exists at all.
+ * @deprecated Use \OC::$server->getGroupManager->getUserGroupIds($user)
+ */
+ public static function getUserGroups($uid) {
+ $user = self::getUserManager()->get($uid);
+ if ($user) {
+ return self::getManager()->getUserGroupIds($user);
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * get a list of all groups
+ * @param string $search
+ * @param int|null $limit
+ * @param int|null $offset
+ * @return array an array of group names
+ *
+ * Returns a list with all groups
+ */
+ public static function getGroups($search = '', $limit = null, $offset = null) {
+ $groups = self::getManager()->search($search, $limit, $offset);
+ $groupIds = array();
+ foreach ($groups as $group) {
+ $groupIds[] = $group->getGID();
+ }
+ return $groupIds;
+ }
+
+ /**
+ * check if a group exists
+ *
+ * @param string $gid
+ * @return bool
+ * @deprecated Use \OC::$server->getGroupManager->groupExists($gid)
+ */
+ public static function groupExists($gid) {
+ return self::getManager()->groupExists($gid);
+ }
+
+ /**
+ * get a list of all users in a group
+ * @param string $gid
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return array an array of user ids
+ */
+ public static function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
+ $group = self::getManager()->get($gid);
+ if ($group) {
+ $users = $group->searchUsers($search, $limit, $offset);
+ $userIds = array();
+ foreach ($users as $user) {
+ $userIds[] = $user->getUID();
+ }
+ return $userIds;
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * get a list of all users in several groups
+ * @param string[] $gids
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return array an array of user ids
+ */
+ public static function usersInGroups($gids, $search = '', $limit = -1, $offset = 0) {
+ $users = array();
+ foreach ($gids as $gid) {
+ // TODO Need to apply limits to groups as total
+ $users = array_merge(array_diff(self::usersInGroup($gid, $search, $limit, $offset), $users), $users);
+ }
+ return $users;
+ }
+
+ /**
+ * get a list of all display names in a group
+ * @param string $gid
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return array an array of display names (value) and user ids(key)
+ * @deprecated Use \OC::$server->getGroupManager->displayNamesInGroup($gid, $search, $limit, $offset)
+ */
+ public static function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) {
+ return self::getManager()->displayNamesInGroup($gid, $search, $limit, $offset);
+ }
+
+ /**
+ * get a list of all display names in several groups
+ * @param array $gids
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return array an array of display names (Key) user ids (value)
+ */
+ public static function displayNamesInGroups($gids, $search = '', $limit = -1, $offset = 0) {
+ $displayNames = array();
+ foreach ($gids as $gid) {
+ // TODO Need to apply limits to groups as total
+ $diff = array_diff(
+ self::displayNamesInGroup($gid, $search, $limit, $offset),
+ $displayNames
+ );
+ if ($diff) {
+ // A fix for LDAP users. array_merge loses keys...
+ $displayNames = $diff + $displayNames;
+ }
+ }
+ return $displayNames;
+ }
+}
diff --git a/lib/private/legacy/helper.php b/lib/private/legacy/helper.php
new file mode 100644
index 00000000000..e6aaed0fd15
--- /dev/null
+++ b/lib/private/legacy/helper.php
@@ -0,0 +1,703 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Christopher Schรคpers <kondou@ts.unde.re>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Fabian Henze <flyser42@gmx.de>
+ * @author Felix Moeller <mail@felixmoeller.de>
+ * @author Georg Ehrke <georg@owncloud.com>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Michael Gapczynski <GapczynskiM@gmail.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Olivier Paroz <github@oparoz.com>
+ * @author Pellaeon Lin <nfsmwlin@gmail.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author Simon Kรถnnecke <simonkoennecke@gmail.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Thomas Tanghus <thomas@tanghus.net>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+use Symfony\Component\Process\ExecutableFinder;
+
+/**
+ * Collection of useful functions
+ */
+class OC_Helper {
+ private static $templateManager;
+
+ /**
+ * Creates an absolute url for public use
+ * @param string $service id
+ * @param bool $add_slash
+ * @return string the url
+ *
+ * Returns a absolute url to the given service.
+ */
+ public static function linkToPublic($service, $add_slash = false) {
+ if ($service === 'files') {
+ $url = OC::$server->getURLGenerator()->getAbsoluteURL('/s');
+ } else {
+ $url = OC::$server->getURLGenerator()->getAbsoluteURL(OC::$server->getURLGenerator()->linkTo('', 'public.php').'?service='.$service);
+ }
+ return $url . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : '');
+ }
+
+ /**
+ * get path to preview of file
+ * @param string $path path
+ * @return string the url
+ *
+ * Returns the path to the preview of the file.
+ */
+ public static function previewIcon($path) {
+ return \OC::$server->getURLGenerator()->linkToRoute('core_ajax_preview', ['x' => 32, 'y' => 32, 'file' => $path]);
+ }
+
+ public static function publicPreviewIcon( $path, $token ) {
+ return \OC::$server->getURLGenerator()->linkToRoute('core_ajax_public_preview', ['x' => 32, 'y' => 32, 'file' => $path, 't' => $token]);
+ }
+
+ /**
+ * Make a human file size
+ * @param int $bytes file size in bytes
+ * @return string a human readable file size
+ *
+ * Makes 2048 to 2 kB.
+ */
+ public static function humanFileSize($bytes) {
+ if ($bytes < 0) {
+ return "?";
+ }
+ if ($bytes < 1024) {
+ return "$bytes B";
+ }
+ $bytes = round($bytes / 1024, 0);
+ if ($bytes < 1024) {
+ return "$bytes KB";
+ }
+ $bytes = round($bytes / 1024, 1);
+ if ($bytes < 1024) {
+ return "$bytes MB";
+ }
+ $bytes = round($bytes / 1024, 1);
+ if ($bytes < 1024) {
+ return "$bytes GB";
+ }
+ $bytes = round($bytes / 1024, 1);
+ if ($bytes < 1024) {
+ return "$bytes TB";
+ }
+
+ $bytes = round($bytes / 1024, 1);
+ return "$bytes PB";
+ }
+
+ /**
+ * Make a php file size
+ * @param int $bytes file size in bytes
+ * @return string a php parseable file size
+ *
+ * Makes 2048 to 2k and 2^41 to 2048G
+ */
+ public static function phpFileSize($bytes) {
+ if ($bytes < 0) {
+ return "?";
+ }
+ if ($bytes < 1024) {
+ return $bytes . "B";
+ }
+ $bytes = round($bytes / 1024, 1);
+ if ($bytes < 1024) {
+ return $bytes . "K";
+ }
+ $bytes = round($bytes / 1024, 1);
+ if ($bytes < 1024) {
+ return $bytes . "M";
+ }
+ $bytes = round($bytes / 1024, 1);
+ return $bytes . "G";
+ }
+
+ /**
+ * Make a computer file size
+ * @param string $str file size in human readable format
+ * @return float a file size in bytes
+ *
+ * Makes 2kB to 2048.
+ *
+ * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418
+ */
+ public static function computerFileSize($str) {
+ $str = strtolower($str);
+ if (is_numeric($str)) {
+ return floatval($str);
+ }
+
+ $bytes_array = array(
+ 'b' => 1,
+ 'k' => 1024,
+ 'kb' => 1024,
+ 'mb' => 1024 * 1024,
+ 'm' => 1024 * 1024,
+ 'gb' => 1024 * 1024 * 1024,
+ 'g' => 1024 * 1024 * 1024,
+ 'tb' => 1024 * 1024 * 1024 * 1024,
+ 't' => 1024 * 1024 * 1024 * 1024,
+ 'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
+ 'p' => 1024 * 1024 * 1024 * 1024 * 1024,
+ );
+
+ $bytes = floatval($str);
+
+ if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
+ $bytes *= $bytes_array[$matches[1]];
+ } else {
+ return false;
+ }
+
+ $bytes = round($bytes);
+
+ return $bytes;
+ }
+
+ /**
+ * Recursive copying of folders
+ * @param string $src source folder
+ * @param string $dest target folder
+ *
+ */
+ static function copyr($src, $dest) {
+ if (is_dir($src)) {
+ if (!is_dir($dest)) {
+ mkdir($dest);
+ }
+ $files = scandir($src);
+ foreach ($files as $file) {
+ if ($file != "." && $file != "..") {
+ self::copyr("$src/$file", "$dest/$file");
+ }
+ }
+ } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
+ copy($src, $dest);
+ }
+ }
+
+ /**
+ * Recursive deletion of folders
+ * @param string $dir path to the folder
+ * @param bool $deleteSelf if set to false only the content of the folder will be deleted
+ * @return bool
+ */
+ static function rmdirr($dir, $deleteSelf = true) {
+ if (is_dir($dir)) {
+ $files = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
+ RecursiveIteratorIterator::CHILD_FIRST
+ );
+
+ foreach ($files as $fileInfo) {
+ /** @var SplFileInfo $fileInfo */
+ if ($fileInfo->isDir()) {
+ rmdir($fileInfo->getRealPath());
+ } else {
+ unlink($fileInfo->getRealPath());
+ }
+ }
+ if ($deleteSelf) {
+ rmdir($dir);
+ }
+ } elseif (file_exists($dir)) {
+ if ($deleteSelf) {
+ unlink($dir);
+ }
+ }
+ if (!$deleteSelf) {
+ return true;
+ }
+
+ return !file_exists($dir);
+ }
+
+ /**
+ * @return \OC\Files\Type\TemplateManager
+ */
+ static public function getFileTemplateManager() {
+ if (!self::$templateManager) {
+ self::$templateManager = new \OC\Files\Type\TemplateManager();
+ }
+ return self::$templateManager;
+ }
+
+ /**
+ * detect if a given program is found in the search PATH
+ *
+ * @param string $name
+ * @param bool $path
+ * @internal param string $program name
+ * @internal param string $optional search path, defaults to $PATH
+ * @return bool true if executable program found in path
+ */
+ public static function canExecute($name, $path = false) {
+ // path defaults to PATH from environment if not set
+ if ($path === false) {
+ $path = getenv("PATH");
+ }
+ // check method depends on operating system
+ if (!strncmp(PHP_OS, "WIN", 3)) {
+ // on Windows an appropriate COM or EXE file needs to exist
+ $exts = array(".exe", ".com");
+ $check_fn = "file_exists";
+ } else {
+ // anywhere else we look for an executable file of that name
+ $exts = array("");
+ $check_fn = "is_executable";
+ }
+ // Default check will be done with $path directories :
+ $dirs = explode(PATH_SEPARATOR, $path);
+ // WARNING : We have to check if open_basedir is enabled :
+ $obd = OC::$server->getIniWrapper()->getString('open_basedir');
+ if ($obd != "none") {
+ $obd_values = explode(PATH_SEPARATOR, $obd);
+ if (count($obd_values) > 0 and $obd_values[0]) {
+ // open_basedir is in effect !
+ // We need to check if the program is in one of these dirs :
+ $dirs = $obd_values;
+ }
+ }
+ foreach ($dirs as $dir) {
+ foreach ($exts as $ext) {
+ if ($check_fn("$dir/$name" . $ext))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * copy the contents of one stream to another
+ *
+ * @param resource $source
+ * @param resource $target
+ * @return array the number of bytes copied and result
+ */
+ public static function streamCopy($source, $target) {
+ if (!$source or !$target) {
+ return array(0, false);
+ }
+ $bufSize = 8192;
+ $result = true;
+ $count = 0;
+ while (!feof($source)) {
+ $buf = fread($source, $bufSize);
+ $bytesWritten = fwrite($target, $buf);
+ if ($bytesWritten !== false) {
+ $count += $bytesWritten;
+ }
+ // note: strlen is expensive so only use it when necessary,
+ // on the last block
+ if ($bytesWritten === false
+ || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
+ ) {
+ // write error, could be disk full ?
+ $result = false;
+ break;
+ }
+ }
+ return array($count, $result);
+ }
+
+ /**
+ * Adds a suffix to the name in case the file exists
+ *
+ * @param string $path
+ * @param string $filename
+ * @return string
+ */
+ public static function buildNotExistingFileName($path, $filename) {
+ $view = \OC\Files\Filesystem::getView();
+ return self::buildNotExistingFileNameForView($path, $filename, $view);
+ }
+
+ /**
+ * Adds a suffix to the name in case the file exists
+ *
+ * @param string $path
+ * @param string $filename
+ * @return string
+ */
+ public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
+ if ($path === '/') {
+ $path = '';
+ }
+ if ($pos = strrpos($filename, '.')) {
+ $name = substr($filename, 0, $pos);
+ $ext = substr($filename, $pos);
+ } else {
+ $name = $filename;
+ $ext = '';
+ }
+
+ $newpath = $path . '/' . $filename;
+ if ($view->file_exists($newpath)) {
+ if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
+ //Replace the last "(number)" with "(number+1)"
+ $last_match = count($matches[0]) - 1;
+ $counter = $matches[1][$last_match][0] + 1;
+ $offset = $matches[0][$last_match][1];
+ $match_length = strlen($matches[0][$last_match][0]);
+ } else {
+ $counter = 2;
+ $match_length = 0;
+ $offset = false;
+ }
+ do {
+ if ($offset) {
+ //Replace the last "(number)" with "(number+1)"
+ $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
+ } else {
+ $newname = $name . ' (' . $counter . ')';
+ }
+ $newpath = $path . '/' . $newname . $ext;
+ $counter++;
+ } while ($view->file_exists($newpath));
+ }
+
+ return $newpath;
+ }
+
+ /**
+ * Checks if $sub is a subdirectory of $parent
+ *
+ * @param string $sub
+ * @param string $parent
+ * @return bool
+ */
+ public static function isSubDirectory($sub, $parent) {
+ $realpathSub = realpath($sub);
+ $realpathParent = realpath($parent);
+
+ // realpath() may return false in case the directory does not exist
+ // since we can not be sure how different PHP versions may behave here
+ // we do an additional check whether realpath returned false
+ if($realpathSub === false || $realpathParent === false) {
+ return false;
+ }
+
+ // Check whether $sub is a subdirectory of $parent
+ if (strpos($realpathSub, $realpathParent) === 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
+ *
+ * @param array $input The array to work on
+ * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
+ * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
+ * @return array
+ *
+ * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
+ * based on http://www.php.net/manual/en/function.array-change-key-case.php#107715
+ *
+ */
+ public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
+ $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
+ $ret = array();
+ foreach ($input as $k => $v) {
+ $ret[mb_convert_case($k, $case, $encoding)] = $v;
+ }
+ return $ret;
+ }
+
+ /**
+ * performs a search in a nested array
+ * @param array $haystack the array to be searched
+ * @param string $needle the search string
+ * @param string $index optional, only search this key name
+ * @return mixed the key of the matching field, otherwise false
+ *
+ * performs a search in a nested array
+ *
+ * taken from http://www.php.net/manual/en/function.array-search.php#97645
+ */
+ public static function recursiveArraySearch($haystack, $needle, $index = null) {
+ $aIt = new RecursiveArrayIterator($haystack);
+ $it = new RecursiveIteratorIterator($aIt);
+
+ while ($it->valid()) {
+ if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) {
+ return $aIt->key();
+ }
+
+ $it->next();
+ }
+
+ return false;
+ }
+
+ /**
+ * Shortens str to maxlen by replacing characters in the middle with '...', eg.
+ * ellipsis('a very long string with lots of useless info to make a better example', 14) becomes 'a very ...example'
+ *
+ * @param string $str the string
+ * @param string $maxlen the maximum length of the result
+ * @return string with at most maxlen characters
+ */
+ public static function ellipsis($str, $maxlen) {
+ if (strlen($str) > $maxlen) {
+ $characters = floor($maxlen / 2);
+ return substr($str, 0, $characters) . '...' . substr($str, -1 * $characters);
+ }
+ return $str;
+ }
+
+ /**
+ * calculates the maximum upload size respecting system settings, free space and user quota
+ *
+ * @param string $dir the current folder where the user currently operates
+ * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
+ * @return int number of bytes representing
+ */
+ public static function maxUploadFilesize($dir, $freeSpace = null) {
+ if (is_null($freeSpace) || $freeSpace < 0){
+ $freeSpace = self::freeSpace($dir);
+ }
+ return min($freeSpace, self::uploadLimit());
+ }
+
+ /**
+ * Calculate free space left within user quota
+ *
+ * @param string $dir the current folder where the user currently operates
+ * @return int number of bytes representing
+ */
+ public static function freeSpace($dir) {
+ $freeSpace = \OC\Files\Filesystem::free_space($dir);
+ if ($freeSpace !== \OCP\Files\FileInfo::SPACE_UNKNOWN) {
+ $freeSpace = max($freeSpace, 0);
+ return $freeSpace;
+ } else {
+ return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
+ }
+ }
+
+ /**
+ * Calculate PHP upload limit
+ *
+ * @return int PHP upload file size limit
+ */
+ public static function uploadLimit() {
+ $ini = \OC::$server->getIniWrapper();
+ $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
+ $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
+ if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
+ return INF;
+ } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
+ return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
+ } else {
+ return min($upload_max_filesize, $post_max_size);
+ }
+ }
+
+ /**
+ * Checks if a function is available
+ *
+ * @param string $function_name
+ * @return bool
+ */
+ public static function is_function_enabled($function_name) {
+ if (!function_exists($function_name)) {
+ return false;
+ }
+ $ini = \OC::$server->getIniWrapper();
+ $disabled = explode(',', $ini->get('disable_functions'));
+ $disabled = array_map('trim', $disabled);
+ if (in_array($function_name, $disabled)) {
+ return false;
+ }
+ $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist'));
+ $disabled = array_map('trim', $disabled);
+ if (in_array($function_name, $disabled)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Try to find a program
+ * Note: currently windows is not supported
+ *
+ * @param string $program
+ * @return null|string
+ */
+ public static function findBinaryPath($program) {
+ $memcache = \OC::$server->getMemCacheFactory()->create('findBinaryPath');
+ if ($memcache->hasKey($program)) {
+ return $memcache->get($program);
+ }
+ $result = null;
+ if (!\OC_Util::runningOnWindows() && self::is_function_enabled('exec')) {
+ $exeSniffer = new ExecutableFinder();
+ // Returns null if nothing is found
+ $result = $exeSniffer->find($program);
+ if (empty($result)) {
+ $paths = getenv('PATH');
+ if (empty($paths)) {
+ $paths = '/usr/local/bin /usr/bin /opt/bin /bin';
+ } else {
+ $paths = str_replace(':',' ',getenv('PATH'));
+ }
+ $command = 'find ' . $paths . ' -name ' . escapeshellarg($program) . ' 2> /dev/null';
+ exec($command, $output, $returnCode);
+ if (count($output) > 0) {
+ $result = escapeshellcmd($output[0]);
+ }
+ }
+ }
+ // store the value for 5 minutes
+ $memcache->set($program, $result, 300);
+ return $result;
+ }
+
+ /**
+ * Calculate the disc space for the given path
+ *
+ * @param string $path
+ * @param \OCP\Files\FileInfo $rootInfo (optional)
+ * @return array
+ * @throws \OCP\Files\NotFoundException
+ */
+ public static function getStorageInfo($path, $rootInfo = null) {
+ // return storage info without adding mount points
+ $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
+
+ if (!$rootInfo) {
+ $rootInfo = \OC\Files\Filesystem::getFileInfo($path, false);
+ }
+ if (!$rootInfo instanceof \OCP\Files\FileInfo) {
+ throw new \OCP\Files\NotFoundException();
+ }
+ $used = $rootInfo->getSize();
+ if ($used < 0) {
+ $used = 0;
+ }
+ $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
+ $storage = $rootInfo->getStorage();
+ $sourceStorage = $storage;
+ if ($storage->instanceOfStorage('\OC\Files\Storage\Shared')) {
+ $includeExtStorage = false;
+ $sourceStorage = $storage->getSourceStorage();
+ }
+ if ($includeExtStorage) {
+ $quota = OC_Util::getUserQuota(\OCP\User::getUser());
+ if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
+ // always get free space / total space from root + mount points
+ return self::getGlobalStorageInfo();
+ }
+ }
+
+ // TODO: need a better way to get total space from storage
+ if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
+ /** @var \OC\Files\Storage\Wrapper\Quota $storage */
+ $quota = $sourceStorage->getQuota();
+ }
+ $free = $sourceStorage->free_space('');
+ if ($free >= 0) {
+ $total = $free + $used;
+ } else {
+ $total = $free; //either unknown or unlimited
+ }
+ if ($total > 0) {
+ if ($quota > 0 && $total > $quota) {
+ $total = $quota;
+ }
+ // prevent division by zero or error codes (negative values)
+ $relative = round(($used / $total) * 10000) / 100;
+ } else {
+ $relative = 0;
+ }
+
+ $ownerId = $storage->getOwner($path);
+ $ownerDisplayName = '';
+ $owner = \OC::$server->getUserManager()->get($ownerId);
+ if($owner) {
+ $ownerDisplayName = $owner->getDisplayName();
+ }
+
+ return [
+ 'free' => $free,
+ 'used' => $used,
+ 'quota' => $quota,
+ 'total' => $total,
+ 'relative' => $relative,
+ 'owner' => $ownerId,
+ 'ownerDisplayName' => $ownerDisplayName,
+ ];
+ }
+
+ /**
+ * Get storage info including all mount points and quota
+ *
+ * @return array
+ */
+ private static function getGlobalStorageInfo() {
+ $quota = OC_Util::getUserQuota(\OCP\User::getUser());
+
+ $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
+ $used = $rootInfo['size'];
+ if ($used < 0) {
+ $used = 0;
+ }
+
+ $total = $quota;
+ $free = $quota - $used;
+
+ if ($total > 0) {
+ if ($quota > 0 && $total > $quota) {
+ $total = $quota;
+ }
+ // prevent division by zero or error codes (negative values)
+ $relative = round(($used / $total) * 10000) / 100;
+ } else {
+ $relative = 0;
+ }
+
+ return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative);
+
+ }
+
+ /**
+ * Returns whether the config file is set manually to read-only
+ * @return bool
+ */
+ public static function isReadOnlyConfigEnabled() {
+ return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
+ }
+}
diff --git a/lib/private/legacy/hook.php b/lib/private/legacy/hook.php
new file mode 100644
index 00000000000..76f3b657eb9
--- /dev/null
+++ b/lib/private/legacy/hook.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Sam Tuke <mail@samtuke.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+class OC_Hook{
+ public static $thrownExceptions = [];
+
+ static private $registered = array();
+
+ /**
+ * connects a function to a hook
+ *
+ * @param string $signalClass class name of emitter
+ * @param string $signalName name of signal
+ * @param string|object $slotClass class name of slot
+ * @param string $slotName name of slot
+ * @return bool
+ *
+ * This function makes it very easy to connect to use hooks.
+ *
+ * TODO: write example
+ */
+ static public function connect($signalClass, $signalName, $slotClass, $slotName ) {
+ // If we're trying to connect to an emitting class that isn't
+ // yet registered, register it
+ if( !array_key_exists($signalClass, self::$registered )) {
+ self::$registered[$signalClass] = array();
+ }
+ // If we're trying to connect to an emitting method that isn't
+ // yet registered, register it with the emitting class
+ if( !array_key_exists( $signalName, self::$registered[$signalClass] )) {
+ self::$registered[$signalClass][$signalName] = array();
+ }
+
+ // don't connect hooks twice
+ foreach (self::$registered[$signalClass][$signalName] as $hook) {
+ if ($hook['class'] === $slotClass and $hook['name'] === $slotName) {
+ return false;
+ }
+ }
+ // Connect the hook handler to the requested emitter
+ self::$registered[$signalClass][$signalName][] = array(
+ "class" => $slotClass,
+ "name" => $slotName
+ );
+
+ // No chance for failure ;-)
+ return true;
+ }
+
+ /**
+ * emits a signal
+ *
+ * @param string $signalClass class name of emitter
+ * @param string $signalName name of signal
+ * @param mixed $params default: array() array with additional data
+ * @return bool true if slots exists or false if not
+ * @throws \OC\HintException
+ * @throws \OC\ServerNotAvailableException Emits a signal. To get data from the slot use references!
+ *
+ * TODO: write example
+ */
+ static public function emit($signalClass, $signalName, $params = []) {
+
+ // Return false if no hook handlers are listening to this
+ // emitting class
+ if( !array_key_exists($signalClass, self::$registered )) {
+ return false;
+ }
+
+ // Return false if no hook handlers are listening to this
+ // emitting method
+ if( !array_key_exists( $signalName, self::$registered[$signalClass] )) {
+ return false;
+ }
+
+ // Call all slots
+ foreach( self::$registered[$signalClass][$signalName] as $i ) {
+ try {
+ call_user_func( array( $i["class"], $i["name"] ), $params );
+ } catch (Exception $e){
+ self::$thrownExceptions[] = $e;
+ \OC::$server->getLogger()->logException($e);
+ if($e instanceof \OC\HintException) {
+ throw $e;
+ }
+ if($e instanceof \OC\ServerNotAvailableException) {
+ throw $e;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * clear hooks
+ * @param string $signalClass
+ * @param string $signalName
+ */
+ static public function clear($signalClass='', $signalName='') {
+ if ($signalClass) {
+ if ($signalName) {
+ self::$registered[$signalClass][$signalName]=array();
+ }else{
+ self::$registered[$signalClass]=array();
+ }
+ }else{
+ self::$registered=array();
+ }
+ }
+
+ /**
+ * DO NOT USE!
+ * For unit tests ONLY!
+ */
+ static public function getHooks() {
+ return self::$registered;
+ }
+}
diff --git a/lib/private/legacy/image.php b/lib/private/legacy/image.php
new file mode 100644
index 00000000000..a97347df623
--- /dev/null
+++ b/lib/private/legacy/image.php
@@ -0,0 +1,1147 @@
+<?php
+/**
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bartek Przybylski <bart.p.pl@gmail.com>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Byron Marohn <combustible@live.com>
+ * @author Christopher Schรคpers <kondou@ts.unde.re>
+ * @author Georg Ehrke <georg@owncloud.com>
+ * @author j-ed <juergen@eisfair.org>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Johannes Willnecker <johannes@willnecker.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @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>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+/**
+ * Class for basic image manipulation
+ */
+class OC_Image implements \OCP\IImage {
+ /** @var false|resource */
+ protected $resource = false; // tmp resource.
+ /** @var int */
+ protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident.
+ /** @var string */
+ protected $mimeType = 'image/png'; // Default to png
+ /** @var int */
+ protected $bitDepth = 24;
+ /** @var null|string */
+ protected $filePath = null;
+ /** @var finfo */
+ private $fileInfo;
+ /** @var \OCP\ILogger */
+ private $logger;
+
+ /**
+ * Get mime type for an image file.
+ *
+ * @param string|null $filePath The path to a local image file.
+ * @return string The mime type if the it could be determined, otherwise an empty string.
+ */
+ static public function getMimeTypeForFile($filePath) {
+ // exif_imagetype throws "read error!" if file is less than 12 byte
+ if ($filePath !== null && filesize($filePath) > 11) {
+ $imageType = exif_imagetype($filePath);
+ } else {
+ $imageType = false;
+ }
+ return $imageType ? image_type_to_mime_type($imageType) : '';
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by
+ * an imagecreate* function.
+ * @param \OCP\ILogger $logger
+ */
+ public function __construct($imageRef = null, $logger = null) {
+ $this->logger = $logger;
+ if (is_null($logger)) {
+ $this->logger = \OC::$server->getLogger();
+ }
+
+ if (!extension_loaded('gd') || !function_exists('gd_info')) {
+ $this->logger->error(__METHOD__ . '(): GD module not installed', array('app' => 'core'));
+ return false;
+ }
+
+ if (\OC_Util::fileInfoLoaded()) {
+ $this->fileInfo = new finfo(FILEINFO_MIME_TYPE);
+ }
+
+ if (!is_null($imageRef)) {
+ $this->load($imageRef);
+ }
+ }
+
+ /**
+ * Determine whether the object contains an image resource.
+ *
+ * @return bool
+ */
+ public function valid() { // apparently you can't name a method 'empty'...
+ return is_resource($this->resource);
+ }
+
+ /**
+ * Returns the MIME type of the image or an empty string if no image is loaded.
+ *
+ * @return string
+ */
+ public function mimeType() {
+ return $this->valid() ? $this->mimeType : '';
+ }
+
+ /**
+ * Returns the width of the image or -1 if no image is loaded.
+ *
+ * @return int
+ */
+ public function width() {
+ return $this->valid() ? imagesx($this->resource) : -1;
+ }
+
+ /**
+ * Returns the height of the image or -1 if no image is loaded.
+ *
+ * @return int
+ */
+ public function height() {
+ return $this->valid() ? imagesy($this->resource) : -1;
+ }
+
+ /**
+ * Returns the width when the image orientation is top-left.
+ *
+ * @return int
+ */
+ public function widthTopLeft() {
+ $o = $this->getOrientation();
+ $this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, array('app' => 'core'));
+ switch ($o) {
+ case -1:
+ case 1:
+ case 2: // Not tested
+ case 3:
+ case 4: // Not tested
+ return $this->width();
+ case 5: // Not tested
+ case 6:
+ case 7: // Not tested
+ case 8:
+ return $this->height();
+ }
+ return $this->width();
+ }
+
+ /**
+ * Returns the height when the image orientation is top-left.
+ *
+ * @return int
+ */
+ public function heightTopLeft() {
+ $o = $this->getOrientation();
+ $this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, array('app' => 'core'));
+ switch ($o) {
+ case -1:
+ case 1:
+ case 2: // Not tested
+ case 3:
+ case 4: // Not tested
+ return $this->height();
+ case 5: // Not tested
+ case 6:
+ case 7: // Not tested
+ case 8:
+ return $this->width();
+ }
+ return $this->height();
+ }
+
+ /**
+ * Outputs the image.
+ *
+ * @param string $mimeType
+ * @return bool
+ */
+ public function show($mimeType = null) {
+ if ($mimeType === null) {
+ $mimeType = $this->mimeType();
+ }
+ header('Content-Type: ' . $mimeType);
+ return $this->_output(null, $mimeType);
+ }
+
+ /**
+ * Saves the image.
+ *
+ * @param string $filePath
+ * @param string $mimeType
+ * @return bool
+ */
+
+ public function save($filePath = null, $mimeType = null) {
+ if ($mimeType === null) {
+ $mimeType = $this->mimeType();
+ }
+ if ($filePath === null && $this->filePath === null) {
+ $this->logger->error(__METHOD__ . '(): called with no path.', array('app' => 'core'));
+ return false;
+ } elseif ($filePath === null && $this->filePath !== null) {
+ $filePath = $this->filePath;
+ }
+ return $this->_output($filePath, $mimeType);
+ }
+
+ /**
+ * Outputs/saves the image.
+ *
+ * @param string $filePath
+ * @param string $mimeType
+ * @return bool
+ * @throws Exception
+ */
+ private function _output($filePath = null, $mimeType = null) {
+ if ($filePath) {
+ if (!file_exists(dirname($filePath)))
+ mkdir(dirname($filePath), 0777, true);
+ if (!is_writable(dirname($filePath))) {
+ $this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', array('app' => 'core'));
+ return false;
+ } elseif (is_writable(dirname($filePath)) && file_exists($filePath) && !is_writable($filePath)) {
+ $this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', array('app' => 'core'));
+ return false;
+ }
+ }
+ if (!$this->valid()) {
+ return false;
+ }
+
+ $imageType = $this->imageType;
+ if ($mimeType !== null) {
+ switch ($mimeType) {
+ case 'image/gif':
+ $imageType = IMAGETYPE_GIF;
+ break;
+ case 'image/jpeg':
+ $imageType = IMAGETYPE_JPEG;
+ break;
+ case 'image/png':
+ $imageType = IMAGETYPE_PNG;
+ break;
+ case 'image/x-xbitmap':
+ $imageType = IMAGETYPE_XBM;
+ break;
+ case 'image/bmp':
+ case 'image/x-ms-bmp':
+ $imageType = IMAGETYPE_BMP;
+ break;
+ default:
+ throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format');
+ }
+ }
+
+ switch ($imageType) {
+ case IMAGETYPE_GIF:
+ $retVal = imagegif($this->resource, $filePath);
+ break;
+ case IMAGETYPE_JPEG:
+ $retVal = imagejpeg($this->resource, $filePath);
+ break;
+ case IMAGETYPE_PNG:
+ $retVal = imagepng($this->resource, $filePath);
+ break;
+ case IMAGETYPE_XBM:
+ if (function_exists('imagexbm')) {
+ $retVal = imagexbm($this->resource, $filePath);
+ } else {
+ throw new Exception('\OC_Image::_output(): imagexbm() is not supported.');
+ }
+
+ break;
+ case IMAGETYPE_WBMP:
+ $retVal = imagewbmp($this->resource, $filePath);
+ break;
+ case IMAGETYPE_BMP:
+ $retVal = imagebmp($this->resource, $filePath, $this->bitDepth);
+ break;
+ default:
+ $retVal = imagepng($this->resource, $filePath);
+ }
+ return $retVal;
+ }
+
+ /**
+ * Prints the image when called as $image().
+ */
+ public function __invoke() {
+ return $this->show();
+ }
+
+ /**
+ * @return resource Returns the image resource in any.
+ */
+ public function resource() {
+ return $this->resource;
+ }
+
+ /**
+ * @return null|string Returns the raw image data.
+ */
+ public function data() {
+ if (!$this->valid()) {
+ return null;
+ }
+ ob_start();
+ switch ($this->mimeType) {
+ case "image/png":
+ $res = imagepng($this->resource);
+ break;
+ case "image/jpeg":
+ $res = imagejpeg($this->resource);
+ break;
+ case "image/gif":
+ $res = imagegif($this->resource);
+ break;
+ default:
+ $res = imagepng($this->resource);
+ $this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', array('app' => 'core'));
+ break;
+ }
+ if (!$res) {
+ $this->logger->error('OC_Image->data. Error getting image data.', array('app' => 'core'));
+ }
+ return ob_get_clean();
+ }
+
+ /**
+ * @return string - base64 encoded, which is suitable for embedding in a VCard.
+ */
+ function __toString() {
+ return base64_encode($this->data());
+ }
+
+ /**
+ * (I'm open for suggestions on better method name ;)
+ * Get the orientation based on EXIF data.
+ *
+ * @return int The orientation or -1 if no EXIF data is available.
+ */
+ public function getOrientation() {
+ if ($this->imageType !== IMAGETYPE_JPEG) {
+ $this->logger->debug('OC_Image->fixOrientation() Image is not a JPEG.', array('app' => 'core'));
+ return -1;
+ }
+ if (!is_callable('exif_read_data')) {
+ $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core'));
+ return -1;
+ }
+ if (!$this->valid()) {
+ $this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core'));
+ return -1;
+ }
+ if (is_null($this->filePath) || !is_readable($this->filePath)) {
+ $this->logger->debug('OC_Image->fixOrientation() No readable file path set.', array('app' => 'core'));
+ return -1;
+ }
+ $exif = @exif_read_data($this->filePath, 'IFD0');
+ if (!$exif) {
+ return -1;
+ }
+ if (!isset($exif['Orientation'])) {
+ return -1;
+ }
+ return $exif['Orientation'];
+ }
+
+ /**
+ * (I'm open for suggestions on better method name ;)
+ * Fixes orientation based on EXIF data.
+ *
+ * @return bool.
+ */
+ public function fixOrientation() {
+ $o = $this->getOrientation();
+ $this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, array('app' => 'core'));
+ $rotate = 0;
+ $flip = false;
+ switch ($o) {
+ case -1:
+ return false; //Nothing to fix
+ case 1:
+ $rotate = 0;
+ break;
+ case 2:
+ $rotate = 0;
+ $flip = true;
+ break;
+ case 3:
+ $rotate = 180;
+ break;
+ case 4:
+ $rotate = 180;
+ $flip = true;
+ break;
+ case 5:
+ $rotate = 90;
+ $flip = true;
+ break;
+ case 6:
+ $rotate = 270;
+ break;
+ case 7:
+ $rotate = 270;
+ $flip = true;
+ break;
+ case 8:
+ $rotate = 90;
+ break;
+ }
+ if($flip && function_exists('imageflip')) {
+ imageflip($this->resource, IMG_FLIP_HORIZONTAL);
+ }
+ if ($rotate) {
+ $res = imagerotate($this->resource, $rotate, 0);
+ if ($res) {
+ if (imagealphablending($res, true)) {
+ if (imagesavealpha($res, true)) {
+ imagedestroy($this->resource);
+ $this->resource = $res;
+ return true;
+ } else {
+ $this->logger->debug('OC_Image->fixOrientation() Error during alpha-saving', array('app' => 'core'));
+ return false;
+ }
+ } else {
+ $this->logger->debug('OC_Image->fixOrientation() Error during alpha-blending', array('app' => 'core'));
+ return false;
+ }
+ } else {
+ $this->logger->debug('OC_Image->fixOrientation() Error during orientation fixing', array('app' => 'core'));
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function.
+ *
+ * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by an imagecreate* function or a file resource (file handle ).
+ * @return resource|false An image resource or false on error
+ */
+ public function load($imageRef) {
+ if (is_resource($imageRef)) {
+ if (get_resource_type($imageRef) == 'gd') {
+ $this->resource = $imageRef;
+ return $this->resource;
+ } elseif (in_array(get_resource_type($imageRef), array('file', 'stream'))) {
+ return $this->loadFromFileHandle($imageRef);
+ }
+ } elseif ($this->loadFromBase64($imageRef) !== false) {
+ return $this->resource;
+ } elseif ($this->loadFromFile($imageRef) !== false) {
+ return $this->resource;
+ } elseif ($this->loadFromData($imageRef) !== false) {
+ return $this->resource;
+ }
+ $this->logger->debug(__METHOD__ . '(): could not load anything. Giving up!', array('app' => 'core'));
+ return false;
+ }
+
+ /**
+ * Loads an image from an open file handle.
+ * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again.
+ *
+ * @param resource $handle
+ * @return resource|false An image resource or false on error
+ */
+ public function loadFromFileHandle($handle) {
+ $contents = stream_get_contents($handle);
+ if ($this->loadFromData($contents)) {
+ return $this->resource;
+ }
+ return false;
+ }
+
+ /**
+ * Loads an image from a local file.
+ *
+ * @param bool|string $imagePath The path to a local file.
+ * @return bool|resource An image resource or false on error
+ */
+ public function loadFromFile($imagePath = false) {
+ // exif_imagetype throws "read error!" if file is less than 12 byte
+ if (!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) {
+ return false;
+ }
+ $iType = exif_imagetype($imagePath);
+ switch ($iType) {
+ case IMAGETYPE_GIF:
+ if (imagetypes() & IMG_GIF) {
+ $this->resource = imagecreatefromgif($imagePath);
+ // Preserve transparency
+ imagealphablending($this->resource, true);
+ imagesavealpha($this->resource, true);
+ } else {
+ $this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, array('app' => 'core'));
+ }
+ break;
+ case IMAGETYPE_JPEG:
+ if (imagetypes() & IMG_JPG) {
+ $this->resource = imagecreatefromjpeg($imagePath);
+ } else {
+ $this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, array('app' => 'core'));
+ }
+ break;
+ case IMAGETYPE_PNG:
+ if (imagetypes() & IMG_PNG) {
+ $this->resource = imagecreatefrompng($imagePath);
+ // Preserve transparency
+ imagealphablending($this->resource, true);
+ imagesavealpha($this->resource, true);
+ } else {
+ $this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, array('app' => 'core'));
+ }
+ break;
+ case IMAGETYPE_XBM:
+ if (imagetypes() & IMG_XPM) {
+ $this->resource = imagecreatefromxbm($imagePath);
+ } else {
+ $this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, array('app' => 'core'));
+ }
+ break;
+ case IMAGETYPE_WBMP:
+ if (imagetypes() & IMG_WBMP) {
+ $this->resource = imagecreatefromwbmp($imagePath);
+ } else {
+ $this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, array('app' => 'core'));
+ }
+ break;
+ case IMAGETYPE_BMP:
+ $this->resource = $this->imagecreatefrombmp($imagePath);
+ break;
+ /*
+ case IMAGETYPE_TIFF_II: // (intel byte order)
+ break;
+ case IMAGETYPE_TIFF_MM: // (motorola byte order)
+ break;
+ case IMAGETYPE_JPC:
+ break;
+ case IMAGETYPE_JP2:
+ break;
+ case IMAGETYPE_JPX:
+ break;
+ case IMAGETYPE_JB2:
+ break;
+ case IMAGETYPE_SWC:
+ break;
+ case IMAGETYPE_IFF:
+ break;
+ case IMAGETYPE_ICO:
+ break;
+ case IMAGETYPE_SWF:
+ break;
+ case IMAGETYPE_PSD:
+ break;
+ */
+ default:
+
+ // this is mostly file created from encrypted file
+ $this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagePath)));
+ $iType = IMAGETYPE_PNG;
+ $this->logger->debug('OC_Image->loadFromFile, Default', array('app' => 'core'));
+ break;
+ }
+ if ($this->valid()) {
+ $this->imageType = $iType;
+ $this->mimeType = image_type_to_mime_type($iType);
+ $this->filePath = $imagePath;
+ }
+ return $this->resource;
+ }
+
+ /**
+ * Loads an image from a string of data.
+ *
+ * @param string $str A string of image data as read from a file.
+ * @return bool|resource An image resource or false on error
+ */
+ public function loadFromData($str) {
+ if (is_resource($str)) {
+ return false;
+ }
+ $this->resource = @imagecreatefromstring($str);
+ if ($this->fileInfo) {
+ $this->mimeType = $this->fileInfo->buffer($str);
+ }
+ if (is_resource($this->resource)) {
+ imagealphablending($this->resource, false);
+ imagesavealpha($this->resource, true);
+ }
+
+ if (!$this->resource) {
+ $this->logger->debug('OC_Image->loadFromFile, could not load', array('app' => 'core'));
+ return false;
+ }
+ return $this->resource;
+ }
+
+ /**
+ * Loads an image from a base64 encoded string.
+ *
+ * @param string $str A string base64 encoded string of image data.
+ * @return bool|resource An image resource or false on error
+ */
+ public function loadFromBase64($str) {
+ if (!is_string($str)) {
+ return false;
+ }
+ $data = base64_decode($str);
+ if ($data) { // try to load from string data
+ $this->resource = @imagecreatefromstring($data);
+ if ($this->fileInfo) {
+ $this->mimeType = $this->fileInfo->buffer($data);
+ }
+ if (!$this->resource) {
+ $this->logger->debug('OC_Image->loadFromBase64, could not load', array('app' => 'core'));
+ return false;
+ }
+ return $this->resource;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Create a new image from file or URL
+ *
+ * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm
+ * @version 1.00
+ * @param string $fileName <p>
+ * Path to the BMP image.
+ * </p>
+ * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors.
+ */
+ private function imagecreatefrombmp($fileName) {
+ if (!($fh = fopen($fileName, 'rb'))) {
+ $this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName, array('app' => 'core'));
+ return false;
+ }
+ // read file header
+ $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
+ // check for bitmap
+ if ($meta['type'] != 19778) {
+ fclose($fh);
+ $this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
+ return false;
+ }
+ // read image header
+ $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
+ // read additional 16bit header
+ if ($meta['bits'] == 16) {
+ $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
+ }
+ // set bytes and padding
+ $meta['bytes'] = $meta['bits'] / 8;
+ $this->bitDepth = $meta['bits']; //remember the bit depth for the imagebmp call
+ $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4)));
+ if ($meta['decal'] == 4) {
+ $meta['decal'] = 0;
+ }
+ // obtain imagesize
+ if ($meta['imagesize'] < 1) {
+ $meta['imagesize'] = $meta['filesize'] - $meta['offset'];
+ // in rare cases filesize is equal to offset so we need to read physical size
+ if ($meta['imagesize'] < 1) {
+ $meta['imagesize'] = @filesize($fileName) - $meta['offset'];
+ if ($meta['imagesize'] < 1) {
+ fclose($fh);
+ $this->logger->warning('imagecreatefrombmp: Can not obtain file size of ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
+ return false;
+ }
+ }
+ }
+ // calculate colors
+ $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
+ // read color palette
+ $palette = array();
+ if ($meta['bits'] < 16) {
+ $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
+ // in rare cases the color value is signed
+ if ($palette[1] < 0) {
+ foreach ($palette as $i => $color) {
+ $palette[$i] = $color + 16777216;
+ }
+ }
+ }
+ // create gd image
+ $im = imagecreatetruecolor($meta['width'], $meta['height']);
+ if ($im == false) {
+ fclose($fh);
+ $this->logger->warning(
+ 'imagecreatefrombmp: imagecreatetruecolor failed for file "' . $fileName . '" with dimensions ' . $meta['width'] . 'x' . $meta['height'],
+ array('app' => 'core'));
+ return false;
+ }
+
+ $data = fread($fh, $meta['imagesize']);
+ $p = 0;
+ $vide = chr(0);
+ $y = $meta['height'] - 1;
+ $error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!';
+ // loop through the image data beginning with the lower left corner
+ while ($y >= 0) {
+ $x = 0;
+ while ($x < $meta['width']) {
+ switch ($meta['bits']) {
+ case 32:
+ case 24:
+ if (!($part = substr($data, $p, 3))) {
+ $this->logger->warning($error, array('app' => 'core'));
+ return $im;
+ }
+ $color = unpack('V', $part . $vide);
+ break;
+ case 16:
+ if (!($part = substr($data, $p, 2))) {
+ fclose($fh);
+ $this->logger->warning($error, array('app' => 'core'));
+ return $im;
+ }
+ $color = unpack('v', $part);
+ $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3);
+ break;
+ case 8:
+ $color = unpack('n', $vide . substr($data, $p, 1));
+ $color[1] = $palette[$color[1] + 1];
+ break;
+ case 4:
+ $color = unpack('n', $vide . substr($data, floor($p), 1));
+ $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
+ $color[1] = $palette[$color[1] + 1];
+ break;
+ case 1:
+ $color = unpack('n', $vide . substr($data, floor($p), 1));
+ switch (($p * 8) % 8) {
+ case 0:
+ $color[1] = $color[1] >> 7;
+ break;
+ case 1:
+ $color[1] = ($color[1] & 0x40) >> 6;
+ break;
+ case 2:
+ $color[1] = ($color[1] & 0x20) >> 5;
+ break;
+ case 3:
+ $color[1] = ($color[1] & 0x10) >> 4;
+ break;
+ case 4:
+ $color[1] = ($color[1] & 0x8) >> 3;
+ break;
+ case 5:
+ $color[1] = ($color[1] & 0x4) >> 2;
+ break;
+ case 6:
+ $color[1] = ($color[1] & 0x2) >> 1;
+ break;
+ case 7:
+ $color[1] = ($color[1] & 0x1);
+ break;
+ }
+ $color[1] = $palette[$color[1] + 1];
+ break;
+ default:
+ fclose($fh);
+ $this->logger->warning('imagecreatefrombmp: ' . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!', array('app' => 'core'));
+ return false;
+ }
+ imagesetpixel($im, $x, $y, $color[1]);
+ $x++;
+ $p += $meta['bytes'];
+ }
+ $y--;
+ $p += $meta['decal'];
+ }
+ fclose($fh);
+ return $im;
+ }
+
+ /**
+ * Resizes the image preserving ratio.
+ *
+ * @param integer $maxSize The maximum size of either the width or height.
+ * @return bool
+ */
+ public function resize($maxSize) {
+ if (!$this->valid()) {
+ $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
+ return false;
+ }
+ $widthOrig = imageSX($this->resource);
+ $heightOrig = imageSY($this->resource);
+ $ratioOrig = $widthOrig / $heightOrig;
+
+ if ($ratioOrig > 1) {
+ $newHeight = round($maxSize / $ratioOrig);
+ $newWidth = $maxSize;
+ } else {
+ $newWidth = round($maxSize * $ratioOrig);
+ $newHeight = $maxSize;
+ }
+
+ $this->preciseResize(round($newWidth), round($newHeight));
+ return true;
+ }
+
+ /**
+ * @param int $width
+ * @param int $height
+ * @return bool
+ */
+ public function preciseResize($width, $height) {
+ if (!$this->valid()) {
+ $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
+ return false;
+ }
+ $widthOrig = imageSX($this->resource);
+ $heightOrig = imageSY($this->resource);
+ $process = imagecreatetruecolor($width, $height);
+
+ if ($process == false) {
+ $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
+ imagedestroy($process);
+ return false;
+ }
+
+ // preserve transparency
+ if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
+ imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
+ imagealphablending($process, false);
+ imagesavealpha($process, true);
+ }
+
+ imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
+ if ($process == false) {
+ $this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core'));
+ imagedestroy($process);
+ return false;
+ }
+ imagedestroy($this->resource);
+ $this->resource = $process;
+ return true;
+ }
+
+ /**
+ * Crops the image to the middle square. If the image is already square it just returns.
+ *
+ * @param int $size maximum size for the result (optional)
+ * @return bool for success or failure
+ */
+ public function centerCrop($size = 0) {
+ if (!$this->valid()) {
+ $this->logger->error('OC_Image->centerCrop, No image loaded', array('app' => 'core'));
+ return false;
+ }
+ $widthOrig = imageSX($this->resource);
+ $heightOrig = imageSY($this->resource);
+ if ($widthOrig === $heightOrig and $size == 0) {
+ return true;
+ }
+ $ratioOrig = $widthOrig / $heightOrig;
+ $width = $height = min($widthOrig, $heightOrig);
+
+ if ($ratioOrig > 1) {
+ $x = ($widthOrig / 2) - ($width / 2);
+ $y = 0;
+ } else {
+ $y = ($heightOrig / 2) - ($height / 2);
+ $x = 0;
+ }
+ if ($size > 0) {
+ $targetWidth = $size;
+ $targetHeight = $size;
+ } else {
+ $targetWidth = $width;
+ $targetHeight = $height;
+ }
+ $process = imagecreatetruecolor($targetWidth, $targetHeight);
+ if ($process == false) {
+ $this->logger->error('OC_Image->centerCrop, Error creating true color image', array('app' => 'core'));
+ imagedestroy($process);
+ return false;
+ }
+
+ // preserve transparency
+ if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
+ imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
+ imagealphablending($process, false);
+ imagesavealpha($process, true);
+ }
+
+ imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
+ if ($process == false) {
+ $this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core'));
+ imagedestroy($process);
+ return false;
+ }
+ imagedestroy($this->resource);
+ $this->resource = $process;
+ return true;
+ }
+
+ /**
+ * Crops the image from point $x$y with dimension $wx$h.
+ *
+ * @param int $x Horizontal position
+ * @param int $y Vertical position
+ * @param int $w Width
+ * @param int $h Height
+ * @return bool for success or failure
+ */
+ public function crop($x, $y, $w, $h) {
+ if (!$this->valid()) {
+ $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
+ return false;
+ }
+ $process = imagecreatetruecolor($w, $h);
+ if ($process == false) {
+ $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
+ imagedestroy($process);
+ return false;
+ }
+
+ // preserve transparency
+ if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
+ imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
+ imagealphablending($process, false);
+ imagesavealpha($process, true);
+ }
+
+ imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
+ if ($process == false) {
+ $this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, array('app' => 'core'));
+ imagedestroy($process);
+ return false;
+ }
+ imagedestroy($this->resource);
+ $this->resource = $process;
+ return true;
+ }
+
+ /**
+ * Resizes the image to fit within a boundary while preserving ratio.
+ *
+ * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up
+ *
+ * @param integer $maxWidth
+ * @param integer $maxHeight
+ * @return bool
+ */
+ public function fitIn($maxWidth, $maxHeight) {
+ if (!$this->valid()) {
+ $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
+ return false;
+ }
+ $widthOrig = imageSX($this->resource);
+ $heightOrig = imageSY($this->resource);
+ $ratio = $widthOrig / $heightOrig;
+
+ $newWidth = min($maxWidth, $ratio * $maxHeight);
+ $newHeight = min($maxHeight, $maxWidth / $ratio);
+
+ $this->preciseResize(round($newWidth), round($newHeight));
+ return true;
+ }
+
+ /**
+ * Shrinks larger images to fit within specified boundaries while preserving ratio.
+ *
+ * @param integer $maxWidth
+ * @param integer $maxHeight
+ * @return bool
+ */
+ public function scaleDownToFit($maxWidth, $maxHeight) {
+ if (!$this->valid()) {
+ $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
+ return false;
+ }
+ $widthOrig = imageSX($this->resource);
+ $heightOrig = imageSY($this->resource);
+
+ if ($widthOrig > $maxWidth || $heightOrig > $maxHeight) {
+ return $this->fitIn($maxWidth, $maxHeight);
+ }
+
+ return false;
+ }
+
+ /**
+ * Destroys the current image and resets the object
+ */
+ public function destroy() {
+ if ($this->valid()) {
+ imagedestroy($this->resource);
+ }
+ $this->resource = null;
+ }
+
+ public function __destruct() {
+ $this->destroy();
+ }
+}
+
+if (!function_exists('imagebmp')) {
+ /**
+ * Output a BMP image to either the browser or a file
+ *
+ * @link http://www.ugia.cn/wp-data/imagebmp.php
+ * @author legend <legendsky@hotmail.com>
+ * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm
+ * @author mgutt <marc@gutt.it>
+ * @version 1.00
+ * @param string $fileName [optional] <p>The path to save the file to.</p>
+ * @param int $bit [optional] <p>Bit depth, (default is 24).</p>
+ * @param int $compression [optional]
+ * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
+ */
+ function imagebmp($im, $fileName = '', $bit = 24, $compression = 0) {
+ if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) {
+ $bit = 24;
+ } else if ($bit == 32) {
+ $bit = 24;
+ }
+ $bits = pow(2, $bit);
+ imagetruecolortopalette($im, true, $bits);
+ $width = imagesx($im);
+ $height = imagesy($im);
+ $colorsNum = imagecolorstotal($im);
+ $rgbQuad = '';
+ if ($bit <= 8) {
+ for ($i = 0; $i < $colorsNum; $i++) {
+ $colors = imagecolorsforindex($im, $i);
+ $rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0";
+ }
+ $bmpData = '';
+ if ($compression == 0 || $bit < 8) {
+ $compression = 0;
+ $extra = '';
+ $padding = 4 - ceil($width / (8 / $bit)) % 4;
+ if ($padding % 4 != 0) {
+ $extra = str_repeat("\0", $padding);
+ }
+ for ($j = $height - 1; $j >= 0; $j--) {
+ $i = 0;
+ while ($i < $width) {
+ $bin = 0;
+ $limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0;
+ for ($k = 8 - $bit; $k >= $limit; $k -= $bit) {
+ $index = imagecolorat($im, $i, $j);
+ $bin |= $index << $k;
+ $i++;
+ }
+ $bmpData .= chr($bin);
+ }
+ $bmpData .= $extra;
+ }
+ } // RLE8
+ else if ($compression == 1 && $bit == 8) {
+ for ($j = $height - 1; $j >= 0; $j--) {
+ $lastIndex = "\0";
+ $sameNum = 0;
+ for ($i = 0; $i <= $width; $i++) {
+ $index = imagecolorat($im, $i, $j);
+ if ($index !== $lastIndex || $sameNum > 255) {
+ if ($sameNum != 0) {
+ $bmpData .= chr($sameNum) . chr($lastIndex);
+ }
+ $lastIndex = $index;
+ $sameNum = 1;
+ } else {
+ $sameNum++;
+ }
+ }
+ $bmpData .= "\0\0";
+ }
+ $bmpData .= "\0\1";
+ }
+ $sizeQuad = strlen($rgbQuad);
+ $sizeData = strlen($bmpData);
+ } else {
+ $extra = '';
+ $padding = 4 - ($width * ($bit / 8)) % 4;
+ if ($padding % 4 != 0) {
+ $extra = str_repeat("\0", $padding);
+ }
+ $bmpData = '';
+ for ($j = $height - 1; $j >= 0; $j--) {
+ for ($i = 0; $i < $width; $i++) {
+ $index = imagecolorat($im, $i, $j);
+ $colors = imagecolorsforindex($im, $index);
+ if ($bit == 16) {
+ $bin = 0 << $bit;
+ $bin |= ($colors['red'] >> 3) << 10;
+ $bin |= ($colors['green'] >> 3) << 5;
+ $bin |= $colors['blue'] >> 3;
+ $bmpData .= pack("v", $bin);
+ } else {
+ $bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']);
+ }
+ }
+ $bmpData .= $extra;
+ }
+ $sizeQuad = 0;
+ $sizeData = strlen($bmpData);
+ $colorsNum = 0;
+ }
+ $fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
+ $infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0);
+ if ($fileName != '') {
+ $fp = fopen($fileName, 'wb');
+ fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData);
+ fclose($fp);
+ return true;
+ }
+ echo $fileHeader . $infoHeader . $rgbQuad . $bmpData;
+ return true;
+ }
+}
+
+if (!function_exists('exif_imagetype')) {
+ /**
+ * Workaround if exif_imagetype does not exist
+ *
+ * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383
+ * @param string $fileName
+ * @return string|boolean
+ */
+ function exif_imagetype($fileName) {
+ if (($info = getimagesize($fileName)) !== false) {
+ return $info[2];
+ }
+ return false;
+ }
+}
diff --git a/lib/private/legacy/installer.php b/lib/private/legacy/installer.php
new file mode 100644
index 00000000000..24c79b2dd8c
--- /dev/null
+++ b/lib/private/legacy/installer.php
@@ -0,0 +1,638 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Brice Maron <brice@bmaron.net>
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @author Christopher Schรคpers <kondou@ts.unde.re>
+ * @author Frank Karlitschek <frank@owncloud.org>
+ * @author Georg Ehrke <georg@owncloud.com>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Kamil Domanski <kdomanski@kdemail.net>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author michag86 <micha_g@arcor.de>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author root <root@oc.(none)>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Thomas Tanghus <thomas@tanghus.net>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+use OC\App\CodeChecker\CodeChecker;
+use OC\App\CodeChecker\EmptyCheck;
+use OC\App\CodeChecker\PrivateCheck;
+use OC\OCSClient;
+
+/**
+ * This class provides the functionality needed to install, update and remove plugins/apps
+ */
+class OC_Installer{
+
+ /**
+ *
+ * This function installs an app. All information needed are passed in the
+ * associative array $data.
+ * The following keys are required:
+ * - source: string, can be "path" or "http"
+ *
+ * One of the following keys is required:
+ * - path: path to the file containing the app
+ * - href: link to the downloadable file containing the app
+ *
+ * The following keys are optional:
+ * - pretend: boolean, if set true the system won't do anything
+ * - noinstall: boolean, if true appinfo/install.php won't be loaded
+ * - inactive: boolean, if set true the appconfig/app.sample.php won't be
+ * renamed
+ *
+ * This function works as follows
+ * -# fetching the file
+ * -# unzipping it
+ * -# check the code
+ * -# installing the database at appinfo/database.xml
+ * -# including appinfo/install.php
+ * -# setting the installed version
+ *
+ * It is the task of oc_app_install to create the tables and do whatever is
+ * needed to get the app working.
+ *
+ * Installs an app
+ * @param array $data with all information
+ * @throws \Exception
+ * @return integer
+ */
+ public static function installApp( $data = array()) {
+ $l = \OC::$server->getL10N('lib');
+
+ list($extractDir, $path) = self::downloadApp($data);
+
+ $info = self::checkAppsIntegrity($data, $extractDir, $path);
+ $appId = OC_App::cleanAppId($info['id']);
+ $basedir = OC_App::getInstallPath().'/'.$appId;
+ //check if the destination directory already exists
+ if(is_dir($basedir)) {
+ OC_Helper::rmdirr($extractDir);
+ if($data['source']=='http') {
+ unlink($path);
+ }
+ throw new \Exception($l->t("App directory already exists"));
+ }
+
+ if(!empty($data['pretent'])) {
+ return false;
+ }
+
+ //copy the app to the correct place
+ if(@!mkdir($basedir)) {
+ OC_Helper::rmdirr($extractDir);
+ if($data['source']=='http') {
+ unlink($path);
+ }
+ throw new \Exception($l->t("Can't create app folder. Please fix permissions. %s", array($basedir)));
+ }
+
+ $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
+ OC_Helper::rmdirr($extractDir);
+
+ //install the database
+ if(is_file($basedir.'/appinfo/database.xml')) {
+ if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) {
+ OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
+ } else {
+ OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml');
+ }
+ }
+
+ //run appinfo/install.php
+ if((!isset($data['noinstall']) or $data['noinstall']==false)) {
+ self::includeAppScript($basedir . '/appinfo/install.php');
+ }
+
+ //set the installed version
+ \OC::$server->getAppConfig()->setValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id']));
+ \OC::$server->getAppConfig()->setValue($info['id'], 'enabled', 'no');
+
+ //set remote/public handelers
+ foreach($info['remote'] as $name=>$path) {
+ OCP\CONFIG::setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
+ }
+ foreach($info['public'] as $name=>$path) {
+ OCP\CONFIG::setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
+ }
+
+ OC_App::setAppTypes($info['id']);
+
+ return $info['id'];
+ }
+
+ /**
+ * @brief checks whether or not an app is installed
+ * @param string $app app
+ * @returns bool
+ *
+ * Checks whether or not an app is installed, i.e. registered in apps table.
+ */
+ public static function isInstalled( $app ) {
+ return (\OC::$server->getAppConfig()->getValue($app, "installed_version") !== null);
+ }
+
+ /**
+ * @brief Update an application
+ * @param array $info
+ * @param bool $isShipped
+ * @throws Exception
+ * @return bool
+ *
+ * This function could work like described below, but currently it disables and then
+ * enables the app again. This does result in an updated app.
+ *
+ *
+ * This function installs an app. All information needed are passed in the
+ * associative array $info.
+ * The following keys are required:
+ * - source: string, can be "path" or "http"
+ *
+ * One of the following keys is required:
+ * - path: path to the file containing the app
+ * - href: link to the downloadable file containing the app
+ *
+ * The following keys are optional:
+ * - pretend: boolean, if set true the system won't do anything
+ * - noupgrade: boolean, if true appinfo/upgrade.php won't be loaded
+ *
+ * This function works as follows
+ * -# fetching the file
+ * -# removing the old files
+ * -# unzipping new file
+ * -# including appinfo/upgrade.php
+ * -# setting the installed version
+ *
+ * upgrade.php can determine the current installed version of the app using
+ * "\OC::$server->getAppConfig()->getValue($appid, 'installed_version')"
+ */
+ public static function updateApp($info=array(), $isShipped=false) {
+ list($extractDir, $path) = self::downloadApp($info);
+ $info = self::checkAppsIntegrity($info, $extractDir, $path, $isShipped);
+
+ $currentDir = OC_App::getAppPath($info['id']);
+ $basedir = OC_App::getInstallPath();
+ $basedir .= '/';
+ $basedir .= $info['id'];
+
+ if($currentDir !== false && is_writable($currentDir)) {
+ $basedir = $currentDir;
+ }
+ if(is_dir($basedir)) {
+ OC_Helper::rmdirr($basedir);
+ }
+
+ $appInExtractDir = $extractDir;
+ if (substr($extractDir, -1) !== '/') {
+ $appInExtractDir .= '/';
+ }
+
+ $appInExtractDir .= $info['id'];
+ OC_Helper::copyr($appInExtractDir, $basedir);
+ OC_Helper::rmdirr($extractDir);
+
+ return OC_App::updateApp($info['id']);
+ }
+
+ /**
+ * update an app by it's id
+ *
+ * @param integer $ocsId
+ * @return bool
+ * @throws Exception
+ */
+ public static function updateAppByOCSId($ocsId) {
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($ocsId, \OCP\Util::getVersion());
+ $download = $ocsClient->getApplicationDownload($ocsId, \OCP\Util::getVersion());
+
+ if (isset($download['downloadlink']) && trim($download['downloadlink']) !== '') {
+ $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
+ $info = array(
+ 'source' => 'http',
+ 'href' => $download['downloadlink'],
+ 'appdata' => $appData
+ );
+ } else {
+ throw new \Exception('Could not fetch app info!');
+ }
+
+ return self::updateApp($info);
+ }
+
+ /**
+ * @param array $data
+ * @return array
+ * @throws Exception
+ */
+ public static function downloadApp($data = array()) {
+ $l = \OC::$server->getL10N('lib');
+
+ if(!isset($data['source'])) {
+ throw new \Exception($l->t("No source specified when installing app"));
+ }
+
+ //download the file if necessary
+ if($data['source']=='http') {
+ $pathInfo = pathinfo($data['href']);
+ $extension = isset($pathInfo['extension']) ? '.' . $pathInfo['extension'] : '';
+ $path = \OC::$server->getTempManager()->getTemporaryFile($extension);
+ if(!isset($data['href'])) {
+ throw new \Exception($l->t("No href specified when installing app from http"));
+ }
+ $client = \OC::$server->getHTTPClientService()->newClient();
+ $client->get($data['href'], ['save_to' => $path]);
+ } else {
+ if(!isset($data['path'])) {
+ throw new \Exception($l->t("No path specified when installing app from local file"));
+ }
+ $path=$data['path'];
+ }
+
+ //detect the archive type
+ $mime = \OC::$server->getMimeTypeDetector()->detect($path);
+ if ($mime !=='application/zip' && $mime !== 'application/x-gzip' && $mime !== 'application/x-bzip2') {
+ throw new \Exception($l->t("Archives of type %s are not supported", array($mime)));
+ }
+
+ //extract the archive in a temporary folder
+ $extractDir = \OC::$server->getTempManager()->getTemporaryFolder();
+ OC_Helper::rmdirr($extractDir);
+ mkdir($extractDir);
+ if($archive=OC_Archive::open($path)) {
+ $archive->extract($extractDir);
+ } else {
+ OC_Helper::rmdirr($extractDir);
+ if($data['source']=='http') {
+ unlink($path);
+ }
+ throw new \Exception($l->t("Failed to open archive when installing app"));
+ }
+
+ return array(
+ $extractDir,
+ $path
+ );
+ }
+
+ /**
+ * check an app's integrity
+ * @param array $data
+ * @param string $extractDir
+ * @param string $path
+ * @param bool $isShipped
+ * @return array
+ * @throws \Exception
+ */
+ public static function checkAppsIntegrity($data, $extractDir, $path, $isShipped = false) {
+ $l = \OC::$server->getL10N('lib');
+ //load the info.xml file of the app
+ if(!is_file($extractDir.'/appinfo/info.xml')) {
+ //try to find it in a subdir
+ $dh=opendir($extractDir);
+ if(is_resource($dh)) {
+ while (($folder = readdir($dh)) !== false) {
+ if($folder[0]!='.' and is_dir($extractDir.'/'.$folder)) {
+ if(is_file($extractDir.'/'.$folder.'/appinfo/info.xml')) {
+ $extractDir.='/'.$folder;
+ }
+ }
+ }
+ }
+ }
+ if(!is_file($extractDir.'/appinfo/info.xml')) {
+ OC_Helper::rmdirr($extractDir);
+ if($data['source'] === 'http') {
+ unlink($path);
+ }
+ throw new \Exception($l->t("App does not provide an info.xml file"));
+ }
+
+ $info = OC_App::getAppInfo($extractDir.'/appinfo/info.xml', true);
+ if(!is_array($info)) {
+ throw new \Exception($l->t('App cannot be installed because appinfo file cannot be read.'));
+ }
+
+ // We can't trust the parsed info.xml file as it may have been tampered
+ // with by an attacker and thus we need to use the local data to check
+ // whether the application needs to be signed.
+ $appId = OC_App::cleanAppId($data['appdata']['id']);
+ $appBelongingToId = OC_App::getInternalAppIdByOcs($appId);
+ if(is_string($appBelongingToId)) {
+ $previouslySigned = \OC::$server->getConfig()->getAppValue($appBelongingToId, 'signed', 'false');
+ } else {
+ $appBelongingToId = $info['id'];
+ $previouslySigned = 'false';
+ }
+ if($data['appdata']['level'] === OC_App::officialApp || $previouslySigned === 'true') {
+ \OC::$server->getConfig()->setAppValue($appBelongingToId, 'signed', 'true');
+ $integrityResult = \OC::$server->getIntegrityCodeChecker()->verifyAppSignature(
+ $appBelongingToId,
+ $extractDir
+ );
+ if($integrityResult !== []) {
+ $e = new \Exception(
+ $l->t(
+ 'Signature could not get checked. Please contact the app developer and check your admin screen.'
+ )
+ );
+ throw $e;
+ }
+ }
+
+ // check the code for not allowed calls
+ if(!$isShipped && !OC_Installer::checkCode($extractDir)) {
+ OC_Helper::rmdirr($extractDir);
+ throw new \Exception($l->t("App can't be installed because of not allowed code in the App"));
+ }
+
+ // check if the app is compatible with this version of ownCloud
+ if(!OC_App::isAppCompatible(\OCP\Util::getVersion(), $info)) {
+ OC_Helper::rmdirr($extractDir);
+ throw new \Exception($l->t("App can't be installed because it is not compatible with this version of ownCloud"));
+ }
+
+ // check if shipped tag is set which is only allowed for apps that are shipped with ownCloud
+ if(!$isShipped && isset($info['shipped']) && ($info['shipped']=='true')) {
+ OC_Helper::rmdirr($extractDir);
+ throw new \Exception($l->t("App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps"));
+ }
+
+ // check if the ocs version is the same as the version in info.xml/version
+ $version = trim($info['version']);
+
+ if(isset($data['appdata']['version']) && $version<>trim($data['appdata']['version'])) {
+ OC_Helper::rmdirr($extractDir);
+ throw new \Exception($l->t("App can't be installed because the version in info.xml is not the same as the version reported from the app store"));
+ }
+
+ return $info;
+ }
+
+ /**
+ * Check if an update for the app is available
+ * @param string $app
+ * @return string|false false or the version number of the update
+ *
+ * The function will check if an update for a version is available
+ */
+ public static function isUpdateAvailable( $app ) {
+ static $isInstanceReadyForUpdates = null;
+
+ if ($isInstanceReadyForUpdates === null) {
+ $installPath = OC_App::getInstallPath();
+ if ($installPath === false || $installPath === null) {
+ $isInstanceReadyForUpdates = false;
+ } else {
+ $isInstanceReadyForUpdates = true;
+ }
+ }
+
+ if ($isInstanceReadyForUpdates === false) {
+ return false;
+ }
+
+ $ocsid=\OC::$server->getAppConfig()->getValue( $app, 'ocsid', '');
+
+ if($ocsid<>'') {
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $ocsdata = $ocsClient->getApplication($ocsid, \OCP\Util::getVersion());
+ $ocsversion= (string) $ocsdata['version'];
+ $currentversion=OC_App::getAppVersion($app);
+ if (version_compare($ocsversion, $currentversion, '>')) {
+ return($ocsversion);
+ }else{
+ return false;
+ }
+
+ }else{
+ return false;
+ }
+
+ }
+
+ /**
+ * Check if app is already downloaded
+ * @param string $name name of the application to remove
+ * @return boolean
+ *
+ * The function will check if the app is already downloaded in the apps repository
+ */
+ public static function isDownloaded( $name ) {
+ foreach(OC::$APPSROOTS as $dir) {
+ $dirToTest = $dir['path'];
+ $dirToTest .= '/';
+ $dirToTest .= $name;
+ $dirToTest .= '/';
+
+ if (is_dir($dirToTest)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes an app
+ * @param string $name name of the application to remove
+ * @param array $options options
+ * @return boolean
+ *
+ * This function removes an app. $options is an associative array. The
+ * following keys are optional:ja
+ * - keeppreferences: boolean, if true the user preferences won't be deleted
+ * - keepappconfig: boolean, if true the config will be kept
+ * - keeptables: boolean, if true the database will be kept
+ * - keepfiles: boolean, if true the user files will be kept
+ *
+ * This function works as follows
+ * -# including appinfo/remove.php
+ * -# removing the files
+ *
+ * The function will not delete preferences, tables and the configuration,
+ * this has to be done by the function oc_app_uninstall().
+ */
+ public static function removeApp( $name, $options = array()) {
+
+ if(isset($options['keeppreferences']) and $options['keeppreferences']==false ) {
+ // todo
+ // remove preferences
+ }
+
+ if(isset($options['keepappconfig']) and $options['keepappconfig']==false ) {
+ // todo
+ // remove app config
+ }
+
+ if(isset($options['keeptables']) and $options['keeptables']==false ) {
+ // todo
+ // remove app database tables
+ }
+
+ if(isset($options['keepfiles']) and $options['keepfiles']==false ) {
+ // todo
+ // remove user files
+ }
+
+ if(OC_Installer::isDownloaded( $name )) {
+ $appdir=OC_App::getInstallPath().'/'.$name;
+ OC_Helper::rmdirr($appdir);
+
+ return true;
+ }else{
+ \OCP\Util::writeLog('core', 'can\'t remove app '.$name.'. It is not installed.', \OCP\Util::ERROR);
+
+ return false;
+ }
+
+ }
+
+ /**
+ * Installs shipped apps
+ *
+ * This function installs all apps found in the 'apps' directory that should be enabled by default;
+ * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
+ * working ownCloud at the end instead of an aborted update.
+ * @return array Array of error messages (appid => Exception)
+ */
+ public static function installShippedApps($softErrors = false) {
+ $errors = [];
+ foreach(OC::$APPSROOTS as $app_dir) {
+ if($dir = opendir( $app_dir['path'] )) {
+ while( false !== ( $filename = readdir( $dir ))) {
+ if( substr( $filename, 0, 1 ) != '.' and is_dir($app_dir['path']."/$filename") ) {
+ if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) {
+ if(!OC_Installer::isInstalled($filename)) {
+ $info=OC_App::getAppInfo($filename);
+ $enabled = isset($info['default_enable']);
+ if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps()))
+ && \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') {
+ if ($softErrors) {
+ try {
+ OC_Installer::installShippedApp($filename);
+ } catch (\Doctrine\DBAL\Exception\TableExistsException $e) {
+ $errors[$filename] = $e;
+ continue;
+ }
+ } else {
+ OC_Installer::installShippedApp($filename);
+ }
+ \OC::$server->getConfig()->setAppValue($filename, 'enabled', 'yes');
+ }
+ }
+ }
+ }
+ }
+ closedir( $dir );
+ }
+ }
+
+ return $errors;
+ }
+
+ /**
+ * install an app already placed in the app folder
+ * @param string $app id of the app to install
+ * @return integer
+ */
+ public static function installShippedApp($app) {
+ //install the database
+ $appPath = OC_App::getAppPath($app);
+ if(is_file("$appPath/appinfo/database.xml")) {
+ OC_DB::createDbFromStructure("$appPath/appinfo/database.xml");
+ }
+
+ //run appinfo/install.php
+ \OC::$loader->addValidRoot($appPath);
+ self::includeAppScript("$appPath/appinfo/install.php");
+
+ $info = OC_App::getAppInfo($app);
+ if (is_null($info)) {
+ return false;
+ }
+
+ $config = \OC::$server->getConfig();
+
+ $config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app));
+ if (array_key_exists('ocsid', $info)) {
+ $config->setAppValue($app, 'ocsid', $info['ocsid']);
+ }
+
+ //set remote/public handlers
+ foreach($info['remote'] as $name=>$path) {
+ $config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
+ }
+ foreach($info['public'] as $name=>$path) {
+ $config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
+ }
+
+ OC_App::setAppTypes($info['id']);
+
+ return $info['id'];
+ }
+
+ /**
+ * check the code of an app with some static code checks
+ * @param string $folder the folder of the app to check
+ * @return boolean true for app is o.k. and false for app is not o.k.
+ */
+ public static function checkCode($folder) {
+ // is the code checker enabled?
+ if(!\OC::$server->getConfig()->getSystemValue('appcodechecker', false)) {
+ return true;
+ }
+
+ $codeChecker = new CodeChecker(new PrivateCheck(new EmptyCheck()));
+ $errors = $codeChecker->analyseFolder($folder);
+
+ return empty($errors);
+ }
+
+ /**
+ * @param $basedir
+ */
+ private static function includeAppScript($script) {
+ if ( file_exists($script) ){
+ include $script;
+ }
+ }
+}
diff --git a/lib/private/legacy/json.php b/lib/private/legacy/json.php
new file mode 100644
index 00000000000..74aebd476fb
--- /dev/null
+++ b/lib/private/legacy/json.php
@@ -0,0 +1,179 @@
+<?php
+/**
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @author Felix Moeller <mail@felixmoeller.de>
+ * @author Georg Ehrke <georg@owncloud.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Thomas Tanghus <thomas@tanghus.net>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+/**
+ * Class OC_JSON
+ * @deprecated Use a AppFramework JSONResponse instead
+ */
+class OC_JSON{
+ static protected $send_content_type_header = false;
+ /**
+ * set Content-Type header to jsonrequest
+ * @deprecated Use a AppFramework JSONResponse instead
+ */
+ public static function setContentTypeHeader($type='application/json') {
+ if (!self::$send_content_type_header) {
+ // We send json data
+ header( 'Content-Type: '.$type . '; charset=utf-8');
+ self::$send_content_type_header = true;
+ }
+ }
+
+ /**
+ * Check if the app is enabled, send json error msg if not
+ * @param string $app
+ * @deprecated Use the AppFramework instead. It will automatically check if the app is enabled.
+ */
+ public static function checkAppEnabled($app) {
+ if( !OC_App::isEnabled($app)) {
+ $l = \OC::$server->getL10N('lib');
+ self::error(array( 'data' => array( 'message' => $l->t('Application is not enabled'), 'error' => 'application_not_enabled' )));
+ exit();
+ }
+ }
+
+ /**
+ * Check if the user is logged in, send json error msg if not
+ * @deprecated Use annotation based ACLs from the AppFramework instead
+ */
+ public static function checkLoggedIn() {
+ if( !OC_User::isLoggedIn()) {
+ $l = \OC::$server->getL10N('lib');
+ http_response_code(\OCP\AppFramework\Http::STATUS_UNAUTHORIZED);
+ self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' )));
+ exit();
+ }
+ }
+
+ /**
+ * Check an ajax get/post call if the request token is valid, send json error msg if not.
+ * @deprecated Use annotation based CSRF checks from the AppFramework instead
+ */
+ public static function callCheck() {
+ if( !(\OC::$server->getRequest()->passesCSRFCheck())) {
+ $l = \OC::$server->getL10N('lib');
+ self::error(array( 'data' => array( 'message' => $l->t('Token expired. Please reload page.'), 'error' => 'token_expired' )));
+ exit();
+ }
+ }
+
+ /**
+ * Check if the user is a admin, send json error msg if not.
+ * @deprecated Use annotation based ACLs from the AppFramework instead
+ */
+ public static function checkAdminUser() {
+ if( !OC_User::isAdminUser(OC_User::getUser())) {
+ $l = \OC::$server->getL10N('lib');
+ self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' )));
+ exit();
+ }
+ }
+
+ /**
+ * Check is a given user exists - send json error msg if not
+ * @param string $user
+ * @deprecated Use a AppFramework JSONResponse instead
+ */
+ public static function checkUserExists($user) {
+ if (!OCP\User::userExists($user)) {
+ $l = \OC::$server->getL10N('lib');
+ OCP\JSON::error(array('data' => array('message' => $l->t('Unknown user'), 'error' => 'unknown_user' )));
+ exit;
+ }
+ }
+
+
+ /**
+ * Check if the user is a subadmin, send json error msg if not
+ * @deprecated Use annotation based ACLs from the AppFramework instead
+ */
+ public static function checkSubAdminUser() {
+ $userObject = \OC::$server->getUserSession()->getUser();
+ $isSubAdmin = false;
+ if($userObject !== null) {
+ $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
+ }
+
+ if(!$isSubAdmin) {
+ $l = \OC::$server->getL10N('lib');
+ self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' )));
+ exit();
+ }
+ }
+
+ /**
+ * Send json error msg
+ * @deprecated Use a AppFramework JSONResponse instead
+ */
+ public static function error($data = array()) {
+ $data['status'] = 'error';
+ self::encodedPrint($data);
+ }
+
+ /**
+ * Send json success msg
+ * @deprecated Use a AppFramework JSONResponse instead
+ */
+ public static function success($data = array()) {
+ $data['status'] = 'success';
+ self::encodedPrint($data);
+ }
+
+ /**
+ * Convert OC_L10N_String to string, for use in json encodings
+ */
+ protected static function to_string(&$value) {
+ if ($value instanceof OC_L10N_String) {
+ $value = (string)$value;
+ }
+ }
+
+ /**
+ * Encode and print $data in json format
+ * @deprecated Use a AppFramework JSONResponse instead
+ */
+ public static function encodedPrint($data, $setContentType=true) {
+ if($setContentType) {
+ self::setContentTypeHeader();
+ }
+ echo self::encode($data);
+ }
+
+ /**
+ * Encode JSON
+ * @deprecated Use a AppFramework JSONResponse instead
+ */
+ public static function encode($data) {
+ if (is_array($data)) {
+ array_walk_recursive($data, array('OC_JSON', 'to_string'));
+ }
+ return json_encode($data, JSON_HEX_TAG);
+ }
+}
diff --git a/lib/private/legacy/ocs.php b/lib/private/legacy/ocs.php
new file mode 100644
index 00000000000..f48879a98b1
--- /dev/null
+++ b/lib/private/legacy/ocs.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Frank Karlitschek <frank@owncloud.org>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+use OCP\API;
+
+/**
+ * Class to handle open collaboration services API requests
+ */
+class OC_OCS {
+ /**
+ * Called when a not existing OCS endpoint has been called
+ */
+ public static function notFound() {
+ $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";
+ OC_API::respond(new OC_OCS_Result(null, API::RESPOND_UNKNOWN_ERROR, $txt), $format);
+ }
+
+}
diff --git a/lib/private/legacy/response.php b/lib/private/legacy/response.php
new file mode 100644
index 00000000000..51e0ff75e6a
--- /dev/null
+++ b/lib/private/legacy/response.php
@@ -0,0 +1,268 @@
+<?php
+/**
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+class OC_Response {
+ const STATUS_FOUND = 304;
+ const STATUS_NOT_MODIFIED = 304;
+ const STATUS_TEMPORARY_REDIRECT = 307;
+ const STATUS_BAD_REQUEST = 400;
+ const STATUS_NOT_FOUND = 404;
+ const STATUS_INTERNAL_SERVER_ERROR = 500;
+ const STATUS_SERVICE_UNAVAILABLE = 503;
+
+ /**
+ * Enable response caching by sending correct HTTP headers
+ * @param integer $cache_time time to cache the response
+ * >0 cache time in seconds
+ * 0 and <0 enable default browser caching
+ * null cache indefinitely
+ */
+ static public function enableCaching($cache_time = null) {
+ if (is_numeric($cache_time)) {
+ header('Pragma: public');// enable caching in IE
+ if ($cache_time > 0) {
+ self::setExpiresHeader('PT'.$cache_time.'S');
+ header('Cache-Control: max-age='.$cache_time.', must-revalidate');
+ }
+ else {
+ self::setExpiresHeader(0);
+ header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+ }
+ }
+ else {
+ header('Cache-Control: cache');
+ header('Pragma: cache');
+ }
+
+ }
+
+ /**
+ * disable browser caching
+ * @see enableCaching with cache_time = 0
+ */
+ static public function disableCaching() {
+ self::enableCaching(0);
+ }
+
+ /**
+ * Set response status
+ * @param int $status a HTTP status code, see also the STATUS constants
+ */
+ static public function setStatus($status) {
+ $protocol = \OC::$server->getRequest()->getHttpProtocol();
+ switch($status) {
+ case self::STATUS_NOT_MODIFIED:
+ $status = $status . ' Not Modified';
+ break;
+ case self::STATUS_TEMPORARY_REDIRECT:
+ if ($protocol == 'HTTP/1.1') {
+ $status = $status . ' Temporary Redirect';
+ break;
+ } else {
+ $status = self::STATUS_FOUND;
+ // fallthrough
+ }
+ case self::STATUS_FOUND;
+ $status = $status . ' Found';
+ break;
+ case self::STATUS_NOT_FOUND;
+ $status = $status . ' Not Found';
+ break;
+ case self::STATUS_INTERNAL_SERVER_ERROR;
+ $status = $status . ' Internal Server Error';
+ break;
+ case self::STATUS_SERVICE_UNAVAILABLE;
+ $status = $status . ' Service Unavailable';
+ break;
+ }
+ header($protocol.' '.$status);
+ }
+
+ /**
+ * Send redirect response
+ * @param string $location to redirect to
+ */
+ static public function redirect($location) {
+ self::setStatus(self::STATUS_TEMPORARY_REDIRECT);
+ header('Location: '.$location);
+ }
+
+ /**
+ * Set response expire time
+ * @param string|DateTime $expires date-time when the response expires
+ * string for DateInterval from now
+ * DateTime object when to expire response
+ */
+ static public function setExpiresHeader($expires) {
+ if (is_string($expires) && $expires[0] == 'P') {
+ $interval = $expires;
+ $expires = new DateTime('now');
+ $expires->add(new DateInterval($interval));
+ }
+ if ($expires instanceof DateTime) {
+ $expires->setTimezone(new DateTimeZone('GMT'));
+ $expires = $expires->format(DateTime::RFC2822);
+ }
+ header('Expires: '.$expires);
+ }
+
+ /**
+ * Checks and set ETag header, when the request matches sends a
+ * 'not modified' response
+ * @param string $etag token to use for modification check
+ */
+ static public function setETagHeader($etag) {
+ if (empty($etag)) {
+ return;
+ }
+ $etag = '"'.$etag.'"';
+ if (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
+ trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) {
+ self::setStatus(self::STATUS_NOT_MODIFIED);
+ exit;
+ }
+ header('ETag: '.$etag);
+ }
+
+ /**
+ * Checks and set Last-Modified header, when the request matches sends a
+ * 'not modified' response
+ * @param int|DateTime|string $lastModified time when the response was last modified
+ */
+ static public function setLastModifiedHeader($lastModified) {
+ if (empty($lastModified)) {
+ return;
+ }
+ if (is_int($lastModified)) {
+ $lastModified = gmdate(DateTime::RFC2822, $lastModified);
+ }
+ if ($lastModified instanceof DateTime) {
+ $lastModified = $lastModified->format(DateTime::RFC2822);
+ }
+ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
+ trim($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {
+ self::setStatus(self::STATUS_NOT_MODIFIED);
+ exit;
+ }
+ header('Last-Modified: '.$lastModified);
+ }
+
+ /**
+ * Sets the content disposition header (with possible workarounds)
+ * @param string $filename file name
+ * @param string $type disposition type, either 'attachment' or 'inline'
+ */
+ static public function setContentDispositionHeader( $filename, $type = 'attachment' ) {
+ if (\OC::$server->getRequest()->isUserAgent(
+ [
+ \OC\AppFramework\Http\Request::USER_AGENT_IE,
+ \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
+ \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
+ ])) {
+ header( 'Content-Disposition: ' . rawurlencode($type) . '; filename="' . rawurlencode( $filename ) . '"' );
+ } else {
+ header( 'Content-Disposition: ' . rawurlencode($type) . '; filename*=UTF-8\'\'' . rawurlencode( $filename )
+ . '; filename="' . rawurlencode( $filename ) . '"' );
+ }
+ }
+
+ /**
+ * Sets the content length header (with possible workarounds)
+ * @param string|int|float $length Length to be sent
+ */
+ static public function setContentLengthHeader($length) {
+ if (PHP_INT_SIZE === 4) {
+ if ($length > PHP_INT_MAX && stripos(PHP_SAPI, 'apache') === 0) {
+ // Apache PHP SAPI casts Content-Length headers to PHP integers.
+ // This enforces a limit of PHP_INT_MAX (2147483647 on 32-bit
+ // platforms). So, if the length is greater than PHP_INT_MAX,
+ // we just do not send a Content-Length header to prevent
+ // bodies from being received incompletely.
+ return;
+ }
+ // Convert signed integer or float to unsigned base-10 string.
+ $lfh = new \OC\LargeFileHelper;
+ $length = $lfh->formatUnsignedInteger($length);
+ }
+ header('Content-Length: '.$length);
+ }
+
+ /**
+ * Send file as response, checking and setting caching headers
+ * @param string $filepath of file to send
+ * @deprecated 8.1.0 - Use \OCP\AppFramework\Http\StreamResponse or another AppFramework controller instead
+ */
+ static public function sendFile($filepath) {
+ $fp = fopen($filepath, 'rb');
+ if ($fp) {
+ self::setLastModifiedHeader(filemtime($filepath));
+ self::setETagHeader(md5_file($filepath));
+
+ self::setContentLengthHeader(filesize($filepath));
+ fpassthru($fp);
+ }
+ else {
+ self::setStatus(self::STATUS_NOT_FOUND);
+ }
+ }
+
+ /**
+ * This function adds some security related headers to all requests served via base.php
+ * The implementation of this function has to happen here to ensure that all third-party
+ * components (e.g. SabreDAV) also benefit from this headers.
+ */
+ public static function addSecurityHeaders() {
+ /**
+ * FIXME: Content Security Policy for legacy ownCloud components. This
+ * can be removed once \OCP\AppFramework\Http\Response from the AppFramework
+ * is used everywhere.
+ * @see \OCP\AppFramework\Http\Response::getHeaders
+ */
+ $policy = 'default-src \'self\'; '
+ . 'script-src \'self\' \'unsafe-eval\'; '
+ . 'style-src \'self\' \'unsafe-inline\'; '
+ . 'frame-src *; '
+ . 'img-src * data: blob:; '
+ . 'font-src \'self\' data:; '
+ . 'media-src *; '
+ . 'connect-src *';
+ header('Content-Security-Policy:' . $policy);
+
+ // Send fallback headers for installations that don't have the possibility to send
+ // custom headers on the webserver side
+ if(getenv('modHeadersAvailable') !== 'true') {
+ header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters
+ header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE
+ header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains
+ header('X-Robots-Tag: none'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
+ header('X-Download-Options: noopen'); // https://msdn.microsoft.com/en-us/library/jj542450(v=vs.85).aspx
+ header('X-Permitted-Cross-Domain-Policies: none'); // https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
+ }
+ }
+
+}
diff --git a/lib/private/legacy/template.php b/lib/private/legacy/template.php
new file mode 100644
index 00000000000..84b963e4ab6
--- /dev/null
+++ b/lib/private/legacy/template.php
@@ -0,0 +1,433 @@
+<?php
+/**
+ * @author Adam Williamson <awilliam@redhat.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Brice Maron <brice@bmaron.net>
+ * @author Frank Karlitschek <frank@owncloud.org>
+ * @author Hendrik Leppelsack <hendrik@leppelsack.de>
+ * @author Individual IT Services <info@individual-it.net>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Raghu Nayyar <hey@raghunayyar.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+use OC\TemplateLayout;
+
+require_once __DIR__.'/../template/functions.php';
+
+/**
+ * This class provides the templates for ownCloud.
+ */
+class OC_Template extends \OC\Template\Base {
+
+ /** @var string */
+ private $renderAs; // Create a full page?
+
+ /** @var string */
+ private $path; // The path to the template
+
+ /** @var array */
+ private $headers = array(); //custom headers
+
+ /** @var string */
+ protected $app; // app id
+
+ protected static $initTemplateEngineFirstRun = true;
+
+ /**
+ * Constructor
+ *
+ * @param string $app app providing the template
+ * @param string $name of the template file (without suffix)
+ * @param string $renderAs If $renderAs is set, OC_Template will try to
+ * produce a full page in the according layout. For
+ * now, $renderAs can be set to "guest", "user" or
+ * "admin".
+ * @param bool $registerCall = true
+ */
+ public function __construct( $app, $name, $renderAs = "", $registerCall = true ) {
+ // Read the selected theme from the config file
+ self::initTemplateEngine($renderAs);
+
+ $theme = OC_Util::getTheme();
+
+ $requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : '';
+
+ $parts = explode('/', $app); // fix translation when app is something like core/lostpassword
+ $l10n = \OC::$server->getL10N($parts[0]);
+ $themeDefaults = new OC_Defaults();
+
+ list($path, $template) = $this->findTemplate($theme, $app, $name);
+
+ // Set the private data
+ $this->renderAs = $renderAs;
+ $this->path = $path;
+ $this->app = $app;
+
+ parent::__construct($template, $requestToken, $l10n, $themeDefaults);
+ }
+
+ /**
+ * @param string $renderAs
+ */
+ public static function initTemplateEngine($renderAs) {
+ if (self::$initTemplateEngineFirstRun){
+
+ //apps that started before the template initialization can load their own scripts/styles
+ //so to make sure this scripts/styles here are loaded first we use OC_Util::addScript() with $prepend=true
+ //meaning the last script/style in this list will be loaded first
+ if (\OC::$server->getSystemConfig()->getValue ('installed', false) && $renderAs !== 'error' && !\OCP\Util::needUpgrade()) {
+ if (\OC::$server->getConfig ()->getAppValue ( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax') {
+ OC_Util::addScript ( 'backgroundjobs', null, true );
+ }
+ }
+
+ OC_Util::addStyle("tooltip",null,true);
+ OC_Util::addStyle('jquery-ui-fixes',null,true);
+ OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true);
+ OC_Util::addStyle("mobile",null,true);
+ OC_Util::addStyle("multiselect",null,true);
+ OC_Util::addStyle("fixes",null,true);
+ OC_Util::addStyle("global",null,true);
+ OC_Util::addStyle("apps",null,true);
+ OC_Util::addStyle("fonts",null,true);
+ OC_Util::addStyle("icons",null,true);
+ OC_Util::addStyle("header",null,true);
+ OC_Util::addStyle("inputs",null,true);
+ OC_Util::addStyle("styles",null,true);
+
+ // avatars
+ if (\OC::$server->getSystemConfig()->getValue('enable_avatars', true) === true) {
+ \OC_Util::addScript('jquery.avatar', null, true);
+ \OC_Util::addScript('placeholder', null, true);
+ }
+
+ OC_Util::addScript('oc-backbone', null, true);
+ OC_Util::addVendorScript('core', 'backbone/backbone', true);
+ OC_Util::addVendorScript('snapjs/dist/latest/snap', null, true);
+ OC_Util::addScript('mimetypelist', null, true);
+ OC_Util::addScript('mimetype', null, true);
+ OC_Util::addScript("apps", null, true);
+ OC_Util::addScript("oc-requesttoken", null, true);
+ OC_Util::addScript('search', 'search', true);
+ OC_Util::addScript("config", null, true);
+ OC_Util::addScript("eventsource", null, true);
+ OC_Util::addScript("octemplate", null, true);
+ OC_Util::addTranslations("core", null, true);
+ OC_Util::addScript("l10n", null, true);
+ OC_Util::addScript("js", null, true);
+ OC_Util::addScript("oc-dialogs", null, true);
+ OC_Util::addScript("jquery.ocdialog", null, true);
+ OC_Util::addStyle("jquery.ocdialog");
+ OC_Util::addScript("compatibility", null, true);
+ OC_Util::addScript("placeholders", null, true);
+ OC_Util::addScript('files/fileinfo');
+ OC_Util::addScript('files/client');
+
+ // Add the stuff we need always
+ // following logic will import all vendor libraries that are
+ // specified in core/js/core.json
+ $fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json');
+ if($fileContent !== false) {
+ $coreDependencies = json_decode($fileContent, true);
+ foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) {
+ // remove trailing ".js" as addVendorScript will append it
+ OC_Util::addVendorScript(
+ substr($vendorLibrary, 0, strlen($vendorLibrary) - 3),null,true);
+ }
+ } else {
+ throw new \Exception('Cannot read core/js/core.json');
+ }
+
+ if (\OC::$server->getRequest()->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE])) {
+ // polyfill for btoa/atob for IE friends
+ OC_Util::addVendorScript('base64/base64');
+ // shim for the davclient.js library
+ \OCP\Util::addScript('files/iedavclient');
+ }
+
+ self::$initTemplateEngineFirstRun = false;
+ }
+
+ }
+
+
+ /**
+ * find the template with the given name
+ * @param string $name of the template file (without suffix)
+ *
+ * Will select the template file for the selected theme.
+ * Checking all the possible locations.
+ * @param string $theme
+ * @param string $app
+ * @return string[]
+ */
+ protected function findTemplate($theme, $app, $name) {
+ // Check if it is a app template or not.
+ if( $app !== '' ) {
+ $dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app));
+ } else {
+ $dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT);
+ }
+ $locator = new \OC\Template\TemplateFileLocator( $dirs );
+ $template = $locator->find($name);
+ $path = $locator->getPath();
+ return array($path, $template);
+ }
+
+ /**
+ * Add a custom element to the header
+ * @param string $tag tag name of the element
+ * @param array $attributes array of attributes for the element
+ * @param string $text the text content for the element. If $text is null then the
+ * element will be written as empty element. So use "" to get a closing tag.
+ */
+ public function addHeader($tag, $attributes, $text=null) {
+ $this->headers[]= array(
+ 'tag' => $tag,
+ 'attributes' => $attributes,
+ 'text' => $text
+ );
+ }
+
+ /**
+ * Process the template
+ * @return boolean|string
+ *
+ * This function process the template. If $this->renderAs is set, it
+ * will produce a full page.
+ */
+ public function fetchPage($additionalParams = null) {
+ $data = parent::fetchPage($additionalParams);
+
+ if( $this->renderAs ) {
+ $page = new TemplateLayout($this->renderAs, $this->app);
+
+ // Add custom headers
+ $headers = '';
+ foreach(OC_Util::$headers as $header) {
+ $headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']);
+ foreach($header['attributes'] as $name=>$value) {
+ $headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
+ }
+ if ($header['text'] !== null) {
+ $headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>';
+ } else {
+ $headers .= '/>';
+ }
+ }
+
+ $page->assign('headers', $headers);
+
+ $page->assign('content', $data);
+ return $page->fetchPage();
+ }
+
+ return $data;
+ }
+
+ /**
+ * Include template
+ *
+ * @param string $file
+ * @param array|null $additionalParams
+ * @return string returns content of included template
+ *
+ * Includes another template. use <?php echo $this->inc('template'); ?> to
+ * do this.
+ */
+ public function inc( $file, $additionalParams = null ) {
+ return $this->load($this->path.$file.'.php', $additionalParams);
+ }
+
+ /**
+ * Shortcut to print a simple page for users
+ * @param string $application The application we render the template for
+ * @param string $name Name of the template
+ * @param array $parameters Parameters for the template
+ * @return boolean|null
+ */
+ public static function printUserPage( $application, $name, $parameters = array() ) {
+ $content = new OC_Template( $application, $name, "user" );
+ foreach( $parameters as $key => $value ) {
+ $content->assign( $key, $value );
+ }
+ print $content->printPage();
+ }
+
+ /**
+ * Shortcut to print a simple page for admins
+ * @param string $application The application we render the template for
+ * @param string $name Name of the template
+ * @param array $parameters Parameters for the template
+ * @return bool
+ */
+ public static function printAdminPage( $application, $name, $parameters = array() ) {
+ $content = new OC_Template( $application, $name, "admin" );
+ foreach( $parameters as $key => $value ) {
+ $content->assign( $key, $value );
+ }
+ return $content->printPage();
+ }
+
+ /**
+ * Shortcut to print a simple page for guests
+ * @param string $application The application we render the template for
+ * @param string $name Name of the template
+ * @param array|string $parameters Parameters for the template
+ * @return bool
+ */
+ public static function printGuestPage( $application, $name, $parameters = array() ) {
+ $content = new OC_Template( $application, $name, "guest" );
+ foreach( $parameters as $key => $value ) {
+ $content->assign( $key, $value );
+ }
+ return $content->printPage();
+ }
+
+ /**
+ * Print a fatal error page and terminates the script
+ * @param string $error_msg The error message to show
+ * @param string $hint An optional hint message - needs to be properly escaped
+ */
+ public static function printErrorPage( $error_msg, $hint = '' ) {
+ try {
+ $content = new \OC_Template( '', 'error', 'error', false );
+ $errors = array(array('error' => $error_msg, 'hint' => $hint));
+ $content->assign( 'errors', $errors );
+ $content->printPage();
+ } catch (\Exception $e) {
+ $logger = \OC::$server->getLogger();
+ $logger->error("$error_msg $hint", ['app' => 'core']);
+ $logger->logException($e, ['app' => 'core']);
+
+ header(self::getHttpProtocol() . ' 500 Internal Server Error');
+ header('Content-Type: text/plain; charset=utf-8');
+ print("$error_msg $hint");
+ }
+ die();
+ }
+
+ /**
+ * print error page using Exception details
+ * @param Exception | Throwable $exception
+ */
+ public static function printExceptionErrorPage($exception, $fetchPage = false) {
+ try {
+ $request = \OC::$server->getRequest();
+ $content = new \OC_Template('', 'exception', 'error', false);
+ $content->assign('errorClass', get_class($exception));
+ $content->assign('errorMsg', $exception->getMessage());
+ $content->assign('errorCode', $exception->getCode());
+ $content->assign('file', $exception->getFile());
+ $content->assign('line', $exception->getLine());
+ $content->assign('trace', $exception->getTraceAsString());
+ $content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false));
+ $content->assign('remoteAddr', $request->getRemoteAddress());
+ $content->assign('requestID', $request->getId());
+ if ($fetchPage) {
+ return $content->fetchPage();
+ }
+ $content->printPage();
+ } catch (\Exception $e) {
+ $logger = \OC::$server->getLogger();
+ $logger->logException($exception, ['app' => 'core']);
+ $logger->logException($e, ['app' => 'core']);
+
+ header(self::getHttpProtocol() . ' 500 Internal Server Error');
+ header('Content-Type: text/plain; charset=utf-8');
+ print("Internal Server Error\n\n");
+ print("The server encountered an internal error and was unable to complete your request.\n");
+ print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n");
+ print("More details can be found in the server log.\n");
+ }
+ die();
+ }
+
+ /**
+ * This is only here to reduce the dependencies in case of an exception to
+ * still be able to print a plain error message.
+ *
+ * Returns the used HTTP protocol.
+ *
+ * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
+ * @internal Don't use this - use AppFramework\Http\Request->getHttpProtocol instead
+ */
+ protected static function getHttpProtocol() {
+ $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
+ $validProtocols = [
+ 'HTTP/1.0',
+ 'HTTP/1.1',
+ 'HTTP/2',
+ ];
+ if(in_array($claimedProtocol, $validProtocols, true)) {
+ return $claimedProtocol;
+ }
+ return 'HTTP/1.1';
+ }
+
+ /**
+ * @return bool
+ */
+ public static function isAssetPipelineEnabled() {
+ try {
+ if (\OCP\Util::needUpgrade()) {
+ // Don't use the compiled asset when we need to do an update
+ return false;
+ }
+ } catch (\Exception $e) {
+ // Catch any exception, because this code is also called when displaying
+ // an exception error page.
+ return false;
+ }
+
+ // asset management enabled?
+ $config = \OC::$server->getConfig();
+ $useAssetPipeline = $config->getSystemValue('asset-pipeline.enabled', false);
+ if (!$useAssetPipeline) {
+ return false;
+ }
+
+ // assets folder exists?
+ $assetDir = $config->getSystemValue('assetdirectory', \OC::$SERVERROOT) . '/assets';
+ if (!is_dir($assetDir)) {
+ if (!mkdir($assetDir)) {
+ \OCP\Util::writeLog('assets',
+ "Folder <$assetDir> does not exist and/or could not be generated.", \OCP\Util::ERROR);
+ return false;
+ }
+ }
+
+ // assets folder can be accessed?
+ if (!touch($assetDir."/.oc")) {
+ \OCP\Util::writeLog('assets',
+ "Folder <$assetDir> could not be accessed.", \OCP\Util::ERROR);
+ return false;
+ }
+ return $useAssetPipeline;
+ }
+
+}
diff --git a/lib/private/legacy/user.php b/lib/private/legacy/user.php
new file mode 100644
index 00000000000..11c35daa0de
--- /dev/null
+++ b/lib/private/legacy/user.php
@@ -0,0 +1,639 @@
+<?php
+/**
+ * @author Aldo "xoen" Giambelluca <xoen@xoen.org>
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bartek Przybylski <bart.p.pl@gmail.com>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Florian Preinstorfer <nblock@archlinux.us>
+ * @author Georg Ehrke <georg@owncloud.com>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author shkdee <louis.traynard@m4x.org>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Tom Needham <tom@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+/**
+ * This class provides wrapper methods for user management. Multiple backends are
+ * supported. User management operations are delegated to the configured backend for
+ * execution.
+ *
+ * Note that &run is deprecated and won't work anymore.
+ *
+ * Hooks provided:
+ * pre_createUser(&run, uid, password)
+ * post_createUser(uid, password)
+ * pre_deleteUser(&run, uid)
+ * post_deleteUser(uid)
+ * pre_setPassword(&run, uid, password, recoveryPassword)
+ * post_setPassword(uid, password, recoveryPassword)
+ * pre_login(&run, uid, password)
+ * post_login(uid)
+ * logout()
+ */
+class OC_User {
+
+ /**
+ * @return \OC\User\Session
+ */
+ public static function getUserSession() {
+ return OC::$server->getUserSession();
+ }
+
+ private static $_backends = array();
+
+ private static $_usedBackends = array();
+
+ private static $_setupedBackends = array();
+
+ // bool, stores if a user want to access a resource anonymously, e.g if he opens a public link
+ private static $incognitoMode = false;
+
+ /**
+ * Adds the backend to the list of used backends
+ *
+ * @param string|\OCP\UserInterface $backend default: database The backend to use for user management
+ * @return bool
+ *
+ * Set the User Authentication Module
+ */
+ public static function useBackend($backend = 'database') {
+ if ($backend instanceof \OCP\UserInterface) {
+ self::$_usedBackends[get_class($backend)] = $backend;
+ \OC::$server->getUserManager()->registerBackend($backend);
+ } else {
+ // You'll never know what happens
+ if (null === $backend OR !is_string($backend)) {
+ $backend = 'database';
+ }
+
+ // Load backend
+ switch ($backend) {
+ case 'database':
+ case 'mysql':
+ case 'sqlite':
+ \OCP\Util::writeLog('core', 'Adding user backend ' . $backend . '.', \OCP\Util::DEBUG);
+ self::$_usedBackends[$backend] = new OC_User_Database();
+ \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
+ break;
+ case 'dummy':
+ self::$_usedBackends[$backend] = new \Test\Util\User\Dummy();
+ \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
+ break;
+ default:
+ \OCP\Util::writeLog('core', 'Adding default user backend ' . $backend . '.', \OCP\Util::DEBUG);
+ $className = 'OC_USER_' . strToUpper($backend);
+ self::$_usedBackends[$backend] = new $className();
+ \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
+ break;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * remove all used backends
+ */
+ public static function clearBackends() {
+ self::$_usedBackends = array();
+ \OC::$server->getUserManager()->clearBackends();
+ }
+
+ /**
+ * setup the configured backends in config.php
+ */
+ public static function setupBackends() {
+ OC_App::loadApps(array('prelogin'));
+ $backends = \OC::$server->getSystemConfig()->getValue('user_backends', array());
+ foreach ($backends as $i => $config) {
+ $class = $config['class'];
+ $arguments = $config['arguments'];
+ if (class_exists($class)) {
+ if (array_search($i, self::$_setupedBackends) === false) {
+ // make a reflection object
+ $reflectionObj = new ReflectionClass($class);
+
+ // use Reflection to create a new instance, using the $args
+ $backend = $reflectionObj->newInstanceArgs($arguments);
+ self::useBackend($backend);
+ self::$_setupedBackends[] = $i;
+ } else {
+ \OCP\Util::writeLog('core', 'User backend ' . $class . ' already initialized.', \OCP\Util::DEBUG);
+ }
+ } else {
+ \OCP\Util::writeLog('core', 'User backend ' . $class . ' not found.', \OCP\Util::ERROR);
+ }
+ }
+ }
+
+ /**
+ * Try to login a user
+ *
+ * @param string $loginname The login name of the user to log in
+ * @param string $password The password of the user
+ * @return boolean|null
+ *
+ * Log in a user and regenerate a new session - if the password is ok
+ */
+ public static function login($loginname, $password) {
+ $result = self::getUserSession()->login($loginname, $password);
+ if ($result) {
+ // Refresh the token
+ \OC::$server->getCsrfTokenManager()->refreshToken();
+ //we need to pass the user name, which may differ from login name
+ $user = self::getUserSession()->getUser()->getUID();
+ OC_Util::setupFS($user);
+ //trigger creation of user home and /files folder
+ \OC::$server->getUserFolder($user);
+ }
+ return $result;
+ }
+
+ /**
+ * Try to login a user using the magic cookie (remember login)
+ *
+ * @param string $uid The username of the user to log in
+ * @param string $token
+ * @return bool
+ */
+ public static function loginWithCookie($uid, $token) {
+ return self::getUserSession()->loginWithCookie($uid, $token);
+ }
+
+ /**
+ * Try to login a user, assuming authentication
+ * has already happened (e.g. via Single Sign On).
+ *
+ * Log in a user and regenerate a new session.
+ *
+ * @param \OCP\Authentication\IApacheBackend $backend
+ * @return bool
+ */
+ public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) {
+
+ $uid = $backend->getCurrentUserId();
+ $run = true;
+ OC_Hook::emit("OC_User", "pre_login", array("run" => &$run, "uid" => $uid));
+
+ if ($uid) {
+ if (self::getUser() !== $uid) {
+ self::setUserId($uid);
+ self::setDisplayName($uid);
+ self::getUserSession()->setLoginName($uid);
+ // setup the filesystem
+ OC_Util::setupFS($uid);
+ //trigger creation of user home and /files folder
+ \OC::$server->getUserFolder($uid);
+
+ OC_Hook::emit("OC_User", "post_login", array("uid" => $uid, 'password' => ''));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Verify with Apache whether user is authenticated.
+ *
+ * @return boolean|null
+ * true: authenticated
+ * false: not authenticated
+ * null: not handled / no backend available
+ */
+ public static function handleApacheAuth() {
+ $backend = self::findFirstActiveUsedBackend();
+ if ($backend) {
+ OC_App::loadApps();
+
+ //setup extra user backends
+ self::setupBackends();
+ self::unsetMagicInCookie();
+
+ return self::loginWithApache($backend);
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Sets user id for session and triggers emit
+ */
+ public static function setUserId($uid) {
+ $userSession = \OC::$server->getUserSession();
+ $userManager = \OC::$server->getUserManager();
+ if ($user = $userManager->get($uid)) {
+ $userSession->setUser($user);
+ } else {
+ \OC::$server->getSession()->set('user_id', $uid);
+ }
+ }
+
+ /**
+ * Sets user display name for session
+ *
+ * @param string $uid
+ * @param string $displayName
+ * @return bool Whether the display name could get set
+ */
+ public static function setDisplayName($uid, $displayName = null) {
+ if (is_null($displayName)) {
+ $displayName = $uid;
+ }
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ return $user->setDisplayName($displayName);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Tries to login the user with HTTP Basic Authentication
+ */
+ public static function tryBasicAuthLogin() {
+ if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
+ $result = \OC_User::login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
+ if($result === true) {
+ /**
+ * Add DAV authenticated. This should in an ideal world not be
+ * necessary but the iOS App reads cookies from anywhere instead
+ * only the DAV endpoint.
+ * This makes sure that the cookies will be valid for the whole scope
+ * @see https://github.com/owncloud/core/issues/22893
+ */
+ \OC::$server->getSession()->set(
+ \OCA\DAV\Connector\Sabre\Auth::DAV_AUTHENTICATED,
+ \OC::$server->getUserSession()->getUser()->getUID()
+ );
+ }
+ }
+ }
+
+ /**
+ * Check if the user is logged in, considers also the HTTP basic credentials
+ *
+ * @return bool
+ */
+ public static function isLoggedIn() {
+ if (\OC::$server->getSession()->get('user_id') !== null && self::$incognitoMode === false) {
+ return self::userExists(\OC::$server->getSession()->get('user_id'));
+ }
+
+ return false;
+ }
+
+ /**
+ * set incognito mode, e.g. if a user wants to open a public link
+ *
+ * @param bool $status
+ */
+ public static function setIncognitoMode($status) {
+ self::$incognitoMode = $status;
+ }
+
+ /**
+ * get incognito mode status
+ *
+ * @return bool
+ */
+ public static function isIncognitoMode() {
+ return self::$incognitoMode;
+ }
+
+ /**
+ * Supplies an attribute to the logout hyperlink. The default behaviour
+ * is to return an href with '?logout=true' appended. However, it can
+ * supply any attribute(s) which are valid for <a>.
+ *
+ * @return string with one or more HTML attributes.
+ */
+ public static function getLogoutAttribute() {
+ $backend = self::findFirstActiveUsedBackend();
+ if ($backend) {
+ return $backend->getLogoutAttribute();
+ }
+
+ $logoutUrl = \OC::$server->getURLGenerator()->linkToRouteAbsolute(
+ 'core.login.logout',
+ [
+ 'requesttoken' => \OCP\Util::callRegister(),
+ ]
+ );
+
+ return 'href="'.$logoutUrl.'"';
+ }
+
+ /**
+ * Check if the user is an admin user
+ *
+ * @param string $uid uid of the admin
+ * @return bool
+ */
+ public static function isAdminUser($uid) {
+ if (OC_Group::inGroup($uid, 'admin') && self::$incognitoMode === false) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * get the user id of the user currently logged in.
+ *
+ * @return string|bool uid or false
+ */
+ public static function getUser() {
+ $uid = \OC::$server->getSession() ? \OC::$server->getSession()->get('user_id') : null;
+ if (!is_null($uid) && self::$incognitoMode === false) {
+ return $uid;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * get the display name of the user currently logged in.
+ *
+ * @param string $uid
+ * @return string uid or false
+ */
+ public static function getDisplayName($uid = null) {
+ if ($uid) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ return $user->getDisplayName();
+ } else {
+ return $uid;
+ }
+ } else {
+ $user = self::getUserSession()->getUser();
+ if ($user) {
+ return $user->getDisplayName();
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Autogenerate a password
+ *
+ * @return string
+ *
+ * generates a password
+ */
+ public static function generatePassword() {
+ return \OC::$server->getSecureRandom()->generate(30);
+ }
+
+ /**
+ * Set password
+ *
+ * @param string $uid The username
+ * @param string $password The new password
+ * @param string $recoveryPassword for the encryption app to reset encryption keys
+ * @return bool
+ *
+ * Change the password of a user
+ */
+ public static function setPassword($uid, $password, $recoveryPassword = null) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ return $user->setPassword($password, $recoveryPassword);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Check whether user can change his avatar
+ *
+ * @param string $uid The username
+ * @return bool
+ *
+ * Check whether a specified user can change his avatar
+ */
+ public static function canUserChangeAvatar($uid) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ return $user->canChangeAvatar();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Check whether user can change his password
+ *
+ * @param string $uid The username
+ * @return bool
+ *
+ * Check whether a specified user can change his password
+ */
+ public static function canUserChangePassword($uid) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ return $user->canChangePassword();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Check whether user can change his display name
+ *
+ * @param string $uid The username
+ * @return bool
+ *
+ * Check whether a specified user can change his display name
+ */
+ public static function canUserChangeDisplayName($uid) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ return $user->canChangeDisplayName();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Check if the password is correct
+ *
+ * @param string $uid The username
+ * @param string $password The password
+ * @return string|false user id a string on success, false otherwise
+ *
+ * Check if the password is correct without logging in the user
+ * returns the user id or false
+ */
+ public static function checkPassword($uid, $password) {
+ $manager = \OC::$server->getUserManager();
+ $username = $manager->checkPassword($uid, $password);
+ if ($username !== false) {
+ return $username->getUID();
+ }
+ return false;
+ }
+
+ /**
+ * @param string $uid The username
+ * @return string
+ *
+ * returns the path to the users home directory
+ * @deprecated Use \OC::$server->getUserManager->getHome()
+ */
+ public static function getHome($uid) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ return $user->getHome();
+ } else {
+ return \OC::$server->getSystemConfig()->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $uid;
+ }
+ }
+
+ /**
+ * Get a list of all users
+ *
+ * @return array an array of all uids
+ *
+ * Get a list of all users.
+ * @param string $search
+ * @param integer $limit
+ * @param integer $offset
+ */
+ public static function getUsers($search = '', $limit = null, $offset = null) {
+ $users = \OC::$server->getUserManager()->search($search, $limit, $offset);
+ $uids = array();
+ foreach ($users as $user) {
+ $uids[] = $user->getUID();
+ }
+ return $uids;
+ }
+
+ /**
+ * Get a list of all users display name
+ *
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return array associative array with all display names (value) and corresponding uids (key)
+ *
+ * Get a list of all display names and user ids.
+ * @deprecated Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead.
+ */
+ public static function getDisplayNames($search = '', $limit = null, $offset = null) {
+ $displayNames = array();
+ $users = \OC::$server->getUserManager()->searchDisplayName($search, $limit, $offset);
+ foreach ($users as $user) {
+ $displayNames[$user->getUID()] = $user->getDisplayName();
+ }
+ return $displayNames;
+ }
+
+ /**
+ * check if a user exists
+ *
+ * @param string $uid the username
+ * @return boolean
+ */
+ public static function userExists($uid) {
+ return \OC::$server->getUserManager()->userExists($uid);
+ }
+
+ /**
+ * disables a user
+ *
+ * @param string $uid the user to disable
+ */
+ public static function disableUser($uid) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ $user->setEnabled(false);
+ }
+ }
+
+ /**
+ * enable a user
+ *
+ * @param string $uid
+ */
+ public static function enableUser($uid) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ $user->setEnabled(true);
+ }
+ }
+
+ /**
+ * checks if a user is enabled
+ *
+ * @param string $uid
+ * @return bool
+ */
+ public static function isEnabled($uid) {
+ $user = \OC::$server->getUserManager()->get($uid);
+ if ($user) {
+ return $user->isEnabled();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Set cookie value to use in next page load
+ *
+ * @param string $username username to be set
+ * @param string $token
+ */
+ public static function setMagicInCookie($username, $token) {
+ self::getUserSession()->setMagicInCookie($username, $token);
+ }
+
+ /**
+ * Remove cookie for "remember username"
+ */
+ public static function unsetMagicInCookie() {
+ self::getUserSession()->unsetMagicInCookie();
+ }
+
+ /**
+ * Returns the first active backend from self::$_usedBackends.
+ *
+ * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend
+ */
+ private static function findFirstActiveUsedBackend() {
+ foreach (self::$_usedBackends as $backend) {
+ if ($backend instanceof OCP\Authentication\IApacheBackend) {
+ if ($backend->isSessionActive()) {
+ return $backend;
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php
new file mode 100644
index 00000000000..b3432470f03
--- /dev/null
+++ b/lib/private/legacy/util.php
@@ -0,0 +1,1450 @@
+<?php
+/**
+ * @author Adam Williamson <awilliam@redhat.com>
+ * @author Andreas Bรถhler <dev@aboehler.at>
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @author Birk Borkason <daniel.niccoli@gmail.com>
+ * @author Bjรถrn SchieรŸle <schiessle@owncloud.com>
+ * @author Brice Maron <brice@bmaron.net>
+ * @author Christopher Schรคpers <kondou@ts.unde.re>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author cmeh <cmeh@users.noreply.github.com>
+ * @author Florin Peter <github@florin-peter.de>
+ * @author Frank Karlitschek <frank@owncloud.org>
+ * @author Georg Ehrke <georg@owncloud.com>
+ * @author helix84 <helix84@centrum.sk>
+ * @author Individual IT Services <info@individual-it.net>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jรถrn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Markus Goetz <markus@woboq.com>
+ * @author Martin Mattel <martin.mattel@diemattels.at>
+ * @author Marvin Thomas Rabe <mrabe@marvinrabe.de>
+ * @author Michael Gapczynski <GapczynskiM@gmail.com>
+ * @author Michael Gรถhler <somebody.here@gmx.de>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author Stefan Rado <owncloud@sradonia.net>
+ * @author Thomas Mรผller <thomas.mueller@tmit.eu>
+ * @author Thomas Schmidt <tschmidt@suse.de>
+ * @author Thomas Tanghus <thomas@tanghus.net>
+ * @author Victor Dubiniuk <dubiniuk@owncloud.com>
+ * @author Vincent Chan <plus.vincchan@gmail.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ * @author Volkan Gezer <volkangezer@gmail.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+use OCP\IConfig;
+use OCP\IGroupManager;
+use OCP\IUser;
+
+class OC_Util {
+ public static $scripts = array();
+ public static $styles = array();
+ public static $headers = array();
+ private static $rootMounted = false;
+ private static $fsSetup = false;
+
+ protected static function getAppManager() {
+ return \OC::$server->getAppManager();
+ }
+
+ private static function initLocalStorageRootFS() {
+ // mount local file backend as root
+ $configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
+ //first set up the local "root" storage
+ \OC\Files\Filesystem::initMountManager();
+ if (!self::$rootMounted) {
+ \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
+ self::$rootMounted = true;
+ }
+ }
+
+ /**
+ * mounting an object storage as the root fs will in essence remove the
+ * necessity of a data folder being present.
+ * TODO make home storage aware of this and use the object storage instead of local disk access
+ *
+ * @param array $config containing 'class' and optional 'arguments'
+ */
+ private static function initObjectStoreRootFS($config) {
+ // check misconfiguration
+ if (empty($config['class'])) {
+ \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
+ }
+ if (!isset($config['arguments'])) {
+ $config['arguments'] = array();
+ }
+
+ // instantiate object store implementation
+ $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
+ // mount with plain / root object store implementation
+ $config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
+
+ // mount object storage as root
+ \OC\Files\Filesystem::initMountManager();
+ if (!self::$rootMounted) {
+ \OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
+ self::$rootMounted = true;
+ }
+ }
+
+ /**
+ * Can be set up
+ *
+ * @param string $user
+ * @return boolean
+ * @description configure the initial filesystem based on the configuration
+ */
+ public static function setupFS($user = '') {
+ //setting up the filesystem twice can only lead to trouble
+ if (self::$fsSetup) {
+ return false;
+ }
+
+ \OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
+
+ // If we are not forced to load a specific user we load the one that is logged in
+ if ($user === null) {
+ $user = '';
+ } else if ($user == "" && OC_User::isLoggedIn()) {
+ $user = OC_User::getUser();
+ }
+
+ // load all filesystem apps before, so no setup-hook gets lost
+ OC_App::loadApps(array('filesystem'));
+
+ // the filesystem will finish when $user is not empty,
+ // mark fs setup here to avoid doing the setup from loading
+ // OC_Filesystem
+ if ($user != '') {
+ self::$fsSetup = true;
+ }
+
+ \OC\Files\Filesystem::initMountManager();
+
+ \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
+ \OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
+ if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
+ /** @var \OC\Files\Storage\Common $storage */
+ $storage->setMountOptions($mount->getOptions());
+ }
+ return $storage;
+ });
+
+ \OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
+ if (!$mount->getOption('enable_sharing', true)) {
+ return new \OC\Files\Storage\Wrapper\PermissionsMask([
+ 'storage' => $storage,
+ 'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
+ ]);
+ }
+ return $storage;
+ });
+
+ // install storage availability wrapper, before most other wrappers
+ \OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
+ if (!$storage->instanceOfStorage('\OC\Files\Storage\Shared') && !$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
+
+ /**
+ * @var \OC\Files\Storage\Storage $storage
+ */
+ if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
+ || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
+ ) {
+ /** @var \OC\Files\Storage\Home $storage */
+ if (is_object($storage->getUser())) {
+ $user = $storage->getUser()->getUID();
+ $quota = OC_Util::getUserQuota($user);
+ if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
+ return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
+ }
+ }
+ }
+
+ return $storage;
+ });
+
+ OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
+ \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
+
+ //check if we are using an object storage
+ $objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
+ if (isset($objectStore)) {
+ self::initObjectStoreRootFS($objectStore);
+ } else {
+ self::initLocalStorageRootFS();
+ }
+
+ if ($user != '' && !OCP\User::userExists($user)) {
+ \OC::$server->getEventLogger()->end('setup_fs');
+ return false;
+ }
+
+ //if we aren't logged in, there is no use to set up the filesystem
+ if ($user != "") {
+
+ $userDir = '/' . $user . '/files';
+
+ //jail the user into his "home" directory
+ \OC\Files\Filesystem::init($user, $userDir);
+
+ OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
+ }
+ \OC::$server->getEventLogger()->end('setup_fs');
+ return true;
+ }
+
+ /**
+ * check if a password is required for each public link
+ *
+ * @return boolean
+ */
+ public static function isPublicLinkPasswordRequired() {
+ $appConfig = \OC::$server->getAppConfig();
+ $enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password', 'no');
+ return ($enforcePassword === 'yes') ? true : false;
+ }
+
+ /**
+ * check if sharing is disabled for the current user
+ * @param IConfig $config
+ * @param IGroupManager $groupManager
+ * @param IUser|null $user
+ * @return bool
+ */
+ public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
+ if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
+ $groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
+ $excludedGroups = json_decode($groupsList);
+ if (is_null($excludedGroups)) {
+ $excludedGroups = explode(',', $groupsList);
+ $newValue = json_encode($excludedGroups);
+ $config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
+ }
+ $usersGroups = $groupManager->getUserGroupIds($user);
+ if (!empty($usersGroups)) {
+ $remainingGroups = array_diff($usersGroups, $excludedGroups);
+ // if the user is only in groups which are disabled for sharing then
+ // sharing is also disabled for the user
+ if (empty($remainingGroups)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * check if share API enforces a default expire date
+ *
+ * @return boolean
+ */
+ public static function isDefaultExpireDateEnforced() {
+ $isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
+ $enforceDefaultExpireDate = false;
+ if ($isDefaultExpireDateEnabled === 'yes') {
+ $value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
+ $enforceDefaultExpireDate = ($value === 'yes') ? true : false;
+ }
+
+ return $enforceDefaultExpireDate;
+ }
+
+ /**
+ * Get the quota of a user
+ *
+ * @param string $user
+ * @return int Quota bytes
+ */
+ public static function getUserQuota($user) {
+ $userQuota = \OC::$server->getUserManager()->get($user)->getQuota();
+ if($userQuota === 'none') {
+ return \OCP\Files\FileInfo::SPACE_UNLIMITED;
+ }else{
+ return OC_Helper::computerFileSize($userQuota);
+ }
+ }
+
+ /**
+ * copies the skeleton to the users /files
+ *
+ * @param String $userId
+ * @param \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 '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
+ \OCP\Util::DEBUG
+ );
+ self::copyr($skeletonDirectory, $userDirectory);
+ // update the file cache
+ $userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
+ }
+ }
+
+ /**
+ * copies a directory recursively by using streams
+ *
+ * @param string $source
+ * @param \OCP\Files\Folder $target
+ * @return void
+ */
+ public static function copyr($source, \OCP\Files\Folder $target) {
+ $dir = opendir($source);
+ while (false !== ($file = readdir($dir))) {
+ if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
+ if (is_dir($source . '/' . $file)) {
+ $child = $target->newFolder($file);
+ self::copyr($source . '/' . $file, $child);
+ } else {
+ $child = $target->newFile($file);
+ stream_copy_to_stream(fopen($source . '/' . $file,'r'), $child->fopen('w'));
+ }
+ }
+ }
+ closedir($dir);
+ }
+
+ /**
+ * @return void
+ */
+ public static function tearDownFS() {
+ \OC\Files\Filesystem::tearDown();
+ self::$fsSetup = false;
+ self::$rootMounted = false;
+ }
+
+ /**
+ * get the current installed version of ownCloud
+ *
+ * @return array
+ */
+ public static function getVersion() {
+ OC_Util::loadVersion();
+ return \OC::$server->getSession()->get('OC_Version');
+ }
+
+ /**
+ * get the current installed version string of ownCloud
+ *
+ * @return string
+ */
+ public static function getVersionString() {
+ OC_Util::loadVersion();
+ return \OC::$server->getSession()->get('OC_VersionString');
+ }
+
+ /**
+ * @description get the current installed edition of ownCloud. There is the community
+ * edition that just returns an empty string and the enterprise edition
+ * that returns "Enterprise".
+ * @return string
+ */
+ public static function getEditionString() {
+ if (OC_App::isEnabled('enterprise_key')) {
+ return "Enterprise";
+ } else {
+ return "";
+ }
+
+ }
+
+ /**
+ * @description get the update channel of the current installed of ownCloud.
+ * @return string
+ */
+ public static function getChannel() {
+ OC_Util::loadVersion();
+ return \OC::$server->getSession()->get('OC_Channel');
+ }
+
+ /**
+ * @description get the build number of the current installed of ownCloud.
+ * @return string
+ */
+ public static function getBuild() {
+ OC_Util::loadVersion();
+ return \OC::$server->getSession()->get('OC_Build');
+ }
+
+ /**
+ * @description load the version.php into the session as cache
+ */
+ private static function loadVersion() {
+ $timestamp = filemtime(OC::$SERVERROOT . '/version.php');
+ if (!\OC::$server->getSession()->exists('OC_Version') or OC::$server->getSession()->get('OC_Version_Timestamp') != $timestamp) {
+ require OC::$SERVERROOT . '/version.php';
+ $session = \OC::$server->getSession();
+ /** @var $timestamp int */
+ $session->set('OC_Version_Timestamp', $timestamp);
+ /** @var $OC_Version string */
+ $session->set('OC_Version', $OC_Version);
+ /** @var $OC_VersionString string */
+ $session->set('OC_VersionString', $OC_VersionString);
+ /** @var $OC_Build string */
+ $session->set('OC_Build', $OC_Build);
+
+ // Allow overriding update channel
+
+ if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
+ $channel = \OC::$server->getAppConfig()->getValue('core', 'OC_Channel');
+ } else {
+ /** @var $OC_Channel string */
+ $channel = $OC_Channel;
+ }
+
+ if (!is_null($channel)) {
+ $session->set('OC_Channel', $channel);
+ } else {
+ /** @var $OC_Channel string */
+ $session->set('OC_Channel', $OC_Channel);
+ }
+ }
+ }
+
+ /**
+ * generates a path for JS/CSS files. If no application is provided it will create the path for core.
+ *
+ * @param string $application application to get the files from
+ * @param string $directory directory within this application (css, js, vendor, etc)
+ * @param string $file the file inside of the above folder
+ * @return string the path
+ */
+ private static function generatePath($application, $directory, $file) {
+ if (is_null($file)) {
+ $file = $application;
+ $application = "";
+ }
+ if (!empty($application)) {
+ return "$application/$directory/$file";
+ } else {
+ return "$directory/$file";
+ }
+ }
+
+ /**
+ * add a javascript file
+ *
+ * @param string $application application id
+ * @param string|null $file filename
+ * @param bool $prepend prepend the Script to the beginning of the list
+ * @return void
+ */
+ public static function addScript($application, $file = null, $prepend = false) {
+ $path = OC_Util::generatePath($application, 'js', $file);
+
+ // core js files need separate handling
+ if ($application !== 'core' && $file !== null) {
+ self::addTranslations ( $application );
+ }
+ self::addExternalResource($application, $prepend, $path, "script");
+ }
+
+ /**
+ * add a javascript file from the vendor sub folder
+ *
+ * @param string $application application id
+ * @param string|null $file filename
+ * @param bool $prepend prepend the Script to the beginning of the list
+ * @return void
+ */
+ public static function addVendorScript($application, $file = null, $prepend = false) {
+ $path = OC_Util::generatePath($application, 'vendor', $file);
+ self::addExternalResource($application, $prepend, $path, "script");
+ }
+
+ /**
+ * add a translation JS file
+ *
+ * @param string $application application id
+ * @param string $languageCode language code, defaults to the current language
+ * @param bool $prepend prepend the Script to the beginning of the list
+ */
+ public static function addTranslations($application, $languageCode = null, $prepend = false) {
+ if (is_null($languageCode)) {
+ $languageCode = \OC_L10N::findLanguage($application);
+ }
+ if (!empty($application)) {
+ $path = "$application/l10n/$languageCode";
+ } else {
+ $path = "l10n/$languageCode";
+ }
+ self::addExternalResource($application, $prepend, $path, "script");
+ }
+
+ /**
+ * add a css file
+ *
+ * @param string $application application id
+ * @param string|null $file filename
+ * @param bool $prepend prepend the Style to the beginning of the list
+ * @return void
+ */
+ public static function addStyle($application, $file = null, $prepend = false) {
+ $path = OC_Util::generatePath($application, 'css', $file);
+ self::addExternalResource($application, $prepend, $path, "style");
+ }
+
+ /**
+ * add a css file from the vendor sub folder
+ *
+ * @param string $application application id
+ * @param string|null $file filename
+ * @param bool $prepend prepend the Style to the beginning of the list
+ * @return void
+ */
+ public static function addVendorStyle($application, $file = null, $prepend = false) {
+ $path = OC_Util::generatePath($application, 'vendor', $file);
+ self::addExternalResource($application, $prepend, $path, "style");
+ }
+
+ /**
+ * add an external resource css/js file
+ *
+ * @param string $application application id
+ * @param bool $prepend prepend the file to the beginning of the list
+ * @param string $path
+ * @param string $type (script or style)
+ * @return void
+ */
+ private static function addExternalResource($application, $prepend, $path, $type = "script") {
+
+ if ($type === "style") {
+ if (!in_array($path, self::$styles)) {
+ if ($prepend === true) {
+ array_unshift ( self::$styles, $path );
+ } else {
+ self::$styles[] = $path;
+ }
+ }
+ } elseif ($type === "script") {
+ if (!in_array($path, self::$scripts)) {
+ if ($prepend === true) {
+ array_unshift ( self::$scripts, $path );
+ } else {
+ self::$scripts [] = $path;
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a custom element to the header
+ * If $text is null then the element will be written as empty element.
+ * So use "" to get a closing tag.
+ * @param string $tag tag name of the element
+ * @param array $attributes array of attributes for the element
+ * @param string $text the text content for the element
+ */
+ public static function addHeader($tag, $attributes, $text=null) {
+ self::$headers[] = array(
+ 'tag' => $tag,
+ 'attributes' => $attributes,
+ 'text' => $text
+ );
+ }
+
+ /**
+ * formats a timestamp in the "right" way
+ *
+ * @param int $timestamp
+ * @param bool $dateOnly option to omit time from the result
+ * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
+ * @return string timestamp
+ *
+ * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
+ */
+ public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
+ if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
+ $timeZone = new \DateTimeZone($timeZone);
+ }
+
+ /** @var \OC\DateTimeFormatter $formatter */
+ $formatter = \OC::$server->query('DateTimeFormatter');
+ if ($dateOnly) {
+ return $formatter->formatDate($timestamp, 'long', $timeZone);
+ }
+ return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
+ }
+
+ /**
+ * check if the current server configuration is suitable for ownCloud
+ *
+ * @param \OCP\IConfig $config
+ * @return array arrays with error messages and hints
+ */
+ public static function checkServer(\OCP\IConfig $config) {
+ $l = \OC::$server->getL10N('lib');
+ $errors = array();
+ $CONFIG_DATADIRECTORY = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
+
+ if (!self::needUpgrade($config) && $config->getSystemValue('installed', false)) {
+ // this check needs to be done every time
+ $errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
+ }
+
+ // Assume that if checkServer() succeeded before in this session, then all is fine.
+ if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
+ return $errors;
+ }
+
+ $webServerRestart = false;
+ $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
+ new \OC_Defaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
+
+ $urlGenerator = \OC::$server->getURLGenerator();
+
+ $availableDatabases = $setup->getSupportedDatabases();
+ if (empty($availableDatabases)) {
+ $errors[] = array(
+ 'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
+ 'hint' => '' //TODO: sane hint
+ );
+ $webServerRestart = true;
+ }
+
+ // Check if server running on Windows platform
+ if(OC_Util::runningOnWindows()) {
+ $errors[] = [
+ 'error' => $l->t('Microsoft Windows Platform is not supported'),
+ 'hint' => $l->t('Running ownCloud Server on the Microsoft Windows platform is not supported. We suggest you ' .
+ 'use a Linux server in a virtual machine if you have no option for migrating the server itself. ' .
+ 'Find Linux packages as well as easy to deploy virtual machine images on <a href="%s">%s</a>. ' .
+ 'For migrating existing installations to Linux you can find some tips and a migration script ' .
+ 'in <a href="%s">our documentation</a>.',
+ ['https://owncloud.org/install/', 'owncloud.org/install/', 'https://owncloud.org/?p=8045'])
+ ];
+ }
+
+ // Check if config folder is writable.
+ if(!OC_Helper::isReadOnlyConfigEnabled()) {
+ if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
+ $errors[] = array(
+ 'error' => $l->t('Cannot write into "config" directory'),
+ 'hint' => $l->t('This can usually be fixed by '
+ . '%sgiving the webserver write access to the config directory%s.',
+ array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
+ );
+ }
+ }
+
+ // Check if there is a writable install folder.
+ if ($config->getSystemValue('appstoreenabled', true)) {
+ if (OC_App::getInstallPath() === null
+ || !is_writable(OC_App::getInstallPath())
+ || !is_readable(OC_App::getInstallPath())
+ ) {
+ $errors[] = array(
+ 'error' => $l->t('Cannot write into "apps" directory'),
+ 'hint' => $l->t('This can usually be fixed by '
+ . '%sgiving the webserver write access to the apps directory%s'
+ . ' or disabling the appstore in the config file.',
+ array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
+ );
+ }
+ }
+ // Create root dir.
+ if ($config->getSystemValue('installed', false)) {
+ if (!is_dir($CONFIG_DATADIRECTORY)) {
+ $success = @mkdir($CONFIG_DATADIRECTORY);
+ if ($success) {
+ $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
+ } else {
+ $errors[] = array(
+ 'error' => $l->t('Cannot create "data" directory (%s)', array($CONFIG_DATADIRECTORY)),
+ 'hint' => $l->t('This can usually be fixed by '
+ . '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
+ array($urlGenerator->linkToDocs('admin-dir_permissions')))
+ );
+ }
+ } else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
+ //common hint for all file permissions error messages
+ $permissionsHint = $l->t('Permissions can usually be fixed by '
+ . '%sgiving the webserver write access to the root directory%s.',
+ array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'));
+ $errors[] = array(
+ 'error' => 'Data directory (' . $CONFIG_DATADIRECTORY . ') not writable by ownCloud',
+ 'hint' => $permissionsHint
+ );
+ } else {
+ $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
+ }
+ }
+
+ if (!OC_Util::isSetLocaleWorking()) {
+ $errors[] = array(
+ 'error' => $l->t('Setting locale to %s failed',
+ array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
+ . 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
+ 'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
+ );
+ }
+
+ // Contains the dependencies that should be checked against
+ // classes = class_exists
+ // functions = function_exists
+ // defined = defined
+ // ini = ini_get
+ // If the dependency is not found the missing module name is shown to the EndUser
+ // When adding new checks always verify that they pass on Travis as well
+ // for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
+ $dependencies = array(
+ 'classes' => array(
+ 'ZipArchive' => 'zip',
+ 'DOMDocument' => 'dom',
+ 'XMLWriter' => 'XMLWriter',
+ 'XMLReader' => 'XMLReader',
+ ),
+ 'functions' => [
+ 'xml_parser_create' => 'libxml',
+ 'mb_detect_encoding' => 'mb multibyte',
+ 'ctype_digit' => 'ctype',
+ 'json_encode' => 'JSON',
+ 'gd_info' => 'GD',
+ 'gzencode' => 'zlib',
+ 'iconv' => 'iconv',
+ 'simplexml_load_string' => 'SimpleXML',
+ 'hash' => 'HASH Message Digest Framework',
+ 'curl_init' => 'cURL',
+ ],
+ 'defined' => array(
+ 'PDO::ATTR_DRIVER_NAME' => 'PDO'
+ ),
+ 'ini' => [
+ 'default_charset' => 'UTF-8',
+ ],
+ );
+ $missingDependencies = array();
+ $invalidIniSettings = [];
+ $moduleHint = $l->t('Please ask your server administrator to install the module.');
+
+ /**
+ * FIXME: The dependency check does not work properly on HHVM on the moment
+ * and prevents installation. Once HHVM is more compatible with our
+ * approach to check for these values we should re-enable those
+ * checks.
+ */
+ $iniWrapper = \OC::$server->getIniWrapper();
+ if (!self::runningOnHhvm()) {
+ foreach ($dependencies['classes'] as $class => $module) {
+ if (!class_exists($class)) {
+ $missingDependencies[] = $module;
+ }
+ }
+ foreach ($dependencies['functions'] as $function => $module) {
+ if (!function_exists($function)) {
+ $missingDependencies[] = $module;
+ }
+ }
+ foreach ($dependencies['defined'] as $defined => $module) {
+ if (!defined($defined)) {
+ $missingDependencies[] = $module;
+ }
+ }
+ foreach ($dependencies['ini'] as $setting => $expected) {
+ if (is_bool($expected)) {
+ if ($iniWrapper->getBool($setting) !== $expected) {
+ $invalidIniSettings[] = [$setting, $expected];
+ }
+ }
+ if (is_int($expected)) {
+ if ($iniWrapper->getNumeric($setting) !== $expected) {
+ $invalidIniSettings[] = [$setting, $expected];
+ }
+ }
+ if (is_string($expected)) {
+ if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
+ $invalidIniSettings[] = [$setting, $expected];
+ }
+ }
+ }
+ }
+
+ foreach($missingDependencies as $missingDependency) {
+ $errors[] = array(
+ 'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
+ 'hint' => $moduleHint
+ );
+ $webServerRestart = true;
+ }
+ foreach($invalidIniSettings as $setting) {
+ if(is_bool($setting[1])) {
+ $setting[1] = ($setting[1]) ? 'on' : 'off';
+ }
+ $errors[] = [
+ 'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
+ 'hint' => $l->t('Adjusting this setting in php.ini will make ownCloud run again')
+ ];
+ $webServerRestart = true;
+ }
+
+ /**
+ * The mbstring.func_overload check can only be performed if the mbstring
+ * module is installed as it will return null if the checking setting is
+ * not available and thus a check on the boolean value fails.
+ *
+ * TODO: Should probably be implemented in the above generic dependency
+ * check somehow in the long-term.
+ */
+ if($iniWrapper->getBool('mbstring.func_overload') !== null &&
+ $iniWrapper->getBool('mbstring.func_overload') === true) {
+ $errors[] = array(
+ 'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
+ 'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
+ );
+ }
+
+ if(function_exists('xml_parser_create') &&
+ version_compare('2.7.0', LIBXML_DOTTED_VERSION) === 1) {
+ $errors[] = array(
+ 'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [LIBXML_DOTTED_VERSION]),
+ 'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
+ );
+ }
+
+ if (!self::isAnnotationsWorking()) {
+ $errors[] = array(
+ 'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
+ 'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
+ );
+ }
+
+ if (!\OC::$CLI && $webServerRestart) {
+ $errors[] = array(
+ 'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
+ 'hint' => $l->t('Please ask your server administrator to restart the web server.')
+ );
+ }
+
+ $errors = array_merge($errors, self::checkDatabaseVersion());
+
+ // Cache the result of this function
+ \OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
+
+ return $errors;
+ }
+
+ /**
+ * Check the database version
+ *
+ * @return array errors array
+ */
+ public static function checkDatabaseVersion() {
+ $l = \OC::$server->getL10N('lib');
+ $errors = array();
+ $dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
+ if ($dbType === 'pgsql') {
+ // check PostgreSQL version
+ try {
+ $result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
+ $data = $result->fetchRow();
+ if (isset($data['server_version'])) {
+ $version = $data['server_version'];
+ if (version_compare($version, '9.0.0', '<')) {
+ $errors[] = array(
+ 'error' => $l->t('PostgreSQL >= 9 required'),
+ 'hint' => $l->t('Please upgrade your database version')
+ );
+ }
+ }
+ } catch (\Doctrine\DBAL\DBALException $e) {
+ $logger = \OC::$server->getLogger();
+ $logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
+ $logger->logException($e);
+ }
+ }
+ return $errors;
+ }
+
+ /**
+ * Check for correct file permissions of data directory
+ *
+ * @param string $dataDirectory
+ * @return array arrays with error messages and hints
+ */
+ public static function checkDataDirectoryPermissions($dataDirectory) {
+ $l = \OC::$server->getL10N('lib');
+ $errors = array();
+ if (self::runningOnWindows()) {
+ //TODO: permissions checks for windows hosts
+ } else {
+ $permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
+ . ' cannot be listed by other users.');
+ $perms = substr(decoct(@fileperms($dataDirectory)), -3);
+ if (substr($perms, -1) != '0') {
+ chmod($dataDirectory, 0770);
+ clearstatcache();
+ $perms = substr(decoct(@fileperms($dataDirectory)), -3);
+ if (substr($perms, 2, 1) != '0') {
+ $errors[] = array(
+ 'error' => $l->t('Data directory (%s) is readable by other users', array($dataDirectory)),
+ 'hint' => $permissionsModHint
+ );
+ }
+ }
+ }
+ return $errors;
+ }
+
+ /**
+ * Check that the data directory exists and is valid by
+ * checking the existence of the ".ocdata" file.
+ *
+ * @param string $dataDirectory data directory path
+ * @return array errors found
+ */
+ public static function checkDataDirectoryValidity($dataDirectory) {
+ $l = \OC::$server->getL10N('lib');
+ $errors = [];
+ if (!self::runningOnWindows() && $dataDirectory[0] !== '/') {
+ $errors[] = [
+ 'error' => $l->t('Data directory (%s) must be an absolute path', [$dataDirectory]),
+ 'hint' => $l->t('Check the value of "datadirectory" in your configuration')
+ ];
+ }
+ if (!file_exists($dataDirectory . '/.ocdata')) {
+ $errors[] = [
+ 'error' => $l->t('Data directory (%s) is invalid', [$dataDirectory]),
+ 'hint' => $l->t('Please check that the data directory contains a file' .
+ ' ".ocdata" in its root.')
+ ];
+ }
+ return $errors;
+ }
+
+ /**
+ * Check if the user is logged in, redirects to home if not. With
+ * redirect URL parameter to the request URI.
+ *
+ * @return void
+ */
+ public static function checkLoggedIn() {
+ // Check if we are a user
+ if (!OC_User::isLoggedIn()) {
+ header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
+ 'core.login.showLoginForm',
+ [
+ 'redirect_url' => \OC::$server->getRequest()->getRequestUri()
+ ]
+ )
+ );
+ exit();
+ }
+ }
+
+ /**
+ * Check if the user is a admin, redirects to home if not
+ *
+ * @return void
+ */
+ public static function checkAdminUser() {
+ OC_Util::checkLoggedIn();
+ if (!OC_User::isAdminUser(OC_User::getUser())) {
+ header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
+ exit();
+ }
+ }
+
+ /**
+ * Check if it is allowed to remember login.
+ *
+ * @note Every app can set 'rememberlogin' to 'false' to disable the remember login feature
+ *
+ * @return bool
+ */
+ public static function rememberLoginAllowed() {
+
+ $apps = OC_App::getEnabledApps();
+
+ foreach ($apps as $app) {
+ $appInfo = OC_App::getAppInfo($app);
+ if (isset($appInfo['rememberlogin']) && $appInfo['rememberlogin'] === 'false') {
+ return false;
+ }
+
+ }
+ return true;
+ }
+
+ /**
+ * Check if the user is a subadmin, redirects to home if not
+ *
+ * @return null|boolean $groups where the current user is subadmin
+ */
+ public static function checkSubAdminUser() {
+ OC_Util::checkLoggedIn();
+ $userObject = \OC::$server->getUserSession()->getUser();
+ $isSubAdmin = false;
+ if($userObject !== null) {
+ $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
+ }
+
+ if (!$isSubAdmin) {
+ header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
+ exit();
+ }
+ return true;
+ }
+
+ /**
+ * Returns the URL of the default page
+ * based on the system configuration and
+ * the apps visible for the current user
+ *
+ * @return string URL
+ */
+ public static function getDefaultPageUrl() {
+ $urlGenerator = \OC::$server->getURLGenerator();
+ // Deny the redirect if the URL contains a @
+ // This prevents unvalidated redirects like ?redirect_url=:user@domain.com
+ if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
+ $location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
+ } else {
+ $defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage');
+ if ($defaultPage) {
+ $location = $urlGenerator->getAbsoluteURL($defaultPage);
+ } else {
+ $appId = 'files';
+ $defaultApps = explode(',', \OCP\Config::getSystemValue('defaultapp', 'files'));
+ // find the first app that is enabled for the current user
+ foreach ($defaultApps as $defaultApp) {
+ $defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
+ if (static::getAppManager()->isEnabledForUser($defaultApp)) {
+ $appId = $defaultApp;
+ break;
+ }
+ }
+
+ if(getenv('front_controller_active') === 'true') {
+ $location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
+ } else {
+ $location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
+ }
+ }
+ }
+ return $location;
+ }
+
+ /**
+ * Redirect to the user default page
+ *
+ * @return void
+ */
+ public static function redirectToDefaultPage() {
+ $location = self::getDefaultPageUrl();
+ header('Location: ' . $location);
+ exit();
+ }
+
+ /**
+ * get an id unique for this instance
+ *
+ * @return string
+ */
+ public static function getInstanceId() {
+ $id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
+ if (is_null($id)) {
+ // We need to guarantee at least one letter in instanceid so it can be used as the session_name
+ $id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
+ \OC::$server->getSystemConfig()->setValue('instanceid', $id);
+ }
+ return $id;
+ }
+
+ /**
+ * Public function to sanitize HTML
+ *
+ * This function is used to sanitize HTML and should be applied on any
+ * string or array of strings before displaying it on a web page.
+ *
+ * @param string|array $value
+ * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
+ */
+ public static function sanitizeHTML($value) {
+ if (is_array($value)) {
+ $value = array_map(function($value) {
+ return self::sanitizeHTML($value);
+ }, $value);
+ } else {
+ // Specify encoding for PHP<5.4
+ $value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
+ }
+ return $value;
+ }
+
+ /**
+ * Public function to encode url parameters
+ *
+ * This function is used to encode path to file before output.
+ * Encoding is done according to RFC 3986 with one exception:
+ * Character '/' is preserved as is.
+ *
+ * @param string $component part of URI to encode
+ * @return string
+ */
+ public static function encodePath($component) {
+ $encoded = rawurlencode($component);
+ $encoded = str_replace('%2F', '/', $encoded);
+ return $encoded;
+ }
+
+ /**
+ * Check if the .htaccess file is working
+ * @param \OCP\IConfig $config
+ * @return bool
+ * @throws Exception
+ * @throws \OC\HintException If the test file can't get written.
+ */
+ public function isHtaccessWorking(\OCP\IConfig $config) {
+
+ if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
+ return true;
+ }
+
+ // php dev server does not support htaccess
+ if (php_sapi_name() === 'cli-server') {
+ return false;
+ }
+
+ // testdata
+ $fileName = '/htaccesstest.txt';
+ $testContent = 'testcontent';
+
+ // creating a test file
+ $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
+
+ if (file_exists($testFile)) {// already running this test, possible recursive call
+ return false;
+ }
+
+ $fp = @fopen($testFile, 'w');
+ if (!$fp) {
+ throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
+ 'Make sure it is possible for the webserver to write to ' . $testFile);
+ }
+ fwrite($fp, $testContent);
+ fclose($fp);
+
+ // accessing the file via http
+ $url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
+ try {
+ $content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
+ } catch (\Exception $e) {
+ $content = false;
+ }
+
+ // cleanup
+ @unlink($testFile);
+
+ /*
+ * If the content is not equal to test content our .htaccess
+ * is working as required
+ */
+ return $content !== $testContent;
+ }
+
+ /**
+ * Check if the setlocal call does not work. This can happen if the right
+ * local packages are not available on the server.
+ *
+ * @return bool
+ */
+ public static function isSetLocaleWorking() {
+ // setlocale test is pointless on Windows
+ if (OC_Util::runningOnWindows()) {
+ return true;
+ }
+
+ \Patchwork\Utf8\Bootup::initLocale();
+ if ('' === basename('ยง')) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Check if it's possible to get the inline annotations
+ *
+ * @return bool
+ */
+ public static function isAnnotationsWorking() {
+ $reflection = new \ReflectionMethod(__METHOD__);
+ $docs = $reflection->getDocComment();
+
+ return (is_string($docs) && strlen($docs) > 50);
+ }
+
+ /**
+ * Check if the PHP module fileinfo is loaded.
+ *
+ * @return bool
+ */
+ public static function fileInfoLoaded() {
+ return function_exists('finfo_open');
+ }
+
+ /**
+ * clear all levels of output buffering
+ *
+ * @return void
+ */
+ public static function obEnd() {
+ while (ob_get_level()) {
+ ob_end_clean();
+ }
+ }
+
+ /**
+ * Checks whether the server is running on Windows
+ *
+ * @return bool true if running on Windows, false otherwise
+ */
+ public static function runningOnWindows() {
+ return (substr(PHP_OS, 0, 3) === "WIN");
+ }
+
+ /**
+ * Checks whether the server is running on Mac OS X
+ *
+ * @return bool true if running on Mac OS X, false otherwise
+ */
+ public static function runningOnMac() {
+ return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
+ }
+
+ /**
+ * Checks whether server is running on HHVM
+ *
+ * @return bool True if running on HHVM, false otherwise
+ */
+ public static function runningOnHhvm() {
+ return defined('HHVM_VERSION');
+ }
+
+ /**
+ * Handles the case that there may not be a theme, then check if a "default"
+ * theme exists and take that one
+ *
+ * @return string the theme
+ */
+ public static function getTheme() {
+ $theme = \OC::$server->getSystemConfig()->getValue("theme", '');
+
+ if ($theme === '') {
+ if (is_dir(OC::$SERVERROOT . '/themes/default')) {
+ $theme = 'default';
+ }
+ }
+
+ return $theme;
+ }
+
+ /**
+ * Clear a single file from the opcode cache
+ * This is useful for writing to the config file
+ * in case the opcode cache does not re-validate files
+ * Returns true if successful, false if unsuccessful:
+ * caller should fall back on clearing the entire cache
+ * with clearOpcodeCache() if unsuccessful
+ *
+ * @param string $path the path of the file to clear from the cache
+ * @return bool true if underlying function returns true, otherwise false
+ */
+ public static function deleteFromOpcodeCache($path) {
+ $ret = false;
+ if ($path) {
+ // APC >= 3.1.1
+ if (function_exists('apc_delete_file')) {
+ $ret = @apc_delete_file($path);
+ }
+ // Zend OpCache >= 7.0.0, PHP >= 5.5.0
+ if (function_exists('opcache_invalidate')) {
+ $ret = opcache_invalidate($path);
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Clear the opcode cache if one exists
+ * This is necessary for writing to the config file
+ * in case the opcode cache does not re-validate files
+ *
+ * @return void
+ */
+ public static function clearOpcodeCache() {
+ // APC
+ if (function_exists('apc_clear_cache')) {
+ apc_clear_cache();
+ }
+ // Zend Opcache
+ if (function_exists('accelerator_reset')) {
+ accelerator_reset();
+ }
+ // XCache
+ if (function_exists('xcache_clear_cache')) {
+ if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
+ \OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
+ } else {
+ @xcache_clear_cache(XC_TYPE_PHP, 0);
+ }
+ }
+ // Opcache (PHP >= 5.5)
+ if (function_exists('opcache_reset')) {
+ opcache_reset();
+ }
+ }
+
+ /**
+ * Normalize a unicode string
+ *
+ * @param string $value a not normalized string
+ * @return bool|string
+ */
+ public static function normalizeUnicode($value) {
+ if(Normalizer::isNormalized($value)) {
+ return $value;
+ }
+
+ $normalizedValue = Normalizer::normalize($value);
+ if ($normalizedValue === null || $normalizedValue === false) {
+ \OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
+ return $value;
+ }
+
+ return $normalizedValue;
+ }
+
+ /**
+ * @param boolean|string $file
+ * @return string
+ */
+ public static function basename($file) {
+ $file = rtrim($file, '/');
+ $t = explode('/', $file);
+ return array_pop($t);
+ }
+
+ /**
+ * A human readable string is generated based on version, channel and build number
+ *
+ * @return string
+ */
+ public static function getHumanVersion() {
+ $version = OC_Util::getVersionString() . ' (' . OC_Util::getChannel() . ')';
+ $build = OC_Util::getBuild();
+ if (!empty($build) and OC_Util::getChannel() === 'daily') {
+ $version .= ' Build:' . $build;
+ }
+ return $version;
+ }
+
+ /**
+ * Returns whether the given file name is valid
+ *
+ * @param string $file file name to check
+ * @return bool true if the file name is valid, false otherwise
+ * @deprecated use \OC\Files\View::verifyPath()
+ */
+ public static function isValidFileName($file) {
+ $trimmed = trim($file);
+ if ($trimmed === '') {
+ return false;
+ }
+ if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
+ return false;
+ }
+ foreach (str_split($trimmed) as $char) {
+ if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check whether the instance needs to perform an upgrade,
+ * either when the core version is higher or any app requires
+ * an upgrade.
+ *
+ * @param \OCP\IConfig $config
+ * @return bool whether the core or any app needs an upgrade
+ * @throws \OC\HintException When the upgrade from the given version is not allowed
+ */
+ public static function needUpgrade(\OCP\IConfig $config) {
+ if ($config->getSystemValue('installed', false)) {
+ $installedVersion = $config->getSystemValue('version', '0.0.0');
+ $currentVersion = implode('.', \OCP\Util::getVersion());
+ $versionDiff = version_compare($currentVersion, $installedVersion);
+ if ($versionDiff > 0) {
+ return true;
+ } else if ($config->getSystemValue('debug', false) && $versionDiff < 0) {
+ // downgrade with debug
+ $installedMajor = explode('.', $installedVersion);
+ $installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
+ $currentMajor = explode('.', $currentVersion);
+ $currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
+ if ($installedMajor === $currentMajor) {
+ // Same major, allow downgrade for developers
+ return true;
+ } else {
+ // downgrade attempt, throw exception
+ throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
+ }
+ } else if ($versionDiff < 0) {
+ // downgrade attempt, throw exception
+ throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
+ }
+
+ // also check for upgrades for apps (independently from the user)
+ $apps = \OC_App::getEnabledApps(false, true);
+ $shouldUpgrade = false;
+ foreach ($apps as $app) {
+ if (\OC_App::shouldUpgrade($app)) {
+ $shouldUpgrade = true;
+ break;
+ }
+ }
+ return $shouldUpgrade;
+ } else {
+ return false;
+ }
+ }
+
+}