PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/program/include/rcmail_install.php

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