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.

преди 13 години
преди 8 години
преди 9 години
преди 8 години
преди 9 години
преди 8 години
преди 8 години
преди 9 години
преди 8 години
преди 8 години
преди 9 години
преди 13 години
преди 11 години
преди 13 години
преди 13 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 13 години
преди 13 години
преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 13 години
преди 11 години
преди 13 години
преди 13 години
преди 13 години
преди 11 години
преди 13 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 12 години
преди 11 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Adam Williamson <awilliam@redhat.com>
  6. * @author Aldo "xoen" Giambelluca <xoen@xoen.org>
  7. * @author Bart Visscher <bartv@thisnet.nl>
  8. * @author Brice Maron <brice@bmaron.net>
  9. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  10. * @author Daniel Kesselberg <mail@danielkesselberg.de>
  11. * @author Frank Karlitschek <frank@karlitschek.de>
  12. * @author Jakob Sack <mail@jakobsack.de>
  13. * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
  14. * @author Joas Schilling <coding@schilljs.com>
  15. * @author John Molakvoæ <skjnldsv@protonmail.com>
  16. * @author Lukas Reschke <lukas@statuscode.ch>
  17. * @author Michael Gapczynski <GapczynskiM@gmail.com>
  18. * @author Morris Jobke <hey@morrisjobke.de>
  19. * @author Philipp Schaffrath <github@philipp.schaffrath.email>
  20. * @author Robin Appelman <robin@icewind.nl>
  21. * @author Robin McCorkell <robin@mccorkell.me.uk>
  22. * @author Roeland Jago Douma <roeland@famdouma.nl>
  23. *
  24. * @license AGPL-3.0
  25. *
  26. * This code is free software: you can redistribute it and/or modify
  27. * it under the terms of the GNU Affero General Public License, version 3,
  28. * as published by the Free Software Foundation.
  29. *
  30. * This program is distributed in the hope that it will be useful,
  31. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  32. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  33. * GNU Affero General Public License for more details.
  34. *
  35. * You should have received a copy of the GNU Affero General Public License, version 3,
  36. * along with this program. If not, see <http://www.gnu.org/licenses/>
  37. *
  38. */
  39. namespace OC;
  40. use OCP\HintException;
  41. /**
  42. * This class is responsible for reading and writing config.php, the very basic
  43. * configuration file of Nextcloud.
  44. */
  45. class Config {
  46. public const ENV_PREFIX = 'NC_';
  47. /** @var array Associative array ($key => $value) */
  48. protected $cache = [];
  49. /** @var array */
  50. protected $envCache = [];
  51. /** @var string */
  52. protected $configDir;
  53. /** @var string */
  54. protected $configFilePath;
  55. /** @var string */
  56. protected $configFileName;
  57. /** @var bool */
  58. protected $isReadOnly;
  59. /**
  60. * @param string $configDir Path to the config dir, needs to end with '/'
  61. * @param string $fileName (Optional) Name of the config file. Defaults to config.php
  62. */
  63. public function __construct($configDir, $fileName = 'config.php') {
  64. $this->configDir = $configDir;
  65. $this->configFilePath = $this->configDir.$fileName;
  66. $this->configFileName = $fileName;
  67. $this->readData();
  68. $this->isReadOnly = $this->getValue('config_is_read_only', false);
  69. }
  70. /**
  71. * Lists all available config keys
  72. *
  73. * Please note that it does not return the values.
  74. *
  75. * @return array an array of key names
  76. */
  77. public function getKeys() {
  78. return array_keys($this->cache);
  79. }
  80. /**
  81. * Returns a config value
  82. *
  83. * gets its value from an `NC_` prefixed environment variable
  84. * if it doesn't exist from config.php
  85. * if this doesn't exist either, it will return the given `$default`
  86. *
  87. * @param string $key key
  88. * @param mixed $default = null default value
  89. * @return mixed the value or $default
  90. */
  91. public function getValue($key, $default = null) {
  92. $envKey = self::ENV_PREFIX . $key;
  93. if (isset($this->envCache[$envKey])) {
  94. return $this->envCache[$envKey];
  95. }
  96. if (isset($this->cache[$key])) {
  97. return $this->cache[$key];
  98. }
  99. return $default;
  100. }
  101. /**
  102. * Sets and deletes values and writes the config.php
  103. *
  104. * @param array $configs Associative array with `key => value` pairs
  105. * If value is null, the config key will be deleted
  106. * @throws HintException
  107. */
  108. public function setValues(array $configs) {
  109. $needsUpdate = false;
  110. foreach ($configs as $key => $value) {
  111. if ($value !== null) {
  112. $needsUpdate |= $this->set($key, $value);
  113. } else {
  114. $needsUpdate |= $this->delete($key);
  115. }
  116. }
  117. if ($needsUpdate) {
  118. // Write changes
  119. $this->writeData();
  120. }
  121. }
  122. /**
  123. * Sets the value and writes it to config.php if required
  124. *
  125. * @param string $key key
  126. * @param mixed $value value
  127. * @throws HintException
  128. */
  129. public function setValue($key, $value) {
  130. if ($this->set($key, $value)) {
  131. // Write changes
  132. $this->writeData();
  133. }
  134. }
  135. /**
  136. * This function sets the value
  137. *
  138. * @param string $key key
  139. * @param mixed $value value
  140. * @return bool True if the file needs to be updated, false otherwise
  141. * @throws HintException
  142. */
  143. protected function set($key, $value) {
  144. $this->checkReadOnly();
  145. if (!isset($this->cache[$key]) || $this->cache[$key] !== $value) {
  146. // Add change
  147. $this->cache[$key] = $value;
  148. return true;
  149. }
  150. return false;
  151. }
  152. /**
  153. * Removes a key from the config and removes it from config.php if required
  154. *
  155. * @param string $key
  156. * @throws HintException
  157. */
  158. public function deleteKey($key) {
  159. if ($this->delete($key)) {
  160. // Write changes
  161. $this->writeData();
  162. }
  163. }
  164. /**
  165. * This function removes a key from the config
  166. *
  167. * @param string $key
  168. * @return bool True if the file needs to be updated, false otherwise
  169. * @throws HintException
  170. */
  171. protected function delete($key) {
  172. $this->checkReadOnly();
  173. if (isset($this->cache[$key])) {
  174. // Delete key from cache
  175. unset($this->cache[$key]);
  176. return true;
  177. }
  178. return false;
  179. }
  180. /**
  181. * Loads the config file
  182. *
  183. * Reads the config file and saves it to the cache
  184. *
  185. * @throws \Exception If no lock could be acquired or the config file has not been found
  186. */
  187. private function readData() {
  188. // Default config should always get loaded
  189. $configFiles = [$this->configFilePath];
  190. // Add all files in the config dir ending with the same file name
  191. $extra = glob($this->configDir.'*.'.$this->configFileName);
  192. if (is_array($extra)) {
  193. natsort($extra);
  194. $configFiles = array_merge($configFiles, $extra);
  195. }
  196. // Include file and merge config
  197. foreach ($configFiles as $file) {
  198. $fileExistsAndIsReadable = file_exists($file) && is_readable($file);
  199. $filePointer = $fileExistsAndIsReadable ? fopen($file, 'r') : false;
  200. if ($file === $this->configFilePath &&
  201. $filePointer === false) {
  202. // Opening the main config might not be possible, e.g. if the wrong
  203. // permissions are set (likely on a new installation)
  204. continue;
  205. }
  206. // Try to acquire a file lock
  207. if (!flock($filePointer, LOCK_SH)) {
  208. throw new \Exception(sprintf('Could not acquire a shared lock on the config file %s', $file));
  209. }
  210. unset($CONFIG);
  211. include $file;
  212. if (!defined('PHPUNIT_RUN') && headers_sent()) {
  213. // syntax issues in the config file like leading spaces causing PHP to send output
  214. $errorMessage = sprintf('Config file has leading content, please remove everything before "<?php" in %s', basename($file));
  215. if (!defined('OC_CONSOLE')) {
  216. print(\OCP\Util::sanitizeHTML($errorMessage));
  217. }
  218. throw new \Exception($errorMessage);
  219. }
  220. if (isset($CONFIG) && is_array($CONFIG)) {
  221. $this->cache = array_merge($this->cache, $CONFIG);
  222. }
  223. // Close the file pointer and release the lock
  224. flock($filePointer, LOCK_UN);
  225. fclose($filePointer);
  226. }
  227. $this->envCache = getenv();
  228. }
  229. /**
  230. * Writes the config file
  231. *
  232. * Saves the config to the config file.
  233. *
  234. * @throws HintException If the config file cannot be written to
  235. * @throws \Exception If no file lock can be acquired
  236. */
  237. private function writeData() {
  238. $this->checkReadOnly();
  239. if (!is_file(\OC::$configDir.'/CAN_INSTALL') && !isset($this->cache['version'])) {
  240. throw new HintException(sprintf('Configuration was not read or initialized correctly, not overwriting %s', $this->configFilePath));
  241. }
  242. // Create a php file ...
  243. $content = "<?php\n";
  244. $content .= '$CONFIG = ';
  245. $content .= var_export($this->cache, true);
  246. $content .= ";\n";
  247. touch($this->configFilePath);
  248. $filePointer = fopen($this->configFilePath, 'r+');
  249. // Prevent others not to read the config
  250. chmod($this->configFilePath, 0640);
  251. // File does not exist, this can happen when doing a fresh install
  252. if (!is_resource($filePointer)) {
  253. throw new HintException(
  254. "Can't write into config directory!",
  255. 'This can usually be fixed by giving the webserver write access to the config directory.');
  256. }
  257. // Never write file back if disk space should be too low
  258. if (function_exists('disk_free_space')) {
  259. $df = disk_free_space($this->configDir);
  260. $size = strlen($content) + 10240;
  261. if ($df !== false && $df < (float)$size) {
  262. throw new \Exception($this->configDir . " does not have enough space for writing the config file! Not writing it back!");
  263. }
  264. }
  265. // Try to acquire a file lock
  266. if (!flock($filePointer, LOCK_EX)) {
  267. throw new \Exception(sprintf('Could not acquire an exclusive lock on the config file %s', $this->configFilePath));
  268. }
  269. // Write the config and release the lock
  270. ftruncate($filePointer, 0);
  271. fwrite($filePointer, $content);
  272. fflush($filePointer);
  273. flock($filePointer, LOCK_UN);
  274. fclose($filePointer);
  275. if (function_exists('opcache_invalidate')) {
  276. @opcache_invalidate($this->configFilePath, true);
  277. }
  278. }
  279. /**
  280. * @throws HintException
  281. */
  282. private function checkReadOnly(): void {
  283. if ($this->isReadOnly) {
  284. throw new HintException(
  285. 'Config is set to be read-only via option "config_is_read_only".',
  286. 'Unset "config_is_read_only" to allow changes to the config file.');
  287. }
  288. }
  289. }