Configuration::get()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
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.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
     * 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
            'password'                 => true,
44
            'fileupload'               => false,
45
            'burnafterreadingselected' => false,
46
            'defaultformatter'         => 'plaintext',
47
            'syntaxhighlightingtheme'  => '',
48
            'sizelimit'                => 10485760,
49
            'template'                 => 'bootstrap',
50
            'info'                     => 'More information on the <a href=\'https://privatebin.info/\'>project page</a>.',
51
            'notice'                   => '',
52
            'languageselection'        => false,
53
            'languagedefault'          => '',
54
            'urlshortener'             => '',
55
            'qrcode'                   => true,
56
            'email'                    => true,
57
            'icon'                     => 'identicon',
58
            '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',
59
            'zerobincompatibility'     => false,
60
            'httpwarning'              => true,
61
            'compression'              => 'zlib',
62
        ),
63
        'expire' => array(
64
            'default' => '1week',
65
        ),
66
        'expire_options' => array(
67
            '5min'   => 300,
68
            '10min'  => 600,
69
            '1hour'  => 3600,
70
            '1day'   => 86400,
71
            '1week'  => 604800,
72
            '1month' => 2592000,
73
            '1year'  => 31536000,
74
            'never'  => 0,
75
        ),
76
        'formatter_options' => array(
77
            'plaintext'          => 'Plain Text',
78
            'syntaxhighlighting' => 'Source Code',
79
            'markdown'           => 'Markdown',
80
        ),
81
        'traffic' => array(
82
            'limit'     => 10,
83
            'header'    => '',
84
            'exempted'  => '',
85
            'creators'  => '',
86
        ),
87
        'purge' => array(
88
            'limit'     => 300,
89
            'batchsize' => 10,
90
        ),
91
        'model' => array(
92
            'class' => 'Filesystem',
93
        ),
94
        'model_options' => array(
95
            'dir' => 'data',
96
        ),
97
        'yourls' => array(
98
            'signature' => '',
99
            'apiurl'    => '',
100
        ),
101
    );
102
103
    /**
104
     * parse configuration file and ensure default configuration values are present
105
     *
106
     * @throws Exception
107
     */
108 163
    public function __construct()
109
    {
110 163
        $basePaths  = array();
111 163
        $config     = array();
112 163
        $configPath = getenv('CONFIG_PATH');
113 163
        if ($configPath !== false && !empty($configPath)) {
114 1
            $basePaths[] = $configPath;
115
        }
116 163
        $basePaths[] = PATH . 'cfg';
117 163
        foreach ($basePaths as $basePath) {
118 163
            $configFile = $basePath . DIRECTORY_SEPARATOR . 'conf.php';
119 163
            if (is_readable($configFile)) {
120 161
                $config = parse_ini_file($configFile, true);
121 161
                foreach (array('main', 'model', 'model_options') as $section) {
122 161
                    if (!array_key_exists($section, $config)) {
123 4
                        throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2);
124
                    }
125
                }
126 157
                break;
127
            }
128
        }
129
130 159
        $opts = '_options';
