thomascube
2011-02-25 e6bb836e053913ec8a5930a6d51af1fa55eb08e2
commit | author | age
354978 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | rcube_install.php                                                     |
6  |                                                                       |
e019f2 7  | This file is part of the Roundcube Webmail package                    |
f5e7b3 8  | Copyright (C) 2008-2009, The Roundcube Dev Team                       |
354978 9  | Licensed under the GNU Public License                                 |
T 10  +-----------------------------------------------------------------------+
11
12  $Id:  $
13
14 */
15
16
17 /**
e019f2 18  * Class to control the installation process of the Roundcube Webmail package
354978 19  *
T 20  * @category Install
e019f2 21  * @package  Roundcube
354978 22  * @author Thomas Bruederli
T 23  */
24 class rcube_install
25 {
26   var $step;
237119 27   var $is_post = false;
354978 28   var $failures = 0;
c5042d 29   var $config = array();
b3f9df 30   var $configured = false;
c5042d 31   var $last_error = null;
e6bb83 32   var $db_map = array('pgsql' => 'postgres', 'mysqli' => 'mysql', 'sqlsrv' => 'mssql');
ad43e6 33   var $email_pattern = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9])';
871ca9 34   var $bool_config_props = array();
e10712 35
e6bb83 36   var $obsolete_config = array('db_backend', 'double_auth');
cbffc2 37   var $replaced_config = array(
T 38     'skin_path' => 'skin',
39     'locale_string' => 'language',
40     'multiple_identities' => 'identities_level',
2a4135 41     'addrbook_show_images' => 'show_images',
00290a 42     'imap_root' => 'imap_ns_personal',
cbffc2 43   );
e10712 44   
2491c6 45   // these config options are required for a working system
e6bb83 46   var $required_config = array(
T 47     'db_dsnw', 'db_table_contactgroups', 'db_table_contactgroupmembers',
48     'des_key', 'session_lifetime',
49   );
354978 50   
T 51   /**
52    * Constructor
53    */
54   function rcube_install()
55   {
56     $this->step = intval($_REQUEST['_step']);
237119 57     $this->is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
354978 58   }
T 59   
c5042d 60   /**
T 61    * Singleton getter
62    */
63   function get_instance()
64   {
65     static $inst;
66     
67     if (!$inst)
68       $inst = new rcube_install();
69     
70     return $inst;
71   }
354978 72   
T 73   /**
c5042d 74    * Read the default config files and store properties
354978 75    */
190e97 76   function load_defaults()
354978 77   {
c5042d 78     $this->_load_config('.php.dist');
T 79   }
80
81
82   /**
83    * Read the local config files and store properties
84    */
85   function load_config()
86   {
b3f9df 87     $this->config = array();
c5042d 88     $this->_load_config('.php');
b3f9df 89     $this->configured = !empty($this->config);
c5042d 90   }
T 91
92   /**
93    * Read the default config file and store properties
94    * @access private
95    */
96   function _load_config($suffix)
97   {
bba657 98     @include RCMAIL_CONFIG_DIR . '/main.inc' . $suffix;
354978 99     if (is_array($rcmail_config)) {
190e97 100       $this->config += $rcmail_config;
354978 101     }
T 102       
bba657 103     @include RCMAIL_CONFIG_DIR . '/db.inc'. $suffix;
354978 104     if (is_array($rcmail_config)) {
c5042d 105       $this->config += $rcmail_config;
354978 106     }
T 107   }
108   
109   
110   /**
111    * Getter for a certain config property
112    *
113    * @param string Property name
ad43e6 114    * @param string Default value
354978 115    * @return string The property value
T 116    */
fa7539 117   function getprop($name, $default = '')
354978 118   {
b77d0d 119     $value = $this->config[$name];
354978 120     
ccb412 121     if ($name == 'des_key' && !$this->configured && !isset($_REQUEST["_$name"]))
807d17 122       $value = rcube_install::random_key(24);
354978 123     
fa7539 124     return $value !== null && $value !== '' ? $value : $default;
354978 125   }
T 126   
127   
128   /**
129    * Take the default config file and replace the parameters
130    * with the submitted form data
131    *
132    * @param string Which config file (either 'main' or 'db')
133    * @return string The complete config file content
134    */
e10712 135   function create_config($which, $force = false)
354978 136   {
2c3d81 137     $out = @file_get_contents(RCMAIL_CONFIG_DIR . "/{$which}.inc.php.dist");
354978 138     
T 139     if (!$out)
2c3d81 140       return '[Warning: could not read the config template file]';
0c3bde 141
c5042d 142     foreach ($this->config as $prop => $default) {
871ca9 143       $value = (isset($_POST["_$prop"]) || $this->bool_config_props[$prop]) ? $_POST["_$prop"] : $default;
354978 144       
T 145       // convert some form data
b77d0d 146       if ($prop == 'debug_level') {
354978 147         $val = 0;
7d7f67 148         if (is_array($value))
T 149           foreach ($value as $dbgval)
b77d0d 150             $val += intval($dbgval);
7d7f67 151         $value = $val;
354978 152       }
0c3bde 153       else if ($which == 'db' && $prop == 'db_dsnw' && !empty($_POST['_dbtype'])) {
237119 154         if ($_POST['_dbtype'] == 'sqlite')
T 155           $value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'], $_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']);
156         else
b61965 157           $value = sprintf('%s://%s:%s@%s/%s', $_POST['_dbtype'], 
7d7f67 158             rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), $_POST['_dbhost'], $_POST['_dbname']);
c5042d 159       }
T 160       else if ($prop == 'smtp_auth_type' && $value == '0') {
161         $value = '';
162       }
163       else if ($prop == 'default_host' && is_array($value)) {
807d17 164         $value = rcube_install::_clean_array($value);
c5042d 165         if (count($value) <= 1)
T 166           $value = $value[0];
167       }
ccb412 168       else if ($prop == 'pagesize') {
T 169         $value = max(2, intval($value));
170       }
c5042d 171       else if ($prop == 'smtp_user' && !empty($_POST['_smtp_user_u'])) {
T 172         $value = '%u';
173       }
174       else if ($prop == 'smtp_pass' && !empty($_POST['_smtp_user_u'])) {
175         $value = '%p';
176       }
00290a 177       else if ($prop == 'default_imap_folders') {
A 178         $value = Array();
179         foreach ($this->config['default_imap_folders'] as $_folder) {
180           switch($_folder) {
181           case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
182           case 'Sent':   $_folder = $this->config['sent_mbox']; break;
183           case 'Junk':   $_folder = $this->config['junk_mbox']; break;
184           case 'Trash':  $_folder = $this->config['trash_mbox']; break;
569654 185           }
00290a 186         if (!in_array($_folder, $value))
A 187           $value[] = $_folder;
569654 188         }
A 189       }
c5042d 190       else if (is_bool($default)) {
27564f 191         $value = (bool)$value;
T 192       }
193       else if (is_numeric($value)) {
194         $value = intval($value);
c5042d 195       }
T 196       
197       // skip this property
e10712 198       if (!$force && ($value == $default))
c5042d 199         continue;
b77d0d 200
A 201       // save change
202       $this->config[$prop] = $value;
203
354978 204       // replace the matching line in config file
T 205       $out = preg_replace(
206         '/(\$rcmail_config\[\''.preg_quote($prop).'\'\])\s+=\s+(.+);/Uie',
5f25a1 207         "'\\1 = ' . rcube_install::_dump_var(\$value) . ';'",
354978 208         $out);
T 209     }
0c3bde 210
967b34 211     return trim($out);
c5042d 212   }
e10712 213
T 214
215   /**
216    * Check the current configuration for missing properties
217    * and deprecated or obsolete settings
218    *
219    * @return array List with problems detected
220    */
221   function check_config()
222   {
223     $this->config = array();
224     $this->load_defaults();
225     $defaults = $this->config;
226     
227     $this->load_config();
228     if (!$this->configured)
229       return null;
230     
231     $out = $seen = array();
f8c06e 232     $required = array_flip($this->required_config);
e10712 233     
cbffc2 234     // iterate over the current configuration
e10712 235     foreach ($this->config as $prop => $value) {
T 236       if ($replacement = $this->replaced_config[$prop]) {
237         $out['replaced'][] = array('prop' => $prop, 'replacement' => $replacement);
238         $seen[$replacement] = true;
239       }
240       else if (!$seen[$prop] && in_array($prop, $this->obsolete_config)) {
241         $out['obsolete'][] = array('prop' => $prop);
242         $seen[$prop] = true;
243       }
244     }
245     
246     // iterate over default config
247     foreach ($defaults as $prop => $value) {
f8c06e 248       if (!isset($seen[$prop]) && !isset($this->config[$prop]) && isset($required[$prop]))
e10712 249         $out['missing'][] = array('prop' => $prop);
T 250     }
f8c06e 251
871ca9 252     // check config dependencies and contradictions
T 253     if ($this->config['enable_spellcheck'] && $this->config['spellcheck_engine'] == 'pspell') {
254       if (!extension_loaded('pspell')) {
255         $out['dependencies'][] = array('prop' => 'spellcheck_engine',
256           'explain' => 'This requires the <tt>pspell</tt> extension which could not be loaded.');
257       }
924b1a 258       else if (!empty($this->config['spellcheck_languages'])) {
01a8c5 259         foreach ($this->config['spellcheck_languages'] as $lang => $descr)
924b1a 260           if (!pspell_new($lang))
01a8c5 261             $out['dependencies'][] = array('prop' => 'spellcheck_languages',
T 262               'explain' => "You are missing pspell support for language $lang ($descr)");
871ca9 263       }
T 264     }
265     
266     if ($this->config['log_driver'] == 'syslog') {
267       if (!function_exists('openlog')) {
268         $out['dependencies'][] = array('prop' => 'log_driver',
269           'explain' => 'This requires the <tt>sylog</tt> extension which could not be loaded.');
270       }
271       if (empty($this->config['syslog_id'])) {
272         $out['dependencies'][] = array('prop' => 'syslog_id',
273           'explain' => 'Using <tt>syslog</tt> for logging requires a syslog ID to be configured');
274       }
5f25a1 275     }
T 276     
277     // check ldap_public sources having global_search enabled
278     if (is_array($this->config['ldap_public']) && !is_array($this->config['autocomplete_addressbooks'])) {
279       foreach ($this->config['ldap_public'] as $ldap_public) {
280         if ($ldap_public['global_search']) {
281           $out['replaced'][] = array('prop' => 'ldap_public::global_search', 'replacement' => 'autocomplete_addressbooks');
282           break;
283         }
284       }
871ca9 285     }
T 286     
e10712 287     return $out;
T 288   }
289   
290   
291   /**
292    * Merge the current configuration with the defaults
293    * and copy replaced values to the new options.
294    */
295   function merge_config()
296   {
297     $current = $this->config;
298     $this->config = array();
299     $this->load_defaults();
300     
e6bb83 301     foreach ($this->replaced_config as $prop => $replacement) {
e10712 302       if (isset($current[$prop])) {
T 303         if ($prop == 'skin_path')
304           $this->config[$replacement] = preg_replace('#skins/(\w+)/?$#', '\\1', $current[$prop]);
cbffc2 305         else if ($prop == 'multiple_identities')
T 306           $this->config[$replacement] = $current[$prop] ? 2 : 0;
e10712 307         else
T 308           $this->config[$replacement] = $current[$prop];
e6bb83 309       }
T 310       unset($current[$prop]);
e10712 311     }
T 312     
313     foreach ($this->obsolete_config as $prop) {
314       unset($current[$prop]);
315     }
316     
5f25a1 317     // add all ldap_public sources having global_search enabled to autocomplete_addressbooks
T 318     if (is_array($current['ldap_public'])) {
319       foreach ($current['ldap_public'] as $key => $ldap_public) {
320         if ($ldap_public['global_search']) {
321           $this->config['autocomplete_addressbooks'][] = $key;
322           unset($current['ldap_public'][$key]['global_search']);
323         }
324       }
325     }
e6bb83 326     
T 327     if ($current['keep_alive'] && $current['session_lifetime'] < $current['keep_alive'])
328       $current['session_lifetime'] = max(10, ceil($current['keep_alive'] / 60) * 2);
5f25a1 329     
T 330     $this->config  = array_merge($this->config, $current);
331     
332     foreach ((array)$current['ldap_public'] as $key => $values) {
333       $this->config['ldap_public'][$key] = $current['ldap_public'][$key];
334     }
e10712 335   }
c5042d 336   
2491c6 337   /**
T 338    * Compare the local database schema with the reference schema
e019f2 339    * required for this version of Roundcube
2491c6 340    *
T 341    * @param boolean True if the schema schould be updated
342    * @return boolean True if the schema is up-to-date, false if not or an error occured
343    */
344   function db_schema_check($DB, $update = false)
345   {
346     if (!$this->configured)
347       return false;
348     
e6bb83 349     // read reference schema from mysql.initial.sql
T 350     $db_schema = $this->db_read_schema(INSTALL_PATH . 'SQL/mysql.initial.sql');
2491c6 351     $errors = array();
T 352     
353     // check list of tables
354     $existing_tables = $DB->list_tables();
f6ee6f 355
2491c6 356     foreach ($db_schema as $table => $cols) {
f6ee6f 357       $table = !empty($this->config['db_table_'.$table]) ? $this->config['db_table_'.$table] : $table;
e6bb83 358       if (!in_array($table, $existing_tables)) {
T 359         $errors[] = "Missing table '".$table."'";
360       }
361       else {  // compare cols
362         $db_cols = $DB->list_cols($table);
363         $diff = array_diff(array_keys($cols), $db_cols);
364         if (!empty($diff))
365           $errors[] = "Missing columns in table '$table': " . join(',', $diff);
366       }
2491c6 367     }
T 368     
369     return !empty($errors) ? $errors : false;
370   }
e6bb83 371
T 372   /**
373    * Utility function to read database schema from an .sql file
374    */
375   private function db_read_schema($schemafile)
376   {
377     $lines = file($schemafile);
378     $table_block = false;
379     $schema = array();
380     foreach ($lines as $line) {
381       if (preg_match('/^\s*create table `?([a-z0-9_]+)`?/i', $line, $m)) {
382         $table_block = $m[1];
383       }
384       else if ($table_block && preg_match('/^\s*`?([a-z0-9_-]+)`?\s+([a-z]+)/', $line, $m)) {
385         $col = $m[1];
386         if (!in_array(strtoupper($col), array('PRIMARY','KEY','INDEX','UNIQUE','CONSTRAINT','REFERENCES','FOREIGN'))) {
387           $schema[$table_block][$col] = $m[2];
388         }
389       }
390     }
391     
392     return $schema;
393   }
394   
c5042d 395   
T 396   /**
871ca9 397    * Compare the local database schema with the reference schema
e019f2 398    * required for this version of Roundcube
871ca9 399    *
T 400    * @param boolean True if the schema schould be updated
401    * @return boolean True if the schema is up-to-date, false if not or an error occured
402    */
2491c6 403   function mdb2_schema_check($update = false)
871ca9 404   {
T 405     if (!$this->configured)
406       return false;
407     
408     $options = array(
409       'use_transactions' => false,
410       'log_line_break' => "\n",
411       'idxname_format' => '%s',
412       'debug' => false,
413       'quote_identifier' => true,
414       'force_defaults' => false,
415       'portability' => true
416     );
417     
924b1a 418     $dsnw = $this->config['db_dsnw'];
T 419     $schema = MDB2_Schema::factory($dsnw, $options);
871ca9 420     $schema->db->supported['transactions'] = false;
T 421     
422     if (PEAR::isError($schema)) {
423       $this->raise_error(array('code' => $schema->getCode(), 'message' => $schema->getMessage() . ' ' . $schema->getUserInfo()));
424       return false;
425     }
426     else {
427       $definition = $schema->getDefinitionFromDatabase();
428       $definition['charset'] = 'utf8';
429       
430       if (PEAR::isError($definition)) {
431         $this->raise_error(array('code' => $definition->getCode(), 'message' => $definition->getMessage() . ' ' . $definition->getUserInfo()));
432         return false;
433       }
434       
435       // load reference schema
924b1a 436       $dsn_arr = MDB2::parseDSN($this->config['db_dsnw']);
T 437
438       $ref_schema = INSTALL_PATH . 'SQL/' . $dsn_arr['phptype'] . '.schema.xml';
871ca9 439       
924b1a 440       if (is_readable($ref_schema)) {
871ca9 441         $reference = $schema->parseDatabaseDefinition($ref_schema, false, array(), $schema->options['fail_on_invalid_names']);
T 442         
443         if (PEAR::isError($reference)) {
444           $this->raise_error(array('code' => $reference->getCode(), 'message' => $reference->getMessage() . ' ' . $reference->getUserInfo()));
445         }
446         else {
447           $diff = $schema->compareDefinitions($reference, $definition);
448           
449           if (empty($diff)) {
450             return true;
451           }
452           else if ($update) {
453             // update database schema with the diff from the above check
454             $success = $schema->alterDatabase($reference, $definition, $diff);
455             
456             if (PEAR::isError($success)) {
457               $this->raise_error(array('code' => $success->getCode(), 'message' => $success->getMessage() . ' ' . $success->getUserInfo()));
458             }
459             else
460               return true;
461           }
462           echo '<pre>'; var_dump($diff); echo '</pre>';
463           return false;
464         }
465       }
466       else
467         $this->raise_error(array('message' => "Could not find reference schema file ($ref_schema)"));
468         return false;
469     }
470     
471     return false;
472   }
473   
474   
475   /**
c5042d 476    * Getter for the last error message
T 477    *
478    * @return string Error message or null if none exists
479    */
480   function get_error()
481   {
482       return $this->last_error['message'];
354978 483   }
T 484   
485   
486   /**
112c54 487    * Return a list with all imap hosts configured
T 488    *
489    * @return array Clean list with imap hosts
490    */
491   function get_hostlist()
492   {
493     $default_hosts = (array)$this->getprop('default_host');
494     $out = array();
495     
496     foreach ($default_hosts as $key => $name) {
497       if (!empty($name))
058eb6 498         $out[] = rcube_parse_host(is_numeric($key) ? $name : $key);
112c54 499     }
T 500     
501     return $out;
e6bb83 502   }
T 503   
504   /**
505    * Create a HTML dropdown to select a previous version of Roundcube
506    */
507   function versions_select($attrib = array())
508   {
509     $select = new html_select($attrib);
510     $select->add(array('0.1-stable', '0.1.1', '0.2-alpha', '0.2-beta', '0.2-stable', '0.3-stable', '0.3.1', '0.4-beta', '0.4.2', '0.5-beta', '0.5', '0.5.1'));
511     return $select;
112c54 512   }
T 513   
514   
515   /**
354978 516    * Display OK status
T 517    *
518    * @param string Test name
519    * @param string Confirm message
520    */
521   function pass($name, $message = '')
522   {
523     echo Q($name) . ':&nbsp; <span class="success">OK</span>';
6557d3 524     $this->_showhint($message);
354978 525   }
T 526   
527   
528   /**
529    * Display an error status and increase failure count
530    *
531    * @param string Test name
532    * @param string Error message
533    * @param string URL for details
534    */
535   function fail($name, $message = '', $url = '')
536   {
537     $this->failures++;
538     
539     echo Q($name) . ':&nbsp; <span class="fail">NOT OK</span>';
6557d3 540     $this->_showhint($message, $url);
354978 541   }
11e670 542
A 543
544   /**
545    * Display an error status for optional settings/features
546    *
547    * @param string Test name
548    * @param string Error message
549    * @param string URL for details
550    */
551   function optfail($name, $message = '', $url = '')
552   {
553     echo Q($name) . ':&nbsp; <span class="na">NOT OK</span>';
554     $this->_showhint($message, $url);
555   }
354978 556   
T 557   
558   /**
559    * Display warning status
560    *
561    * @param string Test name
562    * @param string Warning message
563    * @param string URL for details
564    */
6557d3 565   function na($name, $message = '', $url = '')
354978 566   {
6557d3 567     echo Q($name) . ':&nbsp; <span class="na">NOT AVAILABLE</span>';
T 568     $this->_showhint($message, $url);
569   }
570   
571   
572   function _showhint($message, $url = '')
573   {
574     $hint = Q($message);
575     
354978 576     if ($url)
6557d3 577       $hint .= ($hint ? '; ' : '') . 'See <a href="' . Q($url) . '" target="_blank">' . Q($url) . '</a>';
T 578       
579     if ($hint)
580       echo '<span class="indent">(' . $hint . ')</span>';
354978 581   }
T 582   
583   
5f25a1 584   static function _clean_array($arr)
c5042d 585   {
T 586     $out = array();
587     
5f25a1 588     foreach (array_unique($arr) as $k => $val) {
T 589       if (!empty($val)) {
590         if (is_numeric($k))
591           $out[] = $val;
592         else
593           $out[$k] = $val;
594       }
595     }
c5042d 596     
T 597     return $out;
598   }
599   
190e97 600   
5f25a1 601   static function _dump_var($var) {
T 602     if (is_array($var)) {
603       if (empty($var)) {
604         return 'array()';
605       }
606       else {  // check if all keys are numeric
607         $isnum = true;
608         foreach ($var as $key => $value) {
609           if (!is_numeric($key)) {
610             $isnum = false;
611             break;
612           }
613         }
614         
615         if ($isnum)
616           return 'array(' . join(', ', array_map(array('rcube_install', '_dump_var'), $var)) . ')';
617       }
618     }
619     
620     return var_export($var, true);
621   }
622   
623   
190e97 624   /**
T 625    * Initialize the database with the according schema
626    *
627    * @param object rcube_db Database connection
628    * @return boolen True on success, False on error
629    */
630   function init_db($DB)
631   {
e6bb83 632     $engine = isset($this->db_map[$DB->db_provider]) ? $this->db_map[$DB->db_provider] : $DB->db_provider;
190e97 633     
T 634     // read schema file from /SQL/*
e6bb83 635     $fname = INSTALL_PATH . "SQL/$engine.initial.sql";
T 636     if ($sql = @file_get_contents($fname)) {
637       $this->exec_sql($sql, $DB);
190e97 638     }
T 639     else {
640       $this->fail('DB Schema', "Cannot read the schema file: $fname");
641       return false;
642     }
643     
644     if ($err = $this->get_error()) {
645       $this->fail('DB Schema', "Error creating database schema: $err");
646       return false;
647     }
648
649     return true;
650   }
651   
e6bb83 652   
T 653   /**
654    * Update database with SQL statements from SQL/*.update.sql
655    *
656    * @param object rcube_db Database connection
657    * @param string Version to update from
658    * @return boolen True on success, False on error
659    */
660   function update_db($DB, $version)
661   {
662     $version = strtolower($version);
663     $engine = isset($this->db_map[$DB->db_provider]) ? $this->db_map[$DB->db_provider] : $DB->db_provider;
664     
665     // read schema file from /SQL/*
666     $fname = INSTALL_PATH . "SQL/$engine.update.sql";
667     if ($lines = @file($fname, FILE_SKIP_EMPTY_LINES)) {
668       $from = false; $sql = '';
669       foreach ($lines as $line) {
670         $is_comment = preg_match('/^--/', $line);
671         if (!$from && $is_comment && preg_match('/from version\s([0-9.]+[a-z-]*)/', $line, $m)) {
672           $v = strtolower($m[1]);
673           if ($v == $version || version_compare($version, $v, '<='))
674             $from = true;
675         }
676         if ($from && !$is_comment)
677           $sql .= $line. "\n";
678       }
679       
680       if ($sql)
681         $this->exec_sql($sql, $DB);
682     }
683     else {
684       $this->fail('DB Schema', "Cannot read the update file: $fname");
685       return false;
686     }
687     
688     if ($err = $this->get_error()) {
689       $this->fail('DB Schema', "Error updating database: $err");
690       return false;
691     }
692
693     return true;
694   }
695   
696   
697   /**
698    * Execute the given SQL queries on the database connection
699    *
700    * @param string SQL queries to execute
701    * @param object rcube_db Database connection
702    * @return boolen True on success, False on error
703    */
704   function exec_sql($sql, $DB)
705   {
706     $buff = '';
707     foreach (explode("\n", $sql) as $line) {
708       if (preg_match('/^--/', $line) || trim($line) == '')
709         continue;
710         
711       $buff .= $line . "\n";
712       if (preg_match('/(;|^GO)$/', trim($line))) {
713         $DB->query($buff);
714         $buff = '';
715         if ($DB->is_error())
716           break;
717       }
718     }
719     
720     return !$DB->is_error();
721   }
722   
723   
c5042d 724   /**
e019f2 725    * Handler for Roundcube errors
c5042d 726    */
T 727   function raise_error($p)
728   {
729       $this->last_error = $p;
730   }
731   
732   
354978 733   /**
T 734    * Generarte a ramdom string to be used as encryption key
735    *
736    * @param int Key length
737    * @return string The generated random string
738    * @static
739    */
740   function random_key($length)
741   {
742     $alpha = 'ABCDEFGHIJKLMNOPQERSTUVXYZabcdefghijklmnopqrtsuvwxyz0123456789+*%&?!$-_=';
743     $out = '';
744     
745     for ($i=0; $i < $length; $i++)
746       $out .= $alpha{rand(0, strlen($alpha)-1)};
747     
748     return $out;
749   }
750   
c5042d 751 }
T 752