aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs/Lib/Config.php
blob: b1754df09459b7e2df9a94ee75153cea0de72c34 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<?php

namespace Zotlabs\Lib;

use App;

class Config {

	/**
	 * @brief Loads the hub's configuration from database to a cached storage.
	 *
	 * Retrieve a category ($family) of config variables from database to a cached
	 * storage in the global App::$config[$family].
	 *
	 * @param string $family
	 *  The category of the configuration value
	 */
	public static function Load($family, $recursionCounter = 0) {
		if (! array_key_exists($family, App::$config)) {
			App::$config[$family] = [];
		}

		// We typically continue when presented with minor DB issues,
		// but loading the site configuration is more important.

		// Check for query returning false and give it approx 30 seconds
		// to recover if there's a problem. This is intended to fix a
		// rare issue on Galera where temporary sync issues were causing
		// the site encryption keys to be regenerated, which was causing
		// communication issues for members.

		// This code probably belongs at the database layer, but we don't
		// necessarily want to shut the site down for problematic queries
		// caused by bad data. That could be used in a denial of service
		// attack. Those do need to be (and they are) logged.

		if (! array_key_exists('config_loaded', App::$config[$family])) {
			$r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family));
			if ($r === false) {
				sleep(3);
				$recursionCounter ++;
				if ($recursionCounter > 10) {
					system_unavailable();
				}
				self::Load($family, $recursionCounter);
			}
			else {
				foreach ($r as $rr) {
					$k = $rr['k'];
					App::$config[$family][$k] = $rr['v'];
				}
				App::$config[$family]['config_loaded'] = true;
			}


		}
	}

	/**
	 * @brief Sets a configuration value for the hub.
	 *
	 * Stores a config value ($value) in the category ($family) under the key ($key).
	 *
	 * @param string $family
	 *  The category of the configuration value
	 * @param string $key
	 *  The configuration key to set
	 * @param mixed $value
	 *  The value to store in the configuration
	 * @return mixed
	 *  Return the set value, or false if the database update failed
	 */
	public static function Set($family, $key, $value) {
		// manage array value
		$dbvalue = ((is_array($value))  ? 'json:' . json_encode($value) : $value);
		$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue)  : $dbvalue);

		if (self::Get($family, $key) === false || (! self::get_from_storage($family, $key))) {
			$ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ",
				dbesc($family),
				dbesc($key),
				dbesc($dbvalue)
			);
			if ($ret) {
				App::$config[$family][$key] = $value;
				$ret = $value;
			}
			return $ret;
		}

		$ret = q("UPDATE config SET v = '%s' WHERE cat = '%s' AND k = '%s'",
			dbesc($dbvalue),
			dbesc($family),
			dbesc($key)
		);

		if ($ret) {
			App::$config[$family][$key] = $value;
			$ret = $value;
		}

		return $ret;
	}

	/**
	 * @brief Get a particular config variable given the category name ($family)
	 * and a key.
	 *
	 * Get a particular config variable from the given category ($family) and the
	 * $key from a cached storage in App::$config[$family]. If a key is found in the
	 * DB but does not exist in local config cache, pull it into the cache so we
	 * do not have to hit the DB again for this item.
	 *
	 * Returns false if not set.
	 *
	 * @param string $family
	 *  The category of the configuration value
	 * @param string $key
	 *  The configuration key to query
	 * @param string $default (optional) default false
	 * @return mixed Return value or false on error or if not set
	 */
	public static function Get($family, $key, $default = false) {

		if (App::$install) {
			// The DB is not initalized yet
			return false;
		}

		if ((! array_key_exists($family, App::$config)) || (! array_key_exists('config_loaded', App::$config[$family]))) {
			self::Load($family);
		}

		if (array_key_exists('config_loaded', App::$config[$family])) {
			if (! array_key_exists($key, App::$config[$family])) {
				return $default;
			}

			$value = App::$config[$family][$key];

			if (! is_array($value)) {
				if (substr($value, 0, 5) == 'json:') {
					return json_decode(substr($value, 5), true);
				} else if (preg_match('|^a:[0-9]+:{.*}$|s', $value)) {
					// Unserialize in inherently unsafe. Try to mitigate by not
					// allowing unserializing objects. Only kept for backwards
					// compatibility. JSON serialization should be prefered.
					return unserialize($value, array('allowed_classes' => false));
				} else {
					return $value;
				}
			}
		}

		return $default;
	}

	/**
	 * @brief Deletes the given key from the hub's configuration database.
	 *
	 * Removes the configured value from the stored cache in App::$config[$family]
	 * and removes it from the database.
	 *
	 * @param string $family
	 *  The category of the configuration value
	 * @param string $key
	 *  The configuration key to delete
	 * @return mixed
	 */
	public static function Delete($family, $key) {

		$ret = false;

		if (array_key_exists($family, App::$config) && array_key_exists($key, App::$config[$family])) {
			unset(App::$config[$family][$key]);
		}

		$ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'",
			dbesc($family),
			dbesc($key)
		);

		return $ret;
	}


	/**
	 * @brief Returns a record directly from the database configuration storage.
	 *
	 * This function queries directly the database and bypasses the cached storage
	 * from get_config($family, $key).
	 *
	 * @param string $family
	 *  The category of the configuration value
	 * @param string $key
	 *  The configuration key to query
	 * @return mixed
	 */
	private static function get_from_storage($family, $key) {
		$ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1",
			dbesc($family),
			dbesc($key)
		);

		return $ret;
	}
}