131 159
        foreach (self::getDefaults() as $section => $values) {
132
            // fill missing sections with default values
133 159
            if (!array_key_exists($section, $config) || count($config[$section]) == 0) {
134 147
                $this->_configuration[$section] = $values;
135 147
                if (array_key_exists('dir', $this->_configuration[$section])) {
136 6
                    $this->_configuration[$section]['dir'] = PATH . $this->_configuration[$section]['dir'];
137
                }
138 147
                continue;
139
            }
140
            // provide different defaults for database model
141
            elseif (
142 153
                $section == 'model_options' && in_array(
143 153
                    $this->_configuration['model']['class'],
144 153
                    array('Database', 'privatebin_db', 'zerobin_db')
145 153
                )
146
            ) {
147 58
                $values = array(
148 58
                    'dsn' => 'sqlite:' . PATH . 'data' . DIRECTORY_SEPARATOR . 'db.sq3',
149 58
                    'tbl' => null,
150 58
                    'usr' => null,
151 58
                    'pwd' => null,
152 58
                    'opt' => array(PDO::ATTR_PERSISTENT => true),
153 58
                );
154
            } elseif (
155 153
                $section == 'model_options' && in_array(
156 153
                    $this->_configuration['model']['class'],
157 153
                    array('GoogleCloudStorage')
158 153
                )
159
            ) {
160 36
                $values = array(
161 36
                    'bucket'     => getenv('PRIVATEBIN_GCS_BUCKET') ? getenv('PRIVATEBIN_GCS_BUCKET') : null,
162 36
                    'prefix'     => 'pastes',
163 36
                    'uniformacl' => false,
164 36
                );
165
            } elseif (
166 153
                $section == 'model_options' && in_array(
167 153
                    $this->_configuration['model']['class'],
168 153
                    array('S3Storage')
169 153
                )
170
            ) {
171
                $values = array(
172
                    'region'                  => null,
173
                    'version'                 => null,
174
                    'endpoint'                => null,
175
                    'accesskey'               => null,
176
                    'secretkey'               => null,
177
                    'use_path_style_endpoint' => null,
178
                    'bucket'                  => null,
179
                    'prefix'                  => '',
180
                );
181
            }
182
183
            // "*_options" sections don't require all defaults to be set
184
            if (
185 153
                $section !== 'model_options' &&
186 153
                ($from = strlen($section) - strlen($opts)) >= 0 &&
187 153
                strpos($section, $opts, $from) !== false
188
            ) {
189 153
                if (is_int(current($values))) {
190 153
                    $config[$section] = array_map('intval', $config[$section]);
191
                }
192 153
                $this->_configuration[$section] = $config[$section];
193
            }
194
            // check for missing keys and set defaults if necessary
195
            else {
196 153
                foreach ($values as $key => $val) {
197 153
                    if ($key == 'dir') {
198 60
                        $val = PATH . $val;
199
                    }
200 153
                    $result = $val;
201 153
                    if (array_key_exists($key, $config[$section])) {
202 153
                        if ($val === null) {
203 36
                            $result = $config[$section][$key];
204 153
                        } elseif (is_bool($val)) {
205 153
                            $val = strtolower($config[$section][$key]);
206 153
                            if (in_array($val, array('true', 'yes', 'on'))) {
207 1
                                $result = true;
208 153
                            } elseif (in_array($val, array('false', 'no', 'off'))) {
209 1
                                $result = false;
210
                            } else {
211 153
                                $result = (bool) $config[$section][$key];
212
                            }
213 153
                        } elseif (is_int($val)) {
214 153
                            $result = (int) $config[$section][$key];
215 153
                        } elseif (is_string($val) && !empty($config[$section][$key])) {
216 153
                            $result = (string) $config[$section][$key];
217
                        }
218
                    }
219 153
                    $this->_configuration[$section][$key] = $result;
220
                }
221
            }
222
        }
223
224
        // support for old config file format, before the fork was renamed and PSR-4 introduced
225 159
        $this->_configuration['model']['class'] = str_replace(
226 159
            'zerobin_', 'privatebin_',
227 159
            $this->_configuration['model']['class']
228 159
        );
229
230 159
        $this->_configuration['model']['class'] = str_replace(
231 159
            array('privatebin_data', 'privatebin_db'),
232 159
            array('Filesystem', 'Database'),
233 159
            $this->_configuration['model']['class']
234 159
        );
235
236
        // ensure a valid expire default key is set
237 159
        if (!array_key_exists($this->_configuration['expire']['default'], $this->_configuration['expire_options'])) {
238 1
            $this->_configuration['expire']['default'] = key($this->_configuration['expire_options']);
239
        }
240
241
        // ensure the basepath ends in a slash, if one is set
242
        if (
243 159
            strlen($this->_configuration['main']['basepath']) &&
244 159
            substr_compare($this->_configuration['main']['basepath'], '/', -1) !== 0
245
        ) {
246 3
            $this->_configuration['main']['basepath'] .= '/';
247
        }
248
    }
249
250
    /**
251
     * get configuration as array
252
     *
253
     * @return array
254
     */
255 6
    public function get()
256
    {
257 6
        return $this->_configuration;
258
    }
259
260
    /**
261
     * get default configuration as array
262
     *
263
     * @return array
264
     */
265 160
    public static function getDefaults()
266
    {
267 160
        return self::$_defaults;
268
    }
269
270
    /**
271
     * get a key from the configuration, typically the main section or all keys
272
     *
273
     * @param string $key
274
     * @param string $section defaults to main
275
     * @throws Exception
276
     * @return mixed
277
     */
278 152
    public function getKey($key, $section = 'main')
279
    {
280 152
        $options = $this->getSection($section);
281 152
        if (!array_key_exists($key, $options)) {
282 1
            throw new Exception(I18n::_('Invalid data.') . " $section / $key", 4);
283
        }
284 151
        return $this->_configuration[$section][$key];
285
    }
286
287
    /**
288
     * get a section from the configuration, must exist
289
     *
290
     * @param string $section
291
     * @throws Exception
292
     * @return mixed
293
     */
294 152
    public function getSection($section)
295
    {
296 152
        if (!array_key_exists($section, $this->_configuration)) {
297 1
            throw new Exception(I18n::_('%s requires configuration section [%s] to be present in configuration file.', I18n::_($this->getKey('name')), $section), 3);
298
        }
299 152
        return $this->_configuration[$section];
300
    }
301
}
302