You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1472 lines
46 KiB

15 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
11 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
10 years ago
10 years ago
10 years ago
10 years ago
15 years ago
15 years ago
15 years ago
10 years ago
10 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
Merge https://github.com/owncloud/core/pull/3827 Squashed commit of the following: commit eed4b49cebcbcc252a75ed85097730b73213b0da Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 8 10:03:23 2013 +0200 initialize OC_Defaults only once commit bf6f07ccc8fb87535a069ca341789a590cb187ee Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 8 10:02:48 2013 +0200 link to doc in error messages commit ea61ee60e06ee98f2671aec1fdaff666c50f47c2 Merge: e41af3d 3c1308f Author: Björn Schießle <schiessle@owncloud.com> Date: Thu Jul 4 11:10:56 2013 +0200 Merge branch 'master' into better-messages commit e41af3d7bad26aa5ca9ab21ec7dcbadd3cfe5d4f Author: Björn Schießle <schiessle@owncloud.com> Date: Thu Jul 4 11:10:41 2013 +0200 move to non-static defaults.php commit 9e4258b1905244bdf34943a825421f041cbed43d Author: Björn Schießle <schiessle@owncloud.com> Date: Tue Jul 2 10:32:13 2013 +0200 no sprintf here, lets t() handle it commit fe1df349e248667a137f70d78b04225e5b42a111 Merge: d8f6859 cb5811b Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 1 11:13:38 2013 +0200 Merge branch 'master' into better-messages commit d8f68595df2dc0e0917d916cbde511ec5333010b Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 1 11:10:57 2013 +0200 use document base url from defaults.php commit 6c5403748a45717125a2aa375550f05646317d72 Merge: bea6b1c 7b0e3e6 Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 1 11:00:15 2013 +0200 Merge branch 'master' into better-messages commit bea6b1c8a0968846065b9153e0a3f46a4e3245ee Author: Jan-Christoph Borchardt <hey@jancborchardt.net> Date: Mon Jun 24 16:21:12 2013 +0200 link to docs in WebDAV message, ref #3791 commit 3119b364a1094769e711283d1ce9014505f64ab9 Author: Jan-Christoph Borchardt <hey@jancborchardt.net> Date: Mon Jun 24 16:10:21 2013 +0200 link to docs in error messages, fix #3819
13 years ago
Merge https://github.com/owncloud/core/pull/3827 Squashed commit of the following: commit eed4b49cebcbcc252a75ed85097730b73213b0da Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 8 10:03:23 2013 +0200 initialize OC_Defaults only once commit bf6f07ccc8fb87535a069ca341789a590cb187ee Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 8 10:02:48 2013 +0200 link to doc in error messages commit ea61ee60e06ee98f2671aec1fdaff666c50f47c2 Merge: e41af3d 3c1308f Author: Björn Schießle <schiessle@owncloud.com> Date: Thu Jul 4 11:10:56 2013 +0200 Merge branch 'master' into better-messages commit e41af3d7bad26aa5ca9ab21ec7dcbadd3cfe5d4f Author: Björn Schießle <schiessle@owncloud.com> Date: Thu Jul 4 11:10:41 2013 +0200 move to non-static defaults.php commit 9e4258b1905244bdf34943a825421f041cbed43d Author: Björn Schießle <schiessle@owncloud.com> Date: Tue Jul 2 10:32:13 2013 +0200 no sprintf here, lets t() handle it commit fe1df349e248667a137f70d78b04225e5b42a111 Merge: d8f6859 cb5811b Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 1 11:13:38 2013 +0200 Merge branch 'master' into better-messages commit d8f68595df2dc0e0917d916cbde511ec5333010b Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 1 11:10:57 2013 +0200 use document base url from defaults.php commit 6c5403748a45717125a2aa375550f05646317d72 Merge: bea6b1c 7b0e3e6 Author: Björn Schießle <schiessle@owncloud.com> Date: Mon Jul 1 11:00:15 2013 +0200 Merge branch 'master' into better-messages commit bea6b1c8a0968846065b9153e0a3f46a4e3245ee Author: Jan-Christoph Borchardt <hey@jancborchardt.net> Date: Mon Jun 24 16:21:12 2013 +0200 link to docs in WebDAV message, ref #3791 commit 3119b364a1094769e711283d1ce9014505f64ab9 Author: Jan-Christoph Borchardt <hey@jancborchardt.net> Date: Mon Jun 24 16:10:21 2013 +0200 link to docs in error messages, fix #3819
13 years ago
10 years ago
15 years ago
10 years ago
15 years ago
15 years ago
12 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
14 years ago
13 years ago
12 years ago
13 years ago
12 years ago
11 years ago
15 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Adam Williamson <awilliam@redhat.com>
  6. * @author Andreas Fischer <bantu@owncloud.com>
  7. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  8. * @author Bart Visscher <bartv@thisnet.nl>
  9. * @author Bernhard Posselt <dev@bernhard-posselt.com>
  10. * @author Birk Borkason <daniel.niccoli@gmail.com>
  11. * @author Björn Schießle <bjoern@schiessle.org>
  12. * @author Brice Maron <brice@bmaron.net>
  13. * @author Christopher Schäpers <kondou@ts.unde.re>
  14. * @author Christoph Wurst <christoph@owncloud.com>
  15. * @author Clark Tomlinson <fallen013@gmail.com>
  16. * @author cmeh <cmeh@users.noreply.github.com>
  17. * @author Florin Peter <github@florin-peter.de>
  18. * @author Frank Karlitschek <frank@karlitschek.de>
  19. * @author Georg Ehrke <georg@owncloud.com>
  20. * @author helix84 <helix84@centrum.sk>
  21. * @author Individual IT Services <info@individual-it.net>
  22. * @author Jakob Sack <mail@jakobsack.de>
  23. * @author Joas Schilling <coding@schilljs.com>
  24. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  25. * @author Lukas Reschke <lukas@statuscode.ch>
  26. * @author Markus Goetz <markus@woboq.com>
  27. * @author Martin Mattel <martin.mattel@diemattels.at>
  28. * @author Marvin Thomas Rabe <mrabe@marvinrabe.de>
  29. * @author Michael Gapczynski <GapczynskiM@gmail.com>
  30. * @author Morris Jobke <hey@morrisjobke.de>
  31. * @author Robin Appelman <robin@icewind.nl>
  32. * @author Robin McCorkell <robin@mccorkell.me.uk>
  33. * @author Roeland Jago Douma <roeland@famdouma.nl>
  34. * @author Stefan Rado <owncloud@sradonia.net>
  35. * @author Stefan Weil <sw@weilnetz.de>
  36. * @author Thomas Müller <thomas.mueller@tmit.eu>
  37. * @author Thomas Tanghus <thomas@tanghus.net>
  38. * @author Victor Dubiniuk <dubiniuk@owncloud.com>
  39. * @author Vincent Petry <pvince81@owncloud.com>
  40. * @author Volkan Gezer <volkangezer@gmail.com>
  41. *
  42. * @license AGPL-3.0
  43. *
  44. * This code is free software: you can redistribute it and/or modify
  45. * it under the terms of the GNU Affero General Public License, version 3,
  46. * as published by the Free Software Foundation.
  47. *
  48. * This program is distributed in the hope that it will be useful,
  49. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  50. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  51. * GNU Affero General Public License for more details.
  52. *
  53. * You should have received a copy of the GNU Affero General Public License, version 3,
  54. * along with this program. If not, see <http://www.gnu.org/licenses/>
  55. *
  56. */
  57. use OCP\IConfig;
  58. use OCP\IGroupManager;
  59. use OCP\IUser;
  60. class OC_Util {
  61. public static $scripts = array();
  62. public static $styles = array();
  63. public static $headers = array();
  64. private static $rootMounted = false;
  65. private static $fsSetup = false;
  66. /** @var array Local cache of version.php */
  67. private static $versionCache = null;
  68. protected static function getAppManager() {
  69. return \OC::$server->getAppManager();
  70. }
  71. private static function initLocalStorageRootFS() {
  72. // mount local file backend as root
  73. $configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
  74. //first set up the local "root" storage
  75. \OC\Files\Filesystem::initMountManager();
  76. if (!self::$rootMounted) {
  77. \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
  78. self::$rootMounted = true;
  79. }
  80. }
  81. /**
  82. * mounting an object storage as the root fs will in essence remove the
  83. * necessity of a data folder being present.
  84. * TODO make home storage aware of this and use the object storage instead of local disk access
  85. *
  86. * @param array $config containing 'class' and optional 'arguments'
  87. */
  88. private static function initObjectStoreRootFS($config) {
  89. // check misconfiguration
  90. if (empty($config['class'])) {
  91. \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
  92. }
  93. if (!isset($config['arguments'])) {
  94. $config['arguments'] = array();
  95. }
  96. // instantiate object store implementation
  97. $name = $config['class'];
  98. if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
  99. $segments = explode('\\', $name);
  100. OC_App::loadApp(strtolower($segments[1]));
  101. }
  102. $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
  103. // mount with plain / root object store implementation
  104. $config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
  105. // mount object storage as root
  106. \OC\Files\Filesystem::initMountManager();
  107. if (!self::$rootMounted) {
  108. \OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
  109. self::$rootMounted = true;
  110. }
  111. }
  112. /**
  113. * Can be set up
  114. *
  115. * @param string $user
  116. * @return boolean
  117. * @description configure the initial filesystem based on the configuration
  118. */
  119. public static function setupFS($user = '') {
  120. //setting up the filesystem twice can only lead to trouble
  121. if (self::$fsSetup) {
  122. return false;
  123. }
  124. \OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
  125. // If we are not forced to load a specific user we load the one that is logged in
  126. if ($user === null) {
  127. $user = '';
  128. } else if ($user == "" && OC_User::isLoggedIn()) {
  129. $user = OC_User::getUser();
  130. }
  131. // load all filesystem apps before, so no setup-hook gets lost
  132. OC_App::loadApps(array('filesystem'));
  133. // the filesystem will finish when $user is not empty,
  134. // mark fs setup here to avoid doing the setup from loading
  135. // OC_Filesystem
  136. if ($user != '') {
  137. self::$fsSetup = true;
  138. }
  139. \OC\Files\Filesystem::initMountManager();
  140. \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
  141. \OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
  142. if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
  143. /** @var \OC\Files\Storage\Common $storage */
  144. $storage->setMountOptions($mount->getOptions());
  145. }
  146. return $storage;
  147. });
  148. \OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
  149. if (!$mount->getOption('enable_sharing', true)) {
  150. return new \OC\Files\Storage\Wrapper\PermissionsMask([
  151. 'storage' => $storage,
  152. 'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
  153. ]);
  154. }
  155. return $storage;
  156. });
  157. // install storage availability wrapper, before most other wrappers
  158. \OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
  159. if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
  160. return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
  161. }
  162. return $storage;
  163. });
  164. \OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
  165. if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
  166. return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
  167. }
  168. return $storage;
  169. });
  170. \OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
  171. // set up quota for home storages, even for other users
  172. // which can happen when using sharing
  173. /**
  174. * @var \OC\Files\Storage\Storage $storage
  175. */
  176. if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
  177. || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
  178. ) {
  179. /** @var \OC\Files\Storage\Home $storage */
  180. if (is_object($storage->getUser())) {
  181. $user = $storage->getUser()->getUID();
  182. $quota = OC_Util::getUserQuota($user);
  183. if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
  184. return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
  185. }
  186. }
  187. }
  188. return $storage;
  189. });
  190. OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
  191. \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
  192. //check if we are using an object storage
  193. $objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
  194. if (isset($objectStore)) {
  195. self::initObjectStoreRootFS($objectStore);
  196. } else {
  197. self::initLocalStorageRootFS();
  198. }
  199. if ($user != '' && !OCP\User::userExists($user)) {
  200. \OC::$server->getEventLogger()->end('setup_fs');
  201. return false;
  202. }
  203. //if we aren't logged in, there is no use to set up the filesystem
  204. if ($user != "") {
  205. $userDir = '/' . $user . '/files';
  206. //jail the user into his "home" directory
  207. \OC\Files\Filesystem::init($user, $userDir);
  208. OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
  209. }
  210. \OC::$server->getEventLogger()->end('setup_fs');
  211. return true;
  212. }
  213. /**
  214. * check if a password is required for each public link
  215. *
  216. * @return boolean
  217. */
  218. public static function isPublicLinkPasswordRequired() {
  219. $appConfig = \OC::$server->getAppConfig();
  220. $enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password', 'no');
  221. return ($enforcePassword === 'yes') ? true : false;
  222. }
  223. /**
  224. * check if sharing is disabled for the current user
  225. * @param IConfig $config
  226. * @param IGroupManager $groupManager
  227. * @param IUser|null $user
  228. * @return bool
  229. */
  230. public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
  231. if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
  232. $groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
  233. $excludedGroups = json_decode($groupsList);
  234. if (is_null($excludedGroups)) {
  235. $excludedGroups = explode(',', $groupsList);
  236. $newValue = json_encode($excludedGroups);
  237. $config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
  238. }
  239. $usersGroups = $groupManager->getUserGroupIds($user);
  240. if (!empty($usersGroups)) {
  241. $remainingGroups = array_diff($usersGroups, $excludedGroups);
  242. // if the user is only in groups which are disabled for sharing then
  243. // sharing is also disabled for the user
  244. if (empty($remainingGroups)) {
  245. return true;
  246. }
  247. }
  248. }
  249. return false;
  250. }
  251. /**
  252. * check if share API enforces a default expire date
  253. *
  254. * @return boolean
  255. */
  256. public static function isDefaultExpireDateEnforced() {
  257. $isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
  258. $enforceDefaultExpireDate = false;
  259. if ($isDefaultExpireDateEnabled === 'yes') {
  260. $value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
  261. $enforceDefaultExpireDate = ($value === 'yes') ? true : false;
  262. }
  263. return $enforceDefaultExpireDate;
  264. }
  265. /**
  266. * Get the quota of a user
  267. *
  268. * @param string $userId
  269. * @return int Quota bytes
  270. */
  271. public static function getUserQuota($userId) {
  272. $user = \OC::$server->getUserManager()->get($userId);
  273. if (is_null($user)) {
  274. return \OCP\Files\FileInfo::SPACE_UNLIMITED;
  275. }
  276. $userQuota = $user->getQuota();
  277. if($userQuota === 'none') {
  278. return \OCP\Files\FileInfo::SPACE_UNLIMITED;
  279. }
  280. return OC_Helper::computerFileSize($userQuota);
  281. }
  282. /**
  283. * copies the skeleton to the users /files
  284. *
  285. * @param String $userId
  286. * @param \OCP\Files\Folder $userDirectory
  287. * @throws \RuntimeException
  288. */
  289. public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
  290. $skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
  291. $instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
  292. if ($instanceId === null) {
  293. throw new \RuntimeException('no instance id!');
  294. }
  295. $appdata = 'appdata_' . $instanceId;
  296. if ($userId === $appdata) {
  297. throw new \RuntimeException('username is reserved name: ' . $appdata);
  298. }
  299. if (!empty($skeletonDirectory)) {
  300. \OCP\Util::writeLog(
  301. 'files_skeleton',
  302. 'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
  303. \OCP\Util::DEBUG
  304. );
  305. self::copyr($skeletonDirectory, $userDirectory);
  306. // update the file cache
  307. $userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
  308. }
  309. }
  310. /**
  311. * copies a directory recursively by using streams
  312. *
  313. * @param string $source
  314. * @param \OCP\Files\Folder $target
  315. * @return void
  316. */
  317. public static function copyr($source, \OCP\Files\Folder $target) {
  318. $logger = \OC::$server->getLogger();
  319. // Verify if folder exists
  320. $dir = opendir($source);
  321. if($dir === false) {
  322. $logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
  323. return;
  324. }
  325. // Copy the files
  326. while (false !== ($file = readdir($dir))) {
  327. if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
  328. if (is_dir($source . '/' . $file)) {
  329. $child = $target->newFolder($file);
  330. self::copyr($source . '/' . $file, $child);
  331. } else {
  332. $child = $target->newFile($file);
  333. $sourceStream = fopen($source . '/' . $file, 'r');
  334. if($sourceStream === false) {
  335. $logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
  336. closedir($dir);
  337. return;
  338. }
  339. stream_copy_to_stream($sourceStream, $child->fopen('w'));
  340. }
  341. }
  342. }
  343. closedir($dir);
  344. }
  345. /**
  346. * @return void
  347. */
  348. public static function tearDownFS() {
  349. \OC\Files\Filesystem::tearDown();
  350. \OC::$server->getRootFolder()->clearCache();
  351. self::$fsSetup = false;
  352. self::$rootMounted = false;
  353. }
  354. /**
  355. * get the current installed version of ownCloud
  356. *
  357. * @return array
  358. */
  359. public static function getVersion() {
  360. OC_Util::loadVersion();
  361. return self::$versionCache['OC_Version'];
  362. }
  363. /**
  364. * get the current installed version string of ownCloud
  365. *
  366. * @return string
  367. */
  368. public static function getVersionString() {
  369. OC_Util::loadVersion();
  370. return self::$versionCache['OC_VersionString'];
  371. }
  372. /**
  373. * @deprecated the value is of no use anymore
  374. * @return string
  375. */
  376. public static function getEditionString() {
  377. return '';
  378. }
  379. /**
  380. * @description get the update channel of the current installed of ownCloud.
  381. * @return string
  382. */
  383. public static function getChannel() {
  384. OC_Util::loadVersion();
  385. // Allow overriding update channel
  386. if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
  387. self::$versionCache['OC_Channel'] = \OC::$server->getAppConfig()->getValue('core', 'OC_Channel');
  388. }
  389. return self::$versionCache['OC_Channel'];
  390. }
  391. /**
  392. * @description get the build number of the current installed of ownCloud.
  393. * @return string
  394. */
  395. public static function getBuild() {
  396. OC_Util::loadVersion();
  397. return self::$versionCache['OC_Build'];
  398. }
  399. /**
  400. * @description load the version.php into the session as cache
  401. */
  402. private static function loadVersion() {
  403. if (self::$versionCache !== null) {
  404. return;
  405. }
  406. $timestamp = filemtime(OC::$SERVERROOT . '/version.php');
  407. require OC::$SERVERROOT . '/version.php';
  408. /** @var $timestamp int */
  409. self::$versionCache['OC_Version_Timestamp'] = $timestamp;
  410. /** @var $OC_Version string */
  411. self::$versionCache['OC_Version'] = $OC_Version;
  412. /** @var $OC_VersionString string */
  413. self::$versionCache['OC_VersionString'] = $OC_VersionString;
  414. /** @var $OC_Build string */
  415. self::$versionCache['OC_Build'] = $OC_Build;
  416. /** @var $OC_Channel string */
  417. self::$versionCache['OC_Channel'] = $OC_Channel;
  418. }
  419. /**
  420. * generates a path for JS/CSS files. If no application is provided it will create the path for core.
  421. *
  422. * @param string $application application to get the files from
  423. * @param string $directory directory within this application (css, js, vendor, etc)
  424. * @param string $file the file inside of the above folder
  425. * @return string the path
  426. */
  427. private static function generatePath($application, $directory, $file) {
  428. if (is_null($file)) {
  429. $file = $application;
  430. $application = "";
  431. }
  432. if (!empty($application)) {
  433. return "$application/$directory/$file";
  434. } else {
  435. return "$directory/$file";
  436. }
  437. }
  438. /**
  439. * add a javascript file
  440. *
  441. * @param string $application application id
  442. * @param string|null $file filename
  443. * @param bool $prepend prepend the Script to the beginning of the list
  444. * @return void
  445. */
  446. public static function addScript($application, $file = null, $prepend = false) {
  447. $path = OC_Util::generatePath($application, 'js', $file);
  448. // core js files need separate handling
  449. if ($application !== 'core' && $file !== null) {
  450. self::addTranslations ( $application );
  451. }
  452. self::addExternalResource($application, $prepend, $path, "script");
  453. }
  454. /**
  455. * add a javascript file from the vendor sub folder
  456. *
  457. * @param string $application application id
  458. * @param string|null $file filename
  459. * @param bool $prepend prepend the Script to the beginning of the list
  460. * @return void
  461. */
  462. public static function addVendorScript($application, $file = null, $prepend = false) {
  463. $path = OC_Util::generatePath($application, 'vendor', $file);
  464. self::addExternalResource($application, $prepend, $path, "script");
  465. }
  466. /**
  467. * add a translation JS file
  468. *
  469. * @param string $application application id
  470. * @param string $languageCode language code, defaults to the current language
  471. * @param bool $prepend prepend the Script to the beginning of the list
  472. */
  473. public static function addTranslations($application, $languageCode = null, $prepend = false) {
  474. if (is_null($languageCode)) {
  475. $languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
  476. }
  477. if (!empty($application)) {
  478. $path = "$application/l10n/$languageCode";
  479. } else {
  480. $path = "l10n/$languageCode";
  481. }
  482. self::addExternalResource($application, $prepend, $path, "script");
  483. }
  484. /**
  485. * add a css file
  486. *
  487. * @param string $application application id
  488. * @param string|null $file filename
  489. * @param bool $prepend prepend the Style to the beginning of the list
  490. * @return void
  491. */
  492. public static function addStyle($application, $file = null, $prepend = false) {
  493. $path = OC_Util::generatePath($application, 'css', $file);
  494. self::addExternalResource($application, $prepend, $path, "style");
  495. }
  496. /**
  497. * add a css file from the vendor sub folder
  498. *
  499. * @param string $application application id
  500. * @param string|null $file filename
  501. * @param bool $prepend prepend the Style to the beginning of the list
  502. * @return void
  503. */
  504. public static function addVendorStyle($application, $file = null, $prepend = false) {
  505. $path = OC_Util::generatePath($application, 'vendor', $file);
  506. self::addExternalResource($application, $prepend, $path, "style");
  507. }
  508. /**
  509. * add an external resource css/js file
  510. *
  511. * @param string $application application id
  512. * @param bool $prepend prepend the file to the beginning of the list
  513. * @param string $path
  514. * @param string $type (script or style)
  515. * @return void
  516. */
  517. private static function addExternalResource($application, $prepend, $path, $type = "script") {
  518. if ($type === "style") {
  519. if (!in_array($path, self::$styles)) {
  520. if ($prepend === true) {
  521. array_unshift ( self::$styles, $path );
  522. } else {
  523. self::$styles[] = $path;
  524. }
  525. }
  526. } elseif ($type === "script") {
  527. if (!in_array($path, self::$scripts)) {
  528. if ($prepend === true) {
  529. array_unshift ( self::$scripts, $path );
  530. } else {
  531. self::$scripts [] = $path;
  532. }
  533. }
  534. }
  535. }
  536. /**
  537. * Add a custom element to the header
  538. * If $text is null then the element will be written as empty element.
  539. * So use "" to get a closing tag.
  540. * @param string $tag tag name of the element
  541. * @param array $attributes array of attributes for the element
  542. * @param string $text the text content for the element
  543. */
  544. public static function addHeader($tag, $attributes, $text=null) {
  545. self::$headers[] = array(
  546. 'tag' => $tag,
  547. 'attributes' => $attributes,
  548. 'text' => $text
  549. );
  550. }
  551. /**
  552. * formats a timestamp in the "right" way
  553. *
  554. * @param int $timestamp
  555. * @param bool $dateOnly option to omit time from the result
  556. * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
  557. * @return string timestamp
  558. *
  559. * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
  560. */
  561. public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
  562. if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
  563. $timeZone = new \DateTimeZone($timeZone);
  564. }
  565. /** @var \OC\DateTimeFormatter $formatter */
  566. $formatter = \OC::$server->query('DateTimeFormatter');
  567. if ($dateOnly) {
  568. return $formatter->formatDate($timestamp, 'long', $timeZone);
  569. }
  570. return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
  571. }
  572. /**
  573. * check if the current server configuration is suitable for ownCloud
  574. *
  575. * @param \OCP\IConfig $config
  576. * @return array arrays with error messages and hints
  577. */
  578. public static function checkServer(\OCP\IConfig $config) {
  579. $l = \OC::$server->getL10N('lib');
  580. $errors = array();
  581. $CONFIG_DATADIRECTORY = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
  582. if (!self::needUpgrade($config) && $config->getSystemValue('installed', false)) {
  583. // this check needs to be done every time
  584. $errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
  585. }
  586. // Assume that if checkServer() succeeded before in this session, then all is fine.
  587. if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
  588. return $errors;
  589. }
  590. $webServerRestart = false;
  591. $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
  592. \OC::$server->getThemingDefaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
  593. $urlGenerator = \OC::$server->getURLGenerator();
  594. $availableDatabases = $setup->getSupportedDatabases();
  595. if (empty($availableDatabases)) {
  596. $errors[] = array(
  597. 'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
  598. 'hint' => '' //TODO: sane hint
  599. );
  600. $webServerRestart = true;
  601. }
  602. // Check if config folder is writable.
  603. if(!OC_Helper::isReadOnlyConfigEnabled()) {
  604. if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
  605. $errors[] = array(
  606. 'error' => $l->t('Cannot write into "config" directory'),
  607. 'hint' => $l->t('This can usually be fixed by '
  608. . '%sgiving the webserver write access to the config directory%s.',
  609. array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
  610. );
  611. }
  612. }
  613. // Check if there is a writable install folder.
  614. if ($config->getSystemValue('appstoreenabled', true)) {
  615. if (OC_App::getInstallPath() === null
  616. || !is_writable(OC_App::getInstallPath())
  617. || !is_readable(OC_App::getInstallPath())
  618. ) {
  619. $errors[] = array(
  620. 'error' => $l->t('Cannot write into "apps" directory'),
  621. 'hint' => $l->t('This can usually be fixed by '
  622. . '%sgiving the webserver write access to the apps directory%s'
  623. . ' or disabling the appstore in the config file.',
  624. array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
  625. );
  626. }
  627. }
  628. // Create root dir.
  629. if ($config->getSystemValue('installed', false)) {
  630. if (!is_dir($CONFIG_DATADIRECTORY)) {
  631. $success = @mkdir($CONFIG_DATADIRECTORY);
  632. if ($success) {
  633. $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
  634. } else {
  635. $errors[] = array(
  636. 'error' => $l->t('Cannot create "data" directory (%s)', array($CONFIG_DATADIRECTORY)),
  637. 'hint' => $l->t('This can usually be fixed by '
  638. . '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
  639. array($urlGenerator->linkToDocs('admin-dir_permissions')))
  640. );
  641. }
  642. } else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
  643. //common hint for all file permissions error messages
  644. $permissionsHint = $l->t('Permissions can usually be fixed by '
  645. . '%sgiving the webserver write access to the root directory%s.',
  646. array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'));
  647. $errors[] = array(
  648. 'error' => 'Data directory (' . $CONFIG_DATADIRECTORY . ') not writable',
  649. 'hint' => $permissionsHint
  650. );
  651. } else {
  652. $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
  653. }
  654. }
  655. if (!OC_Util::isSetLocaleWorking()) {
  656. $errors[] = array(
  657. 'error' => $l->t('Setting locale to %s failed',
  658. array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
  659. . 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
  660. 'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
  661. );
  662. }
  663. // Contains the dependencies that should be checked against
  664. // classes = class_exists
  665. // functions = function_exists
  666. // defined = defined
  667. // ini = ini_get
  668. // If the dependency is not found the missing module name is shown to the EndUser
  669. // When adding new checks always verify that they pass on Travis as well
  670. // for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
  671. $dependencies = array(
  672. 'classes' => array(
  673. 'ZipArchive' => 'zip',
  674. 'DOMDocument' => 'dom',
  675. 'XMLWriter' => 'XMLWriter',
  676. 'XMLReader' => 'XMLReader',
  677. ),
  678. 'functions' => [
  679. 'xml_parser_create' => 'libxml',
  680. 'mb_strcut' => 'mb multibyte',
  681. 'ctype_digit' => 'ctype',
  682. 'json_encode' => 'JSON',
  683. 'gd_info' => 'GD',
  684. 'gzencode' => 'zlib',
  685. 'iconv' => 'iconv',
  686. 'simplexml_load_string' => 'SimpleXML',
  687. 'hash' => 'HASH Message Digest Framework',
  688. 'curl_init' => 'cURL',
  689. 'openssl_verify' => 'OpenSSL',
  690. ],
  691. 'defined' => array(
  692. 'PDO::ATTR_DRIVER_NAME' => 'PDO'
  693. ),
  694. 'ini' => [
  695. 'default_charset' => 'UTF-8',
  696. ],
  697. );
  698. $missingDependencies = array();
  699. $invalidIniSettings = [];
  700. $moduleHint = $l->t('Please ask your server administrator to install the module.');
  701. /**
  702. * FIXME: The dependency check does not work properly on HHVM on the moment
  703. * and prevents installation. Once HHVM is more compatible with our
  704. * approach to check for these values we should re-enable those
  705. * checks.
  706. */
  707. $iniWrapper = \OC::$server->getIniWrapper();
  708. if (!self::runningOnHhvm()) {
  709. foreach ($dependencies['classes'] as $class => $module) {
  710. if (!class_exists($class)) {
  711. $missingDependencies[] = $module;
  712. }
  713. }
  714. foreach ($dependencies['functions'] as $function => $module) {
  715. if (!function_exists($function)) {
  716. $missingDependencies[] = $module;
  717. }
  718. }
  719. foreach ($dependencies['defined'] as $defined => $module) {
  720. if (!defined($defined)) {
  721. $missingDependencies[] = $module;
  722. }
  723. }
  724. foreach ($dependencies['ini'] as $setting => $expected) {
  725. if (is_bool($expected)) {
  726. if ($iniWrapper->getBool($setting) !== $expected) {
  727. $invalidIniSettings[] = [$setting, $expected];
  728. }
  729. }
  730. if (is_int($expected)) {
  731. if ($iniWrapper->getNumeric($setting) !== $expected) {
  732. $invalidIniSettings[] = [$setting, $expected];
  733. }
  734. }
  735. if (is_string($expected)) {
  736. if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
  737. $invalidIniSettings[] = [$setting, $expected];
  738. }
  739. }
  740. }
  741. }
  742. foreach($missingDependencies as $missingDependency) {
  743. $errors[] = array(
  744. 'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
  745. 'hint' => $moduleHint
  746. );
  747. $webServerRestart = true;
  748. }
  749. foreach($invalidIniSettings as $setting) {
  750. if(is_bool($setting[1])) {
  751. $setting[1] = ($setting[1]) ? 'on' : 'off';
  752. }
  753. $errors[] = [
  754. 'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
  755. 'hint' => $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
  756. ];
  757. $webServerRestart = true;
  758. }
  759. /**
  760. * The mbstring.func_overload check can only be performed if the mbstring
  761. * module is installed as it will return null if the checking setting is
  762. * not available and thus a check on the boolean value fails.
  763. *
  764. * TODO: Should probably be implemented in the above generic dependency
  765. * check somehow in the long-term.
  766. */
  767. if($iniWrapper->getBool('mbstring.func_overload') !== null &&
  768. $iniWrapper->getBool('mbstring.func_overload') === true) {
  769. $errors[] = array(
  770. 'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
  771. 'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
  772. );
  773. }
  774. if(function_exists('xml_parser_create') &&
  775. LIBXML_LOADED_VERSION < 20700 ) {
  776. $version = LIBXML_LOADED_VERSION;
  777. $major = floor($version/10000);
  778. $version -= ($major * 10000);
  779. $minor = floor($version/100);
  780. $version -= ($minor * 100);
  781. $patch = $version;
  782. $errors[] = array(
  783. 'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
  784. 'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
  785. );
  786. }
  787. if (!self::isAnnotationsWorking()) {
  788. $errors[] = array(
  789. 'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
  790. 'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
  791. );
  792. }
  793. if (!\OC::$CLI && $webServerRestart) {
  794. $errors[] = array(
  795. 'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
  796. 'hint' => $l->t('Please ask your server administrator to restart the web server.')
  797. );
  798. }
  799. $errors = array_merge($errors, self::checkDatabaseVersion());
  800. // Cache the result of this function
  801. \OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
  802. return $errors;
  803. }
  804. /**
  805. * Check the database version
  806. *
  807. * @return array errors array
  808. */
  809. public static function checkDatabaseVersion() {
  810. $l = \OC::$server->getL10N('lib');
  811. $errors = array();
  812. $dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
  813. if ($dbType === 'pgsql') {
  814. // check PostgreSQL version
  815. try {
  816. $result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
  817. $data = $result->fetchRow();
  818. if (isset($data['server_version'])) {
  819. $version = $data['server_version'];
  820. if (version_compare($version, '9.0.0', '<')) {
  821. $errors[] = array(
  822. 'error' => $l->t('PostgreSQL >= 9 required'),
  823. 'hint' => $l->t('Please upgrade your database version')
  824. );
  825. }
  826. }
  827. } catch (\Doctrine\DBAL\DBALException $e) {
  828. $logger = \OC::$server->getLogger();
  829. $logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
  830. $logger->logException($e);
  831. }
  832. }
  833. return $errors;
  834. }
  835. /**
  836. * Check for correct file permissions of data directory
  837. *
  838. * @param string $dataDirectory
  839. * @return array arrays with error messages and hints
  840. */
  841. public static function checkDataDirectoryPermissions($dataDirectory) {
  842. $l = \OC::$server->getL10N('lib');
  843. $errors = array();
  844. $permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
  845. . ' cannot be listed by other users.');
  846. $perms = substr(decoct(@fileperms($dataDirectory)), -3);
  847. if (substr($perms, -1) != '0') {
  848. chmod($dataDirectory, 0770);
  849. clearstatcache();
  850. $perms = substr(decoct(@fileperms($dataDirectory)), -3);
  851. if (substr($perms, 2, 1) != '0') {
  852. $errors[] = array(
  853. 'error' => $l->t('Data directory (%s) is readable by other users', array($dataDirectory)),
  854. 'hint' => $permissionsModHint
  855. );
  856. }
  857. }
  858. return $errors;
  859. }
  860. /**
  861. * Check that the data directory exists and is valid by
  862. * checking the existence of the ".ocdata" file.
  863. *
  864. * @param string $dataDirectory data directory path
  865. * @return array errors found
  866. */
  867. public static function checkDataDirectoryValidity($dataDirectory) {
  868. $l = \OC::$server->getL10N('lib');
  869. $errors = [];
  870. if ($dataDirectory[0] !== '/') {
  871. $errors[] = [
  872. 'error' => $l->t('Data directory (%s) must be an absolute path', [$dataDirectory]),
  873. 'hint' => $l->t('Check the value of "datadirectory" in your configuration')
  874. ];
  875. }
  876. if (!file_exists($dataDirectory . '/.ocdata')) {
  877. $errors[] = [
  878. 'error' => $l->t('Data directory (%s) is invalid', [$dataDirectory]),
  879. 'hint' => $l->t('Please check that the data directory contains a file' .
  880. ' ".ocdata" in its root.')
  881. ];
  882. }
  883. return $errors;
  884. }
  885. /**
  886. * Check if the user is logged in, redirects to home if not. With
  887. * redirect URL parameter to the request URI.
  888. *
  889. * @return void
  890. */
  891. public static function checkLoggedIn() {
  892. // Check if we are a user
  893. if (!OC_User::isLoggedIn()) {
  894. header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
  895. 'core.login.showLoginForm',
  896. [
  897. 'redirect_url' => urlencode(\OC::$server->getRequest()->getRequestUri()),
  898. ]
  899. )
  900. );
  901. exit();
  902. }
  903. // Redirect to index page if 2FA challenge was not solved yet
  904. if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
  905. header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
  906. exit();
  907. }
  908. }
  909. /**
  910. * Check if the user is a admin, redirects to home if not
  911. *
  912. * @return void
  913. */
  914. public static function checkAdminUser() {
  915. OC_Util::checkLoggedIn();
  916. if (!OC_User::isAdminUser(OC_User::getUser())) {
  917. header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
  918. exit();
  919. }
  920. }
  921. /**
  922. * Check if it is allowed to remember login.
  923. *
  924. * @note Every app can set 'rememberlogin' to 'false' to disable the remember login feature
  925. *
  926. * @return bool
  927. */
  928. public static function rememberLoginAllowed() {
  929. $apps = OC_App::getEnabledApps();
  930. foreach ($apps as $app) {
  931. $appInfo = OC_App::getAppInfo($app);
  932. if (isset($appInfo['rememberlogin']) && $appInfo['rememberlogin'] === 'false') {
  933. return false;
  934. }
  935. }
  936. return true;
  937. }
  938. /**
  939. * Check if the user is a subadmin, redirects to home if not
  940. *
  941. * @return null|boolean $groups where the current user is subadmin
  942. */
  943. public static function checkSubAdminUser() {
  944. OC_Util::checkLoggedIn();
  945. $userObject = \OC::$server->getUserSession()->getUser();
  946. $isSubAdmin = false;
  947. if($userObject !== null) {
  948. $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
  949. }
  950. if (!$isSubAdmin) {
  951. header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
  952. exit();
  953. }
  954. return true;
  955. }
  956. /**
  957. * Returns the URL of the default page
  958. * based on the system configuration and
  959. * the apps visible for the current user
  960. *
  961. * @return string URL
  962. */
  963. public static function getDefaultPageUrl() {
  964. $urlGenerator = \OC::$server->getURLGenerator();
  965. // Deny the redirect if the URL contains a @
  966. // This prevents unvalidated redirects like ?redirect_url=:user@domain.com
  967. if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
  968. $location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
  969. } else {
  970. $defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage');
  971. if ($defaultPage) {
  972. $location = $urlGenerator->getAbsoluteURL($defaultPage);
  973. } else {
  974. $appId = 'files';
  975. $defaultApps = explode(',', \OCP\Config::getSystemValue('defaultapp', 'files'));
  976. // find the first app that is enabled for the current user
  977. foreach ($defaultApps as $defaultApp) {
  978. $defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
  979. if (static::getAppManager()->isEnabledForUser($defaultApp)) {
  980. $appId = $defaultApp;
  981. break;
  982. }
  983. }
  984. if(getenv('front_controller_active') === 'true') {
  985. $location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
  986. } else {
  987. $location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
  988. }
  989. }
  990. }
  991. return $location;
  992. }
  993. /**
  994. * Redirect to the user default page
  995. *
  996. * @return void
  997. */
  998. public static function redirectToDefaultPage() {
  999. $location = self::getDefaultPageUrl();
  1000. header('Location: ' . $location);
  1001. exit();
  1002. }
  1003. /**
  1004. * get an id unique for this instance
  1005. *
  1006. * @return string
  1007. */
  1008. public static function getInstanceId() {
  1009. $id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
  1010. if (is_null($id)) {
  1011. // We need to guarantee at least one letter in instanceid so it can be used as the session_name
  1012. $id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
  1013. \OC::$server->getSystemConfig()->setValue('instanceid', $id);
  1014. }
  1015. return $id;
  1016. }
  1017. /**
  1018. * Public function to sanitize HTML
  1019. *
  1020. * This function is used to sanitize HTML and should be applied on any
  1021. * string or array of strings before displaying it on a web page.
  1022. *
  1023. * @param string|array $value
  1024. * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
  1025. */
  1026. public static function sanitizeHTML($value) {
  1027. if (is_array($value)) {
  1028. $value = array_map(function($value) {
  1029. return self::sanitizeHTML($value);
  1030. }, $value);
  1031. } else {
  1032. // Specify encoding for PHP<5.4
  1033. $value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
  1034. }
  1035. return $value;
  1036. }
  1037. /**
  1038. * Public function to encode url parameters
  1039. *
  1040. * This function is used to encode path to file before output.
  1041. * Encoding is done according to RFC 3986 with one exception:
  1042. * Character '/' is preserved as is.
  1043. *
  1044. * @param string $component part of URI to encode
  1045. * @return string
  1046. */
  1047. public static function encodePath($component) {
  1048. $encoded = rawurlencode($component);
  1049. $encoded = str_replace('%2F', '/', $encoded);
  1050. return $encoded;
  1051. }
  1052. public function createHtaccessTestFile(\OCP\IConfig $config) {
  1053. // php dev server does not support htaccess
  1054. if (php_sapi_name() === 'cli-server') {
  1055. return false;
  1056. }
  1057. // testdata
  1058. $fileName = '/htaccesstest.txt';
  1059. $testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
  1060. // creating a test file
  1061. $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
  1062. if (file_exists($testFile)) {// already running this test, possible recursive call
  1063. return false;
  1064. }
  1065. $fp = @fopen($testFile, 'w');
  1066. if (!$fp) {
  1067. throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
  1068. 'Make sure it is possible for the webserver to write to ' . $testFile);
  1069. }
  1070. fwrite($fp, $testContent);
  1071. fclose($fp);
  1072. }
  1073. /**
  1074. * Check if the .htaccess file is working
  1075. * @param \OCP\IConfig $config
  1076. * @return bool
  1077. * @throws Exception
  1078. * @throws \OC\HintException If the test file can't get written.
  1079. */
  1080. public function isHtaccessWorking(\OCP\IConfig $config) {
  1081. if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
  1082. return true;
  1083. }
  1084. $testContent = $this->createHtaccessTestFile($config);
  1085. if ($testContent === false) {
  1086. return false;
  1087. }
  1088. $fileName = '/htaccesstest.txt';
  1089. $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
  1090. // accessing the file via http
  1091. $url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
  1092. try {
  1093. $content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
  1094. } catch (\Exception $e) {
  1095. $content = false;
  1096. }
  1097. // cleanup
  1098. @unlink($testFile);
  1099. /*
  1100. * If the content is not equal to test content our .htaccess
  1101. * is working as required
  1102. */
  1103. return $content !== $testContent;
  1104. }
  1105. /**
  1106. * Check if the setlocal call does not work. This can happen if the right
  1107. * local packages are not available on the server.
  1108. *
  1109. * @return bool
  1110. */
  1111. public static function isSetLocaleWorking() {
  1112. \Patchwork\Utf8\Bootup::initLocale();
  1113. if ('' === basename('§')) {
  1114. return false;
  1115. }
  1116. return true;
  1117. }
  1118. /**
  1119. * Check if it's possible to get the inline annotations
  1120. *
  1121. * @return bool
  1122. */
  1123. public static function isAnnotationsWorking() {
  1124. $reflection = new \ReflectionMethod(__METHOD__);
  1125. $docs = $reflection->getDocComment();
  1126. return (is_string($docs) && strlen($docs) > 50);
  1127. }
  1128. /**
  1129. * Check if the PHP module fileinfo is loaded.
  1130. *
  1131. * @return bool
  1132. */
  1133. public static function fileInfoLoaded() {
  1134. return function_exists('finfo_open');
  1135. }
  1136. /**
  1137. * clear all levels of output buffering
  1138. *
  1139. * @return void
  1140. */
  1141. public static function obEnd() {
  1142. while (ob_get_level()) {
  1143. ob_end_clean();
  1144. }
  1145. }
  1146. /**
  1147. * Checks whether the server is running on Mac OS X
  1148. *
  1149. * @return bool true if running on Mac OS X, false otherwise
  1150. */
  1151. public static function runningOnMac() {
  1152. return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
  1153. }
  1154. /**
  1155. * Checks whether server is running on HHVM
  1156. *
  1157. * @return bool True if running on HHVM, false otherwise
  1158. */
  1159. public static function runningOnHhvm() {
  1160. return defined('HHVM_VERSION');
  1161. }
  1162. /**
  1163. * Handles the case that there may not be a theme, then check if a "default"
  1164. * theme exists and take that one
  1165. *
  1166. * @return string the theme
  1167. */
  1168. public static function getTheme() {
  1169. $theme = \OC::$server->getSystemConfig()->getValue("theme", '');
  1170. if ($theme === '') {
  1171. if (is_dir(OC::$SERVERROOT . '/themes/default')) {
  1172. $theme = 'default';
  1173. }
  1174. }
  1175. return $theme;
  1176. }
  1177. /**
  1178. * Clear a single file from the opcode cache
  1179. * This is useful for writing to the config file
  1180. * in case the opcode cache does not re-validate files
  1181. * Returns true if successful, false if unsuccessful:
  1182. * caller should fall back on clearing the entire cache
  1183. * with clearOpcodeCache() if unsuccessful
  1184. *
  1185. * @param string $path the path of the file to clear from the cache
  1186. * @return bool true if underlying function returns true, otherwise false
  1187. */
  1188. public static function deleteFromOpcodeCache($path) {
  1189. $ret = false;
  1190. if ($path) {
  1191. // APC >= 3.1.1
  1192. if (function_exists('apc_delete_file')) {
  1193. $ret = @apc_delete_file($path);
  1194. }
  1195. // Zend OpCache >= 7.0.0, PHP >= 5.5.0
  1196. if (function_exists('opcache_invalidate')) {
  1197. $ret = opcache_invalidate($path);
  1198. }
  1199. }
  1200. return $ret;
  1201. }
  1202. /**
  1203. * Clear the opcode cache if one exists
  1204. * This is necessary for writing to the config file
  1205. * in case the opcode cache does not re-validate files
  1206. *
  1207. * @return void
  1208. */
  1209. public static function clearOpcodeCache() {
  1210. // APC
  1211. if (function_exists('apc_clear_cache')) {
  1212. apc_clear_cache();
  1213. }
  1214. // Zend Opcache
  1215. if (function_exists('accelerator_reset')) {
  1216. accelerator_reset();
  1217. }
  1218. // XCache
  1219. if (function_exists('xcache_clear_cache')) {
  1220. if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
  1221. \OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
  1222. } else {
  1223. @xcache_clear_cache(XC_TYPE_PHP, 0);
  1224. }
  1225. }
  1226. // Opcache (PHP >= 5.5)
  1227. if (function_exists('opcache_reset')) {
  1228. opcache_reset();
  1229. }
  1230. }
  1231. /**
  1232. * Normalize a unicode string
  1233. *
  1234. * @param string $value a not normalized string
  1235. * @return bool|string
  1236. */
  1237. public static function normalizeUnicode($value) {
  1238. if(Normalizer::isNormalized($value)) {
  1239. return $value;
  1240. }
  1241. $normalizedValue = Normalizer::normalize($value);
  1242. if ($normalizedValue === null || $normalizedValue === false) {
  1243. \OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
  1244. return $value;
  1245. }
  1246. return $normalizedValue;
  1247. }
  1248. /**
  1249. * @param boolean|string $file
  1250. * @return string
  1251. */
  1252. public static function basename($file) {
  1253. $file = rtrim($file, '/');
  1254. $t = explode('/', $file);
  1255. return array_pop($t);
  1256. }
  1257. /**
  1258. * A human readable string is generated based on version, channel and build number
  1259. *
  1260. * @return string
  1261. */
  1262. public static function getHumanVersion() {
  1263. $version = OC_Util::getVersionString() . ' (' . OC_Util::getChannel() . ')';
  1264. $build = OC_Util::getBuild();
  1265. if (!empty($build) and OC_Util::getChannel() === 'daily') {
  1266. $version .= ' Build:' . $build;
  1267. }
  1268. return $version;
  1269. }
  1270. /**
  1271. * Returns whether the given file name is valid
  1272. *
  1273. * @param string $file file name to check
  1274. * @return bool true if the file name is valid, false otherwise
  1275. * @deprecated use \OC\Files\View::verifyPath()
  1276. */
  1277. public static function isValidFileName($file) {
  1278. $trimmed = trim($file);
  1279. if ($trimmed === '') {
  1280. return false;
  1281. }
  1282. if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
  1283. return false;
  1284. }
  1285. foreach (str_split($trimmed) as $char) {
  1286. if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
  1287. return false;
  1288. }
  1289. }
  1290. return true;
  1291. }
  1292. /**
  1293. * Check whether the instance needs to perform an upgrade,
  1294. * either when the core version is higher or any app requires
  1295. * an upgrade.
  1296. *
  1297. * @param \OCP\IConfig $config
  1298. * @return bool whether the core or any app needs an upgrade
  1299. * @throws \OC\HintException When the upgrade from the given version is not allowed
  1300. */
  1301. public static function needUpgrade(\OCP\IConfig $config) {
  1302. if ($config->getSystemValue('installed', false)) {
  1303. $installedVersion = $config->getSystemValue('version', '0.0.0');
  1304. $currentVersion = implode('.', \OCP\Util::getVersion());
  1305. $versionDiff = version_compare($currentVersion, $installedVersion);
  1306. if ($versionDiff > 0) {
  1307. return true;
  1308. } else if ($config->getSystemValue('debug', false) && $versionDiff < 0) {
  1309. // downgrade with debug
  1310. $installedMajor = explode('.', $installedVersion);
  1311. $installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
  1312. $currentMajor = explode('.', $currentVersion);
  1313. $currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
  1314. if ($installedMajor === $currentMajor) {
  1315. // Same major, allow downgrade for developers
  1316. return true;
  1317. } else {
  1318. // downgrade attempt, throw exception
  1319. throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
  1320. }
  1321. } else if ($versionDiff < 0) {
  1322. // downgrade attempt, throw exception
  1323. throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
  1324. }
  1325. // also check for upgrades for apps (independently from the user)
  1326. $apps = \OC_App::getEnabledApps(false, true);
  1327. $shouldUpgrade = false;
  1328. foreach ($apps as $app) {
  1329. if (\OC_App::shouldUpgrade($app)) {
  1330. $shouldUpgrade = true;
  1331. break;
  1332. }
  1333. }
  1334. return $shouldUpgrade;
  1335. } else {
  1336. return false;
  1337. }
  1338. }
  1339. }