config.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. /**
  3. * @author Adam Williamson <awilliam@redhat.com>
  4. * @author Aldo "xoen" Giambelluca <xoen@xoen.org>
  5. * @author Bart Visscher <bartv@thisnet.nl>
  6. * @author Brice Maron <brice@bmaron.net>
  7. * @author Frank Karlitschek <frank@owncloud.org>
  8. * @author Jakob Sack <mail@jakobsack.de>
  9. * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
  10. * @author Joas Schilling <nickvergessen@owncloud.com>
  11. * @author Lukas Reschke <lukas@owncloud.com>
  12. * @author Michael Gapczynski <GapczynskiM@gmail.com>
  13. * @author Morris Jobke <hey@morrisjobke.de>
  14. * @author Robin Appelman <icewind@owncloud.com>
  15. * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
  16. * @author Volkan Gezer <volkangezer@gmail.com>
  17. *
  18. * @copyright Copyright (c) 2015, ownCloud, Inc.
  19. * @license AGPL-3.0
  20. *
  21. * This code is free software: you can redistribute it and/or modify
  22. * it under the terms of the GNU Affero General Public License, version 3,
  23. * as published by the Free Software Foundation.
  24. *
  25. * This program is distributed in the hope that it will be useful,
  26. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. * GNU Affero General Public License for more details.
  29. *
  30. * You should have received a copy of the GNU Affero General Public License, version 3,
  31. * along with this program. If not, see <http://www.gnu.org/licenses/>
  32. *
  33. */
  34. namespace OC;
  35. /**
  36. * This class is responsible for reading and writing config.php, the very basic
  37. * configuration file of ownCloud.
  38. */
  39. class Config {
  40. /** @var array Associative array ($key => $value) */
  41. protected $cache = array();
  42. /** @var string */
  43. protected $configDir;
  44. /** @var string */
  45. protected $configFilePath;
  46. /** @var string */
  47. protected $configFileName;
  48. /** @var bool */
  49. protected $debugMode;
  50. /**
  51. * @param string $configDir Path to the config dir, needs to end with '/'
  52. * @param string $fileName (Optional) Name of the config file. Defaults to config.php
  53. */
  54. public function __construct($configDir, $fileName = 'config.php') {
  55. $this->configDir = $configDir;
  56. $this->configFilePath = $this->configDir.$fileName;
  57. $this->configFileName = $fileName;
  58. $this->readData();
  59. $this->debugMode = (defined('DEBUG') && DEBUG);
  60. }
  61. /**
  62. * Lists all available config keys
  63. *
  64. * Please note that it does not return the values.
  65. *
  66. * @return array an array of key names
  67. */
  68. public function getKeys() {
  69. return array_keys($this->cache);
  70. }
  71. /**
  72. * Gets a value from config.php
  73. *
  74. * If it does not exist, $default will be returned.
  75. *
  76. * @param string $key key
  77. * @param mixed $default = null default value
  78. * @return mixed the value or $default
  79. */
  80. public function getValue($key, $default = null) {
  81. if (isset($this->cache[$key])) {
  82. return $this->cache[$key];
  83. }
  84. return $default;
  85. }
  86. /**
  87. * Sets and deletes values and writes the config.php
  88. *
  89. * @param array $configs Associative array with `key => value` pairs
  90. * If value is null, the config key will be deleted
  91. */
  92. public function setValues(array $configs) {
  93. $needsUpdate = false;
  94. foreach ($configs as $key => $value) {
  95. if ($value !== null) {
  96. $needsUpdate |= $this->set($key, $value);
  97. } else {
  98. $needsUpdate |= $this->delete($key);
  99. }
  100. }
  101. if ($needsUpdate) {
  102. // Write changes
  103. $this->writeData();
  104. }
  105. }
  106. /**
  107. * Sets the value and writes it to config.php if required
  108. *
  109. * @param string $key key
  110. * @param mixed $value value
  111. */
  112. public function setValue($key, $value) {
  113. if ($this->set($key, $value)) {
  114. // Write changes
  115. $this->writeData();
  116. }
  117. }
  118. /**
  119. * This function sets the value
  120. *
  121. * @param string $key key
  122. * @param mixed $value value
  123. * @return bool True if the file needs to be updated, false otherwise
  124. */
  125. protected function set($key, $value) {
  126. if (!isset($this->cache[$key]) || $this->cache[$key] !== $value) {
  127. // Add change
  128. $this->cache[$key] = $value;
  129. return true;
  130. }
  131. return false;
  132. }
  133. /**
  134. * Removes a key from the config and removes it from config.php if required
  135. * @param string $key
  136. */
  137. public function deleteKey($key) {
  138. if ($this->delete($key)) {
  139. // Write changes
  140. $this->writeData();
  141. }
  142. }
  143. /**
  144. * This function removes a key from the config
  145. *
  146. * @param string $key
  147. * @return bool True if the file needs to be updated, false otherwise
  148. */
  149. protected function delete($key) {
  150. if (isset($this->cache[$key])) {
  151. // Delete key from cache
  152. unset($this->cache[$key]);
  153. return true;
  154. }
  155. return false;
  156. }
  157. /**
  158. * Loads the config file
  159. *
  160. * Reads the config file and saves it to the cache
  161. *
  162. * @throws \Exception If no lock could be acquired or the config file has not been found
  163. */
  164. private function readData() {
  165. // Default config should always get loaded
  166. $configFiles = array($this->configFilePath);
  167. // Add all files in the config dir ending with the same file name
  168. $extra = glob($this->configDir.'*.'.$this->configFileName);
  169. if (is_array($extra)) {
  170. natsort($extra);
  171. $configFiles = array_merge($configFiles, $extra);
  172. }
  173. // Include file and merge config
  174. foreach ($configFiles as $file) {
  175. $filePointer = @fopen($file, 'r');
  176. if($file === $this->configFilePath &&
  177. $filePointer === false &&
  178. @!file_exists($this->configFilePath)) {
  179. // Opening the main config might not be possible, e.g. if the wrong
  180. // permissions are set (likely on a new installation)
  181. continue;
  182. }
  183. // Try to acquire a file lock
  184. if(!flock($filePointer, LOCK_SH)) {
  185. throw new \Exception(sprintf('Could not acquire a shared lock on the config file %s', $file));
  186. }
  187. unset($CONFIG);
  188. include $file;
  189. if(isset($CONFIG) && is_array($CONFIG)) {
  190. $this->cache = array_merge($this->cache, $CONFIG);
  191. }
  192. // Close the file pointer and release the lock
  193. flock($filePointer, LOCK_UN);
  194. fclose($filePointer);
  195. }
  196. }
  197. /**
  198. * Writes the config file
  199. *
  200. * Saves the config to the config file.
  201. *
  202. * @throws HintException If the config file cannot be written to
  203. * @throws \Exception If no file lock can be acquired
  204. */
  205. private function writeData() {
  206. // Create a php file ...
  207. $content = "<?php\n";
  208. if ($this->debugMode) {
  209. $content .= "define('DEBUG',true);\n";
  210. }
  211. $content .= '$CONFIG = ';
  212. $content .= var_export($this->cache, true);
  213. $content .= ";\n";
  214. touch ($this->configFilePath);
  215. $filePointer = fopen($this->configFilePath, 'r+');
  216. // Prevent others not to read the config
  217. chmod($this->configFilePath, 0640);
  218. // File does not exist, this can happen when doing a fresh install
  219. if(!is_resource ($filePointer)) {
  220. $url = \OC_Helper::linkToDocs('admin-dir_permissions');
  221. throw new HintException(
  222. "Can't write into config directory!",
  223. 'This can usually be fixed by '
  224. .'<a href="' . $url . '" target="_blank">giving the webserver write access to the config directory</a>.');
  225. }
  226. // Try to acquire a file lock
  227. if(!flock($filePointer, LOCK_EX)) {
  228. throw new \Exception(sprintf('Could not acquire an exclusive lock on the config file %s', $this->configFilePath));
  229. }
  230. // Write the config and release the lock
  231. ftruncate ($filePointer, 0);
  232. fwrite($filePointer, $content);
  233. fflush($filePointer);
  234. flock($filePointer, LOCK_UN);
  235. fclose($filePointer);
  236. // Try invalidating the opcache just for the file we wrote...
  237. if (!\OC_Util::deleteFromOpcodeCache($this->configFilePath)) {
  238. // But if that doesn't work, clear the whole cache.
  239. \OC_Util::clearOpcodeCache();
  240. }
  241. }
  242. }