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.

456 lines
14 KiB

10 years ago
10 years ago
10 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Bart Visscher <bartv@thisnet.nl>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  8. * @author Lukas Reschke <lukas@statuscode.ch>
  9. * @author Morris Jobke <hey@morrisjobke.de>
  10. * @author Robin Appelman <robin@icewind.nl>
  11. * @author Robin McCorkell <robin@mccorkell.me.uk>
  12. * @author Thomas Müller <thomas.mueller@tmit.eu>
  13. * @author Vincent Petry <pvince81@owncloud.com>
  14. *
  15. * @license AGPL-3.0
  16. *
  17. * This code is free software: you can redistribute it and/or modify
  18. * it under the terms of the GNU Affero General Public License, version 3,
  19. * as published by the Free Software Foundation.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU Affero General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU Affero General Public License, version 3,
  27. * along with this program. If not, see <http://www.gnu.org/licenses/>
  28. *
  29. */
  30. namespace OC;
  31. use OC\Cache\CappedMemoryCache;
  32. use OCP\IDBConnection;
  33. use OCP\PreConditionNotMetException;
  34. /**
  35. * Class to combine all the configuration options ownCloud offers
  36. */
  37. class AllConfig implements \OCP\IConfig {
  38. /** @var SystemConfig */
  39. private $systemConfig;
  40. /** @var IDBConnection */
  41. private $connection;
  42. /**
  43. * 3 dimensional array with the following structure:
  44. * [ $userId =>
  45. * [ $appId =>
  46. * [ $key => $value ]
  47. * ]
  48. * ]
  49. *
  50. * database table: preferences
  51. *
  52. * methods that use this:
  53. * - setUserValue
  54. * - getUserValue
  55. * - getUserKeys
  56. * - deleteUserValue
  57. * - deleteAllUserValues
  58. * - deleteAppFromAllUsers
  59. *
  60. * @var CappedMemoryCache $userCache
  61. */
  62. private $userCache;
  63. /**
  64. * @param SystemConfig $systemConfig
  65. */
  66. function __construct(SystemConfig $systemConfig) {
  67. $this->userCache = new CappedMemoryCache();
  68. $this->systemConfig = $systemConfig;
  69. }
  70. /**
  71. * TODO - FIXME This fixes an issue with base.php that cause cyclic
  72. * dependencies, especially with autoconfig setup
  73. *
  74. * Replace this by properly injected database connection. Currently the
  75. * base.php triggers the getDatabaseConnection too early which causes in
  76. * autoconfig setup case a too early distributed database connection and
  77. * the autoconfig then needs to reinit all already initialized dependencies
  78. * that use the database connection.
  79. *
  80. * otherwise a SQLite database is created in the wrong directory
  81. * because the database connection was created with an uninitialized config
  82. */
  83. private function fixDIInit() {
  84. if($this->connection === null) {
  85. $this->connection = \OC::$server->getDatabaseConnection();
  86. }
  87. }
  88. /**
  89. * Sets and deletes system wide values
  90. *
  91. * @param array $configs Associative array with `key => value` pairs
  92. * If value is null, the config key will be deleted
  93. */
  94. public function setSystemValues(array $configs) {
  95. $this->systemConfig->setValues($configs);
  96. }
  97. /**
  98. * Sets a new system wide value
  99. *
  100. * @param string $key the key of the value, under which will be saved
  101. * @param mixed $value the value that should be stored
  102. */
  103. public function setSystemValue($key, $value) {
  104. $this->systemConfig->setValue($key, $value);
  105. }
  106. /**
  107. * Looks up a system wide defined value
  108. *
  109. * @param string $key the key of the value, under which it was saved
  110. * @param mixed $default the default value to be returned if the value isn't set
  111. * @return mixed the value or $default
  112. */
  113. public function getSystemValue($key, $default = '') {
  114. return $this->systemConfig->getValue($key, $default);
  115. }
  116. /**
  117. * Looks up a system wide defined value and filters out sensitive data
  118. *
  119. * @param string $key the key of the value, under which it was saved
  120. * @param mixed $default the default value to be returned if the value isn't set
  121. * @return mixed the value or $default
  122. */
  123. public function getFilteredSystemValue($key, $default = '') {
  124. return $this->systemConfig->getFilteredValue($key, $default);
  125. }
  126. /**
  127. * Delete a system wide defined value
  128. *
  129. * @param string $key the key of the value, under which it was saved
  130. */
  131. public function deleteSystemValue($key) {
  132. $this->systemConfig->deleteValue($key);
  133. }
  134. /**
  135. * Get all keys stored for an app
  136. *
  137. * @param string $appName the appName that we stored the value under
  138. * @return string[] the keys stored for the app
  139. */
  140. public function getAppKeys($appName) {
  141. return \OC::$server->getAppConfig()->getKeys($appName);
  142. }
  143. /**
  144. * Writes a new app wide value
  145. *
  146. * @param string $appName the appName that we want to store the value under
  147. * @param string $key the key of the value, under which will be saved
  148. * @param string|float|int $value the value that should be stored
  149. */
  150. public function setAppValue($appName, $key, $value) {
  151. \OC::$server->getAppConfig()->setValue($appName, $key, $value);
  152. }
  153. /**
  154. * Looks up an app wide defined value
  155. *
  156. * @param string $appName the appName that we stored the value under
  157. * @param string $key the key of the value, under which it was saved
  158. * @param string $default the default value to be returned if the value isn't set
  159. * @return string the saved value
  160. */
  161. public function getAppValue($appName, $key, $default = '') {
  162. return \OC::$server->getAppConfig()->getValue($appName, $key, $default);
  163. }
  164. /**
  165. * Delete an app wide defined value
  166. *
  167. * @param string $appName the appName that we stored the value under
  168. * @param string $key the key of the value, under which it was saved
  169. */
  170. public function deleteAppValue($appName, $key) {
  171. \OC::$server->getAppConfig()->deleteKey($appName, $key);
  172. }
  173. /**
  174. * Removes all keys in appconfig belonging to the app
  175. *
  176. * @param string $appName the appName the configs are stored under
  177. */
  178. public function deleteAppValues($appName) {
  179. \OC::$server->getAppConfig()->deleteApp($appName);
  180. }
  181. /**
  182. * Set a user defined value
  183. *
  184. * @param string $userId the userId of the user that we want to store the value under
  185. * @param string $appName the appName that we want to store the value under
  186. * @param string $key the key under which the value is being stored
  187. * @param string|float|int $value the value that you want to store
  188. * @param string $preCondition only update if the config value was previously the value passed as $preCondition
  189. * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met
  190. * @throws \UnexpectedValueException when trying to store an unexpected value
  191. */
  192. public function setUserValue($userId, $appName, $key, $value, $preCondition = null) {
  193. if (!is_int($value) && !is_float($value) && !is_string($value)) {
  194. throw new \UnexpectedValueException('Only integers, floats and strings are allowed as value');
  195. }
  196. // TODO - FIXME
  197. $this->fixDIInit();
  198. if (isset($this->userCache[$userId][$appName][$key])) {
  199. if ($this->userCache[$userId][$appName][$key] === (string)$value) {
  200. return;
  201. } else if ($preCondition !== null && $this->userCache[$userId][$appName][$key] !== (string)$preCondition) {
  202. return;
  203. } else {
  204. $qb = $this->connection->getQueryBuilder();
  205. $qb->update('preferences')
  206. ->set('configvalue', $qb->createNamedParameter($value))
  207. ->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId)))
  208. ->andWhere($qb->expr()->eq('appid', $qb->createNamedParameter($appName)))
  209. ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key)));
  210. $qb->execute();
  211. $this->userCache[$userId][$appName][$key] = $value;
  212. return;
  213. }
  214. }
  215. $preconditionArray = [];
  216. if (isset($preCondition)) {
  217. $preconditionArray = [
  218. 'configvalue' => $preCondition,
  219. ];
  220. }
  221. $this->connection->setValues('preferences', [
  222. 'userid' => $userId,
  223. 'appid' => $appName,
  224. 'configkey' => $key,
  225. ], [
  226. 'configvalue' => $value,
  227. ], $preconditionArray);
  228. // only add to the cache if we already loaded data for the user
  229. if (isset($this->userCache[$userId])) {
  230. if (!isset($this->userCache[$userId][$appName])) {
  231. $this->userCache[$userId][$appName] = array();
  232. }
  233. $this->userCache[$userId][$appName][$key] = $value;
  234. }
  235. }
  236. /**
  237. * Getting a user defined value
  238. *
  239. * @param string $userId the userId of the user that we want to store the value under
  240. * @param string $appName the appName that we stored the value under
  241. * @param string $key the key under which the value is being stored
  242. * @param mixed $default the default value to be returned if the value isn't set
  243. * @return string
  244. */
  245. public function getUserValue($userId, $appName, $key, $default = '') {
  246. $data = $this->getUserValues($userId);
  247. if (isset($data[$appName]) and isset($data[$appName][$key])) {
  248. return $data[$appName][$key];
  249. } else {
  250. return $default;
  251. }
  252. }
  253. /**
  254. * Get the keys of all stored by an app for the user
  255. *
  256. * @param string $userId the userId of the user that we want to store the value under
  257. * @param string $appName the appName that we stored the value under
  258. * @return string[]
  259. */
  260. public function getUserKeys($userId, $appName) {
  261. $data = $this->getUserValues($userId);
  262. if (isset($data[$appName])) {
  263. return array_keys($data[$appName]);
  264. } else {
  265. return array();
  266. }
  267. }
  268. /**
  269. * Delete a user value
  270. *
  271. * @param string $userId the userId of the user that we want to store the value under
  272. * @param string $appName the appName that we stored the value under
  273. * @param string $key the key under which the value is being stored
  274. */
  275. public function deleteUserValue($userId, $appName, $key) {
  276. // TODO - FIXME
  277. $this->fixDIInit();
  278. $sql = 'DELETE FROM `*PREFIX*preferences` '.
  279. 'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
  280. $this->connection->executeUpdate($sql, array($userId, $appName, $key));
  281. if (isset($this->userCache[$userId]) and isset($this->userCache[$userId][$appName])) {
  282. unset($this->userCache[$userId][$appName][$key]);
  283. }
  284. }
  285. /**
  286. * Delete all user values
  287. *
  288. * @param string $userId the userId of the user that we want to remove all values from
  289. */
  290. public function deleteAllUserValues($userId) {
  291. // TODO - FIXME
  292. $this->fixDIInit();
  293. $sql = 'DELETE FROM `*PREFIX*preferences` '.
  294. 'WHERE `userid` = ?';
  295. $this->connection->executeUpdate($sql, array($userId));
  296. unset($this->userCache[$userId]);
  297. }
  298. /**
  299. * Delete all user related values of one app
  300. *
  301. * @param string $appName the appName of the app that we want to remove all values from
  302. */
  303. public function deleteAppFromAllUsers($appName) {
  304. // TODO - FIXME
  305. $this->fixDIInit();
  306. $sql = 'DELETE FROM `*PREFIX*preferences` '.
  307. 'WHERE `appid` = ?';
  308. $this->connection->executeUpdate($sql, array($appName));
  309. foreach ($this->userCache as &$userCache) {
  310. unset($userCache[$appName]);
  311. }
  312. }
  313. /**
  314. * Returns all user configs sorted by app of one user
  315. *
  316. * @param string $userId the user ID to get the app configs from
  317. * @return array[] - 2 dimensional array with the following structure:
  318. * [ $appId =>
  319. * [ $key => $value ]
  320. * ]
  321. */
  322. private function getUserValues($userId) {
  323. // TODO - FIXME
  324. $this->fixDIInit();
  325. if (isset($this->userCache[$userId])) {
  326. return $this->userCache[$userId];
  327. }
  328. $data = array();
  329. $query = 'SELECT `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
  330. $result = $this->connection->executeQuery($query, array($userId));
  331. while ($row = $result->fetch()) {
  332. $appId = $row['appid'];
  333. if (!isset($data[$appId])) {
  334. $data[$appId] = array();
  335. }
  336. $data[$appId][$row['configkey']] = $row['configvalue'];
  337. }
  338. $this->userCache[$userId] = $data;
  339. return $data;
  340. }
  341. /**
  342. * Fetches a mapped list of userId -> value, for a specified app and key and a list of user IDs.
  343. *
  344. * @param string $appName app to get the value for
  345. * @param string $key the key to get the value for
  346. * @param array $userIds the user IDs to fetch the values for
  347. * @return array Mapped values: userId => value
  348. */
  349. public function getUserValueForUsers($appName, $key, $userIds) {
  350. // TODO - FIXME
  351. $this->fixDIInit();
  352. if (empty($userIds) || !is_array($userIds)) {
  353. return array();
  354. }
  355. $chunkedUsers = array_chunk($userIds, 50, true);
  356. $placeholders50 = implode(',', array_fill(0, 50, '?'));
  357. $userValues = array();
  358. foreach ($chunkedUsers as $chunk) {
  359. $queryParams = $chunk;
  360. // create [$app, $key, $chunkedUsers]
  361. array_unshift($queryParams, $key);
  362. array_unshift($queryParams, $appName);
  363. $placeholders = (sizeof($chunk) == 50) ? $placeholders50 : implode(',', array_fill(0, sizeof($chunk), '?'));
  364. $query = 'SELECT `userid`, `configvalue` ' .
  365. 'FROM `*PREFIX*preferences` ' .
  366. 'WHERE `appid` = ? AND `configkey` = ? ' .
  367. 'AND `userid` IN (' . $placeholders . ')';
  368. $result = $this->connection->executeQuery($query, $queryParams);
  369. while ($row = $result->fetch()) {
  370. $userValues[$row['userid']] = $row['configvalue'];
  371. }
  372. }
  373. return $userValues;
  374. }
  375. /**
  376. * Determines the users that have the given value set for a specific app-key-pair
  377. *
  378. * @param string $appName the app to get the user for
  379. * @param string $key the key to get the user for
  380. * @param string $value the value to get the user for
  381. * @return array of user IDs
  382. */
  383. public function getUsersForUserValue($appName, $key, $value) {
  384. // TODO - FIXME
  385. $this->fixDIInit();
  386. $sql = 'SELECT `userid` FROM `*PREFIX*preferences` ' .
  387. 'WHERE `appid` = ? AND `configkey` = ? ';
  388. if($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
  389. //oracle hack: need to explicitly cast CLOB to CHAR for comparison
  390. $sql .= 'AND to_char(`configvalue`) = ?';
  391. } else {
  392. $sql .= 'AND `configvalue` = ?';
  393. }
  394. $result = $this->connection->executeQuery($sql, array($appName, $key, $value));
  395. $userIDs = array();
  396. while ($row = $result->fetch()) {
  397. $userIDs[] = $row['userid'];
  398. }
  399. return $userIDs;
  400. }
  401. public function getSystemConfig() {
  402. return $this->systemConfig;
  403. }
  404. }