Thomas Bruederli
2012-11-27 60226a75d8e4a3ee9504da6eab6d8f329bb32e7b
commit | author | age
197601 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/include/rcube_config.php                                      |
6  |                                                                       |
e019f2 7  | This file is part of the Roundcube Webmail client                     |
0ac416 8  | Copyright (C) 2008-2012, The Roundcube Dev Team                       |
7fe381 9  |                                                                       |
T 10  | Licensed under the GNU General Public License version 3 or            |
11  | any later version with exceptions for skins & plugins.                |
12  | See the README file for a full license statement.                     |
197601 13  |                                                                       |
T 14  | PURPOSE:                                                              |
15  |   Class to read configuration settings                                |
16  |                                                                       |
17  +-----------------------------------------------------------------------+
18  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
19  +-----------------------------------------------------------------------+
20 */
21
22 /**
e019f2 23  * Configuration class for Roundcube
197601 24  *
9ab346 25  * @package    Framework
AM 26  * @subpackage Core
197601 27  */
T 28 class rcube_config
29 {
aff970 30     const DEFAULT_SKIN = 'larry';
TB 31
2eb794 32     private $prop = array();
A 33     private $errors = array();
34     private $userprefs = array();
0ac416 35
A 36     /**
37      * Renamed options
38      *
39      * @var array
40      */
41     private $legacy_props = array(
42         // new name => old name
43         'default_folders'      => 'default_imap_folders',
44         'mail_pagesize'        => 'pagesize',
45         'addressbook_pagesize' => 'pagesize',
651c7b 46         'reply_mode'           => 'top_posting',
f22654 47         'refresh_interval'     => 'keep_alive',
AM 48         'min_refresh_interval' => 'min_keep_alive',
0ac416 49     );
197601 50
T 51
2eb794 52     /**
A 53      * Object constructor
54      */
55     public function __construct()
2471d3 56     {
2eb794 57         $this->load();
baecd8 58
TB 59         // Defaults, that we do not require you to configure,
60         // but contain information that is used in various
61         // locations in the code:
62         $this->set('contactlist_fields', array('name', 'firstname', 'surname', 'email'));
2471d3 63     }
2eb794 64
A 65
66     /**
67      * Load config from local config file
68      *
69      * @todo Remove global $CONFIG
70      */
71     private function load()
2471d3 72     {
2eb794 73         // load main config file
592668 74         if (!$this->load_from_file(RCUBE_CONFIG_DIR . 'main.inc.php'))
2eb794 75             $this->errors[] = 'main.inc.php was not found.';
A 76
77         // load database config
592668 78         if (!$this->load_from_file(RCUBE_CONFIG_DIR . 'db.inc.php'))
2eb794 79             $this->errors[] = 'db.inc.php was not found.';
f52c4f 80
2eb794 81         // load host-specific configuration
A 82         $this->load_host_config();
83
84         // set skin (with fallback to old 'skin_path' property)
740875 85         if (empty($this->prop['skin'])) {
AM 86             if (!empty($this->prop['skin_path'])) {
87                 $this->prop['skin'] = str_replace('skins/', '', unslashify($this->prop['skin_path']));
88             }
89             else {
aff970 90                 $this->prop['skin'] = self::DEFAULT_SKIN;
740875 91             }
AM 92         }
9f1652 93
TB 94         // larry is the new default skin :-)
95         if ($this->prop['skin'] == 'default')
aff970 96             $this->prop['skin'] = self::DEFAULT_SKIN;
2eb794 97
A 98         // fix paths
9be2f4 99         $this->prop['log_dir'] = $this->prop['log_dir'] ? realpath(unslashify($this->prop['log_dir'])) : RCUBE_INSTALL_PATH . 'logs';
TB 100         $this->prop['temp_dir'] = $this->prop['temp_dir'] ? realpath(unslashify($this->prop['temp_dir'])) : RCUBE_INSTALL_PATH . 'temp';
f52c4f 101
2eb794 102         // fix default imap folders encoding
A 103         foreach (array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox') as $folder)
a92beb 104             $this->prop[$folder] = rcube_charset::convert($this->prop[$folder], RCUBE_CHARSET, 'UTF7-IMAP');
2eb794 105
c321a9 106         if (!empty($this->prop['default_folders']))
T 107             foreach ($this->prop['default_folders'] as $n => $folder)
a92beb 108                 $this->prop['default_folders'][$n] = rcube_charset::convert($folder, RCUBE_CHARSET, 'UTF7-IMAP');
2eb794 109
A 110         // set PHP error logging according to config
111         if ($this->prop['debug_level'] & 1) {
112             ini_set('log_errors', 1);
113
114             if ($this->prop['log_driver'] == 'syslog') {
115                 ini_set('error_log', 'syslog');
116             }
117             else {
118                 ini_set('error_log', $this->prop['log_dir'].'/errors');
119             }
120         }
23b495 121
A 122         // enable display_errors in 'show' level, but not for ajax requests
123         ini_set('display_errors', intval(empty($_REQUEST['_remote']) && ($this->prop['debug_level'] & 4)));
0829b7 124
65082b 125         // set timezone auto settings values
T 126         if ($this->prop['timezone'] == 'auto') {
5879c0 127           $this->prop['_timezone_value'] = $this->client_timezone();
0829b7 128         }
7bcd29 129         else if (is_numeric($this->prop['timezone']) && ($tz = timezone_name_from_abbr("", $this->prop['timezone'] * 3600, 0))) {
TB 130           $this->prop['timezone'] = $tz;
131         }
132         else if (empty($this->prop['timezone'])) {
133           $this->prop['timezone'] = 'UTC';
65082b 134         }
5879c0 135
T 136         // remove deprecated properties
137         unset($this->prop['dst_active']);
2eb794 138
A 139         // export config data
140         $GLOBALS['CONFIG'] = &$this->prop;
2471d3 141     }
1854c4 142
2eb794 143     /**
A 144      * Load a host-specific config file if configured
145      * This will merge the host specific configuration with the given one
146      */
147     private function load_host_config()
148     {
149         $fname = null;
10eedb 150
2eb794 151         if (is_array($this->prop['include_host_config'])) {
A 152             $fname = $this->prop['include_host_config'][$_SERVER['HTTP_HOST']];
153         }
154         else if (!empty($this->prop['include_host_config'])) {
155             $fname = preg_replace('/[^a-z0-9\.\-_]/i', '', $_SERVER['HTTP_HOST']) . '.inc.php';
156         }
2471d3 157
2eb794 158         if ($fname) {
592668 159             $this->load_from_file(RCUBE_CONFIG_DIR . $fname);
2eb794 160         }
83a763 161     }
T 162
2eb794 163
A 164     /**
165      * Read configuration from a file
166      * and merge with the already stored config values
167      *
5c461b 168      * @param string $fpath Full path to the config file to be loaded
2eb794 169      * @return booelan True on success, false on failure
A 170      */
171     public function load_from_file($fpath)
172     {
173         if (is_file($fpath) && is_readable($fpath)) {
7c9850 174             // use output buffering, we don't need any output here 
A 175             ob_start();
2eb794 176             include($fpath);
7c9850 177             ob_end_clean();
A 178
2eb794 179             if (is_array($rcmail_config)) {
A 180                 $this->prop = array_merge($this->prop, $rcmail_config, $this->userprefs);
181                 return true;
182             }
183         }
184
185         return false;
186     }
187
188
189     /**
190      * Getter for a specific config parameter
191      *
5c461b 192      * @param  string $name Parameter name
A 193      * @param  mixed  $def  Default value if not set
2eb794 194      * @return mixed  The requested config value
A 195      */
196     public function get($name, $def = null)
197     {
0ac416 198         if (isset($this->prop[$name])) {
A 199             $result = $this->prop[$name];
200         }
e65c36 201         else if (isset($this->legacy_props[$name])) {
T 202             return $this->get($this->legacy_props[$name], $def);
0ac416 203         }
A 204         else {
205             $result = $def;
206         }
207
be98df 208         $rcube = rcube::get_instance();
0ac416 209
8fb4f0 210         if ($name == 'timezone' && isset($this->prop['_timezone_value'])) {
801b69 211             $result = $this->prop['_timezone_value'];
8fb4f0 212         }
TB 213         else if ($name == 'client_mimetypes') {
214             if ($result == null && $def == null)
215                 $result = 'text/plain,text/html,text/xml,image/jpeg,image/gif,image/png,image/bmp,image/tiff,application/x-javascript,application/pdf,application/x-shockwave-flash';
216             if ($result && is_string($result))
217                 $result = explode(',', $result);
218         }
183717 219
be98df 220         $plugin = $rcube->plugins->exec_hook('config_get', array(
A 221             'name' => $name, 'default' => $def, 'result' => $result));
183717 222
be98df 223         return $plugin['result'];
2eb794 224     }
A 225
226
227     /**
228      * Setter for a config parameter
229      *
5c461b 230      * @param string $name  Parameter name
A 231      * @param mixed  $value Parameter value
2eb794 232      */
A 233     public function set($name, $value)
234     {
235         $this->prop[$name] = $value;
236     }
237
238
239     /**
240      * Override config options with the given values (eg. user prefs)
241      *
5c461b 242      * @param array $prefs Hash array with config props to merge over
2eb794 243      */
A 244     public function merge($prefs)
245     {
246         $this->prop = array_merge($this->prop, $prefs, $this->userprefs);
247     }
248
249
250     /**
251      * Merge the given prefs over the current config
252      * and make sure that they survive further merging.
253      *
5c461b 254      * @param array $prefs Hash array with user prefs
2eb794 255      */
A 256     public function set_user_prefs($prefs)
257     {
bfb7d6 258         // Honor the dont_override setting for any existing user preferences
A 259         $dont_override = $this->get('dont_override');
260         if (is_array($dont_override) && !empty($dont_override)) {
9f1652 261             foreach ($dont_override as $key) {
TB 262                 unset($prefs[$key]);
bfb7d6 263             }
A 264         }
265
c21d7f 266         // convert user's timezone into the new format
7bcd29 267         if (is_numeric($prefs['timezone']) && ($tz = timezone_name_from_abbr('', $prefs['timezone'] * 3600, 0))) {
TB 268             $prefs['timezone'] = $tz;
c21d7f 269         }
A 270
a9cbba 271         // larry is the new default skin :-)
TB 272         if ($prefs['skin'] == 'default') {
aff970 273             $prefs['skin'] = self::DEFAULT_SKIN;
a9cbba 274         }
TB 275
2eb794 276         $this->userprefs = $prefs;
bfb7d6 277         $this->prop      = array_merge($this->prop, $prefs);
65082b 278
T 279         // override timezone settings with client values
801b69 280         if ($this->prop['timezone'] == 'auto') {
5879c0 281             $this->prop['_timezone_value'] = isset($_SESSION['timezone']) ? $this->client_timezone() : $this->prop['_timezone_value'];
65082b 282         }
985e41 283         else if (isset($this->prop['_timezone_value']))
T 284            unset($this->prop['_timezone_value']);
2eb794 285     }
A 286
287
288     /**
289      * Getter for all config options
290      *
291      * @return array  Hash array containg all config properties
292      */
293     public function all()
294     {
295         return $this->prop;
296     }
297
da7178 298     /**
65082b 299      * Special getter for user's timezone offset including DST
T 300      *
301      * @return float  Timezone offset (in hours)
5879c0 302      * @deprecated
da7178 303      */
T 304     public function get_timezone()
305     {
e86a21 306       if ($tz = $this->get('timezone')) {
A 307         try {
308           $tz = new DateTimeZone($tz);
309           return $tz->getOffset(new DateTime('now')) / 3600;
310         }
311         catch (Exception $e) {
312         }
5879c0 313       }
T 314
315       return 0;
da7178 316     }
2eb794 317
A 318     /**
319      * Return requested DES crypto key.
320      *
5c461b 321      * @param string $key Crypto key name
2eb794 322      * @return string Crypto key
A 323      */
324     public function get_crypto_key($key)
325     {
326         // Bomb out if the requested key does not exist
327         if (!array_key_exists($key, $this->prop)) {
0c2596 328             rcube::raise_error(array(
2eb794 329                 'code' => 500, 'type' => 'php',
A 330                 'file' => __FILE__, 'line' => __LINE__,
331                 'message' => "Request for unconfigured crypto key \"$key\""
332             ), true, true);
333         }
334
335         $key = $this->prop[$key];
336
337         // Bomb out if the configured key is not exactly 24 bytes long
338         if (strlen($key) != 24) {
0c2596 339             rcube::raise_error(array(
2eb794 340                 'code' => 500, 'type' => 'php',
413df0 341                 'file' => __FILE__, 'line' => __LINE__,
2eb794 342                 'message' => "Configured crypto key '$key' is not exactly 24 bytes long"
A 343             ), true, true);
344         }
345
346         return $key;
347     }
348
349
350     /**
351      * Try to autodetect operating system and find the correct line endings
352      *
353      * @return string The appropriate mail header delimiter
354      */
355     public function header_delimiter()
356     {
357         // use the configured delimiter for headers
086767 358         if (!empty($this->prop['mail_header_delimiter'])) {
A 359             $delim = $this->prop['mail_header_delimiter'];
360             if ($delim == "\n" || $delim == "\r\n")
361                 return $delim;
362             else
0c2596 363                 rcube::raise_error(array(
086767 364                     'code' => 500, 'type' => 'php',
413df0 365                     'file' => __FILE__, 'line' => __LINE__,
086767 366                     'message' => "Invalid mail_header_delimiter setting"
A 367                 ), true, false);
368         }
2eb794 369
A 370         $php_os = strtolower(substr(PHP_OS, 0, 3));
371
372         if ($php_os == 'win')
373             return "\r\n";
374
375         if ($php_os == 'mac')
376             return "\r\n";
377
378         return "\n";
379     }
380
381
382     /**
383      * Return the mail domain configured for the given host
384      *
5c461b 385      * @param string  $host   IMAP host
A 386      * @param boolean $encode If true, domain name will be converted to IDN ASCII
2eb794 387      * @return string Resolved SMTP host
A 388      */
e99991 389     public function mail_domain($host, $encode=true)
2eb794 390     {
A 391         $domain = $host;
392
393         if (is_array($this->prop['mail_domain'])) {
394             if (isset($this->prop['mail_domain'][$host]))
395                 $domain = $this->prop['mail_domain'][$host];
396         }
1aceb9 397         else if (!empty($this->prop['mail_domain'])) {
A 398             $domain = rcube_utils::parse_host($this->prop['mail_domain']);
399         }
bb8721 400
1aceb9 401         if ($encode) {
A 402             $domain = rcube_utils::idn_to_ascii($domain);
403         }
e99991 404
2eb794 405         return $domain;
A 406     }
bb8721 407
A 408
2eb794 409     /**
A 410      * Getter for error state
411      *
412      * @return mixed Error message on error, False if no errors
413      */
414     public function get_error()
415     {
416         return empty($this->errors) ? false : join("\n", $this->errors);
417     }
83a763 418
5879c0 419
T 420     /**
421      * Internal getter for client's (browser) timezone identifier
422      */
423     private function client_timezone()
424     {
086b15 425         if (isset($_SESSION['timezone']) && is_numeric($_SESSION['timezone'])
TB 426               && ($ctz = timezone_name_from_abbr("", $_SESSION['timezone'] * 3600, 0))) {
427             return $ctz;
428         }
429         else if (!empty($_SESSION['timezone'])) {
430             try {
431                 $tz = timezone_open($_SESSION['timezone']);
432                 return $tz->getName();
433             }
434             catch (Exception $e) { /* gracefully ignore */ }
435         }
436
437         // fallback to server's timezone
438         return date_default_timezone_get();
5879c0 439     }
T 440
197601 441 }