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