Changeset 5807 in subversion
- Timestamp:
- Jan 20, 2012 6:23:32 AM (17 months ago)
- Location:
- branches/devel-framework/roundcubemail
- Files:
-
- 2 added
- 10 edited
-
installer/index.php (modified) (1 diff)
-
installer/utils.php (modified) (2 diffs)
-
program/include/clisetup.php (modified) (1 diff)
-
program/include/html.php (modified) (2 diffs)
-
program/include/iniset.php (modified) (1 diff)
-
program/include/main.inc (modified) (6 diffs)
-
program/include/rcmail.php (modified) (2 diffs)
-
program/include/rcube_base_replacer.php (added)
-
program/include/rcube_mdb2.php (modified) (1 diff)
-
program/include/rcube_session.php (modified) (2 diffs)
-
program/include/rcube_shared.inc (modified) (7 diffs)
-
program/include/rcube_ui.php (added)
Legend:
- Unmodified
- Added
- Removed
-
branches/devel-framework/roundcubemail/installer/index.php
r5152 r5807 43 43 44 44 require_once 'utils.php'; 45 require_once 'rcube_shared.inc'; 46 // deprecated aliases (to be removed) 45 47 require_once 'main.inc'; 46 48 -
branches/devel-framework/roundcubemail/installer/utils.php
r5152 r5807 69 69 * Local callback function for PEAR errors 70 70 */ 71 function rcube_pear_error($err)71 function __pear_error($err) 72 72 { 73 73 raise_error(array( … … 78 78 79 79 // set PEAR error handling (will also load the PEAR main class) 80 PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, ' rcube_pear_error');80 PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, '__pear_error'); -
branches/devel-framework/roundcubemail/program/include/clisetup.php
r5299 r5807 53 53 54 54 $args[$key] = preg_replace(array('/^["\']/', '/["\']$/'), '', $value); 55 55 56 56 if ($alias = $aliases[$key]) 57 57 $args[$alias] = $args[$key]; -
branches/devel-framework/roundcubemail/program/include/html.php
r5672 r5807 304 304 return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : ''; 305 305 } 306 307 /** 308 * Convert a HTML attribute string attributes to an associative array (name => value) 309 * 310 * @param string Input string 311 * @return array Key-value pairs of parsed attributes 312 */ 313 public static function parse_attrib_string($str) 314 { 315 $attrib = array(); 316 $regexp = '/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui'; 317 318 preg_match_all($regexp, stripslashes($str), $regs, PREG_SET_ORDER); 319 320 // convert attributes to an associative array (name => value) 321 if ($regs) { 322 foreach ($regs as $attr) { 323 $attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]); 324 } 325 } 326 327 return $attrib; 328 } 306 329 } 307 330 … … 804 827 805 828 } 806 -
branches/devel-framework/roundcubemail/program/include/iniset.php
r5768 r5807 78 78 } 79 79 80 /** 81 * Use PHP5 autoload for dynamic class loading 82 * 83 * @todo Make Zend, PEAR etc play with this 84 * @todo Make our classes conform to a more straight forward CS. 85 */ 86 function rcube_autoload($classname) 87 { 88 $filename = preg_replace( 89 array( 90 '/MDB2_(.+)/', 91 '/Mail_(.+)/', 92 '/Net_(.+)/', 93 '/Auth_(.+)/', 94 '/^html_.+/', 95 '/^utf8$/', 96 ), 97 array( 98 'MDB2/\\1', 99 'Mail/\\1', 100 'Net/\\1', 101 'Auth/\\1', 102 'html', 103 'utf8.class', 104 ), 105 $classname 106 ); 80 // include global functions 81 require_once INSTALL_PATH . 'program/include/rcube_shared.inc'; 107 82 108 if ($fp = @fopen("$filename.php", 'r', true)) { 109 fclose($fp); 110 include_once("$filename.php"); 111 return true; 112 } 113 114 return false; 115 } 116 83 // Register autoloader 117 84 spl_autoload_register('rcube_autoload'); 118 119 /**120 * Local callback function for PEAR errors121 */122 function rcube_pear_error($err)123 {124 error_log(sprintf("%s (%s): %s",125 $err->getMessage(),126 $err->getCode(),127 $err->getUserinfo()), 0);128 }129 85 130 86 // set PEAR error handling (will also load the PEAR main class) 131 87 PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'rcube_pear_error'); 132 88 133 // include global functions89 // backward compatybility (to be removed) 134 90 require_once INSTALL_PATH . 'program/include/main.inc'; 135 require_once INSTALL_PATH . 'program/include/rcube_shared.inc'; -
branches/devel-framework/roundcubemail/program/include/main.inc
r5776 r5807 6 6 | | 7 7 | This file is part of the Roundcube Webmail client | 8 | Copyright (C) 2005-201 1, The Roundcube Dev Team |8 | Copyright (C) 2005-2012, The Roundcube Dev Team | 9 9 | Licensed under the GNU GPL | 10 10 | | 11 11 | PURPOSE: | 12 | Provide basic functions for the webmail package|12 | Provide deprecated functions aliases for backward compatibility | 13 13 | | 14 14 +-----------------------------------------------------------------------+ … … 21 21 22 22 /** 23 * Roundcube Webmail commonfunctions23 * Roundcube Webmail deprecated functions 24 24 * 25 25 * @package Core … … 27 27 */ 28 28 29 require_once INSTALL_PATH . 'program/include/rcube_shared.inc'; 30 31 // define constannts for input reading 32 define('RCUBE_INPUT_GET', 0x0101); 33 define('RCUBE_INPUT_POST', 0x0102); 34 define('RCUBE_INPUT_GPC', 0x0103); 35 36 37 38 /** 39 * Return correct name for a specific database table 40 * 41 * @param string Table name 42 * @return string Translated table name 43 */ 29 // constants for input reading 30 define('RCUBE_INPUT_GET', rcube_ui::INPUT_GET); 31 define('RCUBE_INPUT_POST', rcube_ui::INPUT_POST); 32 define('RCUBE_INPUT_GPC', rcube_ui::INPUT_GPC); 33 34 44 35 function get_table_name($table) 45 { 46 global $CONFIG; 47 48 // return table name if configured 49 $config_key = 'db_table_'.$table; 50 51 if (strlen($CONFIG[$config_key])) 52 return $CONFIG[$config_key]; 53 54 return $table; 55 } 56 57 58 /** 59 * Return correct name for a specific database sequence 60 * (used for Postgres only) 61 * 62 * @param string Secuence name 63 * @return string Translated sequence name 64 */ 36 { 37 return rcmail::get_instance()->db->table_name($table); 38 } 39 65 40 function get_sequence_name($sequence) 66 { 67 // return sequence name if configured 68 $config_key = 'db_sequence_'.$sequence; 69 $opt = rcmail::get_instance()->config->get($config_key); 70 71 if (!empty($opt)) 72 return $opt; 73 74 return $sequence; 75 } 76 77 78 /** 79 * Get localized text in the desired language 80 * It's a global wrapper for rcmail::gettext() 81 * 82 * @param mixed Named parameters array or label name 83 * @param string Domain to search in (e.g. plugin name) 84 * @return string Localized text 85 * @see rcmail::gettext() 86 */ 41 { 42 return rcmail::get_instance()->db->sequence_name($sequence); 43 } 44 87 45 function rcube_label($p, $domain=null) 88 46 { 89 return rcmail::get_instance()->gettext($p, $domain); 90 } 91 92 93 /** 94 * Global wrapper of rcmail::text_exists() 95 * to check whether a text label is defined 96 * 97 * @see rcmail::text_exists() 98 */ 47 return rcmail::get_instance()->gettext($p, $domain); 48 } 49 99 50 function rcube_label_exists($name, $domain=null, &$ref_domain = null) 100 51 { 101 return rcmail::get_instance()->text_exists($name, $domain, $ref_domain); 102 } 103 104 105 /** 106 * Overwrite action variable 107 * 108 * @param string New action value 109 */ 52 return rcmail::get_instance()->text_exists($name, $domain, $ref_domain); 53 } 54 110 55 function rcmail_overwrite_action($action) 111 { 112 $app = rcmail::get_instance(); 113 $app->action = $action; 114 $app->output->set_env('action', $action); 115 } 116 117 118 /** 119 * Compose an URL for a specific action 120 * 121 * @param string Request action 122 * @param array More URL parameters 123 * @param string Request task (omit if the same) 124 * @return The application URL 125 */ 56 { 57 rcmail::get_instance()->overwrite_action($action); 58 } 59 126 60 function rcmail_url($action, $p=array(), $task=null) 127 61 { 128 $app = rcmail::get_instance(); 129 return $app->url((array)$p + array('_action' => $action, 'task' => $task)); 130 } 131 132 133 /** 134 * Garbage collector function for temp files. 135 * Remove temp files older than two days 136 */ 62 return rcube_ui::url($action, $p, $task); 63 } 64 137 65 function rcmail_temp_gc() 138 66 { 139 $rcmail = rcmail::get_instance(); 140 141 $tmp = unslashify($rcmail->config->get('temp_dir')); 142 $expire = mktime() - 172800; // expire in 48 hours 143 144 if ($dir = opendir($tmp)) { 145 while (($fname = readdir($dir)) !== false) { 146 if ($fname{0} == '.') 147 continue; 148 149 if (filemtime($tmp.'/'.$fname) < $expire) 150 @unlink($tmp.'/'.$fname); 151 } 152 153 closedir($dir); 154 } 155 } 156 157 158 /** 159 * Garbage collector for cache entries. 160 * Remove all expired message cache records 161 * @return void 162 */ 67 $rcmail = rcmail::get_instance()->temp_gc(); 68 } 69 163 70 function rcmail_cache_gc() 164 71 { 165 $rcmail = rcmail::get_instance(); 166 $db = $rcmail->get_dbh(); 167 168 // get target timestamp 169 $ts = get_offset_time($rcmail->config->get('message_cache_lifetime', '30d'), -1); 170 171 $db->query("DELETE FROM ".get_table_name('cache_messages') 172 ." WHERE changed < " . $db->fromunixtime($ts)); 173 174 $db->query("DELETE FROM ".get_table_name('cache_index') 175 ." WHERE changed < " . $db->fromunixtime($ts)); 176 177 $db->query("DELETE FROM ".get_table_name('cache_thread') 178 ." WHERE changed < " . $db->fromunixtime($ts)); 179 180 $db->query("DELETE FROM ".get_table_name('cache') 181 ." WHERE created < " . $db->fromunixtime($ts)); 182 } 183 184 185 // Deprecated 72 $rcmail = rcmail::get_instance()->cache_gc(); 73 } 74 186 75 function rcube_charset_convert($str, $from, $to=NULL) 187 76 { … … 189 78 } 190 79 191 192 // Deprecated193 80 function rc_detect_encoding($string, $failover='') 194 81 { … … 196 83 } 197 84 198 199 // Deprecated200 85 function rc_utf8_clean($input) 201 86 { … … 203 88 } 204 89 205 206 /**207 * Convert a variable into a javascript object notation208 *209 * @param mixed Input value210 * @return string Serialized JSON string211 */212 90 function json_serialize($input) 213 91 { 214 $input = rcube_charset::clean($input); 215 216 // sometimes even using rcube_charset::clean() the input contains invalid UTF-8 sequences 217 // that's why we have @ here 218 return @json_encode($input); 219 } 220 221 222 /** 223 * Replacing specials characters to a specific encoding type 224 * 225 * @param string Input string 226 * @param string Encoding type: text|html|xml|js|url 227 * @param string Replace mode for tags: show|replace|remove 228 * @param boolean Convert newlines 229 * @return string The quoted string 230 */ 92 return rcube_ui::json_serialize($input); 93 } 94 231 95 function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) 232 { 233 static $html_encode_arr = false; 234 static $js_rep_table = false; 235 static $xml_rep_table = false; 236 237 if (!$enctype) 238 $enctype = $OUTPUT->type; 239 240 // encode for HTML output 241 if ($enctype=='html') 242 { 243 if (!$html_encode_arr) 244 { 245 $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); 246 unset($html_encode_arr['?']); 247 } 248 249 $ltpos = strpos($str, '<'); 250 $encode_arr = $html_encode_arr; 251 252 // don't replace quotes and html tags 253 if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) 254 { 255 unset($encode_arr['"']); 256 unset($encode_arr['<']); 257 unset($encode_arr['>']); 258 unset($encode_arr['&']); 259 } 260 else if ($mode=='remove') 261 $str = strip_tags($str); 262 263 $out = strtr($str, $encode_arr); 264 265 // avoid douple quotation of & 266 $out = preg_replace('/&([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', $out); 267 268 return $newlines ? nl2br($out) : $out; 269 } 270 271 // if the replace tables for XML and JS are not yet defined 272 if ($js_rep_table===false) 273 { 274 $js_rep_table = $xml_rep_table = array(); 275 $xml_rep_table['&'] = '&'; 276 277 for ($c=160; $c<256; $c++) // can be increased to support more charsets 278 $xml_rep_table[chr($c)] = "&#$c;"; 279 280 $xml_rep_table['"'] = '"'; 281 $js_rep_table['"'] = '\\"'; 282 $js_rep_table["'"] = "\\'"; 283 $js_rep_table["\\"] = "\\\\"; 284 // Unicode line and paragraph separators (#1486310) 285 $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A8))] = '
'; 286 $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A9))] = '
'; 287 } 288 289 // encode for javascript use 290 if ($enctype=='js') 291 return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), strtr($str, $js_rep_table)); 292 293 // encode for plaintext 294 if ($enctype=='text') 295 return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); 296 297 if ($enctype=='url') 298 return rawurlencode($str); 299 300 // encode for XML 301 if ($enctype=='xml') 302 return strtr($str, $xml_rep_table); 303 304 // no encoding given -> return original string 305 return $str; 306 } 307 308 /** 309 * Quote a given string. 310 * Shortcut function for rep_specialchars_output 311 * 312 * @return string HTML-quoted string 313 * @see rep_specialchars_output() 314 */ 96 { 97 return rcube_ui::rep_specialchars_output($str, $enctype, $mode, $newlines); 98 } 99 315 100 function Q($str, $mode='strict', $newlines=TRUE) 316 { 317 return rep_specialchars_output($str, 'html', $mode, $newlines); 318 } 319 320 /** 321 * Quote a given string for javascript output. 322 * Shortcut function for rep_specialchars_output 323 * 324 * @return string JS-quoted string 325 * @see rep_specialchars_output() 326 */ 101 { 102 return rcube_ui::Q($str, $mode, $newlines); 103 } 104 327 105 function JQ($str) 328 { 329 return rep_specialchars_output($str, 'js'); 330 } 331 332 333 /** 334 * Read input value and convert it for internal use 335 * Performs stripslashes() and charset conversion if necessary 336 * 337 * @param string Field name to read 338 * @param int Source to get value from (GPC) 339 * @param boolean Allow HTML tags in field value 340 * @param string Charset to convert into 341 * @return string Field value or NULL if not available 342 */ 106 { 107 return rcube_ui::JQ($str); 108 } 109 343 110 function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL) 344 111 { 345 $value = NULL; 346 347 if ($source == RCUBE_INPUT_GET) { 348 if (isset($_GET[$fname])) 349 $value = $_GET[$fname]; 350 } 351 else if ($source == RCUBE_INPUT_POST) { 352 if (isset($_POST[$fname])) 353 $value = $_POST[$fname]; 354 } 355 else if ($source == RCUBE_INPUT_GPC) { 356 if (isset($_POST[$fname])) 357 $value = $_POST[$fname]; 358 else if (isset($_GET[$fname])) 359 $value = $_GET[$fname]; 360 else if (isset($_COOKIE[$fname])) 361 $value = $_COOKIE[$fname]; 362 } 363 364 return parse_input_value($value, $allow_html, $charset); 365 } 366 367 /** 368 * Parse/validate input value. See get_input_value() 369 * Performs stripslashes() and charset conversion if necessary 370 * 371 * @param string Input value 372 * @param boolean Allow HTML tags in field value 373 * @param string Charset to convert into 374 * @return string Parsed value 375 */ 112 return rcube_ui::get_input_value($fname, $source, $allow_html, $charset); 113 } 114 376 115 function parse_input_value($value, $allow_html=FALSE, $charset=NULL) 377 116 { 378 global $OUTPUT; 379 380 if (empty($value)) 381 return $value; 382 383 if (is_array($value)) { 384 foreach ($value as $idx => $val) 385 $value[$idx] = parse_input_value($val, $allow_html, $charset); 386 return $value; 387 } 388 389 // strip single quotes if magic_quotes_sybase is enabled 390 if (ini_get('magic_quotes_sybase')) 391 $value = str_replace("''", "'", $value); 392 // strip slashes if magic_quotes enabled 393 else if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) 394 $value = stripslashes($value); 395 396 // remove HTML tags if not allowed 397 if (!$allow_html) 398 $value = strip_tags($value); 399 400 $output_charset = is_object($OUTPUT) ? $OUTPUT->get_charset() : null; 401 402 // remove invalid characters (#1488124) 403 if ($output_charset == 'UTF-8') 404 $value = rc_utf8_clean($value); 405 406 // convert to internal charset 407 if ($charset && $output_charset) 408 $value = rcube_charset_convert($value, $output_charset, $charset); 409 410 return $value; 411 } 412 413 /** 414 * Convert array of request parameters (prefixed with _) 415 * to a regular array with non-prefixed keys. 416 * 417 * @param int Source to get value from (GPC) 418 * @return array Hash array with all request parameters 419 */ 117 return rcube_ui::parse_input_value($value, $allow_html, $charset); 118 } 119 420 120 function request2param($mode = RCUBE_INPUT_GPC, $ignore = 'task|action') 421 121 { 422 $out = array(); 423 $src = $mode == RCUBE_INPUT_GET ? $_GET : ($mode == RCUBE_INPUT_POST ? $_POST : $_REQUEST); 424 foreach ($src as $key => $value) { 425 $fname = $key[0] == '_' ? substr($key, 1) : $key; 426 if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) 427 $out[$fname] = get_input_value($key, $mode); 428 } 429 430 return $out; 431 } 432 433 /** 434 * Remove all non-ascii and non-word chars 435 * except ., -, _ 436 */ 437 function asciiwords($str, $css_id = false, $replace_with = '') 438 { 439 $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : ''); 440 return preg_replace("/[^$allowed]/i", $replace_with, $str); 441 } 442 443 /** 444 * Convert the given string into a valid HTML identifier 445 * Same functionality as done in app.js with rcube_webmail.html_identifier() 446 */ 122 return rcube_ui::request2param($mode, $ignore); 123 } 124 447 125 function html_identifier($str, $encode=false) 448 126 { 449 if ($encode) 450 return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); 451 else 452 return asciiwords($str, true, '_'); 453 } 454 455 /** 456 * Remove single and double quotes from given string 457 * 458 * @param string Input value 459 * @return string Dequoted string 460 */ 461 function strip_quotes($str) 462 { 463 return str_replace(array("'", '"'), '', $str); 464 } 465 466 467 /** 468 * Remove new lines characters from given string 469 * 470 * @param string Input value 471 * @return string Stripped string 472 */ 473 function strip_newlines($str) 474 { 475 return preg_replace('/[\r\n]/', '', $str); 476 } 477 478 479 /** 480 * Create a HTML table based on the given data 481 * 482 * @param array Named table attributes 483 * @param mixed Table row data. Either a two-dimensional array or a valid SQL result set 484 * @param array List of cols to show 485 * @param string Name of the identifier col 486 * @return string HTML table code 487 */ 127 return rcube_ui::html_identifier($str, $encode); 128 } 129 488 130 function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col) 489 131 { 490 global $RCMAIL; 491 492 $table = new html_table(/*array('cols' => count($a_show_cols))*/); 493 494 // add table header 495 if (!$attrib['noheader']) 496 foreach ($a_show_cols as $col) 497 $table->add_header($col, Q(rcube_label($col))); 498 499 $c = 0; 500 if (!is_array($table_data)) 501 { 502 $db = $RCMAIL->get_dbh(); 503 while ($table_data && ($sql_arr = $db->fetch_assoc($table_data))) 504 { 505 $table->add_row(array('id' => 'rcmrow' . html_identifier($sql_arr[$id_col]))); 506 507 // format each col 508 foreach ($a_show_cols as $col) 509 $table->add($col, Q($sql_arr[$col])); 510 511 $c++; 512 } 513 } 514 else { 515 foreach ($table_data as $row_data) 516 { 517 $class = !empty($row_data['class']) ? $row_data['class'] : ''; 518 519 $table->add_row(array('id' => 'rcmrow' . html_identifier($row_data[$id_col]), 'class' => $class)); 520 521 // format each col 522 foreach ($a_show_cols as $col) 523 $table->add($col, Q(is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col])); 524 525 $c++; 526 } 527 } 528 529 return $table->show($attrib); 530 } 531 532 533 /** 534 * Create an edit field for inclusion on a form 535 * 536 * @param string col field name 537 * @param string value field value 538 * @param array attrib HTML element attributes for field 539 * @param string type HTML element type (default 'text') 540 * @return string HTML field definition 541 */ 132 return rcube_ui::table_output($attrib, $table_data, $a_show_cols, $id_col); 133 } 134 542 135 function rcmail_get_edit_field($col, $value, $attrib, $type='text') 543 136 { 544 static $colcounts = array(); 545 546 $fname = '_'.$col; 547 $attrib['name'] = $fname . ($attrib['array'] ? '[]' : ''); 548 $attrib['class'] = trim($attrib['class'] . ' ff_' . $col); 549 550 if ($type == 'checkbox') { 551 $attrib['value'] = '1'; 552 $input = new html_checkbox($attrib); 553 } 554 else if ($type == 'textarea') { 555 $attrib['cols'] = $attrib['size']; 556 $input = new html_textarea($attrib); 557 } 558 else if ($type == 'select') { 559 $input = new html_select($attrib); 560 $input->add('---', ''); 561 $input->add(array_values($attrib['options']), array_keys($attrib['options'])); 562 } 563 else if ($attrib['type'] == 'password') { 564 $input = new html_passwordfield($attrib); 565 } 566 else { 567 if ($attrib['type'] != 'text' && $attrib['type'] != 'hidden') 568 $attrib['type'] = 'text'; 569 $input = new html_inputfield($attrib); 570 } 571 572 // use value from post 573 if (isset($_POST[$fname])) { 574 $postvalue = get_input_value($fname, RCUBE_INPUT_POST, true); 575 $value = $attrib['array'] ? $postvalue[intval($colcounts[$col]++)] : $postvalue; 576 } 577 578 $out = $input->show($value); 579 580 return $out; 581 } 582 583 584 /** 585 * Replace all css definitions with #container [def] 586 * and remove css-inlined scripting 587 * 588 * @param string CSS source code 589 * @param string Container ID to use as prefix 590 * @return string Modified CSS source 591 */ 137 return rcube_ui::get_edit_field($col, $value, $attrib, $type); 138 } 139 592 140 function rcmail_mod_css_styles($source, $container_id, $allow_remote=false) 593 { 594 $last_pos = 0; 595 $replacements = new rcube_string_replacer; 596 597 // ignore the whole block if evil styles are detected 598 $source = rcmail_xss_entity_decode($source); 599 $stripped = preg_replace('/[^a-z\(:;]/i', '', $source); 600 $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\(' : ''); 601 if (preg_match("/$evilexpr/i", $stripped)) 602 return '/* evil! */'; 603 604 // cut out all contents between { and } 605 while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) { 606 $styles = substr($source, $pos+1, $pos2-($pos+1)); 607 608 // check every line of a style block... 609 if ($allow_remote) { 610 $a_styles = preg_split('/;[\r\n]*/', $styles, -1, PREG_SPLIT_NO_EMPTY); 611 foreach ($a_styles as $line) { 612 $stripped = preg_replace('/[^a-z\(:;]/i', '', $line); 613 // ... and only allow strict url() values 614 if (stripos($stripped, 'url(') && !preg_match('!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims', $line)) { 615 $a_styles = array('/* evil! */'); 616 break; 617 } 618 } 619 $styles = join(";\n", $a_styles); 620 } 621 622 $key = $replacements->add($styles); 623 $source = substr($source, 0, $pos+1) . $replacements->get_replacement($key) . substr($source, $pos2, strlen($source)-$pos2); 624 $last_pos = $pos+2; 625 } 626 627 // remove html comments and add #container to each tag selector. 628 // also replace body definition because we also stripped off the <body> tag 629 $styles = preg_replace( 630 array( 631 '/(^\s*<!--)|(-->\s*$)/', 632 '/(^\s*|,\s*|\}\s*)([a-z0-9\._#\*][a-z0-9\.\-_]*)/im', 633 '/'.preg_quote($container_id, '/').'\s+body/i', 634 ), 635 array( 636 '', 637 "\\1#$container_id \\2", 638 $container_id, 639 ), 640 $source); 641 642 // put block contents back in 643 $styles = $replacements->resolve($styles); 644 645 return $styles; 646 } 647 648 649 /** 650 * Decode escaped entities used by known XSS exploits. 651 * See http://downloads.securityfocus.com/vulnerabilities/exploits/26800.eml for examples 652 * 653 * @param string CSS content to decode 654 * @return string Decoded string 655 */ 141 { 142 return rcube_ui::mod_css_styles($source, $container_id, $allow_remote); 143 } 144 656 145 function rcmail_xss_entity_decode($content) 657 146 { 658 $out = html_entity_decode(html_entity_decode($content)); 659 $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', 'rcmail_xss_entity_decode_callback', $out); 660 $out = preg_replace('#/\*.*\*/#Ums', '', $out); 661 return $out; 662 } 663 664 665 /** 666 * preg_replace_callback callback for rcmail_xss_entity_decode_callback 667 * 668 * @param array matches result from preg_replace_callback 669 * @return string decoded entity 670 */ 671 function rcmail_xss_entity_decode_callback($matches) 672 { 673 return chr(hexdec($matches[1])); 674 } 675 676 /** 677 * Compose a valid attribute string for HTML tags 678 * 679 * @param array Named tag attributes 680 * @param array List of allowed attributes 681 * @return string HTML formatted attribute string 682 */ 147 return rcube_ui::xss_entity_decode($content); 148 } 149 683 150 function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style')) 684 { 685 // allow the following attributes to be added to the <iframe> tag 686 $attrib_str = ''; 687 foreach ($allowed_attribs as $a) 688 if (isset($attrib[$a])) 689 $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '"', $attrib[$a])); 690 691 return $attrib_str; 692 } 693 694 695 /** 696 * Convert a HTML attribute string attributes to an associative array (name => value) 697 * 698 * @param string Input string 699 * @return array Key-value pairs of parsed attributes 700 */ 151 { 152 return html::attrib_string($attrib, $allowed_attribs); 153 } 154 701 155 function parse_attrib_string($str) 702 { 703 $attrib = array(); 704 preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER); 705 706 // convert attributes to an associative array (name => value) 707 if ($regs) { 708 foreach ($regs as $attr) { 709 $attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]); 710 } 711 } 712 713 return $attrib; 714 } 715 716 717 /** 718 * Improved equivalent to strtotime() 719 * 720 * @param string Date string 721 * @return int 722 */ 723 function rcube_strtotime($date) 724 { 725 // check for MS Outlook vCard date format YYYYMMDD 726 if (preg_match('/^([12][90]\d\d)([01]\d)(\d\d)$/', trim($date), $matches)) { 727 return mktime(0,0,0, intval($matches[2]), intval($matches[3]), intval($matches[1])); 728 } 729 else if (is_numeric($date)) 730 return $date; 731 732 // support non-standard "GMTXXXX" literal 733 $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date); 734 735 // if date parsing fails, we have a date in non-rfc format. 736 // remove token from the end and try again 737 while ((($ts = @strtotime($date)) === false) || ($ts < 0)) { 738 $d = explode(' ', $date); 739 array_pop($d); 740 if (!$d) break; 741 $date = implode(' ', $d); 742 } 743 744 return $ts; 745 } 746 747 748 /** 749 * Convert the given date to a human readable form 750 * This uses the date formatting properties from config 751 * 752 * @param mixed Date representation (string or timestamp) 753 * @param string Date format to use 754 * @param bool Enables date convertion according to user timezone 755 * 756 * @return string Formatted date string 757 */ 156 { 157 return html::parse_attrib_string($str); 158 } 159 758 160 function format_date($date, $format=NULL, $convert=true) 759 161 { 760 global $RCMAIL, $CONFIG; 761 762 if (!empty($date)) 763 $ts = rcube_strtotime($date); 764 765 if (empty($ts)) 766 return ''; 767 768 try { 769 $date = new DateTime("@".$ts); 770 } 771 catch (Exception $e) { 772 return ''; 773 } 774 775 try { 776 // convert to the right timezone 777 $stz = date_default_timezone_get(); 778 $tz = new DateTimeZone($convert ? $RCMAIL->config->get('timezone') : 'GMT'); 779 $date->setTimezone($tz); 780 date_default_timezone_set($tz->getName()); 781 782 $timestamp = $date->format('U'); 783 } 784 catch (Exception $e) { 785 $timestamp = $ts; 786 } 787 788 // define date format depending on current time 789 if (!$format) { 790 $now_date = getdate($now); 791 $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']); 792 $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']); 793 794 if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now) { 795 $format = $RCMAIL->config->get('date_today', $RCMAIL->config->get('time_format', 'H:i')); 796 $today = true; 797 } 798 else if ($CONFIG['prettydate'] && $timestamp > $week_limit && $timestamp < $now) 799 $format = $RCMAIL->config->get('date_short', 'D H:i'); 800 else 801 $format = $RCMAIL->config->get('date_long', 'Y-m-d H:i'); 802 } 803 804 // strftime() format 805 if (preg_match('/%[a-z]+/i', $format)) { 806 $format = strftime($format, $timestamp); 807 date_default_timezone_set($stz); 808 return $today ? (rcube_label('today') . ' ' . $format) : $format; 809 } 810 811 // parse format string manually in order to provide localized weekday and month names 812 // an alternative would be to convert the date() format string to fit with strftime() 813 $out = ''; 814 for($i=0; $i<strlen($format); $i++) { 815 if ($format[$i]=='\\') // skip escape chars 816 continue; 817 818 // write char "as-is" 819 if ($format[$i]==' ' || $format{$i-1}=='\\') 820 $out .= $format[$i]; 821 // weekday (short) 822 else if ($format[$i]=='D') 823 $out .= rcube_label(strtolower($date->format('D'))); 824 // weekday long 825 else if ($format[$i]=='l') 826 $out .= rcube_label(strtolower($date->format('l'))); 827 // month name (short) 828 else if ($format[$i]=='M') 829 $out .= rcube_label(strtolower($date->format('M'))); 830 // month name (long) 831 else if ($format[$i]=='F') 832 $out .= rcube_label('long'.strtolower($date->format('M'))); 833 else if ($format[$i]=='x') 834 $out .= strftime('%x %X', $timestamp); 835 else 836 $out .= $date->format($format[$i]); 837 } 838 839 if ($today) { 840 $label = rcube_label('today'); 841 // replcae $ character with "Today" label (#1486120) 842 if (strpos($out, '$') !== false) { 843 $out = preg_replace('/\$/', $label, $out, 1); 844 } 845 else { 846 $out = $label . ' ' . $out; 847 } 848 } 849 850 date_default_timezone_set($stz); 851 return $out; 852 } 853 854 855 /** 856 * Compose a valid representation of name and e-mail address 857 * 858 * @param string E-mail address 859 * @param string Person name 860 * @return string Formatted string 861 */ 862 function format_email_recipient($email, $name='') 863 { 864 if ($name && $name != $email) { 865 // Special chars as defined by RFC 822 need to in quoted string (or escaped). 866 return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, trim($email)); 867 } 868 869 return trim($email); 870 } 871 872 873 /** 874 * Return the mailboxlist in HTML 875 * 876 * @param array Named parameters 877 * @return string HTML code for the gui object 878 */ 162 return rcube_ui::format_date($date, $format, $convert); 163 } 164 879 165 function rcmail_mailbox_list($attrib) 880 166 { 881 global $RCMAIL; 882 static $a_mailboxes; 883 884 $attrib += array('maxlength' => 100, 'realnames' => false, 'unreadwrap' => ' (%s)'); 885 886 // add some labels to client 887 $RCMAIL->output->add_label('purgefolderconfirm', 'deletemessagesconfirm'); 888 889 $type = $attrib['type'] ? $attrib['type'] : 'ul'; 890 unset($attrib['type']); 891 892 if ($type=='ul' && !$attrib['id']) 893 $attrib['id'] = 'rcmboxlist'; 894 895 if (empty($attrib['folder_name'])) 896 $attrib['folder_name'] = '*'; 897 898 // get mailbox list 899 $mbox_name = $RCMAIL->storage->get_folder(); 900 901 // build the folders tree 902 if (empty($a_mailboxes)) { 903 // get mailbox list 904 $a_folders = $RCMAIL->storage->list_folders_subscribed('', $attrib['folder_name'], $attrib['folder_filter']); 905 $delimiter = $RCMAIL->storage->get_hierarchy_delimiter(); 906 $a_mailboxes = array(); 907 908 foreach ($a_folders as $folder) 909 rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter); 910 } 911 912 // allow plugins to alter the folder tree or to localize folder names 913 $hook = $RCMAIL->plugins->exec_hook('render_mailboxlist', array('list' => $a_mailboxes, 'delimiter' => $delimiter)); 914 915 if ($type == 'select') { 916 $select = new html_select($attrib); 917 918 // add no-selection option 919 if ($attrib['noselection']) 920 $select->add(rcube_label($attrib['noselection']), ''); 921 922 rcmail_render_folder_tree_select($hook['list'], $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']); 923 $out = $select->show(); 924 } 925 else { 926 $js_mailboxlist = array(); 927 $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($hook['list'], $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib); 928 929 $RCMAIL->output->add_gui_object('mailboxlist', $attrib['id']); 930 $RCMAIL->output->set_env('mailboxes', $js_mailboxlist); 931 $RCMAIL->output->set_env('unreadwrap', $attrib['unreadwrap']); 932 $RCMAIL->output->set_env('collapsed_folders', (string)$RCMAIL->config->get('collapsed_folders')); 933 } 934 935 return $out; 936 } 937 938 939 /** 940 * Return the mailboxlist as html_select object 941 * 942 * @param array Named parameters 943 * @return html_select HTML drop-down object 944 */ 945 function rcmail_mailbox_select($p = array()) 946 { 947 global $RCMAIL; 948 949 $p += array('maxlength' => 100, 'realnames' => false); 950 $a_mailboxes = array(); 951 $storage = $RCMAIL->get_storage(); 952 953 if (empty($p['folder_name'])) { 954 $p['folder_name'] = '*'; 955 } 956 957 if ($p['unsubscribed']) 958 $list = $storage->list_folders('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']); 959 else 960 $list = $storage->list_folders_subscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']); 961 962 $delimiter = $storage->get_hierarchy_delimiter(); 963 964 foreach ($list as $folder) { 965 if (empty($p['exceptions']) || !in_array($folder, $p['exceptions'])) 966 rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter); 967 } 968 969 $select = new html_select($p); 970 971 if ($p['noselection']) 972 $select->add($p['noselection'], ''); 973 974 rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p); 975 976 return $select; 977 } 978 979 980 /** 981 * Create a hierarchical array of the mailbox list 982 * @access private 983 * @return void 984 */ 985 function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='') 986 { 987 global $RCMAIL; 988 989 // Handle namespace prefix 990 $prefix = ''; 991 if (!$path) { 992 $n_folder = $folder; 993 $folder = $RCMAIL->storage->mod_folder($folder); 994 995 if ($n_folder != $folder) { 996 $prefix = substr($n_folder, 0, -strlen($folder)); 997 } 998 } 999 1000 $pos = strpos($folder, $delm); 1001 1002 if ($pos !== false) { 1003 $subFolders = substr($folder, $pos+1); 1004 $currentFolder = substr($folder, 0, $pos); 1005 1006 // sometimes folder has a delimiter as the last character 1007 if (!strlen($subFolders)) 1008 $virtual = false; 1009 else if (!isset($arrFolders[$currentFolder])) 1010 $virtual = true; 1011 else 1012 $virtual = $arrFolders[$currentFolder]['virtual']; 1013 } 1014 else { 1015 $subFolders = false; 1016 $currentFolder = $folder; 1017 $virtual = false; 1018 } 1019 1020 $path .= $prefix.$currentFolder; 1021 1022 if (!isset($arrFolders[$currentFolder])) { 1023 $arrFolders[$currentFolder] = array( 1024 'id' => $path, 1025 'name' => rcube_charset_convert($currentFolder, 'UTF7-IMAP'), 1026 'virtual' => $virtual, 1027 'folders' => array()); 1028 } 1029 else 1030 $arrFolders[$currentFolder]['virtual'] = $virtual; 1031 1032 if (strlen($subFolders)) 1033 rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm); 1034 } 1035 1036 1037 /** 1038 * Return html for a structured list <ul> for the mailbox tree 1039 * @access private 1040 * @return string 1041 */ 1042 function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel=0) 1043 { 1044 global $RCMAIL, $CONFIG; 1045 1046 $maxlength = intval($attrib['maxlength']); 1047 $realnames = (bool)$attrib['realnames']; 1048 $msgcounts = $RCMAIL->storage->get_cache('messagecount'); 1049 1050 $out = ''; 1051 foreach ($arrFolders as $key => $folder) { 1052 $title = null; 1053 $folder_class = rcmail_folder_classname($folder['id']); 1054 $collapsed = strpos($CONFIG['collapsed_folders'], '&'.rawurlencode($folder['id']).'&') !== false; 1055 $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0; 1056 1057 if ($folder_class && !$realnames) { 1058 $foldername = rcube_label($folder_class); 1059 } 1060 else { 1061 $foldername = $folder['name']; 1062 1063 // shorten the folder name to a given length 1064 if ($maxlength && $maxlength > 1) { 1065 $fname = abbreviate_string($foldername, $maxlength); 1066 if ($fname != $foldername) 1067 $title = $foldername; 1068 $foldername = $fname; 1069 } 1070 } 1071 1072 // make folder name safe for ids and class names 1073 $folder_id = html_identifier($folder['id'], true); 1074 $classes = array('mailbox'); 1075 1076 // set special class for Sent, Drafts, Trash and Junk 1077 if ($folder_class) 1078 $classes[] = $folder_class; 1079 1080 if ($folder['id'] == $mbox_name) 1081 $classes[] = 'selected'; 1082 1083 if ($folder['virtual']) 1084 $classes[] = 'virtual'; 1085 else if ($unread) 1086 $classes[] = 'unread'; 1087 1088 $js_name = JQ($folder['id']); 1089 $html_name = Q($foldername) . ($unread ? html::span('unreadcount', sprintf($attrib['unreadwrap'], $unread)) : ''); 1090 $link_attrib = $folder['virtual'] ? array() : array( 1091 'href' => rcmail_url('', array('_mbox' => $folder['id'])), 1092 'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name), 1093 'rel' => $folder['id'], 1094 'title' => $title, 1095 ); 1096 1097 $out .= html::tag('li', array( 1098 'id' => "rcmli".$folder_id, 1099 'class' => join(' ', $classes), 1100 'noclose' => true), 1101 html::a($link_attrib, $html_name) . 1102 (!empty($folder['folders']) ? html::div(array( 1103 'class' => ($collapsed ? 'collapsed' : 'expanded'), 1104 'style' => "position:absolute", 1105 'onclick' => sprintf("%s.command('collapse-folder', '%s')", JS_OBJECT_NAME, $js_name) 1106 ), ' ') : '')); 1107 1108 $jslist[$folder_id] = array('id' => $folder['id'], 'name' => $foldername, 'virtual' => $folder['virtual']); 1109 1110 if (!empty($folder['folders'])) { 1111 $out .= html::tag('ul', array('style' => ($collapsed ? "display:none;" : null)), 1112 rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1)); 1113 } 1114 1115 $out .= "</li>\n"; 1116 } 1117 1118 return $out; 1119 } 1120 1121 1122 /** 1123 * Return html for a flat list <select> for the mailbox tree 1124 * @access private 1125 * @return string 1126 */ 1127 function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0, $opts=array()) 1128 { 1129 global $RCMAIL; 1130 1131 $out = ''; 1132 1133 foreach ($arrFolders as $key => $folder) { 1134 // skip exceptions (and its subfolders) 1135 if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) { 1136 continue; 1137 } 1138 1139 // skip folders in which it isn't possible to create subfolders 1140 if (!empty($opts['skip_noinferiors']) && ($attrs = $RCMAIL->storage->folder_attributes($folder['id'])) 1141 && in_array('\\Noinferiors', $attrs) 1142 ) { 1143 continue; 1144 } 1145 1146 if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id']))) 1147 $foldername = rcube_label($folder_class); 1148 else { 1149 $foldername = $folder['name']; 1150 1151 // shorten the folder name to a given length 1152 if ($maxlength && $maxlength>1) 1153 $foldername = abbreviate_string($foldername, $maxlength); 1154 } 1155 1156 $select->add(str_repeat(' ', $nestLevel*4) . $foldername, $folder['id']); 1157 1158 if (!empty($folder['folders'])) 1159 $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, 1160 $select, $realnames, $nestLevel+1, $opts); 1161 } 1162 1163 return $out; 1164 } 1165 1166 1167 /** 1168 * Return internal name for the given folder if it matches the configured special folders 1169 * @access private 1170 * @return string 1171 */ 1172 function rcmail_folder_classname($folder_id) 1173 { 1174 global $CONFIG; 1175 1176 if ($folder_id == 'INBOX') 1177 return 'inbox'; 1178 1179 // for these mailboxes we have localized labels and css classes 1180 foreach (array('sent', 'drafts', 'trash', 'junk') as $smbx) 1181 { 1182 if ($folder_id == $CONFIG[$smbx.'_mbox']) 1183 return $smbx; 1184 } 1185 } 1186 1187 1188 /** 1189 * Try to localize the given IMAP folder name. 1190 * UTF-7 decode it in case no localized text was found 1191 * 1192 * @param string Folder name 1193 * @return string Localized folder name in UTF-8 encoding 1194 */ 167 return rcube_ui::folder_list($attrib); 168 } 169 170 function rcmail_mailbox_select($attrib = array()) 171 { 172 return rcube_ui::folder_selector($attrib); 173 } 174 1195 175 function rcmail_localize_foldername($name) 1196 176 { 1197 if ($folder_class = rcmail_folder_classname($name)) 1198 return rcube_label($folder_class); 1199 else 1200 return rcube_charset_convert($name, 'UTF7-IMAP'); 1201 } 1202 177 return rcube_ui::localize_foldername($name); 178 } 1203 179 1204 180 function rcmail_localize_folderpath($path) 1205 181 { 1206 global $RCMAIL; 1207 1208 $protect_folders = $RCMAIL->config->get('protect_default_folders'); 1209 $default_folders = (array) $RCMAIL->config->get('default_folders'); 1210 $delimiter = $RCMAIL->storage->get_hierarchy_delimiter(); 1211 $path = explode($delimiter, $path); 1212 $result = array(); 1213 1214 foreach ($path as $idx => $dir) { 1215 $directory = implode($delimiter, array_slice($path, 0, $idx+1)); 1216 if ($protect_folders && in_array($directory, $default_folders)) { 1217 unset($result); 1218 $result[] = rcmail_localize_foldername($directory); 1219 } 1220 else { 1221 $result[] = rcube_charset_convert($dir, 'UTF7-IMAP'); 1222 } 1223 } 1224 1225 return implode($delimiter, $result); 1226 } 1227 182 return rcube_ui::localize_folderpath($path); 183 } 1228 184 1229 185 function rcmail_quota_display($attrib) 1230 186 { 1231 global $OUTPUT; 1232 1233 if (!$attrib['id']) 1234 $attrib['id'] = 'rcmquotadisplay'; 1235 1236 if(isset($attrib['display'])) 1237 $_SESSION['quota_display'] = $attrib['display']; 1238 1239 $OUTPUT->add_gui_object('quotadisplay', $attrib['id']); 1240 1241 $quota = rcmail_quota_content($attrib); 1242 1243 $OUTPUT->add_script('rcmail.set_quota('.json_serialize($quota).');', 'docready'); 1244 1245 return html::span($attrib, ''); 1246 } 1247 1248 1249 function rcmail_quota_content($attrib=NULL) 1250 { 1251 global $RCMAIL; 1252 1253 $quota = $RCMAIL->storage->get_quota(); 1254 $quota = $RCMAIL->plugins->exec_hook('quota', $quota); 1255 1256 $quota_result = (array) $quota; 1257 $quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : ''; 1258 1259 if (!$quota['total'] && $RCMAIL->config->get('quota_zero_as_unlimited')) { 1260 $quota_result['title'] = rcube_label('unlimited'); 1261 $quota_result['percent'] = 0; 1262 } 1263 else if ($quota['total']) { 1264 if (!isset($quota['percent'])) 1265 $quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100)); 1266 1267 $title = sprintf('%s / %s (%.0f%%)', 1268 show_bytes($quota['used'] * 1024), show_bytes($quota['total'] * 1024), 1269 $quota_result['percent']); 1270 1271 $quota_result['title'] = $title; 1272 1273 if ($attrib['width']) 1274 $quota_result['width'] = $attrib['width']; 1275 if ($attrib['height']) 1276 $quota_result['height'] = $attrib['height']; 1277 } 1278 else { 1279 $quota_result['title'] = rcube_label('unknown'); 1280 $quota_result['percent'] = 0; 1281 } 1282 1283 return $quota_result; 1284 } 1285 1286 1287 /** 1288 * Outputs error message according to server error/response codes 1289 * 1290 * @param string Fallback message label 1291 * @param string Fallback message label arguments 1292 * 1293 * @return void 1294 */ 187 return rcube_ui::quota_display($attrib); 188 } 189 190 function rcmail_quota_content($attrib = null) 191 { 192 return rcube_ui::quota_content($attrib); 193 } 194 1295 195 function rcmail_display_server_error($fallback=null, $fallback_args=null) 1296 196 { 1297 global $RCMAIL; 1298 1299 $err_code = $RCMAIL->storage->get_error_code(); 1300 $res_code = $RCMAIL->storage->get_response_code(); 1301 1302 if ($err_code < 0) { 1303 $RCMAIL->output->show_message('storageerror', 'error'); 1304 } 1305 else if ($res_code == rcube_storage::NOPERM) { 1306 $RCMAIL->output->show_message('errornoperm', 'error'); 1307 } 1308 else if ($res_code == rcube_storage::READONLY) { 1309 $RCMAIL->output->show_message('errorreadonly', 'error'); 1310 } 1311 else if ($err_code && ($err_str = $RCMAIL->storage->get_error_str())) { 1312 // try to detect access rights problem and display appropriate message 1313 if (stripos($err_str, 'Permission denied') !== false) 1314 $RCMAIL->output->show_message('errornoperm', 'error'); 1315 else 1316 $RCMAIL->output->show_message('servererrormsg', 'error', array('msg' => $err_str)); 1317 } 1318 else if ($fallback) { 1319 $RCMAIL->output->show_message($fallback, 'error', $fallback_args); 1320 } 1321 1322 return true; 1323 } 1324 1325 1326 /** 1327 * Generate CSS classes from mimetype and filename extension 1328 * 1329 * @param string Mimetype 1330 * @param string The filename 1331 * @return string CSS classes separated by space 1332 */ 197 rcube_ui::display_server_error($fallback, $fallback_args); 198 } 199 1333 200 function rcmail_filetype2classname($mimetype, $filename) 1334 201 { 1335 list($primary, $secondary) = explode('/', $mimetype); 1336 1337 $classes = array($primary ? $primary : 'unknown'); 1338 if ($secondary) 1339 $classes[] = $secondary; 1340 if (preg_match('/\.([a-z0-9]+)$/', $filename, $m)) 1341 $classes[] = $m[1]; 1342 1343 return join(" ", $classes); 1344 } 1345 1346 /** 1347 * Output HTML editor scripts 1348 * 1349 * @param string Editor mode 1350 * @return void 1351 */ 202 return rcube_ui::file2class($mimetype, $filename); 203 } 204 1352 205 function rcube_html_editor($mode='') 1353 206 { 1354 global $RCMAIL; 1355 1356 $hook = $RCMAIL->plugins->exec_hook('html_editor', array('mode' => $mode)); 1357 1358 if ($hook['abort']) 1359 return; 1360 1361 $lang = strtolower($_SESSION['language']); 1362 1363 // TinyMCE uses two-letter lang codes, with exception of Chinese 1364 if (strpos($lang, 'zh_') === 0) 1365 $lang = str_replace('_', '-', $lang); 1366 else 1367 $lang = substr($lang, 0, 2); 1368 1369 if (!file_exists(INSTALL_PATH . 'program/js/tiny_mce/langs/'.$lang.'.js')) 1370 $lang = 'en'; 1371 1372 $RCMAIL->output->include_script('tiny_mce/tiny_mce.js'); 1373 $RCMAIL->output->include_script('editor.js'); 1374 $RCMAIL->output->add_script(sprintf("rcmail_editor_init(%s)", 1375 json_encode(array( 1376 'mode' => $mode, 1377 'skin_path' => '$__skin_path', 1378 'lang' => $lang, 1379 'spellcheck' => intval($RCMAIL->config->get('enable_spellcheck')), 1380 'spelldict' => intval($RCMAIL->config->get('spellcheck_dictionary')), 1381 ))), 'foot'); 1382 } 1383 1384 1385 /** 1386 * Replaces TinyMCE's emoticon images with plain-text representation 1387 * 1388 * @param string HTML content 1389 * @return string HTML content 1390 */ 207 rcube_ui::html_editor($mode); 208 } 209 1391 210 function rcmail_replace_emoticons($html) 1392 211 { 1393 $emoticons = array( 1394 '8-)' => 'smiley-cool', 1395 ':-#' => 'smiley-foot-in-mouth', 1396 ':-*' => 'smiley-kiss', 1397 ':-X' => 'smiley-sealed', 1398 ':-P' => 'smiley-tongue-out', 1399 ':-@' => 'smiley-yell', 1400 ":'(" => 'smiley-cry', 1401 ':-(' => 'smiley-frown', 1402 ':-D' => 'smiley-laughing', 1403 ':-)' => 'smiley-smile', 1404 ':-S' => 'smiley-undecided', 1405 ':-$' => 'smiley-embarassed', 1406 'O:-)' => 'smiley-innocent', 1407 ':-|' => 'smiley-money-mouth', 1408 ':-O' => 'smiley-surprised', 1409 ';-)' => 'smiley-wink', 1410 ); 1411 1412 foreach ($emoticons as $idx => $file) { 1413 // <img title="Cry" src="http://.../program/js/tiny_mce/plugins/emotions/img/smiley-cry.gif" border="0" alt="Cry" /> 1414 $search[] = '/<img title="[a-z ]+" src="https?:\/\/[a-z0-9_.\/-]+\/tiny_mce\/plugins\/emotions\/img\/'.$file.'.gif"[^>]+\/>/i'; 1415 $replace[] = $idx; 1416 } 1417 1418 return preg_replace($search, $replace, $html); 1419 } 1420 1421 1422 /** 1423 * Send the given message using the configured method 1424 * 1425 * @param object $message Reference to Mail_MIME object 1426 * @param string $from Sender address string 1427 * @param array $mailto Array of recipient address strings 1428 * @param array $smtp_error SMTP error array (reference) 1429 * @param string $body_file Location of file with saved message body (reference), 1430 * used when delay_file_io is enabled 1431 * @param array $smtp_opts SMTP options (e.g. DSN request) 1432 * 1433 * @return boolean Send status. 1434 */ 212 return rcube_ui::replace_emoticons($html); 213 } 214 1435 215 function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file=null, $smtp_opts=null) 1436 216 { 1437 global $CONFIG, $RCMAIL; 1438 1439 $headers = $message->headers(); 1440 1441 // send thru SMTP server using custom SMTP library 1442 if ($CONFIG['smtp_server']) { 1443 // generate list of recipients 1444 $a_recipients = array($mailto); 1445 1446 if (strlen($headers['Cc'])) 1447 $a_recipients[] = $headers['Cc']; 1448 if (strlen($headers['Bcc'])) 1449 $a_recipients[] = $headers['Bcc']; 1450 1451 // clean Bcc from header for recipients 1452 $send_headers = $headers; 1453 unset($send_headers['Bcc']); 1454 // here too, it because txtHeaders() below use $message->_headers not only $send_headers 1455 unset($message->_headers['Bcc']); 1456 1457 $smtp_headers = $message->txtHeaders($send_headers, true); 1458 1459 if ($message->getParam('delay_file_io')) { 1460 // use common temp dir 1461 $temp_dir = $RCMAIL->config->get('temp_dir'); 1462 $body_file = tempnam($temp_dir, 'rcmMsg'); 1463 if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) { 1464 raise_error(array('code' => 650, 'type' => 'php', 1465 'file' => __FILE__, 'line' => __LINE__, 1466 'message' => "Could not create message: ".$mime_result->getMessage()), 1467 TRUE, FALSE); 1468 return false; 1469 } 1470 $msg_body = fopen($body_file, 'r'); 1471 } else { 1472 $msg_body = $message->get(); 1473 } 1474 1475 // send message 1476 if (!is_object($RCMAIL->smtp)) 1477 $RCMAIL->smtp_init(true); 1478 1479 $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $smtp_opts); 1480 $smtp_response = $RCMAIL->smtp->get_response(); 1481 $smtp_error = $RCMAIL->smtp->get_error(); 1482 1483 // log error 1484 if (!$sent) 1485 raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__, 1486 'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE); 1487 } 1488 // send mail using PHP's mail() function 1489 else { 1490 // unset some headers because they will be added by the mail() function 1491 $headers_enc = $message->headers($headers); 1492 $headers_php = $message->_headers; 1493 unset($headers_php['To'], $headers_php['Subject']); 1494 1495 // reset stored headers and overwrite 1496 $message->_headers = array(); 1497 $header_str = $message->txtHeaders($headers_php); 1498 1499 // #1485779 1500 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 1501 if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) { 1502 $headers_enc['To'] = implode(', ', $m[1]); 1503 } 1504 } 1505 1506 $msg_body = $message->get(); 1507 1508 if (PEAR::isError($msg_body)) 1509 raise_error(array('code' => 650, 'type' => 'php', 1510 'file' => __FILE__, 'line' => __LINE__, 1511 'message' => "Could not create message: ".$msg_body->getMessage()), 1512 TRUE, FALSE); 1513 else { 1514 $delim = $RCMAIL->config->header_delimiter(); 1515 $to = $headers_enc['To']; 1516 $subject = $headers_enc['Subject']; 1517 $header_str = rtrim($header_str); 1518 1519 if ($delim != "\r\n") { 1520 $header_str = str_replace("\r\n", $delim, $header_str); 1521 $msg_body = str_replace("\r\n", $delim, $msg_body); 1522 $to = str_replace("\r\n", $delim, $to); 1523 $subject = str_replace("\r\n", $delim, $subject); 1524 } 1525 1526 if (ini_get('safe_mode')) 1527 $sent = mail($to, $subject, $msg_body, $header_str); 1528 else 1529 $sent = mail($to, $subject, $msg_body, $header_str, "-f$from"); 1530 } 1531 } 1532 1533 if ($sent) { 1534 $RCMAIL->plugins->exec_hook('message_sent', array('headers' => $headers, 'body' => $msg_body)); 1535 1536 // remove MDN headers after sending 1537 unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']); 1538 1539 // get all recipients 1540 if ($headers['Cc']) 1541 $mailto .= $headers['Cc']; 1542 if ($headers['Bcc']) 1543 $mailto .= $headers['Bcc']; 1544 if (preg_match_all('/<([^@]+@[^>]+)>/', $mailto, $m)) 1545 $mailto = implode(', ', array_unique($m[1])); 1546 1547 if ($CONFIG['smtp_log']) { 1548 write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s", 1549 $RCMAIL->user->get_username(), 1550 $_SERVER['REMOTE_ADDR'], 1551 $mailto, 1552 !empty($smtp_response) ? join('; ', $smtp_response) : '')); 1553 } 1554 } 1555 1556 if (is_resource($msg_body)) { 1557 fclose($msg_body); 1558 } 1559 1560 $message->_headers = array(); 1561 $message->headers($headers); 1562 1563 return $sent; 1564 } 1565 1566 1567 // Returns unique Message-ID 217 return rcmail::get_instance()->deliver_message($message, $from, $mailto, $smtp_error, $body_file, $smtp_opts); 218 } 219 1568 220 function rcmail_gen_message_id() 1569 221 { 1570 global $RCMAIL; 1571 1572 $local_part = md5(uniqid('rcmail'.mt_rand(),true)); 1573 $domain_part = $RCMAIL->user->get_username('domain'); 1574 1575 // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924) 1576 if (!preg_match('/\.[a-z]+$/i', $domain_part)) { 1577 if (($host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST'])) 1578 && preg_match('/\.[a-z]+$/i', $host)) { 1579 $domain_part = $host; 1580 } 1581 else if (($host = preg_replace('/:[0-9]+$/', '', $_SERVER['SERVER_NAME'])) 1582 && preg_match('/\.[a-z]+$/i', $host)) { 1583 $domain_part = $host; 1584 } 1585 } 1586 1587 return sprintf('<%s@%s>', $local_part, $domain_part); 1588 } 1589 1590 1591 // Returns RFC2822 formatted current date in user's timezone 222 return rcmail::get_instance()->gen_message_id(); 223 } 224 1592 225 function rcmail_user_date() 1593 226 { 1594 global $RCMAIL; 1595 1596 // get user's timezone 1597 try { 1598 $tz = new DateTimeZone($RCMAIL->config->get('timezone')); 1599 $date = new DateTime('now', $tz); 1600 } 1601 catch (Exception $e) { 1602 $date = new DateTime(); 1603 } 1604 1605 return $date->format('r'); 1606 } 1607 1608 1609 /** 1610 * Check if we can process not exceeding memory_limit 1611 * 1612 * @param integer Required amount of memory 1613 * @return boolean 1614 */ 227 return rcmail::get_instance()->user_date(); 228 } 229 1615 230 function rcmail_mem_check($need) 1616 231 { 1617 $mem_limit = parse_bytes(ini_get('memory_limit')); 1618 $memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB 1619 1620 return $mem_limit && $memory + $need > $mem_limit ? false : true; 1621 } 1622 1623 1624 /** 1625 * Check if working in SSL mode 1626 * 1627 * @param integer HTTPS port number 1628 * @param boolean Enables 'use_https' option checking 1629 * @return boolean 1630 */ 232 return rcube_ui::mem_check($need); 233 } 234 1631 235 function rcube_https_check($port=null, $use_https=true) 1632 236 { 1633 global $RCMAIL; 1634 1635 if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') 1636 return true; 1637 if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') 1638 return true; 1639 if ($port && $_SERVER['SERVER_PORT'] == $port) 1640 return true; 1641 if ($use_https && isset($RCMAIL) && $RCMAIL->config->get('use_https')) 1642 return true; 1643 1644 return false; 1645 } 1646 1647 1648 /** 1649 * For backward compatibility. 1650 * 1651 * @global rcmail $RCMAIL 1652 * @param string $var_name Variable name. 1653 * @return void 1654 */ 237 return rcube_ui::https_check($port, $use_https); 238 } 239 1655 240 function rcube_sess_unset($var_name=null) 1656 241 { 1657 global $RCMAIL; 1658 1659 $RCMAIL->session->remove($var_name); 1660 } 1661 1662 1663 /** 1664 * Replaces hostname variables 1665 * 1666 * @param string $name Hostname 1667 * @param string $host Optional IMAP hostname 1668 * @return string 1669 */ 242 rcmail::get_instance()->session->remove($var_name); 243 } 244 1670 245 function rcube_parse_host($name, $host='') 1671 246 { 1672 // %n - host 1673 $n = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']); 1674 // %d - domain name without first part, e.g. %n=mail.domain.tld, %d=domain.tld 1675 $d = preg_replace('/^[^\.]+\./', '', $n); 1676 // %h - IMAP host 1677 $h = $_SESSION['storage_host'] ? $_SESSION['storage_host'] : $host; 1678 // %z - IMAP domain without first part, e.g. %h=imap.domain.tld, %z=domain.tld 1679 $z = preg_replace('/^[^\.]+\./', '', $h); 1680 // %s - domain name after the '@' from e-mail address provided at login screen. Returns FALSE if an invalid email is provided 1681 if ( strpos($name, '%s') !== false ){ 1682 $user_email = rcube_idn_convert(get_input_value('_user', RCUBE_INPUT_POST), true); 1683 if ( preg_match('/(.*)@([a-z0-9\.\-\[\]\:]+)/i', $user_email, $s) < 1 || filter_var($s[1]."@".$s[2], FILTER_VALIDATE_EMAIL) === false ) 1684 return false; 1685 } 1686 1687 $name = str_replace(array('%n', '%d', '%h', '%z', '%s'), array($n, $d, $h, $z, $s[2]), $name); 1688 return $name; 1689 } 1690 1691 1692 /** 1693 * E-mail address validation 1694 * 1695 * @param string $email Email address 1696 * @param boolean $dns_check True to check dns 1697 * @return boolean 1698 */ 247 return rcmail::parse_host($name, $host); 248 } 249 1699 250 function check_email($email, $dns_check=true) 1700 251 { 1701 // Check for invalid characters 1702 if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email)) 1703 return false; 1704 1705 // Check for length limit specified by RFC 5321 (#1486453) 1706 if (strlen($email) > 254) 1707 return false; 1708 1709 $email_array = explode('@', $email); 1710 1711 // Check that there's one @ symbol 1712 if (count($email_array) < 2) 1713 return false; 1714 1715 $domain_part = array_pop($email_array); 1716 $local_part = implode('@', $email_array); 1717 1718 // from PEAR::Validate 1719 $regexp = '&^(?: 1720 ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name 1721 ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*)) #2 OR dot-atom (RFC5322) 1722 $&xi'; 1723 1724 if (!preg_match($regexp, $local_part)) 1725 return false; 1726 1727 // Check domain part 1728 if (preg_match('/^\[*(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]*$/', $domain_part)) 1729 return true; // IP address 1730 else { 1731 // If not an IP address 1732 $domain_array = explode('.', $domain_part); 1733 if (sizeof($domain_array) < 2) 1734 return false; // Not enough parts to be a valid domain 1735 1736 foreach ($domain_array as $part) 1737 if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $part)) 1738 return false; 1739 1740 if (!$dns_check || !rcmail::get_instance()->config->get('email_dns_check')) 1741 return true; 1742 1743 if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && version_compare(PHP_VERSION, '5.3.0', '<')) { 1744 $lookup = array(); 1745 @exec("nslookup -type=MX " . escapeshellarg($domain_part) . " 2>&1", $lookup); 1746 foreach ($lookup as $line) { 1747 if (strpos($line, 'MX preference')) 1748 return true; 1749 } 1750 return false; 1751 } 1752 1753 // find MX record(s) 1754 if (getmxrr($domain_part, $mx_records)) 1755 return true; 1756 1757 // find any DNS record 1758 if (checkdnsrr($domain_part, 'ANY')) 1759 return true; 1760 } 1761 1762 return false; 1763 } 1764 1765 /* 1766 * Idn_to_ascii wrapper. 1767 * Intl/Idn modules version of this function doesn't work with e-mail address 1768 */ 1769 function rcube_idn_to_ascii($str) 1770 { 1771 return rcube_idn_convert($str, true); 1772 } 1773 1774 /* 1775 * Idn_to_ascii wrapper. 1776 * Intl/Idn modules version of this function doesn't work with e-mail address 1777 */ 1778 function rcube_idn_to_utf8($str) 1779 { 1780 return rcube_idn_convert($str, false); 1781 } 1782 1783 function rcube_idn_convert($input, $is_utf=false) 1784 { 1785 if ($at = strpos($input, '@')) { 1786 $user = substr($input, 0, $at); 1787 $domain = substr($input, $at+1); 1788 } 1789 else { 1790 $domain = $input; 1791 } 1792 1793 $domain = $is_utf ? idn_to_ascii($domain) : idn_to_utf8($domain); 1794 1795 if ($domain === false) { 1796 return ''; 1797 } 1798 1799 return $at ? $user . '@' . $domain : $domain; 1800 } 1801 1802 1803 /** 1804 * Helper class to turn relative urls into absolute ones 1805 * using a predefined base 1806 */ 1807 class rcube_base_replacer 1808 { 1809 private $base_url; 1810 1811 public function __construct($base) 1812 { 1813 $this->base_url = $base; 1814 } 1815 1816 public function callback($matches) 1817 { 1818 return $matches[1] . '="' . self::absolute_url($matches[3], $this->base_url) . '"'; 1819 } 1820 1821 public function replace($body) 1822 { 1823 return preg_replace_callback(array( 1824 '/(src|background|href)=(["\']?)([^"\'\s]+)(\2|\s|>)/Ui', 1825 '/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/Ui', 1826 ), 1827 array($this, 'callback'), $body); 1828 } 1829 1830 /** 1831 * Convert paths like ../xxx to an absolute path using a base url 1832 * 1833 * @param string $path Relative path 1834 * @param string $base_url Base URL 1835 * 1836 * @return string Absolute URL 1837 */ 1838 public static function absolute_url($path, $base_url) 1839 { 1840 $host_url = $base_url; 1841 $abs_path = $path; 1842 1843 // check if path is an absolute URL 1844 if (preg_match('/^[fhtps]+:\/\//', $path)) { 1845 return $path; 1846 } 1847 1848 // check if path is a content-id scheme 1849 if (strpos($path, 'cid:') === 0) { 1850 return $path; 1851 } 1852 1853 // cut base_url to the last directory 1854 if (strrpos($base_url, '/') > 7) { 1855 $host_url = substr($base_url, 0, strpos($base_url, '/', 7)); 1856 $base_url = substr($base_url, 0, strrpos($base_url, '/')); 1857 } 1858 1859 // $path is absolute 1860 if ($path[0] == '/') { 1861 $abs_path = $host_url.$path; 1862 } 1863 else { 1864 // strip './' because its the same as '' 1865 $path = preg_replace('/^\.\//', '', $path); 1866 1867 if (preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER)) { 1868 foreach ($matches as $a_match) { 1869 if (strrpos($base_url, '/')) { 1870 $base_url = substr($base_url, 0, strrpos($base_url, '/')); 1871 } 1872 $path = substr($path, 3); 1873 } 1874 } 1875 1876 $abs_path = $base_url.'/'.$path; 1877 } 1878 1879 return $abs_path; 1880 } 1881 } 1882 1883 1884 /****** debugging and logging functions ********/ 1885 1886 /** 1887 * Print or write debug messages 1888 * 1889 * @param mixed Debug message or data 1890 * @return void 1891 */ 252 return rcmail::get_instance()->check_email($email, $dns_check); 253 } 254 1892 255 function console() 1893 256 { 1894 $args = func_get_args(); 1895 1896 if (class_exists('rcmail', false)) { 1897 $rcmail = rcmail::get_instance(); 1898 if (is_object($rcmail->plugins)) { 1899 $plugin = $rcmail->plugins->exec_hook('console', array('args' => $args)); 1900 if ($plugin['abort']) 1901 return; 1902 $args = $plugin['args']; 1903 } 1904 } 1905 1906 $msg = array(); 1907 foreach ($args as $arg) 1908 $msg[] = !is_string($arg) ? var_export($arg, true) : $arg; 1909 1910 write_log('console', join(";\n", $msg)); 1911 } 1912 1913 1914 /** 1915 * Append a line to a logfile in the logs directory. 1916 * Date will be added automatically to the line. 1917 * 1918 * @param $name name of log file 1919 * @param line Line to append 1920 * @return void 1921 */ 257 call_user_func_array(array('rcmail', 'console'), func_get_args()); 258 } 259 1922 260 function write_log($name, $line) 1923 261 { 1924 global $CONFIG, $RCMAIL; 1925 1926 if (!is_string($line)) 1927 $line = var_export($line, true); 1928 1929 if (empty($CONFIG['log_date_format'])) 1930 $CONFIG['log_date_format'] = 'd-M-Y H:i:s O'; 1931 1932 $date = date($CONFIG['log_date_format']); 1933 1934 // trigger logging hook 1935 if (is_object($RCMAIL) && is_object($RCMAIL->plugins)) { 1936 $log = $RCMAIL->plugins->exec_hook('write_log', array('name' => $name, 'date' => $date, 'line' => $line)); 1937 $name = $log['name']; 1938 $line = $log['line']; 1939 $date = $log['date']; 1940 if ($log['abort']) 1941 return true; 1942 } 1943 1944 if ($CONFIG['log_driver'] == 'syslog') { 1945 $prio = $name == 'errors' ? LOG_ERR : LOG_INFO; 1946 syslog($prio, $line); 1947 return true; 1948 } 1949 else { 1950 $line = sprintf("[%s]: %s\n", $date, $line); 1951 1952 // log_driver == 'file' is assumed here 1953 if (empty($CONFIG['log_dir'])) 1954 $CONFIG['log_dir'] = INSTALL_PATH.'logs'; 1955 1956 // try to open specific log file for writing 1957 $logfile = $CONFIG['log_dir'].'/'.$name; 1958 if ($fp = @fopen($logfile, 'a')) { 1959 fwrite($fp, $line); 1960 fflush($fp); 1961 fclose($fp); 1962 return true; 1963 } 1964 else 1965 trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING); 1966 } 1967 1968 return false; 1969 } 1970 1971 1972 /** 1973 * Write login data (name, ID, IP address) to the 'userlogins' log file. 1974 * 1975 * @return void 1976 */ 262 return rcmail::write_log($name, $line); 263 } 264 1977 265 function rcmail_log_login() 1978 266 { 1979 global $RCMAIL; 1980 1981 if (!$RCMAIL->config->get('log_logins') || !$RCMAIL->user) 1982 return; 1983 1984 write_log('userlogins', sprintf('Successful login for %s (ID: %d) from %s in session %s', 1985 $RCMAIL->user->get_username(), $RCMAIL->user->ID, rcmail_remote_ip(), session_id())); 1986 } 1987 1988 1989 /** 1990 * Returns remote IP address and forwarded addresses if found 1991 * 1992 * @return string Remote IP address(es) 1993 */ 267 return rcmail::get_instance()->log_login(); 268 } 269 1994 270 function rcmail_remote_ip() 1995 271 { 1996 $address = $_SERVER['REMOTE_ADDR']; 1997 1998 // append the NGINX X-Real-IP header, if set 1999 if (!empty($_SERVER['HTTP_X_REAL_IP'])) { 2000 $remote_ip[] = 'X-Real-IP: ' . $_SERVER['HTTP_X_REAL_IP']; 2001 } 2002 // append the X-Forwarded-For header, if set 2003 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { 2004 $remote_ip[] = 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR']; 2005 } 2006 2007 if (!empty($remote_ip)) 2008 $address .= '(' . implode(',', $remote_ip) . ')'; 2009 2010 return $address; 2011 } 2012 2013 2014 /** 2015 * Check whether the HTTP referer matches the current request 2016 * 2017 * @return boolean True if referer is the same host+path, false if not 2018 */ 272 return rcmail::remote_ip(); 273 } 274 2019 275 function rcube_check_referer() 2020 276 { 2021 $uri = parse_url($_SERVER['REQUEST_URI']); 2022 $referer = parse_url(rc_request_header('Referer')); 2023 return $referer['host'] == rc_request_header('Host') && $referer['path'] == $uri['path']; 2024 } 2025 2026 2027 /** 2028 * @access private 2029 * @return mixed 2030 */ 277 return rcmail::check_referer(); 278 } 279 2031 280 function rcube_timer() 2032 281 { 2033 return microtime(true); 2034 } 2035 2036 2037 /** 2038 * @access private 2039 * @return void 2040 */ 282 return rcmail::timer(); 283 } 284 2041 285 function rcube_print_time($timer, $label='Timer', $dest='console') 2042 286 { 2043 static $print_count = 0; 2044 2045 $print_count++; 2046 $now = rcube_timer(); 2047 $diff = $now-$timer; 2048 2049 if (empty($label)) 2050 $label = 'Timer '.$print_count; 2051 2052 write_log($dest, sprintf("%s: %0.4f sec", $label, $diff)); 2053 } 2054 2055 2056 /** 2057 * Throw system error and show error page 2058 * 2059 * @param array Named parameters 2060 * - code: Error code (required) 2061 * - type: Error type [php|db|imap|javascript] (required) 2062 * - message: Error message 2063 * - file: File where error occured 2064 * - line: Line where error occured 2065 * @param boolean True to log the error 2066 * @param boolean Terminate script execution 2067 */ 287 rcmail::print_timer($timer, $label, $dest); 288 } 289 290 2068 291 // may be defined in Installer 2069 292 if (!function_exists('raise_error')) { 2070 293 function raise_error($arg=array(), $log=false, $terminate=false) 2071 294 { 2072 global $__page_content, $CONFIG, $OUTPUT, $ERROR_CODE, $ERROR_MESSAGE; 2073 2074 // report bug (if not incompatible browser) 2075 if ($log && $arg['type'] && $arg['message']) 2076 rcube_log_bug($arg); 2077 2078 // display error page and terminate script 2079 if ($terminate) { 2080 $ERROR_CODE = $arg['code']; 2081 $ERROR_MESSAGE = $arg['message']; 2082 include INSTALL_PATH . 'program/steps/utils/error.inc'; 2083 exit; 2084 } 2085 } 2086 } 2087 2088 2089 /** 2090 * Report error according to configured debug_level 2091 * 2092 * @param array Named parameters 2093 * @return void 2094 * @see raise_error() 2095 */ 295 rcube_ui::raise_error($arg, $log, $terminate); 296 } 297 } 298 2096 299 function rcube_log_bug($arg_arr) 2097 300 { 2098 global $CONFIG; 2099 2100 $program = strtoupper($arg_arr['type']); 2101 $level = $CONFIG['debug_level']; 2102 2103 // disable errors for ajax requests, write to log instead (#1487831) 2104 if (($level & 4) && !empty($_REQUEST['_remote'])) { 2105 $level = ($level ^ 4) | 1; 2106 } 2107 2108 // write error to local log file 2109 if ($level & 1) { 2110 $post_query = ($_SERVER['REQUEST_METHOD'] == 'POST' ? '?_task='.urlencode($_POST['_task']).'&_action='.urlencode($_POST['_action']) : ''); 2111 $log_entry = sprintf("%s Error: %s%s (%s %s)", 2112 $program, 2113 $arg_arr['message'], 2114 $arg_arr['file'] ? sprintf(' in %s on line %d', $arg_arr['file'], $arg_arr['line']) : '', 2115 $_SERVER['REQUEST_METHOD'], 2116 $_SERVER['REQUEST_URI'] . $post_query); 2117 2118 if (!write_log('errors', $log_entry)) { 2119 // send error to PHPs error handler if write_log didn't succeed 2120 trigger_error($arg_arr['message']); 2121 } 2122 } 2123 2124 // report the bug to the global bug reporting system 2125 if ($level & 2) { 2126 // TODO: Send error via HTTP 2127 } 2128 2129 // show error if debug_mode is on 2130 if ($level & 4) { 2131 print "<b>$program Error"; 2132 2133 if (!empty($arg_arr['file']) && !empty($arg_arr['line'])) 2134 print " in $arg_arr[file] ($arg_arr[line])"; 2135 2136 print ':</b> '; 2137 print nl2br($arg_arr['message']); 2138 print '<br />'; 2139 flush(); 2140 } 301 rcube_ui::log_bug($arg_arr); 2141 302 } 2142 303 2143 304 function rcube_upload_progress() 2144 305 { 2145 global $RCMAIL; 2146 2147 $prefix = ini_get('apc.rfc1867_prefix'); 2148 $params = array( 2149 'action' => $RCMAIL->action, 2150 'name' => get_input_value('_progress', RCUBE_INPUT_GET), 2151 ); 2152 2153 if (function_exists('apc_fetch')) { 2154 $status = apc_fetch($prefix . $params['name']); 2155 2156 if (!empty($status)) { 2157 $status['percent'] = round($status['current']/$status['total']*100); 2158 $params = array_merge($status, $params); 2159 } 2160 } 2161 2162 if (isset($params['percent'])) 2163 $params['text'] = rcube_label(array('name' => 'uploadprogress', 'vars' => array( 2164 'percent' => $params['percent'] . '%', 2165 'current' => show_bytes($params['current']), 2166 'total' => show_bytes($params['total']) 2167 ))); 2168 2169 $RCMAIL->output->command('upload_progress_update', $params); 2170 $RCMAIL->output->send(); 306 rcube_ui::upload_progress(); 2171 307 } 2172 308 2173 309 function rcube_upload_init() 2174 310 { 2175 global $RCMAIL; 2176 2177 // Enable upload progress bar 2178 if (($seconds = $RCMAIL->config->get('upload_progress')) && ini_get('apc.rfc1867')) { 2179 if ($field_name = ini_get('apc.rfc1867_name')) { 2180 $RCMAIL->output->set_env('upload_progress_name', $field_name); 2181 $RCMAIL->output->set_env('upload_progress_time', (int) $seconds); 2182 } 2183 } 2184 2185 // find max filesize value 2186 $max_filesize = parse_bytes(ini_get('upload_max_filesize')); 2187 $max_postsize = parse_bytes(ini_get('post_max_size')); 2188 if ($max_postsize && $max_postsize < $max_filesize) 2189 $max_filesize = $max_postsize; 2190 2191 $RCMAIL->output->set_env('max_filesize', $max_filesize); 2192 $max_filesize = show_bytes($max_filesize); 2193 $RCMAIL->output->set_env('filesizeerror', rcube_label(array( 2194 'name' => 'filesizeerror', 'vars' => array('size' => $max_filesize)))); 2195 2196 return $max_filesize; 2197 } 2198 2199 /** 2200 * Initializes client-side autocompletion 2201 */ 311 return rcube_ui::upload_init(); 312 } 313 2202 314 function rcube_autocomplete_init() 2203 315 { 2204 global $RCMAIL; 2205 static $init; 2206 2207 if ($init) 2208 return; 2209 2210 $init = 1; 2211 2212 if (($threads = (int)$RCMAIL->config->get('autocomplete_threads')) > 0) { 2213 $book_types = (array) $RCMAIL->config->get('autocomplete_addressbooks', 'sql'); 2214 if (count($book_types) > 1) { 2215 $RCMAIL->output->set_env('autocomplete_threads', $threads); 2216 $RCMAIL->output->set_env('autocomplete_sources', $book_types); 2217 } 2218 } 2219 2220 $RCMAIL->output->set_env('autocomplete_max', (int)$RCMAIL->config->get('autocomplete_max', 15)); 2221 $RCMAIL->output->set_env('autocomplete_min_length', $RCMAIL->config->get('autocomplete_min_length')); 2222 $RCMAIL->output->add_label('autocompletechars', 'autocompletemore'); 316 rcube_ui::autocomplete_init(); 2223 317 } 2224 318 2225 319 function rcube_fontdefs($font = null) 2226 320 { 2227 $fonts = array( 2228 'Andale Mono' => '"Andale Mono",Times,monospace', 2229 'Arial' => 'Arial,Helvetica,sans-serif', 2230 'Arial Black' => '"Arial Black","Avant Garde",sans-serif', 2231 'Book Antiqua' => '"Book Antiqua",Palatino,serif', 2232 'Courier New' => '"Courier New",Courier,monospace', 2233 'Georgia' => 'Georgia,Palatino,serif', 2234 'Helvetica' => 'Helvetica,Arial,sans-serif', 2235 'Impact' => 'Impact,Chicago,sans-serif', 2236 'Tahoma' => 'Tahoma,Arial,Helvetica,sans-serif', 2237 'Terminal' => 'Terminal,Monaco,monospace', 2238 'Times New Roman' => '"Times New Roman",Times,serif', 2239 'Trebuchet MS' => '"Trebuchet MS",Geneva,sans-serif', 2240 'Verdana' => 'Verdana,Geneva,sans-serif', 2241 ); 2242 2243 if ($font) 2244 return $fonts[$font]; 2245 2246 return $fonts; 2247 } 321 return rcube_ui::font_defs($font); 322 } 323 324 function send_nocacheing_headers() 325 { 326 rcube_ui::send_nocacheing_headers(); 327 } 328 329 function show_bytes($bytes) 330 { 331 return rcube_ui::show_bytes($bytes); 332 } 333 334 function rc_wordwrap($string, $width=75, $break="\n", $cut=false) 335 { 336 return rcube_wordwrap($string, $width, $break, $cut); 337 } 338 339 function rc_request_header($name) 340 { 341 return rcube_request_header($name); 342 } -
branches/devel-framework/roundcubemail/program/include/rcmail.php
r5778 r5807 756 756 $this->session = new rcube_session($this->get_dbh(), $this->config); 757 757 758 $this->session->register_gc_handler( 'rcmail_temp_gc');758 $this->session->register_gc_handler(array($this, 'temp_gc')); 759 759 if ($this->config->get('enable_caching')) 760 $this->session->register_gc_handler( 'rcmail_cache_gc');760 $this->session->register_gc_handler(array($this, 'cache_gc')); 761 761 762 762 // start PHP session (if not in CLI mode) … … 1754 1754 } 1755 1755 1756 1757 /** 1758 * Overwrite action variable 1759 * 1760 * @param string New action value 1761 */ 1762 public function overwrite_action($action) 1763 { 1764 $this->action = $action; 1765 $this->output->set_env('action', $action); 1766 } 1767 1768 1769 /** 1770 * Send the given message using the configured method. 1771 * 1772 * @param object $message Reference to Mail_MIME object 1773 * @param string $from Sender address string 1774 * @param array $mailto Array of recipient address strings 1775 * @param array $smtp_error SMTP error array (reference) 1776 * @param string $body_file Location of file with saved message body (reference), 1777 * used when delay_file_io is enabled 1778 * @param array $smtp_opts SMTP options (e.g. DSN request) 1779 * 1780 * @return boolean Send status. 1781 */ 1782 public function deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file = null, $smtp_opts = null) 1783 { 1784 $headers = $message->headers(); 1785 1786 // send thru SMTP server using custom SMTP library 1787 if ($this->config->get('smtp_server')) { 1788 // generate list of recipients 1789 $a_recipients = array($mailto); 1790 1791 if (strlen($headers['Cc'])) 1792 $a_recipients[] = $headers['Cc']; 1793 if (strlen($headers['Bcc'])) 1794 $a_recipients[] = $headers['Bcc']; 1795 1796 // clean Bcc from header for recipients 1797 $send_headers = $headers; 1798 unset($send_headers['Bcc']); 1799 // here too, it because txtHeaders() below use $message->_headers not only $send_headers 1800 unset($message->_headers['Bcc']); 1801 1802 $smtp_headers = $message->txtHeaders($send_headers, true); 1803 1804 if ($message->getParam('delay_file_io')) { 1805 // use common temp dir 1806 $temp_dir = $this->config->get('temp_dir'); 1807 $body_file = tempnam($temp_dir, 'rcmMsg'); 1808 if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) { 1809 raise_error(array('code' => 650, 'type' => 'php', 1810 'file' => __FILE__, 'line' => __LINE__, 1811 'message' => "Could not create message: ".$mime_result->getMessage()), 1812 TRUE, FALSE); 1813 return false; 1814 } 1815 $msg_body = fopen($body_file, 'r'); 1816 } 1817 else { 1818 $msg_body = $message->get(); 1819 } 1820 1821 // send message 1822 if (!is_object($this->smtp)) { 1823 $this->smtp_init(true); 1824 } 1825 1826 $sent = $this->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $smtp_opts); 1827 $smtp_response = $this->smtp->get_response(); 1828 $smtp_error = $this->smtp->get_error(); 1829 1830 // log error 1831 if (!$sent) { 1832 raise_error(array('code' => 800, 'type' => 'smtp', 1833 'line' => __LINE__, 'file' => __FILE__, 1834 'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE); 1835 } 1836 } 1837 // send mail using PHP's mail() function 1838 else { 1839 // unset some headers because they will be added by the mail() function 1840 $headers_enc = $message->headers($headers); 1841 $headers_php = $message->_headers; 1842 unset($headers_php['To'], $headers_php['Subject']); 1843 1844 // reset stored headers and overwrite 1845 $message->_headers = array(); 1846 $header_str = $message->txtHeaders($headers_php); 1847 1848 // #1485779 1849 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 1850 if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) { 1851 $headers_enc['To'] = implode(', ', $m[1]); 1852 } 1853 } 1854 1855 $msg_body = $message->get(); 1856 1857 if (PEAR::isError($msg_body)) { 1858 raise_error(array('code' => 650, 'type' => 'php', 1859 'file' => __FILE__, 'line' => __LINE__, 1860 'message' => "Could not create message: ".$msg_body->getMessage()), 1861 TRUE, FALSE); 1862 } 1863 else { 1864 $delim = $this->config->header_delimiter(); 1865 $to = $headers_enc['To']; 1866 $subject = $headers_enc['Subject']; 1867 $header_str = rtrim($header_str); 1868 1869 if ($delim != "\r\n") { 1870 $header_str = str_replace("\r\n", $delim, $header_str); 1871 $msg_body = str_replace("\r\n", $delim, $msg_body); 1872 $to = str_replace("\r\n", $delim, $to); 1873 $subject = str_replace("\r\n", $delim, $subject); 1874 } 1875 1876 if (ini_get('safe_mode')) 1877 $sent = mail($to, $subject, $msg_body, $header_str); 1878 else 1879 $sent = mail($to, $subject, $msg_body, $header_str, "-f$from"); 1880 } 1881 } 1882 1883 if ($sent) { 1884 $this->plugins->exec_hook('message_sent', array('headers' => $headers, 'body' => $msg_body)); 1885 1886 // remove MDN headers after sending 1887 unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']); 1888 1889 // get all recipients 1890 if ($headers['Cc']) 1891 $mailto .= $headers['Cc']; 1892 if ($headers['Bcc']) 1893 $mailto .= $headers['Bcc']; 1894 if (preg_match_all('/<([^@]+@[^>]+)>/', $mailto, $m)) 1895 $mailto = implode(', ', array_unique($m[1])); 1896 1897 if ($this->config->get('smtp_log')) { 1898 write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s", 1899 $this->user->get_username(), 1900 $_SERVER['REMOTE_ADDR'], 1901 $mailto, 1902 !empty($smtp_response) ? join('; ', $smtp_response) : '')); 1903 } 1904 } 1905 1906 if (is_resource($msg_body)) { 1907 fclose($msg_body); 1908 } 1909 1910 $message->_headers = array(); 1911 $message->headers($headers); 1912 1913 return $sent; 1914 } 1915 1916 1917 /** 1918 * Unique Message-ID generator. 1919 * 1920 * @return string Message-ID 1921 */ 1922 public function gen_message_id() 1923 { 1924 $local_part = md5(uniqid('rcmail'.mt_rand(),true)); 1925 $domain_part = $this->user->get_username('domain'); 1926 1927 // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924) 1928 if (!preg_match('/\.[a-z]+$/i', $domain_part)) { 1929 foreach (array($_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']) as $host) { 1930 $host = preg_replace('/:[0-9]+$/', '', $host); 1931 if ($host && preg_match('/\.[a-z]+$/i', $host)) { 1932 $domain_part = $host; 1933 } 1934 } 1935 } 1936 1937 return sprintf('<%s@%s>', $local_part, $domain_part); 1938 } 1939 1940 1941 /** 1942 * Returns RFC2822 formatted current date in user's timezone 1943 * 1944 * @return string Date 1945 */ 1946 public function user_date() 1947 { 1948 // get user's timezone 1949 try { 1950 $tz = new DateTimeZone($this->config->get('timezone')); 1951 $date = new DateTime('now', $tz); 1952 } 1953 catch (Exception $e) { 1954 $date = new DateTime(); 1955 } 1956 1957 return $date->format('r'); 1958 } 1959 1960 1961 /** 1962 * Replaces hostname variables. 1963 * 1964 * @param string $name Hostname 1965 * @param string $host Optional IMAP hostname 1966 * 1967 * @return string Hostname 1968 */ 1969 public static function parse_host($name, $host = '') 1970 { 1971 // %n - host 1972 $n = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']); 1973 // %d - domain name without first part, e.g. %n=mail.domain.tld, %d=domain.tld 1974 $d = preg_replace('/^[^\.]+\./', '', $n); 1975 // %h - IMAP host 1976 $h = $_SESSION['storage_host'] ? $_SESSION['storage_host'] : $host; 1977 // %z - IMAP domain without first part, e.g. %h=imap.domain.tld, %z=domain.tld 1978 $z = preg_replace('/^[^\.]+\./', '', $h); 1979 // %s - domain name after the '@' from e-mail address provided at login screen. Returns FALSE if an invalid email is provided 1980 if (strpos($name, '%s') !== false) { 1981 $user_email = rcube_idn_convert(get_input_value('_user', RCUBE_INPUT_POST), true); 1982 $matches = preg_match('/(.*)@([a-z0-9\.\-\[\]\:]+)/i', $user_email, $s); 1983 if ($matches < 1 || filter_var($s[1]."@".$s[2], FILTER_VALIDATE_EMAIL) === false) { 1984 return false; 1985 } 1986 } 1987 1988 $name = str_replace(array('%n', '%d', '%h', '%z', '%s'), array($n, $d, $h, $z, $s[2]), $name); 1989 return $name; 1990 } 1991 1992 1993 /** 1994 * E-mail address validation. 1995 * 1996 * @param string $email Email address 1997 * @param boolean $dns_check True to check dns 1998 * 1999 * @return boolean True on success, False if address is invalid 2000 */ 2001 public function check_email($email, $dns_check=true) 2002 { 2003 // Check for invalid characters 2004 if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email)) { 2005 return false; 2006 } 2007 2008 // Check for length limit specified by RFC 5321 (#1486453) 2009 if (strlen($email) > 254) { 2010 return false; 2011 } 2012 2013 $email_array = explode('@', $email); 2014 2015 // Check that there's one @ symbol 2016 if (count($email_array) < 2) { 2017 return false; 2018 } 2019 2020 $domain_part = array_pop($email_array); 2021 $local_part = implode('@', $email_array); 2022 2023 // from PEAR::Validate 2024 $regexp = '&^(?: 2025 ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name 2026 ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*)) #2 OR dot-atom (RFC5322) 2027 $&xi'; 2028 2029 if (!preg_match($regexp, $local_part)) { 2030 return false; 2031 } 2032 2033 // Check domain part 2034 if (preg_match('/^\[*(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]*$/', $domain_part)) { 2035 return true; // IP address 2036 } 2037 else { 2038 // If not an IP address 2039 $domain_array = explode('.', $domain_part); 2040 // Not enough parts to be a valid domain 2041 if (sizeof($domain_array) < 2) { 2042 return false; 2043 } 2044 2045 foreach ($domain_array as $part) { 2046 if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $part)) { 2047 return false; 2048 } 2049 } 2050 2051 if (!$dns_check || !$this->config->get('email_dns_check')) { 2052 return true; 2053 } 2054 2055 if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && version_compare(PHP_VERSION, '5.3.0', '<')) { 2056 $lookup = array(); 2057 @exec("nslookup -type=MX " . escapeshellarg($domain_part) . " 2>&1", $lookup); 2058 foreach ($lookup as $line) { 2059 if (strpos($line, 'MX preference')) { 2060 return true; 2061 } 2062 } 2063 return false; 2064 } 2065 2066 // find MX record(s) 2067 if (getmxrr($domain_part, $mx_records)) { 2068 return true; 2069 } 2070 2071 // find any DNS record 2072 if (checkdnsrr($domain_part, 'ANY')) { 2073 return true; 2074 } 2075 } 2076 2077 return false; 2078 } 2079 2080 2081 /** 2082 * Print or write debug messages 2083 * 2084 * @param mixed Debug message or data 2085 */ 2086 public static function console() 2087 { 2088 $args = func_get_args(); 2089 2090 if (class_exists('rcmail', false)) { 2091 $rcmail = rcmail::get_instance(); 2092 if (is_object($rcmail->plugins)) { 2093 $plugin = $rcmail->plugins->exec_hook('console', array('args' => $args)); 2094 if ($plugin['abort']) { 2095 return; 2096 } 2097 $args = $plugin['args']; 2098 } 2099 } 2100 2101 $msg = array(); 2102 foreach ($args as $arg) { 2103 $msg[] = !is_string($arg) ? var_export($arg, true) : $arg; 2104 } 2105 2106 write_log('console', join(";\n", $msg)); 2107 } 2108 2109 2110 /** 2111 * Append a line to a logfile in the logs directory. 2112 * Date will be added automatically to the line. 2113 * 2114 * @param $name name of log file 2115 * @param line Line to append 2116 */ 2117 public static function write_log($name, $line) 2118 { 2119 global $RCMAIL; 2120 2121 if (!is_string($line)) { 2122 $line = var_export($line, true); 2123 } 2124 2125 $date_format = $RCMAIL ? $RCMAIL->config->get('log_date_format') : null; 2126 $log_driver = $RCMAIL ? $RCMAIL->config->get('log_driver') : null; 2127 2128 if (empty($date_format)) { 2129 $date_format = 'd-M-Y H:i:s O'; 2130 } 2131 2132 $date = date($date_format); 2133 2134 // trigger logging hook 2135 if (is_object($RCMAIL) && is_object($RCMAIL->plugins)) { 2136 $log = $RCMAIL->plugins->exec_hook('write_log', array('name' => $name, 'date' => $date, 'line' => $line)); 2137 $name = $log['name']; 2138 $line = $log['line']; 2139 $date = $log['date']; 2140 if ($log['abort']) 2141 return true; 2142 } 2143 2144 if ($log_driver == 'syslog') { 2145 $prio = $name == 'errors' ? LOG_ERR : LOG_INFO; 2146 syslog($prio, $line); 2147 return true; 2148 } 2149 2150 // log_driver == 'file' is assumed here 2151 2152 $line = sprintf("[%s]: %s\n", $date, $line); 2153 $log_dir = $RCMAIL ? $RCMAIL->config->get('log_dir') : null; 2154 2155 if (empty($log_dir)) { 2156 $log_dir = INSTALL_PATH . 'logs'; 2157 } 2158 2159 // try to open specific log file for writing 2160 $logfile = $log_dir.'/'.$name; 2161 2162 if ($fp = @fopen($logfile, 'a')) { 2163 fwrite($fp, $line); 2164 fflush($fp); 2165 fclose($fp); 2166 return true; 2167 } 2168 2169 trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING); 2170 return false; 2171 } 2172 2173 2174 /** 2175 * Write login data (name, ID, IP address) to the 'userlogins' log file. 2176 */ 2177 public function log_login() 2178 { 2179 if (!$this->config->get('log_logins') || !$this->user) { 2180 return; 2181 } 2182 2183 $this->write_log('userlogins', 2184 sprintf('Successful login for %s (ID: %d) from %s in session %s', 2185 $this->user->get_username(), 2186 $this->user->ID, $this->remote_ip(), session_id())); 2187 } 2188 2189 2190 /** 2191 * Returns remote IP address and forwarded addresses if found 2192 * 2193 * @return string Remote IP address(es) 2194 */ 2195 public static function remote_ip() 2196 { 2197 $address = $_SERVER['REMOTE_ADDR']; 2198 2199 // append the NGINX X-Real-IP header, if set 2200 if (!empty($_SERVER['HTTP_X_REAL_IP'])) { 2201 $remote_ip[] = 'X-Real-IP: ' . $_SERVER['HTTP_X_REAL_IP']; 2202 } 2203 // append the X-Forwarded-For header, if set 2204 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { 2205 $remote_ip[] = 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR']; 2206 } 2207 2208 if (!empty($remote_ip)) { 2209 $address .= '(' . implode(',', $remote_ip) . ')'; 2210 } 2211 2212 return $address; 2213 } 2214 2215 2216 /** 2217 * Check whether the HTTP referer matches the current request 2218 * 2219 * @return boolean True if referer is the same host+path, false if not 2220 */ 2221 public static function check_referer() 2222 { 2223 $uri = parse_url($_SERVER['REQUEST_URI']); 2224 $referer = parse_url(rcube_request_header('Referer')); 2225 return $referer['host'] == rcube_request_header('Host') && $referer['path'] == $uri['path']; 2226 } 2227 2228 2229 /** 2230 * Returns current time (with microseconds). 2231 * 2232 * @return float Current time in seconds since the Unix 2233 */ 2234 public static function timer() 2235 { 2236 return microtime(true); 2237 } 2238 2239 2240 /** 2241 * Logs time difference according to provided timer 2242 * 2243 * @param float $timer Timer (self::timer() result) 2244 * @param string $label Log line prefix 2245 * @param string $dest Log file name 2246 * 2247 * @see self::timer() 2248 */ 2249 public static function print_timer($timer, $label = 'Timer', $dest = 'console') 2250 { 2251 static $print_count = 0; 2252 2253 $print_count++; 2254 $now = rcube_timer(); 2255 $diff = $now-$timer; 2256 2257 if (empty($label)) { 2258 $label = 'Timer '.$print_count; 2259 } 2260 2261 self::write_log($dest, sprintf("%s: %0.4f sec", $label, $diff)); 2262 } 2263 2264 2265 /** 2266 * Garbage collector function for temp files. 2267 * Remove temp files older than two days 2268 */ 2269 public function temp_gc() 2270 { 2271 $tmp = unslashify($this->config->get('temp_dir')); 2272 $expire = mktime() - 172800; // expire in 48 hours 2273 2274 if ($dir = opendir($tmp)) { 2275 while (($fname = readdir($dir)) !== false) { 2276 if ($fname{0} == '.') { 2277 continue; 2278 } 2279 2280 if (filemtime($tmp.'/'.$fname) < $expire) { 2281 @unlink($tmp.'/'.$fname); 2282 } 2283 } 2284 2285 closedir($dir); 2286 } 2287 } 2288 2289 2290 /** 2291 * Garbage collector for cache entries. 2292 * Remove all expired message cache records 2293 */ 2294 public function cache_gc() 2295 { 2296 $db = $this->get_dbh(); 2297 2298 // get target timestamp 2299 $ts = get_offset_time($this->config->get('message_cache_lifetime', '30d'), -1); 2300 2301 $db->query("DELETE FROM ".get_table_name('cache_messages') 2302 ." WHERE changed < " . $db->fromunixtime($ts)); 2303 2304 $db->query("DELETE FROM ".get_table_name('cache_index') 2305 ." WHERE changed < " . $db->fromunixtime($ts)); 2306 2307 $db->query("DELETE FROM ".get_table_name('cache_thread') 2308 ." WHERE changed < " . $db->fromunixtime($ts)); 2309 2310 $db->query("DELETE FROM ".get_table_name('cache') 2311 ." WHERE created < " . $db->fromunixtime($ts)); 2312 } 2313 1756 2314 } -
branches/devel-framework/roundcubemail/program/include/rcube_mdb2.php
r5529 r5807 805 805 } 806 806 807 } // end class rcube_db 807 808 /** 809 * Return correct name for a specific database table 810 * 811 * @param string $table Table name 812 * 813 * @return string Translated table name 814 */ 815 public function table_name($table) 816 { 817 $rcmail = rcmail::get_instance(); 818 819 // return table name if configured 820 $config_key = 'db_table_'.$table; 821 822 if ($name = $rcmail->config->get($config_key)) { 823 return $name; 824 } 825 826 return $table; 827 } 828 829 830 /** 831 * Return correct name for a specific database sequence 832 * (used for Postgres only) 833 * 834 * @param string $sequence Secuence name 835 * 836 * @return string Translated sequence name 837 */ 838 public function sequence_name($sequence) 839 { 840 $rcmail = rcmail::get_instance(); 841 842 // return sequence name if configured 843 $config_key = 'db_sequence_'.$sequence; 844 845 if ($name = $rcmail->config->get($config_key)) { 846 return $name; 847 } 848 849 return $sequence; 850 } 851 852 } -
branches/devel-framework/roundcubemail/program/include/rcube_session.php
r5521 r5807 320 320 public function gc() 321 321 { 322 foreach ($this->gc_handlers as $fct) 322 foreach ($this->gc_handlers as $fct) { 323 323 call_user_func($fct); 324 } 324 325 } 325 326 … … 330 331 * @param mixed Callback function 331 332 */ 332 public function register_gc_handler($func_name) 333 { 334 if ($func_name && !in_array($func_name, $this->gc_handlers)) 335 $this->gc_handlers[] = $func_name; 333 public function register_gc_handler($func) 334 { 335 foreach ($this->gc_handlers as $handler) { 336 if ($handler == $func) { 337 return; 338 } 339 } 340 341 $this->gc_handlers[] = $func; 336 342 } 337 343 -
branches/devel-framework/roundcubemail/program/include/rcube_shared.inc
r5776 r5807 29 29 30 30 /** 31 * Send HTTP headers to prevent caching this page32 */33 function send_nocacheing_headers()34 {35 global $OUTPUT;36 37 if (headers_sent())38 return;39 40 header("Expires: ".gmdate("D, d M Y H:i:s")." GMT");41 header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");42 // Request browser to disable DNS prefetching (CVE-2010-0464)43 header("X-DNS-Prefetch-Control: off");44 45 // We need to set the following headers to make downloads work using IE in HTTPS mode.46 if ($OUTPUT->browser->ie && rcube_https_check()) {47 header('Pragma: private');48 header("Cache-Control: private, must-revalidate");49 } else {50 header("Cache-Control: private, no-cache, must-revalidate, post-check=0, pre-check=0");51 header("Pragma: no-cache");52 }53 }54 55 56 /**57 * Send header with expire date 30 days in future58 *59 * @param int Expiration time in seconds60 */61 function send_future_expire_header($offset=2600000)62 {63 if (headers_sent())64 return;65 66 header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+$offset)." GMT");67 header("Cache-Control: max-age=$offset");68 header("Pragma: ");69 }70 71 72 /**73 31 * Similar function as in_array() but case-insensitive 74 32 * … … 138 96 } 139 97 140 /**141 * Create a human readable string for a number of bytes142 *143 * @param int Number of bytes144 * @return string Byte string145 */146 function show_bytes($bytes)147 {148 if ($bytes >= 1073741824)149 {150 $gb = $bytes/1073741824;151 $str = sprintf($gb>=10 ? "%d " : "%.1f ", $gb) . rcube_label('GB');152 }153 else if ($bytes >= 1048576)154 {155 $mb = $bytes/1048576;156 $str = sprintf($mb>=10 ? "%d " : "%.1f ", $mb) . rcube_label('MB');157 }158 else if ($bytes >= 1024)159 $str = sprintf("%d ", round($bytes/1024)) . rcube_label('KB');160 else161 $str = sprintf('%d ', $bytes) . rcube_label('B');162 163 return $str;164 }165 98 166 99 /** 167 100 * Wrapper function for wordwrap 168 101 */ 169 function rc _wordwrap($string, $width=75, $break="\n", $cut=false)102 function rcube_wordwrap($string, $width=75, $break="\n", $cut=false) 170 103 { 171 104 $para = explode($break, $string); … … 215 148 } 216 149 150 217 151 /** 218 152 * Read a specific HTTP request header … … 222 156 * @return mixed Header value or null if not available 223 157 */ 224 function rc _request_header($name)158 function rcube_request_header($name) 225 159 { 226 160 if (function_exists('getallheaders')) … … 346 280 347 281 /** 348 * A method to guess the mime_type of an attachment.349 *350 * @param string $path Path to the file.351 * @param string $name File name (with suffix)352 * @param string $failover Mime type supplied for failover.353 * @param string $is_stream Set to True if $path contains file body354 *355 * @return string356 * @author Till Klampaeckel <till@php.net>357 * @see http://de2.php.net/manual/en/ref.fileinfo.php358 * @see http://de2.php.net/mime_content_type359 */360 function rc_mime_content_type($path, $name, $failover = 'application/octet-stream', $is_stream=false)361 {362 $mime_type = null;363 $mime_magic = rcmail::get_instance()->config->get('mime_magic');364 $mime_ext = @include(RCMAIL_CONFIG_DIR . '/mimetypes.php');365 $suffix = $name ? substr($name, strrpos($name, '.')+1) : '*';366 367 // use file name suffix with hard-coded mime-type map368 if (is_array($mime_ext)) {369 $mime_type = $mime_ext[$suffix];370 }371 // try fileinfo extension if available372 if (!$mime_type && function_exists('finfo_open')) {373 if ($finfo = finfo_open(FILEINFO_MIME, $mime_magic)) {374 if ($is_stream)375 $mime_type = finfo_buffer($finfo, $path);376 else377 $mime_type = finfo_file($finfo, $path);378 finfo_close($finfo);379 }380 }381 // try PHP's mime_content_type382 if (!$mime_type && !$is_stream && function_exists('mime_content_type')) {383 $mime_type = @mime_content_type($path);384 }385 // fall back to user-submitted string386 if (!$mime_type) {387 $mime_type = $failover;388 }389 else {390 // Sometimes (PHP-5.3?) content-type contains charset definition,391 // Remove it (#1487122) also "charset=binary" is useless392 $mime_type = array_shift(preg_split('/[; ]/', $mime_type));393 }394 395 return $mime_type;396 }397 398 399 /**400 * Detect image type of the given binary data by checking magic numbers401 *402 * @param string Binary file content403 * @return string Detected mime-type or jpeg as fallback404 */405 function rc_image_content_type($data)406 {407 $type = 'jpeg';408 if (preg_match('/^\x89\x50\x4E\x47/', $data)) $type = 'png';409 else if (preg_match('/^\x47\x49\x46\x38/', $data)) $type = 'gif';410 else if (preg_match('/^\x00\x00\x01\x00/', $data)) $type = 'ico';411 // else if (preg_match('/^\xFF\xD8\xFF\xE0/', $data)) $type = 'jpeg';412 413 return 'image/' . $type;414 }415 416 417 /**418 282 * Explode quoted string 419 283 * … … 462 326 463 327 /** 328 * Remove all non-ascii and non-word chars except ., -, _ 329 */ 330 function asciiwords($str, $css_id = false, $replace_with = '') 331 { 332 $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : ''); 333 return preg_replace("/[^$allowed]/i", $replace_with, $str); 334 } 335 336 337 /** 338 * Remove single and double quotes from given string 339 * 340 * @param string Input value 341 * 342 * @return string Dequoted string 343 */ 344 function strip_quotes($str) 345 { 346 return str_replace(array("'", '"'), '', $str); 347 } 348 349 350 /** 351 * Remove new lines characters from given string 352 * 353 * @param string Input value 354 * 355 * @return string Stripped string 356 */ 357 function strip_newlines($str) 358 { 359 return preg_replace('/[\r\n]/', '', $str); 360 } 361 362 363 /** 364 * Improved equivalent to strtotime() 365 * 366 * @param string Date string 367 * 368 * @return int Unix timestamp 369 */ 370 function rcube_strtotime($date) 371 { 372 // check for MS Outlook vCard date format YYYYMMDD 373 if (preg_match('/^([12][90]\d\d)([01]\d)(\d\d)$/', trim($date), $matches)) { 374 return mktime(0,0,0, intval($matches[2]), intval($matches[3]), intval($matches[1])); 375 } 376 else if (is_numeric($date)) { 377 return $date; 378 } 379 380 // support non-standard "GMTXXXX" literal 381 $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date); 382 383 // if date parsing fails, we have a date in non-rfc format. 384 // remove token from the end and try again 385 while ((($ts = @strtotime($date)) === false) || ($ts < 0)) { 386 $d = explode(' ', $date); 387 array_pop($d); 388 if (!$d) { 389 break; 390 } 391 $date = implode(' ', $d); 392 } 393 394 return $ts; 395 } 396 397 398 /** 399 * Compose a valid representation of name and e-mail address 400 * 401 * @param string E-mail address 402 * @param string Person name 403 * 404 * @return string Formatted string 405 */ 406 function format_email_recipient($email, $name='') 407 { 408 if ($name && $name != $email) { 409 // Special chars as defined by RFC 822 need to in quoted string (or escaped). 410 return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, trim($email)); 411 } 412 413 return trim($email); 414 } 415 416 417 /** 464 418 * mbstring replacement functions 465 419 */ 466 467 420 if (!extension_loaded('mbstring')) 468 421 { … … 546 499 } 547 500 501 502 /* 503 * Idn_to_ascii wrapper. 504 * Intl/Idn modules version of this function doesn't work with e-mail address 505 */ 506 function rcube_idn_to_ascii($str) 507 { 508 return rcube_idn_convert($str, true); 509 } 510 511 /* 512 * Idn_to_ascii wrapper. 513 * Intl/Idn modules version of this function doesn't work with e-mail address 514 */ 515 function rcube_idn_to_utf8($str) 516 { 517 return rcube_idn_convert($str, false); 518 } 519 520 function rcube_idn_convert($input, $is_utf=false) 521 { 522 if ($at = strpos($input, '@')) { 523 $user = substr($input, 0, $at); 524 $domain = substr($input, $at+1); 525 } 526 else { 527 $domain = $input; 528 } 529 530 $domain = $is_utf ? idn_to_ascii($domain) : idn_to_utf8($domain); 531 532 if ($domain === false) { 533 return ''; 534 } 535 536 return $at ? $user . '@' . $domain : $domain; 537 } 538 539 540 /** 541 * Use PHP5 autoload for dynamic class loading 542 * 543 * @todo Make Zend, PEAR etc play with this 544 * @todo Make our classes conform to a more straight forward CS. 545 */ 546 function rcube_autoload($classname) 547 { 548 $filename = preg_replace( 549 array( 550 '/MDB2_(.+)/', 551 '/Mail_(.+)/', 552 '/Net_(.+)/', 553 '/Auth_(.+)/', 554 '/^html_.+/', 555 '/^utf8$/', 556 ), 557 array( 558 'MDB2/\\1', 559 'Mail/\\1', 560 'Net/\\1', 561 'Auth/\\1', 562 'html', 563 'utf8.class', 564 ), 565 $classname 566 ); 567 568 if ($fp = @fopen("$filename.php", 'r', true)) { 569 fclose($fp); 570 include_once("$filename.php"); 571 return true; 572 } 573 574 return false; 575 } 576 577 /** 578 * Local callback function for PEAR errors 579 */ 580 function rcube_pear_error($err) 581 { 582 error_log(sprintf("%s (%s): %s", 583 $err->getMessage(), 584 $err->getCode(), 585 $err->getUserinfo()), 0); 586 }
Note: See TracChangeset
for help on using the changeset viewer.
