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.1.1 |
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
|
|
|
* First line in INI file, to hide contents |
27
|
|
|
* |
28
|
|
|
* @const string |
29
|
|
|
*/ |
30
|
|
|
const PROTECTION_LINE = ';<?php http_response_code(403); /*'; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* parsed configuration |
34
|
|
|
* |
35
|
|
|
* @var array |
36
|
|
|
*/ |
37
|
|
|
private $_configuration; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* default configuration |
41
|
|
|
* |
42
|
|
|
* @var array |
43
|
|
|
*/ |
44
|
|
|
private static $_defaults = array( |
45
|
|
|
'main' => array( |
46
|
|
|
'name' => 'PrivateBin', |
47
|
|
|
'discussion' => true, |
48
|
|
|
'opendiscussion' => false, |
49
|
|
|
'password' => true, |
50
|
|
|
'fileupload' => false, |
51
|
|
|
'burnafterreadingselected' => false, |
52
|
|
|
'instantburnafterreading' => false, |
53
|
|
|
'defaultformatter' => 'plaintext', |
54
|
|
|
'syntaxhighlightingtheme' => null, |
55
|
|
|
'sizelimit' => 2097152, |
56
|
|
|
'template' => 'bootstrap', |
57
|
|
|
'notice' => '', |
58
|
|
|
'languageselection' => false, |
59
|
|
|
'languagedefault' => '', |
60
|
|
|
'urlshortener' => '', |
61
|
|
|
'icon' => 'identicon', |
62
|
|
|
'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; referrer no-referrer; sandbox allow-same-origin allow-scripts allow-forms allow-popups', |
63
|
|
|
'zerobincompatibility' => false, |
64
|
|
|
), |
65
|
|
|
'expire' => array( |
66
|
|
|
'default' => '1week', |
67
|
|
|
'clone' => true, |
68
|
|
|
), |
69
|
|
|
'expire_options' => array( |
70
|
|
|
'5min' => 300, |
71
|
|
|
'10min' => 600, |
72
|
|
|
'1hour' => 3600, |
73
|
|
|
'1day' => 86400, |
74
|
|
|
'1week' => 604800, |
75
|
|
|
'1month' => 2592000, |
76
|
|
|
'1year' => 31536000, |
77
|
|
|
'never' => 0, |
78
|
|
|
), |
79
|
|
|
'formatter_options' => array( |
80
|
|
|
'plaintext' => 'Plain Text', |
81
|
|
|
'syntaxhighlighting' => 'Source Code', |
82
|
|
|
'markdown' => 'Markdown', |
83
|
|
|
), |
84
|
|
|
'traffic' => array( |
85
|
|
|
'limit' => 10, |
86
|
|
|
'header' => null, |
87
|
|
|
'dir' => 'data', |
88
|
|
|
), |
89
|
|
|
'purge' => array( |
90
|
|
|
'limit' => 300, |
91
|
|
|
'batchsize' => 10, |
92
|
|
|
'dir' => 'data', |
93
|
|
|
), |
94
|
|
|
'model' => array( |
95
|
|
|
'class' => 'Filesystem', |
96
|
|
|
), |
97
|
|
|
'model_options' => array( |
98
|
|
|
'dir' => 'data', |
99
|
|
|
), |
100
|
|
|
); |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* parse configuration file and ensure default configuration values are present |
104
|
|
|
* |
105
|
|
|
* @throws Exception |
106
|
|
|
*/ |
107
|
127 |
|
public function __construct() |
108
|
|
|
{ |
109
|
127 |
|
$config = array(); |
110
|
127 |
|
$configFile = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.php'; |
111
|
127 |
|
$configIni = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini'; |
112
|
|
|
|
113
|
|
|
// rename INI files to avoid configuration leakage |
114
|
127 |
|
if (is_readable($configIni)) { |
115
|
2 |
|
$context = stream_context_create(); |
116
|
|
|
// don't overwrite already converted file |
117
|
2 |
View Code Duplication |
if (!is_file($configFile)) { |
|
|
|
|
118
|
2 |
|
$iniHandle = fopen($configIni, 'r', false, $context); |
119
|
2 |
|
file_put_contents($configFile, self::PROTECTION_LINE . PHP_EOL); |
120
|
2 |
|
file_put_contents($configFile, $iniHandle, FILE_APPEND); |
121
|
2 |
|
fclose($iniHandle); |
122
|
|
|
} |
123
|
2 |
|
unlink($configIni); |
124
|
|
|
|
125
|
|
|
// cleanup sample, too |
126
|
2 |
|
$configSample = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.sample.php'; |
127
|
2 |
|
$configIniSample = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample'; |
128
|
2 |
|
if (is_readable($configIniSample)) { |
129
|
2 |
View Code Duplication |
if (!is_readable($configSample)) { |
|
|
|
|
130
|
1 |
|
$iniSampleHandle = fopen($configIniSample, 'r', false, $context); |
131
|
1 |
|
file_put_contents($configSample, self::PROTECTION_LINE . PHP_EOL); |
132
|
1 |
|
file_put_contents($configSample, $iniSampleHandle, FILE_APPEND); |
133
|
1 |
|
fclose($iniSampleHandle); |
134
|
|
|
} |
135
|
2 |
|
unlink($configIniSample); |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
|
139
|
127 |
|
if (is_readable($configFile)) { |
140
|
125 |
|
$config = parse_ini_file($configFile, true); |
141
|
125 |
|
foreach (array('main', 'model', 'model_options') as $section) { |
142
|
125 |
|
if (!array_key_exists($section, $config)) { |
143
|
125 |
|
throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2); |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
|
148
|
124 |
|
$opts = '_options'; |
149
|
124 |
|
foreach (self::getDefaults() as $section => $values) { |
150
|
|
|
// fill missing sections with default values |
151
|
124 |
|
if (!array_key_exists($section, $config) || count($config[$section]) == 0) { |
152
|
6 |
|
$this->_configuration[$section] = $values; |
153
|
6 |
|
if (array_key_exists('dir', $this->_configuration[$section])) { |
154
|
6 |
|
$this->_configuration[$section]['dir'] = PATH . $this->_configuration[$section]['dir']; |
155
|
|
|
} |
156
|
6 |
|
continue; |
157
|
|
|
} |
158
|
|
|
// provide different defaults for database model |
159
|
|
|
elseif ( |
160
|
118 |
|
$section == 'model_options' && in_array( |
161
|
118 |
|
$this->_configuration['model']['class'], |
162
|
118 |
|
array('Database', 'privatebin_db', 'zerobin_db') |
163
|
|
|
) |
164
|
|
|
) { |
165
|
|
|
$values = array( |
166
|
60 |
|
'dsn' => 'sqlite:' . PATH . 'data' . DIRECTORY_SEPARATOR . 'db.sq3', |
167
|
|
|
'tbl' => null, |
168
|
|
|
'usr' => null, |
169
|
|
|
'pwd' => null, |
170
|
|
|
'opt' => array(PDO::ATTR_PERSISTENT => true), |
171
|
|
|
); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
// "*_options" sections don't require all defaults to be set |
175
|
|
|
if ( |
176
|
118 |
|
$section !== 'model_options' && |
177
|
118 |
|
($from = strlen($section) - strlen($opts)) >= 0 && |
178
|
118 |
|
strpos($section, $opts, $from) !== false |
179
|
|
|
) { |
180
|
118 |
|
if (is_int(current($values))) { |
181
|
118 |
|
$config[$section] = array_map('intval', $config[$section]); |
182
|
|
|
} |
183
|
118 |
|
$this->_configuration[$section] = $config[$section]; |
184
|
|
|
} |
185
|
|
|
// check for missing keys and set defaults if necessary |
186
|
|
|
else { |
187
|
118 |
|
foreach ($values as $key => $val) { |
188
|
118 |
|
if ($key == 'dir') { |
189
|
118 |
|
$val = PATH . $val; |
190
|
|
|
} |
191
|
118 |
|
$result = $val; |
192
|
118 |
|
if (array_key_exists($key, $config[$section])) { |
193
|
118 |
|
if ($val === null) { |
194
|
3 |
|
$result = $config[$section][$key]; |
195
|
118 |
|
} elseif (is_bool($val)) { |
196
|
118 |
|
$val = strtolower($config[$section][$key]); |
197
|
118 |
|
if (in_array($val, array('true', 'yes', 'on'))) { |
198
|
1 |
|
$result = true; |
199
|
118 |
|
} elseif (in_array($val, array('false', 'no', 'off'))) { |
200
|
1 |
|
$result = false; |
201
|
|
|
} else { |
202
|
118 |
|
$result = (bool) $config[$section][$key]; |
203
|
|
|
} |
204
|
118 |
|
} elseif (is_int($val)) { |
205
|
118 |
|
$result = (int) $config[$section][$key]; |
206
|
118 |
|
} elseif (is_string($val) && !empty($config[$section][$key])) { |
207
|
118 |
|
$result = (string) $config[$section][$key]; |
208
|
|
|
} |
209
|
|
|
} |
210
|
118 |
|
$this->_configuration[$section][$key] = $result; |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
// support for old config file format, before the fork was renamed and PSR-4 introduced |
216
|
124 |
|
$this->_configuration['model']['class'] = str_replace( |
217
|
124 |
|
'zerobin_', 'privatebin_', |
218
|
124 |
|
$this->_configuration['model']['class'] |
219
|
|
|
); |
220
|
|
|
|
221
|
124 |
|
$this->_configuration['model']['class'] = str_replace( |
222
|
124 |
|
array('privatebin_data', 'privatebin_db'), |
223
|
124 |
|
array('Filesystem', 'Database'), |
224
|
124 |
|
$this->_configuration['model']['class'] |
225
|
|
|
); |
226
|
|
|
|
227
|
|
|
// ensure a valid expire default key is set |
228
|
124 |
|
if (!array_key_exists($this->_configuration['expire']['default'], $this->_configuration['expire_options'])) { |
229
|
1 |
|
$this->_configuration['expire']['default'] = key($this->_configuration['expire_options']); |
230
|
|
|
} |
231
|
124 |
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* get configuration as array |
235
|
|
|
* |
236
|
|
|
* return array |
237
|
|
|
*/ |
238
|
6 |
|
public function get() |
239
|
|
|
{ |
240
|
6 |
|
return $this->_configuration; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* get default configuration as array |
245
|
|
|
* |
246
|
|
|
* return array |
247
|
|
|
*/ |
248
|
125 |
|
public static function getDefaults() |
249
|
|
|
{ |
250
|
125 |
|
return self::$_defaults; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* get a key from the configuration, typically the main section or all keys |
255
|
|
|
* |
256
|
|
|
* @param string $key |
257
|
|
|
* @param string $section defaults to main |
258
|
|
|
* @throws Exception |
259
|
|
|
* return mixed |
260
|
|
|
*/ |
261
|
116 |
|
public function getKey($key, $section = 'main') |
262
|
|
|
{ |
263
|
116 |
|
$options = $this->getSection($section); |
264
|
116 |
|
if (!array_key_exists($key, $options)) { |
265
|
1 |
|
throw new Exception(I18n::_('Invalid data.') . " $section / $key", 4); |
266
|
|
|
} |
267
|
115 |
|
return $this->_configuration[$section][$key]; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* get a section from the configuration, must exist |
272
|
|
|
* |
273
|
|
|
* @param string $section |
274
|
|
|
* @throws Exception |
275
|
|
|
* return mixed |
276
|
|
|
*/ |
277
|
116 |
|
public function getSection($section) |
278
|
|
|
{ |
279
|
116 |
|
if (!array_key_exists($section, $this->_configuration)) { |
280
|
1 |
|
throw new Exception(I18n::_('%s requires configuration section [%s] to be present in configuration file.', I18n::_($this->getKey('name')), $section), 3); |
281
|
|
|
} |
282
|
116 |
|
return $this->_configuration[$section]; |
283
|
|
|
} |
284
|
|
|
} |
285
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.