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