Aleksander Machniak
2016-05-20 9e64dc2debfa1c7410f82bf71f4d10856751e258
commit | author | age
354978 1 <?php
T 2
a95874 3 /**
354978 4  +-----------------------------------------------------------------------+
eea11e 5  | rcmail_install.php                                                    |
354978 6  |                                                                       |
e019f2 7  | This file is part of the Roundcube Webmail package                    |
3a13b5 8  | Copyright (C) 2008-2016, 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.                     |
354978 13  +-----------------------------------------------------------------------+
T 14 */
15
16 /**
e019f2 17  * Class to control the installation process of the Roundcube Webmail package
354978 18  *
T 19  * @category Install
e019f2 20  * @package  Roundcube
a95874 21  * @author   Thomas Bruederli
354978 22  */
eea11e 23 class rcmail_install
354978 24 {
3a13b5 25     public $step;
AM 26     public $last_error;
27     public $is_post           = false;
28     public $failures          = 0;
29     public $config            = array();
30     public $configured        = false;
31     public $legacy_config     = false;
32     public $email_pattern     = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9])';
33     public $bool_config_props = array();
e10712 34
3a13b5 35     public $local_config    = array('db_dsnw', 'default_host', 'support_url', 'des_key', 'plugins');
AM 36     public $obsolete_config = array('db_backend', 'db_max_length', 'double_auth');
37     public $replaced_config = array(
38         'skin_path'            => 'skin',
39         'locale_string'        => 'language',
40         'multiple_identities'  => 'identities_level',
41         'addrbook_show_images' => 'show_images',
42         'imap_root'            => 'imap_ns_personal',
43         'pagesize'             => 'mail_pagesize',
44         'top_posting'          => 'reply_mode',
45         'keep_alive'           => 'refresh_interval',
46         'min_keep_alive'       => 'min_refresh_interval',
47     );
08ffd9 48
3a13b5 49     // list of supported database drivers
AM 50     public $supported_dbs = array(
51         'MySQL'               => 'pdo_mysql',
52         'PostgreSQL'          => 'pdo_pgsql',
53         'SQLite'              => 'pdo_sqlite',
54         'SQLite (v2)'         => 'pdo_sqlite2',
55         'SQL Server (SQLSRV)' => 'pdo_sqlsrv',
56         'SQL Server (DBLIB)'  => 'pdo_dblib',
57         'Oracle'              => 'oci8',
58     );
398bff 59
AM 60
3a13b5 61     /**
AM 62      * Constructor
63      */
64     public function __construct()
65     {
66         $this->step    = intval($_REQUEST['_step']);
67         $this->is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
461a30 68     }
AM 69
3a13b5 70     /**
AM 71      * Singleton getter
72      */
73     public static function get_instance()
74     {
75         static $inst;
461a30 76
3a13b5 77         if (!$inst) {
AM 78             $inst = new rcmail_install();
79         }
80
81         return $inst;
461a30 82     }
AM 83
3a13b5 84     /**
AM 85      * Read the local config files and store properties
86      */
87     public function load_config()
88     {
89         // defaults
90         if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'defaults.inc.php')) {
91             $this->config   = (array) $config;
92             $this->defaults = $this->config;
93         }
c5042d 94
3a13b5 95         $config = null;
461a30 96
3a13b5 97         // config
AM 98         if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'config.inc.php')) {
99             $this->config = array_merge($this->config, $config);
100         }
101         else {
102             if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'main.inc.php')) {
103                 $this->config        = array_merge($this->config, $config);
104                 $this->legacy_config = true;
472788 105             }
3a13b5 106
AM 107             if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'db.inc.php')) {
108                 $this->config        = array_merge($this->config, $config);
109                 $this->legacy_config = true;
110             }
472788 111         }
TB 112
3a13b5 113         $this->configured = !empty($config);
354978 114     }
d68c90 115
3a13b5 116     /**
AM 117      * Read the default config file and store properties
118      */
119     public function load_config_file($file)
120     {
121         if (!is_readable($file)) {
122             return;
0829b7 123         }
3a13b5 124
AM 125         include $file;
126
127         // read comments from config file
128         if (function_exists('token_get_all')) {
129             $tokens    = token_get_all(file_get_contents($file));
130             $in_config = false;
131             $buffer    = '';
132
133             for ($i = 0; $i < count($tokens); $i++) {
134                 $token = $tokens[$i];
135                 if ($token[0] == T_VARIABLE && ($token[1] == '$config' || $token[1] == '$rcmail_config')) {
136                     $in_config = true;
137                     if ($buffer && $tokens[$i+1] == '[' && $tokens[$i+2][0] == T_CONSTANT_ENCAPSED_STRING) {
138                         $propname = trim($tokens[$i+2][1], "'\"");
139                         $this->comments[$propname] = $buffer;
140                         $buffer = '';
141                         $i += 3;
142                     }
143                 }
144                 else if ($in_config && $token[0] == T_COMMENT) {
145                     $buffer .= strtr($token[1], array('\n' => "\n"));
146                 }
147             }
8f576d 148         }
403f0b 149
3a13b5 150         // deprecated name of config variable
AM 151         if (is_array($rcmail_config)) {
152             return $rcmail_config;
5f25a1 153         }
3a13b5 154
AM 155         return $config;
871ca9 156     }
d68c90 157
3a13b5 158     /**
AM 159      * Getter for a certain config property
160      *
161      * @param string Property name
162      * @param string Default value
163      *
164      * @return string The property value
165      */
166     public function getprop($name, $default = '')
167     {
168         $value = $this->config[$name];
d68c90 169
3a13b5 170         if ($name == 'des_key' && !$this->configured && !isset($_REQUEST["_$name"])) {
AM 171             $value = rcube_utils::random_bytes(24);
5f25a1 172         }
3a13b5 173
AM 174         return $value !== null && $value !== '' ? $value : $default;
5f25a1 175     }
d68c90 176
3a13b5 177     /**
AM 178      * Create configuration file that contains parameters
179      * that differ from default values.
180      *
181      * @return string The complete config file content
182      */
183     public function create_config()
184     {
185         $config = array();
0829b7 186
3a13b5 187         foreach ($this->config as $prop => $default) {
AM 188             $is_default = !isset($_POST["_$prop"]);
189             $value      = !$is_default || $this->bool_config_props[$prop] ? $_POST["_$prop"] : $default;
d68c90 190
3a13b5 191             // always disable installer
AM 192             if ($prop == 'enable_installer') {
193                 $value = false;
194             }
d68c90 195
3a13b5 196             // reset useragent to default (keeps version up-to-date)
AM 197             if ($prop == 'useragent' && stripos($value, 'Roundcube Webmail/') !== false) {
198                 $value = $this->defaults[$prop];
199             }
d68c90 200
3a13b5 201             // generate new encryption key, never use the default value
AM 202             if ($prop == 'des_key' && $value == $this->defaults[$prop]) {
203                 $value = rcube_utils::random_bytes(24);
204             }
f6ee6f 205
3a13b5 206             // convert some form data
AM 207             if ($prop == 'debug_level' && !$is_default) {
208                 if (is_array($value)) {
209                     $val = 0;
210                     foreach ($value as $dbgval) {
211                         $val += intval($dbgval);
212                     }
213                     $value = $val;
214                 }
215             }
216             else if ($prop == 'db_dsnw' && !empty($_POST['_dbtype'])) {
217                 if ($_POST['_dbtype'] == 'sqlite') {
218                     $value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'],
219                         $_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']);
220                 }
221                 else if ($_POST['_dbtype']) {
222                     $value = sprintf('%s://%s:%s@%s/%s', $_POST['_dbtype'],
223                         rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), $_POST['_dbhost'], $_POST['_dbname']);
224                 }
225             }
226             else if ($prop == 'smtp_auth_type' && $value == '0') {
227                 $value = '';
228             }
229             else if ($prop == 'default_host' && is_array($value)) {
230                 $value = self::_clean_array($value);
231                 if (count($value) <= 1) {
232                     $value = $value[0];
233                 }
234             }
235             else if ($prop == 'mail_pagesize' || $prop == 'addressbook_pagesize') {
236                 $value = max(2, intval($value));
237             }
238             else if ($prop == 'smtp_user' && !empty($_POST['_smtp_user_u'])) {
239                 $value = '%u';
240             }
241             else if ($prop == 'smtp_pass' && !empty($_POST['_smtp_user_u'])) {
242                 $value = '%p';
243             }
244             else if (is_bool($default)) {
245                 $value = (bool) $value;
246             }
247             else if (is_numeric($value)) {
248                 $value = intval($value);
249             }
250             else if ($prop == 'plugins' && !empty($_POST['submit'])) {
251                 $value = array();
252                 foreach (array_keys($_POST) as $key) {
253                     if (preg_match('/^_plugins_*/', $key)) {
254                         array_push($value, $_POST[$key]);
255                     }
256                 }
257             }
398bff 258
3a13b5 259             // skip this property
AM 260             if ($value == $this->defaults[$prop]
261                 && (!in_array($prop, $this->local_config)
262                     || in_array($prop, array_merge($this->obsolete_config, array_keys($this->replaced_config)))
263                     || preg_match('/^db_(table|sequence)_/', $prop)
264                 )
265             ) {
266                 continue;
267             }
e6bb83 268
3a13b5 269             // save change
AM 270             $this->config[$prop] = $value;
271             $config[$prop]       = $value;
e6bb83 272         }
3a13b5 273
AM 274         $out = "<?php\n\n";
275         $out .= "/* Local configuration for Roundcube Webmail */\n\n";
276
277         foreach ($config as $prop => $value) {
278             // copy option descriptions from existing config or defaults.inc.php
279             $out .= $this->comments[$prop];
280             $out .= "\$config['$prop'] = " . self::_dump_var($value, $prop) . ";\n\n";
281         }
282
283         return $out;
e6bb83 284     }
398bff 285
3a13b5 286     /**
AM 287      * save generated config file in RCUBE_CONFIG_DIR
288      *
289      * @return boolean True if the file was saved successfully, false if not
290      */
291     public function save_configfile($config)
292     {
293         if (is_writable(RCUBE_CONFIG_DIR)) {
294             return file_put_contents(RCUBE_CONFIG_DIR . 'config.inc.php', $config);
295         }
5fed07 296
3a13b5 297         return false;
8f49e4 298     }
TB 299
3a13b5 300     /**
AM 301      * Check the current configuration for missing properties
302      * and deprecated or obsolete settings
303      *
304      * @return array List with problems detected
305      */
306     public function check_config()
307     {
308         $this->load_config();
8f49e4 309
3a13b5 310         if (!$this->configured) {
AM 311             return;
312         }
8f49e4 313
3a13b5 314         $out = $seen = array();
AM 315
316         // iterate over the current configuration
317         foreach (array_keys($this->config) as $prop) {
318             if ($replacement = $this->replaced_config[$prop]) {
319                 $out['replaced'][]  = array('prop' => $prop, 'replacement' => $replacement);
320                 $seen[$replacement] = true;
321             }
322             else if (!$seen[$prop] && in_array($prop, $this->obsolete_config)) {
323                 $out['obsolete'][] = array('prop' => $prop);
324                 $seen[$prop]       = true;
325             }
326         }
327
328         // the old default mime_magic reference is obsolete
329         if ($this->config['mime_magic'] == '/usr/share/misc/magic') {
330             $out['obsolete'][] = array(
331                 'prop'    => 'mime_magic',
332                 'explain' => "Set value to null in order to use system default"
333             );
334         }
335
336         // check config dependencies and contradictions
337         if ($this->config['enable_spellcheck'] && $this->config['spellcheck_engine'] == 'pspell') {
338             if (!extension_loaded('pspell')) {
339                 $out['dependencies'][] = array(
340                     'prop'    => 'spellcheck_engine',
341                     'explain' => "This requires the <tt>pspell</tt> extension which could not be loaded."
342                 );
343             }
344             else if (!empty($this->config['spellcheck_languages'])) {
345                 foreach ($this->config['spellcheck_languages'] as $lang => $descr) {
346                     if (!@pspell_new($lang)) {
347                         $out['dependencies'][] = array(
348                             'prop'    => 'spellcheck_languages',
349                             'explain' => "You are missing pspell support for language $lang ($descr)"
350                         );
351                     }
352                 }
353             }
354         }
355
356         if ($this->config['log_driver'] == 'syslog') {
357             if (!function_exists('openlog')) {
358                 $out['dependencies'][] = array(
359                     'prop'    => 'log_driver',
360                     'explain' => "This requires the <tt>syslog</tt> extension which could not be loaded."
361                 );
362             }
363
364             if (empty($this->config['syslog_id'])) {
365                 $out['dependencies'][] = array(
366                     'prop'    => 'syslog_id',
367                     'explain' => "Using <tt>syslog</tt> for logging requires a syslog ID to be configured"
368                 );
369             }
370         }
371
372         // check ldap_public sources having global_search enabled
373         if (is_array($this->config['ldap_public']) && !is_array($this->config['autocomplete_addressbooks'])) {
374             foreach ($this->config['ldap_public'] as $ldap_public) {
375                 if ($ldap_public['global_search']) {
376                     $out['replaced'][] = array(
377                         'prop'        => 'ldap_public::global_search',
378                         'replacement' => 'autocomplete_addressbooks'
379                     );
380                     break;
381                 }
382             }
383         }
384
385         return $out;
8f49e4 386     }
TB 387
3a13b5 388     /**
AM 389      * Merge the current configuration with the defaults
390      * and copy replaced values to the new options.
391      */
392     public function merge_config()
393     {
394         $current      = $this->config;
395         $this->config = array();
5fed07 396
3a13b5 397         foreach ($this->replaced_config as $prop => $replacement) {
AM 398             if (isset($current[$prop])) {
399                 if ($prop == 'skin_path') {
400                     $this->config[$replacement] = preg_replace('#skins/(\w+)/?$#', '\\1', $current[$prop]);
401                 }
402                 else if ($prop == 'multiple_identities') {
403                     $this->config[$replacement] = $current[$prop] ? 2 : 0;
404                 }
405                 else {
406                     $this->config[$replacement] = $current[$prop];
407                 }
408             }
5fed07 409
3a13b5 410             unset($current[$prop]);
AM 411         }
5fed07 412
3a13b5 413         foreach ($this->obsolete_config as $prop) {
AM 414             unset($current[$prop]);
415         }
5fed07 416
3a13b5 417         // add all ldap_public sources having global_search enabled to autocomplete_addressbooks
AM 418         if (is_array($current['ldap_public'])) {
419             foreach ($current['ldap_public'] as $key => $ldap_public) {
420                 if ($ldap_public['global_search']) {
421                     $this->config['autocomplete_addressbooks'][] = $key;
422                     unset($current['ldap_public'][$key]['global_search']);
423                 }
424             }
425         }
426
427         $this->config = array_merge($this->config, $current);
428
429         foreach (array_keys((array) $current['ldap_public']) as $key) {
430             $this->config['ldap_public'][$key] = $current['ldap_public'][$key];
431         }
112c54 432     }
5fed07 433
3a13b5 434     /**
AM 435      * Compare the local database schema with the reference schema
436      * required for this version of Roundcube
437      *
438      * @param rcube_db Database object
439      *
440      * @return boolean True if the schema is up-to-date, false if not or an error occurred
441      */
442     public function db_schema_check($DB)
443     {
444         if (!$this->configured) {
445             return false;
446         }
7177b5 447
3a13b5 448         // read reference schema from mysql.initial.sql
AM 449         $db_schema = $this->db_read_schema(INSTALL_PATH . 'SQL/mysql.initial.sql');
450         $errors    = array();
7177b5 451
3a13b5 452         // check list of tables
AM 453         $existing_tables = $DB->list_tables();
5fed07 454
3a13b5 455         foreach ($db_schema as $table => $cols) {
AM 456             $table = $this->config['db_prefix'] . $table;
8f576d 457
3a13b5 458             if (!in_array($table, $existing_tables)) {
AM 459                 $errors[] = "Missing table '".$table."'";
460             }
461             else {  // compare cols
462                 $db_cols = $DB->list_cols($table);
463                 $diff    = array_diff(array_keys($cols), $db_cols);
8f576d 464
3a13b5 465                 if (!empty($diff)) {
AM 466                     $errors[] = "Missing columns in table '$table': " . join(',', $diff);
467                 }
468             }
469         }
8f576d 470
3a13b5 471         return !empty($errors) ? $errors : false;
8f576d 472     }
F 473
3a13b5 474     /**
AM 475      * Utility function to read database schema from an .sql file
476      */
477     private function db_read_schema($schemafile)
478     {
479         $lines       = file($schemafile);
480         $table_block = false;
481         $schema      = array();
482         $keywords    = array('PRIMARY','KEY','INDEX','UNIQUE','CONSTRAINT','REFERENCES','FOREIGN');
8f576d 483
3a13b5 484         foreach ($lines as $line) {
AM 485             if (preg_match('/^\s*create table `?([a-z0-9_]+)`?/i', $line, $m)) {
486                 $table_block = $m[1];
487             }
488             else if ($table_block && preg_match('/^\s*`?([a-z0-9_-]+)`?\s+([a-z]+)/', $line, $m)) {
489                 $col = $m[1];
490                 if (!in_array(strtoupper($col), $keywords)) {
491                     $schema[$table_block][$col] = $m[2];
492                 }
493             }
494         }
5fed07 495
3a13b5 496         return $schema;
e7fa2c 497     }
5fed07 498
3a13b5 499     /**
AM 500      * Try to detect some file's mimetypes to test the correct behavior of fileinfo
501      */
502     public function check_mime_detection()
503     {
504         $errors = array();
505         $files  = array(
506             'skins/larry/images/roundcube_logo.png' => 'image/png',
507             'program/resources/blank.tif'           => 'image/tiff',
508             'program/resources/blocked.gif'         => 'image/gif',
509             'skins/larry/README'                    => 'text/plain',
510         );
11e670 511
3a13b5 512         foreach ($files as $path => $expected) {
AM 513             $mimetype = rcube_mime::file_content_type(INSTALL_PATH . $path, basename($path));
514             if ($mimetype != $expected) {
515                 $errors[] = array($path, $mimetype, $expected);
516             }
517         }
11e670 518
3a13b5 519         return $errors;
5f25a1 520     }
5fed07 521
3a13b5 522     /**
AM 523      * Check the correct configuration of the 'mime_types' mapping option
524      */
525     public function check_mime_extensions()
526     {
527         $errors = array();
528         $types  = array(
529             'application/zip'   => 'zip',
9e64dc 530             'text/css'          => 'css',
3a13b5 531             'application/pdf'   => 'pdf',
AM 532             'image/gif'         => 'gif',
533             'image/svg+xml'     => 'svg',
534         );
5fed07 535
3a13b5 536         foreach ($types as $mimetype => $expected) {
AM 537             $ext = rcube_mime::get_mime_extensions($mimetype);
538             if (!in_array($expected, (array) $ext)) {
539                 $errors[] = array($mimetype, $ext, $expected);
540             }
541         }
5fed07 542
3a13b5 543         return $errors;
461a30 544     }
0829b7 545
3a13b5 546     /**
AM 547      * Getter for the last error message
548      *
549      * @return string Error message or null if none exists
550      */
551     public function get_error()
552     {
553         return $this->last_error['message'];
554     }
555
556     /**
557      * Return a list with all imap hosts configured
558      *
559      * @return array Clean list with imap hosts
560      */
561     public function get_hostlist()
562     {
563         $default_hosts = (array) $this->getprop('default_host');
564         $out           = array();
565
566         foreach ($default_hosts as $key => $name) {
567             if (!empty($name)) {
568                 $out[] = rcube_utils::parse_host(is_numeric($key) ? $name : $key);
569             }
570         }
571
572         return $out;
573     }
574
575     /**
576      * Create a HTML dropdown to select a previous version of Roundcube
577      */
578     public function versions_select($attrib = array())
579     {
580         $select = new html_select($attrib);
581         $select->add(array(
582             '0.1-stable', '0.1.1',
583             '0.2-alpha', '0.2-beta', '0.2-stable',
584             '0.3-stable', '0.3.1',
585             '0.4-beta', '0.4.2',
586             '0.5-beta', '0.5', '0.5.1', '0.5.2', '0.5.3', '0.5.4',
587             '0.6-beta', '0.6',
588             '0.7-beta', '0.7', '0.7.1', '0.7.2', '0.7.3', '0.7.4',
589             '0.8-beta', '0.8-rc', '0.8.0', '0.8.1', '0.8.2', '0.8.3', '0.8.4', '0.8.5', '0.8.6',
590             '0.9-beta', '0.9-rc', '0.9-rc2',
591             // Note: Do not add newer versions here
592         ));
593
594         return $select;
595     }
596
597     /**
598      * Return a list with available subfolders of the skin directory
599      */
600     public function list_skins()
601     {
602         $skins   = array();
603         $skindir = INSTALL_PATH . 'skins/';
604
605         foreach (glob($skindir . '*') as $path) {
606             if (is_dir($path) && is_readable($path)) {
607                 $skins[] = substr($path, strlen($skindir));
608             }
609         }
610
611         return $skins;
612     }
613
614     /**
615      * Return a list with available subfolders of the plugins directory
616      * (with their associated description in composer.json)
617      */
618     public function list_plugins()
619     {
620         $plugins    = array();
621         $plugin_dir = INSTALL_PATH . 'plugins/';
622
623         foreach (glob($plugin_dir . '*') as $path) {
624             if (!is_dir($path)) {
625                 continue;
626             }
627
628             if (is_readable($path . '/composer.json')) {
629                 $file_json   = json_decode(file_get_contents($path . '/composer.json'));
630                 $plugin_desc = $file_json->description ?: 'N/A';
631             }
632             else {
633                 $plugin_desc = 'N/A';
634             }
635
636             $name      = substr($path, strlen($plugin_dir));
637             $plugins[] = array(
638                 'name'    => $name,
639                 'desc'    => $plugin_desc,
640                 'enabled' => in_array($name, (array) $this->config['plugins'])
641             );
642         }
643
644         return $plugins;
645     }
646
647     /**
648      * Display OK status
649      *
650      * @param string Test name
651      * @param string Confirm message
652      */
653     public function pass($name, $message = '')
654     {
655         echo rcube::Q($name) . ':&nbsp; <span class="success">OK</span>';
656         $this->_showhint($message);
657     }
658
659     /**
660      * Display an error status and increase failure count
661      *
662      * @param string Test name
663      * @param string Error message
664      * @param string URL for details
665      * @param bool   Do not count this failure
666      */
667     public function fail($name, $message = '', $url = '', $optional=false)
668     {
669         if (!$optional) {
670             $this->failures++;
671         }
672
673         echo rcube::Q($name) . ':&nbsp; <span class="fail">NOT OK</span>';
674         $this->_showhint($message, $url);
675     }
676
677     /**
678      * Display an error status for optional settings/features
679      *
680      * @param string Test name
681      * @param string Error message
682      * @param string URL for details
683      */
684     public function optfail($name, $message = '', $url = '')
685     {
686         echo rcube::Q($name) . ':&nbsp; <span class="na">NOT OK</span>';
687         $this->_showhint($message, $url);
688     }
689
690     /**
691      * Display warning status
692      *
693      * @param string Test name
694      * @param string Warning message
695      * @param string URL for details
696      */
697     public function na($name, $message = '', $url = '')
698     {
699         echo rcube::Q($name) . ':&nbsp; <span class="na">NOT AVAILABLE</span>';
700         $this->_showhint($message, $url);
701     }
702
703     private function _showhint($message, $url = '')
704     {
705         $hint = rcube::Q($message);
706
707         if ($url) {
708             $hint .= ($hint ? '; ' : '') . 'See <a href="' . rcube::Q($url) . '" target="_blank">' . rcube::Q($url) . '</a>';
709         }
710
711         if ($hint) {
712             echo '<span class="indent">(' . $hint . ')</span>';
713         }
714     }
715
716     private static function _clean_array($arr)
717     {
718         $out = array();
719
720         foreach (array_unique($arr) as $k => $val) {
721             if (!empty($val)) {
722                 if (is_numeric($k)) {
723                     $out[] = $val;
724                 }
725                 else {
726                     $out[$k] = $val;
727                 }
728             }
729         }
730
731         return $out;
732     }
733
734     private static function _dump_var($var, $name=null)
735     {
736         // special values
737         switch ($name) {
738         case 'syslog_facility':
739             $list = array(32 => 'LOG_AUTH', 80 => 'LOG_AUTHPRIV', 72 => ' LOG_CRON',
740                 24 => 'LOG_DAEMON', 0 => 'LOG_KERN', 128 => 'LOG_LOCAL0',
741                 136 => 'LOG_LOCAL1', 144 => 'LOG_LOCAL2', 152 => 'LOG_LOCAL3',
742                 160 => 'LOG_LOCAL4', 168 => 'LOG_LOCAL5', 176 => 'LOG_LOCAL6',
743                 184 => 'LOG_LOCAL7', 48 => 'LOG_LPR', 16 => 'LOG_MAIL',
744                 56 => 'LOG_NEWS', 40 => 'LOG_SYSLOG', 8 => 'LOG_USER', 64 => 'LOG_UUCP'
745             );
746
747             if ($val = $list[$var]) {
748                 return $val;
749             }
5f25a1 750             break;
3a13b5 751
AM 752         case 'mail_header_delimiter':
753             $var = str_replace(array("\r", "\n"), array('\r', '\n'), $var);
754             return '"' . $var. '"';
755 /*
756         // RCMAIL_VERSION is undefined here
757         case 'useragent':
758             if (preg_match('|^(.*)/('.preg_quote(RCMAIL_VERSION, '|').')$|i', $var, $m)) {
759                 return '"' . addcslashes($var, '"') . '/" . RCMAIL_VERSION';
760             }
761             break;
762 */
5f25a1 763         }
5fed07 764
3a13b5 765         if (is_array($var)) {
AM 766             if (empty($var)) {
767                 return 'array()';
768             }
769             else {  // check if all keys are numeric
770                 $isnum = true;
771                 foreach (array_keys($var) as $key) {
772                     if (!is_numeric($key)) {
773                         $isnum = false;
774                         break;
775                     }
776                 }
777
778                 if ($isnum) {
779                     return 'array(' . join(', ', array_map(array('rcmail_install', '_dump_var'), $var)) . ')';
780                 }
781             }
782         }
783
784         return var_export($var, true);
5f25a1 785     }
5fed07 786
3a13b5 787     /**
AM 788      * Initialize the database with the according schema
789      *
790      * @param object rcube_db Database connection
791      * @return boolen True on success, False on error
792      */
793     public function init_db($DB)
794     {
795         $engine = $DB->db_provider;
5fed07 796
3a13b5 797         // read schema file from /SQL/*
AM 798         $fname = INSTALL_PATH . "SQL/$engine.initial.sql";
799         if ($sql = @file_get_contents($fname)) {
800             $DB->set_option('table_prefix', $this->config['db_prefix']);
801             $DB->exec_script($sql);
802         }
803         else {
804             $this->fail('DB Schema', "Cannot read the schema file: $fname");
805             return false;
806         }
5fed07 807
3a13b5 808         if ($err = $this->get_error()) {
AM 809             $this->fail('DB Schema', "Error creating database schema: $err");
810             return false;
811         }
5fed07 812
3a13b5 813         return true;
190e97 814     }
5fed07 815
3a13b5 816     /**
AM 817      * Update database schema
818      *
819      * @param string Version to update from
820      *
821      * @return boolen True on success, False on error
822      */
823     public function update_db($version)
824     {
825         return rcmail_utils::db_update(INSTALL_PATH . 'SQL',
826             'roundcube', $version, array('quiet' => true));
190e97 827     }
T 828
3a13b5 829     /**
AM 830      * Handler for Roundcube errors
831      */
832     public function raise_error($p)
833     {
834         $this->last_error = $p;
835     }
c5042d 836 }