Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
90.29% |
93 / 103 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
Configuration | |
90.29% |
93 / 103 |
|
80.00% |
4 / 5 |
42.54 | |
0.00% |
0 / 1 |
__construct | |
89.36% |
84 / 94 |
|
0.00% |
0 / 1 |
36.47 | |||
get | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDefaults | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getKey | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getSection | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * PrivateBin |
4 | * |
5 | * a zero-knowledge paste bin |
6 | * |
7 | * @link https://github.com/PrivateBin/PrivateBin |
8 | * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) |
9 | * @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License |
10 | * @version 1.7.2 |
11 | */ |
12 | |
13 | namespace PrivateBin; |
14 | |
15 | use Exception; |
16 | use PDO; |
17 | |
18 | /** |
19 | * Configuration |
20 | * |
21 | * parses configuration file, ensures default values present |
22 | */ |
23 | class Configuration |
24 | { |
25 | /** |
26 | * parsed configuration |
27 | * |
28 | * @var array |
29 | */ |
30 | private $_configuration; |
31 | |
32 | /** |
33 | * default configuration |
34 | * |
35 | * @var array |
36 | */ |
37 | private static $_defaults = array( |
38 | 'main' => array( |
39 | 'name' => 'PrivateBin', |
40 | 'basepath' => '', |
41 | 'discussion' => true, |
42 | 'opendiscussion' => false, |
43 | 'discussiondatedisplay' => true, |
44 | 'password' => true, |
45 | 'fileupload' => false, |
46 | 'burnafterreadingselected' => false, |
47 | 'defaultformatter' => 'plaintext', |
48 | 'syntaxhighlightingtheme' => '', |
49 | 'sizelimit' => 10485760, |
50 | 'template' => 'bootstrap', |
51 | 'info' => 'More information on the <a href=\'https://privatebin.info/\'>project page</a>.', |
52 | 'notice' => '', |
53 | 'languageselection' => false, |
54 | 'languagedefault' => '', |
55 | 'urlshortener' => '', |
56 | 'qrcode' => true, |
57 | 'email' => true, |
58 | 'icon' => 'identicon', |
59 | 'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads', |
60 | 'zerobincompatibility' => false, |
61 | 'httpwarning' => true, |
62 | 'compression' => 'zlib', |
63 | ), |
64 | 'expire' => array( |
65 | 'default' => '1week', |
66 | ), |
67 | 'expire_options' => array( |
68 | '5min' => 300, |
69 | '10min' => 600, |
70 | '1hour' => 3600, |
71 | '1day' => 86400, |
72 | '1week' => 604800, |
73 | '1month' => 2592000, |
74 | '1year' => 31536000, |
75 | 'never' => 0, |
76 | ), |
77 | 'formatter_options' => array( |
78 | 'plaintext' => 'Plain Text', |
79 | 'syntaxhighlighting' => 'Source Code', |
80 | 'markdown' => 'Markdown', |
81 | ), |
82 | 'traffic' => array( |
83 | 'limit' => 10, |
84 | 'header' => '', |
85 | 'exempted' => '', |
86 | 'creators' => '', |
87 | ), |
88 | 'purge' => array( |
89 | 'limit' => 300, |
90 | 'batchsize' => 10, |
91 | ), |
92 | 'model' => array( |
93 | 'class' => 'Filesystem', |
94 | ), |
95 | 'model_options' => array( |
96 | 'dir' => 'data', |
97 | ), |
98 | 'yourls' => array( |
99 | 'signature' => '', |
100 | 'apiurl' => '', |
101 | ), |
102 | ); |
103 | |
104 | /** |
105 | * parse configuration file and ensure default configuration values are present |
106 | * |
107 | * @throws Exception |
108 | */ |
109 | public function __construct() |
110 | { |
111 | $basePaths = array(); |
112 | $config = array(); |
113 | $configPath = getenv('CONFIG_PATH'); |
114 | if ($configPath !== false && !empty($configPath)) { |
115 | $basePaths[] = $configPath; |
116 | } |
117 | $basePaths[] = PATH . 'cfg'; |
118 | foreach ($basePaths as $basePath) { |
119 | $configFile = $basePath . DIRECTORY_SEPARATOR . 'conf.php'; |
120 | if (is_readable($configFile)) { |
121 | $config = parse_ini_file($configFile, true); |
122 | foreach (array('main', 'model', 'model_options') as $section) { |
123 | if (!array_key_exists($section, $config)) { |
124 | throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2); |
125 | } |
126 | } |
127 | break; |
128 | } |
129 | } |
130 | |
131 | $opts = '_options'; |
132 | foreach (self::getDefaults() as $section => $values) { |
133 | // fill missing sections with default values |
134 | if (!array_key_exists($section, $config) || count($config[$section]) == 0) { |
135 | $this->_configuration[$section] = $values; |
136 | if (array_key_exists('dir', $this->_configuration[$section])) { |
137 | $this->_configuration[$section]['dir'] = PATH . $this->_configuration[$section]['dir']; |
138 | } |
139 | continue; |
140 | } |
141 | // provide different defaults for database model |
142 | elseif ( |
143 | $section == 'model_options' && in_array( |
144 | $this->_configuration['model']['class'], |
145 | array('Database', 'privatebin_db', 'zerobin_db') |
146 | ) |
147 | ) { |
148 | $values = array( |
149 | 'dsn' => 'sqlite:' . PATH . 'data' . DIRECTORY_SEPARATOR . 'db.sq3', |
150 | 'tbl' => null, |
151 | 'usr' => null, |
152 | 'pwd' => null, |
153 | 'opt' => array(PDO::ATTR_PERSISTENT => true), |
154 | ); |
155 | } elseif ( |
156 | $section == 'model_options' && in_array( |
157 | $this->_configuration['model']['class'], |
158 | array('GoogleCloudStorage') |
159 | ) |
160 | ) { |
161 | $values = array( |
162 | 'bucket' => getenv('PRIVATEBIN_GCS_BUCKET') ? getenv('PRIVATEBIN_GCS_BUCKET') : null, |
163 | 'prefix' => 'pastes', |
164 | 'uniformacl' => false, |
165 | ); |
166 | } elseif ( |
167 | $section == 'model_options' && in_array( |
168 | $this->_configuration['model']['class'], |
169 | array('S3Storage') |
170 | ) |
171 | ) { |
172 | $values = array( |
173 | 'region' => null, |
174 | 'version' => null, |
175 | 'endpoint' => null, |
176 | 'accesskey' => null, |
177 | 'secretkey' => null, |
178 | 'use_path_style_endpoint' => null, |
179 | 'bucket' => null, |
180 | 'prefix' => '', |
181 | ); |
182 | } |
183 | |
184 | // "*_options" sections don't require all defaults to be set |
185 | if ( |
186 | $section !== 'model_options' && |
187 | ($from = strlen($section) - strlen($opts)) >= 0 && |
188 | strpos($section, $opts, $from) !== false |
189 | ) { |
190 | if (is_int(current($values))) { |
191 | $config[$section] = array_map('intval', $config[$section]); |
192 | } |
193 | $this->_configuration[$section] = $config[$section]; |
194 | } |
195 | // check for missing keys and set defaults if necessary |
196 | else { |
197 | foreach ($values as $key => $val) { |
198 | if ($key == 'dir') { |
199 | $val = PATH . $val; |
200 | } |
201 | $result = $val; |
202 | if (array_key_exists($key, $config[$section])) { |
203 | if ($val === null) { |
204 | $result = $config[$section][$key]; |
205 | } elseif (is_bool($val)) { |
206 | $val = strtolower($config[$section][$key]); |
207 | if (in_array($val, array('true', 'yes', 'on'))) { |
208 | $result = true; |
209 | } elseif (in_array($val, array('false', 'no', 'off'))) { |
210 | $result = false; |
211 | } else { |
212 | $result = (bool) $config[$section][$key]; |
213 | } |
214 | } elseif (is_int($val)) { |
215 | $result = (int) $config[$section][$key]; |
216 | } elseif (is_string($val) && !empty($config[$section][$key])) { |
217 | $result = (string) $config[$section][$key]; |
218 | } |
219 | } |
220 | $this->_configuration[$section][$key] = $result; |
221 | } |
222 | } |
223 | } |
224 | |
225 | // support for old config file format, before the fork was renamed and PSR-4 introduced |
226 | $this->_configuration['model']['class'] = str_replace( |
227 | 'zerobin_', 'privatebin_', |
228 | $this->_configuration['model']['class'] |
229 | ); |
230 | |
231 | $this->_configuration['model']['class'] = str_replace( |
232 | array('privatebin_data', 'privatebin_db'), |
233 | array('Filesystem', 'Database'), |
234 | $this->_configuration['model']['class'] |
235 | ); |
236 | |
237 | // ensure a valid expire default key is set |
238 | if (!array_key_exists($this->_configuration['expire']['default'], $this->_configuration['expire_options'])) { |
239 | $this->_configuration['expire']['default'] = key($this->_configuration['expire_options']); |
240 | } |
241 | |
242 | // ensure the basepath ends in a slash, if one is set |
243 | if ( |
244 | !empty($this->_configuration['main']['basepath']) && |
245 | substr_compare($this->_configuration['main']['basepath'], '/', -1) !== 0 |
246 | ) { |
247 | $this->_configuration['main']['basepath'] .= '/'; |
248 | } |
249 | } |
250 | |
251 | /** |
252 | * get configuration as array |
253 | * |
254 | * @return array |
255 | */ |
256 | public function get() |
257 | { |
258 | return $this->_configuration; |
259 | } |
260 | |
261 | /** |
262 | * get default configuration as array |
263 | * |
264 | * @return array |
265 | */ |
266 | public static function getDefaults() |
267 | { |
268 | return self::$_defaults; |
269 | } |
270 | |
271 | /** |
272 | * get a key from the configuration, typically the main section or all keys |
273 | * |
274 | * @param string $key |
275 | * @param string $section defaults to main |
276 | * @throws Exception |
277 | * @return mixed |
278 | */ |
279 | public function getKey($key, $section = 'main') |
280 | { |
281 | $options = $this->getSection($section); |
282 | if (!array_key_exists($key, $options)) { |
283 | throw new Exception(I18n::_('Invalid data.') . " $section / $key", 4); |
284 | } |
285 | return $this->_configuration[$section][$key]; |
286 | } |
287 | |
288 | /** |
289 | * get a section from the configuration, must exist |
290 | * |
291 | * @param string $section |
292 | * @throws Exception |
293 | * @return mixed |
294 | */ |
295 | public function getSection($section) |
296 | { |
297 | if (!array_key_exists($section, $this->_configuration)) { |
298 | throw new Exception(I18n::_('%s requires configuration section [%s] to be present in configuration file.', I18n::_($this->getKey('name')), $section), 3); |
299 | } |
300 | return $this->_configuration[$section]; |
301 | } |
302 | } |