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