Changeset 5881 in subversion for branches/devel-framework/roundcubemail/program/include/rcmail.php
- Timestamp:
- Feb 15, 2012 5:02:48 AM (15 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/devel-framework/roundcubemail/program/include/rcmail.php
r5866 r5881 28 28 * @package Core 29 29 */ 30 class rcmail 30 class rcmail extends rcube 31 31 { 32 32 /** … … 36 36 */ 37 37 static public $main_tasks = array('mail','settings','addressbook','login','logout','utils','dummy'); 38 39 /**40 * Singleton instace of rcmail41 *42 * @var rcmail43 */44 static private $instance;45 46 /**47 * Stores instance of rcube_config.48 *49 * @var rcube_config50 */51 public $config;52 53 /**54 * Stores rcube_user instance.55 *56 * @var rcube_user57 */58 public $user;59 60 /**61 * Instace of database class.62 *63 * @var rcube_mdb264 */65 public $db;66 67 /**68 * Instace of Memcache class.69 *70 * @var rcube_mdb271 */72 public $memcache;73 74 /**75 * Instace of rcube_session class.76 *77 * @var rcube_session78 */79 public $session;80 81 /**82 * Instance of rcube_smtp class.83 *84 * @var rcube_smtp85 */86 public $smtp;87 88 /**89 * Instance of rcube_storage class.90 *91 * @var rcube_storage92 */93 public $storage;94 95 /**96 * Instance of rcube_output class.97 *98 * @var rcube_output99 */100 public $output;101 102 /**103 * Instance of rcube_plugin_api.104 *105 * @var rcube_plugin_api106 */107 public $plugins;108 38 109 39 /** … … 122 52 public $comm_path = './'; 123 53 124 private $texts;125 54 private $address_books = array(); 126 private $caches = array();127 55 private $action_map = array(); 128 private $shutdown_functions = array();129 56 130 57 … … 136 63 static function get_instance() 137 64 { 138 if (!self::$instance ) {65 if (!self::$instance || !is_a(self::$instance, 'rcmail')) { 139 66 self::$instance = new rcmail(); 140 67 self::$instance->startup(); // init AFTER object was linked with self::$instance … … 146 73 147 74 /** 148 * Private constructor149 */150 private function __construct()151 {152 // load configuration153 $this->config = new rcube_config();154 155 register_shutdown_function(array($this, 'shutdown'));156 }157 158 159 /**160 75 * Initial startup function 161 76 * to register session, create database and imap connections 162 77 */ 163 private function startup() 164 { 165 // initialize syslog 166 if ($this->config->get('log_driver') == 'syslog') { 167 $syslog_id = $this->config->get('syslog_id', 'roundcube'); 168 $syslog_facility = $this->config->get('syslog_facility', LOG_USER); 169 openlog($syslog_id, LOG_ODELAY, $syslog_facility); 170 } 171 172 // connect to database 173 $this->get_dbh(); 78 protected function startup() 79 { 80 $this->init(self::INIT_WITH_DB | self::INIT_WITH_PLUGINS); 174 81 175 82 // start session … … 200 107 $GLOBALS['OUTPUT'] = $this->load_gui(!empty($_REQUEST['_framed'])); 201 108 202 // create plugin API and load plugins 203 $this->plugins = rcube_plugin_api::get_instance(); 204 205 // init plugins 206 $this->plugins->init(); 109 // load plugins 110 $this->plugins->init($this, $this->task); 111 $this->plugins->load_plugins((array)$this->config->get('plugins', array()), array('filesystem_attachments', 'jqueryui')); 207 112 } 208 113 … … 252 157 if (in_array($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ'))) 253 158 setlocale(LC_CTYPE, 'en_US' . '.utf8'); 254 }255 256 257 /**258 * Check the given string and return a valid language code259 *260 * @param string Language code261 * @return string Valid language code262 */263 private function language_prop($lang)264 {265 static $rcube_languages, $rcube_language_aliases;266 267 // user HTTP_ACCEPT_LANGUAGE if no language is specified268 if (empty($lang) || $lang == 'auto') {269 $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);270 $lang = str_replace('-', '_', $accept_langs[0]);271 }272 273 if (empty($rcube_languages)) {274 @include(INSTALL_PATH . 'program/localization/index.inc');275 }276 277 // check if we have an alias for that language278 if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {279 $lang = $rcube_language_aliases[$lang];280 }281 // try the first two chars282 else if (!isset($rcube_languages[$lang])) {283 $short = substr($lang, 0, 2);284 285 // check if we have an alias for the short language code286 if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {287 $lang = $rcube_language_aliases[$short];288 }289 // expand 'nn' to 'nn_NN'290 else if (!isset($rcube_languages[$short])) {291 $lang = $short.'_'.strtoupper($short);292 }293 }294 295 if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {296 $lang = 'en_US';297 }298 299 return $lang;300 }301 302 303 /**304 * Get the current database connection305 *306 * @return rcube_mdb2 Database connection object307 */308 public function get_dbh()309 {310 if (!$this->db) {311 $config_all = $this->config->all();312 313 $this->db = new rcube_mdb2($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']);314 $this->db->sqlite_initials = INSTALL_PATH . 'SQL/sqlite.initial.sql';315 $this->db->set_debug((bool)$config_all['sql_debug']);316 }317 318 return $this->db;319 }320 321 322 /**323 * Get global handle for memcache access324 *325 * @return object Memcache326 */327 public function get_memcache()328 {329 if (!isset($this->memcache)) {330 // no memcache support in PHP331 if (!class_exists('Memcache')) {332 $this->memcache = false;333 return false;334 }335 336 $this->memcache = new Memcache;337 $this->mc_available = 0;338 339 // add alll configured hosts to pool340 $pconnect = $this->config->get('memcache_pconnect', true);341 foreach ($this->config->get('memcache_hosts', array()) as $host) {342 list($host, $port) = explode(':', $host);343 if (!$port) $port = 11211;344 $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));345 }346 347 // test connection and failover (will result in $this->mc_available == 0 on complete failure)348 $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist349 350 if (!$this->mc_available)351 $this->memcache = false;352 }353 354 return $this->memcache;355 }356 357 358 /**359 * Callback for memcache failure360 */361 public function memcache_failure($host, $port)362 {363 static $seen = array();364 365 // only report once366 if (!$seen["$host:$port"]++) {367 $this->mc_available--;368 self::raise_error(array('code' => 604, 'type' => 'db',369 'line' => __LINE__, 'file' => __FILE__,370 'message' => "Memcache failure on host $host:$port"),371 true, false);372 }373 }374 375 376 /**377 * Initialize and get cache object378 *379 * @param string $name Cache identifier380 * @param string $type Cache type ('db', 'apc' or 'memcache')381 * @param int $ttl Expiration time for cache items in seconds382 * @param bool $packed Enables/disables data serialization383 *384 * @return rcube_cache Cache object385 */386 public function get_cache($name, $type='db', $ttl=0, $packed=true)387 {388 if (!isset($this->caches[$name])) {389 $this->caches[$name] = new rcube_cache($type, $_SESSION['user_id'], $name, $ttl, $packed);390 }391 392 return $this->caches[$name];393 159 } 394 160 … … 566 332 567 333 /** 568 * Create SMTP object and connect to server569 *570 * @param boolean True if connection should be established571 */572 public function smtp_init($connect = false)573 {574 $this->smtp = new rcube_smtp();575 576 if ($connect)577 $this->smtp->connect();578 }579 580 581 /**582 * Initialize and get storage object583 *584 * @return rcube_storage Storage object585 */586 public function get_storage()587 {588 // already initialized589 if (!is_object($this->storage)) {590 $this->storage_init();591 }592 593 return $this->storage;594 }595 596 597 /**598 * Connect to the IMAP server with stored session data.599 *600 * @return bool True on success, False on error601 * @deprecated602 */603 public function imap_connect()604 {605 return $this->storage_connect();606 }607 608 609 /**610 * Initialize IMAP object.611 *612 * @deprecated613 */614 public function imap_init()615 {616 $this->storage_init();617 }618 619 620 /**621 * Initialize storage object622 */623 public function storage_init()624 {625 // already initialized626 if (is_object($this->storage)) {627 return;628 }629 630 $driver = $this->config->get('storage_driver', 'imap');631 $driver_class = "rcube_{$driver}";632 633 if (!class_exists($driver_class)) {634 self::raise_error(array(635 'code' => 700, 'type' => 'php',636 'file' => __FILE__, 'line' => __LINE__,637 'message' => "Storage driver class ($driver) not found!"),638 true, true);639 }640 641 // Initialize storage object642 $this->storage = new $driver_class;643 644 // for backward compat. (deprecated, will be removed)645 $this->imap = $this->storage;646 647 // enable caching of mail data648 $storage_cache = $this->config->get("{$driver}_cache");649 $messages_cache = $this->config->get('messages_cache');650 // for backward compatybility651 if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {652 $storage_cache = 'db';653 $messages_cache = true;654 }655 656 if ($storage_cache)657 $this->storage->set_caching($storage_cache);658 if ($messages_cache)659 $this->storage->set_messages_caching(true);660 661 // set pagesize from config662 $pagesize = $this->config->get('mail_pagesize');663 if (!$pagesize) {664 $pagesize = $this->config->get('pagesize', 50);665 }666 $this->storage->set_pagesize($pagesize);667 668 // set class options669 $options = array(670 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'),671 'auth_cid' => $this->config->get("{$driver}_auth_cid"),672 'auth_pw' => $this->config->get("{$driver}_auth_pw"),673 'debug' => (bool) $this->config->get("{$driver}_debug"),674 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"),675 'timeout' => (int) $this->config->get("{$driver}_timeout"),676 'skip_deleted' => (bool) $this->config->get('skip_deleted'),677 'driver' => $driver,678 );679 680 if (!empty($_SESSION['storage_host'])) {681 $options['host'] = $_SESSION['storage_host'];682 $options['user'] = $_SESSION['username'];683 $options['port'] = $_SESSION['storage_port'];684 $options['ssl'] = $_SESSION['storage_ssl'];685 $options['password'] = $this->decrypt($_SESSION['password']);686 }687 688 $options = $this->plugins->exec_hook("storage_init", $options);689 690 // for backward compat. (deprecated, to be removed)691 $options = $this->plugins->exec_hook("imap_init", $options);692 693 $this->storage->set_options($options);694 $this->set_storage_prop();695 }696 697 698 /**699 * Connect to the mail storage server with stored session data700 *701 * @return bool True on success, False on error702 */703 public function storage_connect()704 {705 $storage = $this->get_storage();706 707 if ($_SESSION['storage_host'] && !$storage->is_connected()) {708 $host = $_SESSION['storage_host'];709 $user = $_SESSION['username'];710 $port = $_SESSION['storage_port'];711 $ssl = $_SESSION['storage_ssl'];712 $pass = $this->decrypt($_SESSION['password']);713 714 if (!$storage->connect($host, $user, $pass, $port, $ssl)) {715 if ($this->output)716 $this->output->show_message($storage->get_error_code() == -1 ? 'storageerror' : 'sessionerror', 'error');717 }718 else {719 $this->set_storage_prop();720 return $storage->is_connected();721 }722 }723 724 return false;725 }726 727 728 /**729 334 * Create session object and start the session. 730 335 */ … … 972 577 973 578 /** 974 * Set storage parameters.975 * This must be done AFTER connecting to the server!976 */977 private function set_storage_prop()978 {979 $storage = $this->get_storage();980 981 $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));982 983 if ($default_folders = $this->config->get('default_folders')) {984 $storage->set_default_folders($default_folders);985 }986 if (isset($_SESSION['mbox'])) {987 $storage->set_folder($_SESSION['mbox']);988 }989 if (isset($_SESSION['page'])) {990 $storage->set_page($_SESSION['page']);991 }992 }993 994 995 /**996 579 * Auto-select IMAP host based on the posted login information 997 580 * … … 1038 621 1039 622 /** 1040 * Get localized text in the desired language1041 *1042 * @param mixed $attrib Named parameters array or label name1043 * @param string $domain Label domain (plugin) name1044 *1045 * @return string Localized text1046 */1047 public function gettext($attrib, $domain=null)1048 {1049 // load localization files if not done yet1050 if (empty($this->texts))1051 $this->load_language();1052 1053 // extract attributes1054 if (is_string($attrib))1055 $attrib = array('name' => $attrib);1056 1057 $name = $attrib['name'] ? $attrib['name'] : '';1058 1059 // attrib contain text values: use them from now1060 if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us']))1061 $this->texts[$name] = $setval;1062 1063 // check for text with domain1064 if ($domain && ($text = $this->texts[$domain.'.'.$name]))1065 ;1066 // text does not exist1067 else if (!($text = $this->texts[$name])) {1068 return "[$name]";1069 }1070 1071 // replace vars in text1072 if (is_array($attrib['vars'])) {1073 foreach ($attrib['vars'] as $var_key => $var_value)1074 $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text);1075 }1076 1077 // format output1078 if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])1079 return ucfirst($text);1080 else if ($attrib['uppercase'])1081 return mb_strtoupper($text);1082 else if ($attrib['lowercase'])1083 return mb_strtolower($text);1084 1085 return strtr($text, array('\n' => "\n"));1086 }1087 1088 1089 /**1090 * Check if the given text label exists1091 *1092 * @param string $name Label name1093 * @param string $domain Label domain (plugin) name or '*' for all domains1094 * @param string $ref_domain Sets domain name if label is found1095 *1096 * @return boolean True if text exists (either in the current language or in en_US)1097 */1098 public function text_exists($name, $domain = null, &$ref_domain = null)1099 {1100 // load localization files if not done yet1101 if (empty($this->texts))1102 $this->load_language();1103 1104 if (isset($this->texts[$name])) {1105 $ref_domain = '';1106 return true;1107 }1108 1109 // any of loaded domains (plugins)1110 if ($domain == '*') {1111 foreach ($this->plugins->loaded_plugins() as $domain)1112 if (isset($this->texts[$domain.'.'.$name])) {1113 $ref_domain = $domain;1114 return true;1115 }1116 }1117 // specified domain1118 else if ($domain) {1119 $ref_domain = $domain;1120 return isset($this->texts[$domain.'.'.$name]);1121 }1122 1123 return false;1124 }1125 1126 /**1127 * Load a localization package1128 *1129 * @param string Language ID1130 */1131 public function load_language($lang = null, $add = array())1132 {1133 $lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));1134 1135 // load localized texts1136 if (empty($this->texts) || $lang != $_SESSION['language']) {1137 $this->texts = array();1138 1139 // handle empty lines after closing PHP tag in localization files1140 ob_start();1141 1142 // get english labels (these should be complete)1143 @include(INSTALL_PATH . 'program/localization/en_US/labels.inc');1144 @include(INSTALL_PATH . 'program/localization/en_US/messages.inc');1145 1146 if (is_array($labels))1147 $this->texts = $labels;1148 if (is_array($messages))1149 $this->texts = array_merge($this->texts, $messages);1150 1151 // include user language files1152 if ($lang != 'en' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {1153 include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc');1154 include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc');1155 1156 if (is_array($labels))1157 $this->texts = array_merge($this->texts, $labels);1158 if (is_array($messages))1159 $this->texts = array_merge($this->texts, $messages);1160 }1161 1162 ob_end_clean();1163 1164 $_SESSION['language'] = $lang;1165 }1166 1167 // append additional texts (from plugin)1168 if (is_array($add) && !empty($add))1169 $this->texts += $add;1170 }1171 1172 1173 /**1174 * Read directory program/localization and return a list of available languages1175 *1176 * @return array List of available localizations1177 */1178 public function list_languages()1179 {1180 static $sa_languages = array();1181 1182 if (!sizeof($sa_languages)) {1183 @include(INSTALL_PATH . 'program/localization/index.inc');1184 1185 if ($dh = @opendir(INSTALL_PATH . 'program/localization')) {1186 while (($name = readdir($dh)) !== false) {1187 if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name))1188 continue;1189 1190 if ($label = $rcube_languages[$name])1191 $sa_languages[$name] = $label;1192 }1193 closedir($dh);1194 }1195 }1196 1197 return $sa_languages;1198 }1199 1200 1201 /**1202 623 * Destroy session data and remove cookie 1203 624 */ … … 1239 660 $this->user->save_prefs(unserialize($_SESSION['preferences'])); 1240 661 } 1241 }1242 1243 1244 /**1245 * Function to be executed in script shutdown1246 * Registered with register_shutdown_function()1247 */1248 public function shutdown()1249 {1250 foreach ($this->shutdown_functions as $function)1251 call_user_func($function);1252 1253 if (is_object($this->smtp))1254 $this->smtp->disconnect();1255 1256 foreach ($this->address_books as $book) {1257 if (is_object($book) && is_a($book, 'rcube_addressbook'))1258 $book->close();1259 }1260 1261 foreach ($this->caches as $cache) {1262 if (is_object($cache))1263 $cache->close();1264 }1265 1266 if (is_object($this->storage))1267 $this->storage->close();1268 1269 // before closing the database connection, write session data1270 if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {1271 session_write_close();1272 }1273 1274 // write performance stats to logs/console1275 if ($this->config->get('devel_mode')) {1276 if (function_exists('memory_get_usage'))1277 $mem = rcube_ui::show_bytes(memory_get_usage());1278 if (function_exists('memory_get_peak_usage'))1279 $mem .= '/'.rcube_ui::show_bytes(memory_get_peak_usage());1280 1281 $log = $this->task . ($this->action ? '/'.$this->action : '') . ($mem ? " [$mem]" : '');1282 if (defined('RCMAIL_START'))1283 self::print_timer(RCMAIL_START, $log);1284 else1285 self::console($log);1286 }1287 }1288 1289 1290 /**1291 * Registers shutdown function to be executed on shutdown.1292 * The functions will be executed before destroying any1293 * objects like smtp, imap, session, etc.1294 *1295 * @param callback Function callback1296 */1297 public function add_shutdown_function($function)1298 {1299 $this->shutdown_functions[] = $function;1300 662 } 1301 663 … … 1350 712 } 1351 713 1352 1353 /**1354 * Encrypt using 3DES1355 *1356 * @param string $clear clear text input1357 * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'1358 * @param boolean $base64 whether or not to base64_encode() the result before returning1359 *1360 * @return string encrypted text1361 */1362 public function encrypt($clear, $key = 'des_key', $base64 = true)1363 {1364 if (!$clear)1365 return '';1366 /*-1367 * Add a single canary byte to the end of the clear text, which1368 * will help find out how much of padding will need to be removed1369 * upon decryption; see http://php.net/mcrypt_generic#680821370 */1371 $clear = pack("a*H2", $clear, "80");1372 1373 if (function_exists('mcrypt_module_open') &&1374 ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")))1375 {1376 $iv = $this->create_iv(mcrypt_enc_get_iv_size($td));1377 mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);1378 $cipher = $iv . mcrypt_generic($td, $clear);1379 mcrypt_generic_deinit($td);1380 mcrypt_module_close($td);1381 }1382 else {1383 @include_once 'des.inc';1384 1385 if (function_exists('des')) {1386 $des_iv_size = 8;1387 $iv = $this->create_iv($des_iv_size);1388 $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv);1389 }1390 else {1391 self::raise_error(array(1392 'code' => 500, 'type' => 'php',1393 'file' => __FILE__, 'line' => __LINE__,1394 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available"1395 ), true, true);1396 }1397 }1398 1399 return $base64 ? base64_encode($cipher) : $cipher;1400 }1401 1402 /**1403 * Decrypt 3DES-encrypted string1404 *1405 * @param string $cipher encrypted text1406 * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'1407 * @param boolean $base64 whether or not input is base64-encoded1408 *1409 * @return string decrypted text1410 */1411 public function decrypt($cipher, $key = 'des_key', $base64 = true)1412 {1413 if (!$cipher)1414 return '';1415 1416 $cipher = $base64 ? base64_decode($cipher) : $cipher;1417 1418 if (function_exists('mcrypt_module_open') &&1419 ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")))1420 {1421 $iv_size = mcrypt_enc_get_iv_size($td);1422 $iv = substr($cipher, 0, $iv_size);1423 1424 // session corruption? (#1485970)1425 if (strlen($iv) < $iv_size)1426 return '';1427 1428 $cipher = substr($cipher, $iv_size);1429 mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);1430 $clear = mdecrypt_generic($td, $cipher);1431 mcrypt_generic_deinit($td);1432 mcrypt_module_close($td);1433 }1434 else {1435 @include_once 'des.inc';1436 1437 if (function_exists('des')) {1438 $des_iv_size = 8;1439 $iv = substr($cipher, 0, $des_iv_size);1440 $cipher = substr($cipher, $des_iv_size);1441 $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv);1442 }1443 else {1444 self::raise_error(array(1445 'code' => 500, 'type' => 'php',1446 'file' => __FILE__, 'line' => __LINE__,1447 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available"1448 ), true, true);1449 }1450 }1451 1452 /*-1453 * Trim PHP's padding and the canary byte; see note in1454 * rcmail::encrypt() and http://php.net/mcrypt_generic#680821455 */1456 $clear = substr(rtrim($clear, "\0"), 0, -1);1457 1458 return $clear;1459 }1460 1461 /**1462 * Generates encryption initialization vector (IV)1463 *1464 * @param int Vector size1465 * @return string Vector string1466 */1467 private function create_iv($size)1468 {1469 // mcrypt_create_iv() can be slow when system lacks entrophy1470 // we'll generate IV vector manually1471 $iv = '';1472 for ($i = 0; $i < $size; $i++)1473 $iv .= chr(mt_rand(0, 255));1474 return $iv;1475 }1476 714 1477 715 /** … … 1500 738 } 1501 739 return $url; 1502 }1503 1504 1505 /**1506 * Use imagemagick or GD lib to read image properties1507 *1508 * @param string Absolute file path1509 * @return mixed Hash array with image props like type, width, height or False on error1510 */1511 public static function imageprops($filepath)1512 {1513 $rcmail = rcmail::get_instance();1514 if ($cmd = $rcmail->config->get('im_identify_path', false)) {1515 list(, $type, $size) = explode(' ', strtolower(rcmail::exec($cmd. ' 2>/dev/null {in}', array('in' => $filepath))));1516 if ($size)1517 list($width, $height) = explode('x', $size);1518 }1519 else if (function_exists('getimagesize')) {1520 $imsize = @getimagesize($filepath);1521 $width = $imsize[0];1522 $height = $imsize[1];1523 $type = preg_replace('!image/!', '', $imsize['mime']);1524 }1525 1526 return $type ? array('type' => $type, 'width' => $width, 'height' => $height) : false;1527 }1528 1529 1530 /**1531 * Convert an image to a given size and type using imagemagick (ensures input is an image)1532 *1533 * @param $p['in'] Input filename (mandatory)1534 * @param $p['out'] Output filename (mandatory)1535 * @param $p['size'] Width x height of resulting image, e.g. "160x60"1536 * @param $p['type'] Output file type, e.g. "jpg"1537 * @param $p['-opts'] Custom command line options to ImageMagick convert1538 * @return Success of convert as true/false1539 */1540 public static function imageconvert($p)1541 {1542 $result = false;1543 $rcmail = rcmail::get_instance();1544 $convert = $rcmail->config->get('im_convert_path', false);1545 $identify = $rcmail->config->get('im_identify_path', false);1546 1547 // imagemagick is required for this1548 if (!$convert)1549 return false;1550 1551 if (!(($imagetype = @exif_imagetype($p['in'])) && ($type = image_type_to_extension($imagetype, false))))1552 list(, $type) = explode(' ', strtolower(rcmail::exec($identify . ' 2>/dev/null {in}', $p))); # for things like eps1553 1554 $type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps"));1555 $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75);1556 $p['-opts'] = array('-resize' => $p['size'].'>') + (array)$p['-opts'];1557 1558 if (in_array($type, explode(',', $p['types']))) # Valid type?1559 $result = rcmail::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {in} {type}:{out}', $p) === "";1560 1561 return $result;1562 }1563 1564 1565 /**1566 * Construct shell command, execute it and return output as string.1567 * Keywords {keyword} are replaced with arguments1568 *1569 * @param $cmd Format string with {keywords} to be replaced1570 * @param $values (zero, one or more arrays can be passed)1571 * @return output of command. shell errors not detectable1572 */1573 public static function exec(/* $cmd, $values1 = array(), ... */)1574 {1575 $args = func_get_args();1576 $cmd = array_shift($args);1577 $values = $replacements = array();1578 1579 // merge values into one array1580 foreach ($args as $arg)1581 $values += (array)$arg;1582 1583 preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER);1584 foreach ($matches as $tags) {1585 list(, $tag, $option, $key) = $tags;1586 $parts = array();1587 1588 if ($option) {1589 foreach ((array)$values["-$key"] as $key => $value) {1590 if ($value === true || $value === false || $value === null)1591 $parts[] = $value ? $key : "";1592 else foreach ((array)$value as $val)1593 $parts[] = "$key " . escapeshellarg($val);1594 }1595 }1596 else {1597 foreach ((array)$values[$key] as $value)1598 $parts[] = escapeshellarg($value);1599 }1600 1601 $replacements[$tag] = join(" ", $parts);1602 }1603 1604 // use strtr behaviour of going through source string once1605 $cmd = strtr($cmd, $replacements);1606 1607 return (string)shell_exec($cmd);1608 740 } 1609 741 … … 1961 1093 1962 1094 /** 1963 * Replaces hostname variables.1964 *1965 * @param string $name Hostname1966 * @param string $host Optional IMAP hostname1967 *1968 * @return string Hostname1969 */1970 public static function parse_host($name, $host = '')1971 {1972 // %n - host1973 $n = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']);1974 // %d - domain name without first part, e.g. %n=mail.domain.tld, %d=domain.tld1975 $d = preg_replace('/^[^\.]+\./', '', $n);1976 // %h - IMAP host1977 $h = $_SESSION['storage_host'] ? $_SESSION['storage_host'] : $host;1978 // %z - IMAP domain without first part, e.g. %h=imap.domain.tld, %z=domain.tld1979 $z = preg_replace('/^[^\.]+\./', '', $h);1980 // %s - domain name after the '@' from e-mail address provided at login screen. Returns FALSE if an invalid email is provided1981 if (strpos($name, '%s') !== false) {1982 $user_email = rcube_ui::get_input_value('_user', rcube_ui::INPUT_POST);1983 $user_email = rcube_idn_convert($user_email, true);1984 $matches = preg_match('/(.*)@([a-z0-9\.\-\[\]\:]+)/i', $user_email, $s);1985 if ($matches < 1 || filter_var($s[1]."@".$s[2], FILTER_VALIDATE_EMAIL) === false) {1986 return false;1987 }1988 }1989 1990 $name = str_replace(array('%n', '%d', '%h', '%z', '%s'), array($n, $d, $h, $z, $s[2]), $name);1991 return $name;1992 }1993 1994 1995 /**1996 1095 * E-mail address validation. 1997 1096 * … … 2082 1181 2083 1182 /** 2084 * Print or write debug messages2085 *2086 * @param mixed Debug message or data2087 */2088 public static function console()2089 {2090 $args = func_get_args();2091 2092 if (class_exists('rcmail', false)) {2093 $rcmail = rcmail::get_instance();2094 if (is_object($rcmail->plugins)) {2095 $plugin = $rcmail->plugins->exec_hook('console', array('args' => $args));2096 if ($plugin['abort']) {2097 return;2098 }2099 $args = $plugin['args'];2100 }2101 }2102 2103 $msg = array();2104 foreach ($args as $arg) {2105 $msg[] = !is_string($arg) ? var_export($arg, true) : $arg;2106 }2107 2108 self::write_log('console', join(";\n", $msg));2109 }2110 2111 2112 /**2113 * Append a line to a logfile in the logs directory.2114 * Date will be added automatically to the line.2115 *2116 * @param $name name of log file2117 * @param line Line to append2118 */2119 public static function write_log($name, $line)2120 {2121 global $RCMAIL;2122 2123 if (!is_string($line)) {2124 $line = var_export($line, true);2125 }2126 2127 $date_format = $RCMAIL ? $RCMAIL->config->get('log_date_format') : null;2128 $log_driver = $RCMAIL ? $RCMAIL->config->get('log_driver') : null;2129 2130 if (empty($date_format)) {2131 $date_format = 'd-M-Y H:i:s O';2132 }2133 2134 $date = date($date_format);2135 2136 // trigger logging hook2137 if (is_object($RCMAIL) && is_object($RCMAIL->plugins)) {2138 $log = $RCMAIL->plugins->exec_hook('write_log', array('name' => $name, 'date' => $date, 'line' => $line));2139 $name = $log['name'];2140 $line = $log['line'];2141 $date = $log['date'];2142 if ($log['abort'])2143 return true;2144 }2145 2146 if ($log_driver == 'syslog') {2147 $prio = $name == 'errors' ? LOG_ERR : LOG_INFO;2148 syslog($prio, $line);2149 return true;2150 }2151 2152 // log_driver == 'file' is assumed here2153 2154 $line = sprintf("[%s]: %s\n", $date, $line);2155 $log_dir = $RCMAIL ? $RCMAIL->config->get('log_dir') : null;2156 2157 if (empty($log_dir)) {2158 $log_dir = INSTALL_PATH . 'logs';2159 }2160 2161 // try to open specific log file for writing2162 $logfile = $log_dir.'/'.$name;2163 2164 if ($fp = @fopen($logfile, 'a')) {2165 fwrite($fp, $line);2166 fflush($fp);2167 fclose($fp);2168 return true;2169 }2170 2171 trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING);2172 return false;2173 }2174 2175 2176 /**2177 * Throw system error (and show error page).2178 *2179 * @param array Named parameters2180 * - code: Error code (required)2181 * - type: Error type [php|db|imap|javascript] (required)2182 * - message: Error message2183 * - file: File where error occured2184 * - line: Line where error occured2185 * @param boolean True to log the error2186 * @param boolean Terminate script execution2187 */2188 public static function raise_error($arg = array(), $log = false, $terminate = false)2189 {2190 // installer2191 if (class_exists('rcube_install', false)) {2192 $rci = rcube_install::get_instance();2193 $rci->raise_error($arg);2194 return;2195 }2196 2197 if ($log && $arg['type'] && $arg['message']) {2198 self::log_bug($arg);2199 }2200 2201 // display error page and terminate script2202 if ($terminate) {2203 rcube_ui::raise_error($arg['code'], $arg['message']);2204 }2205 }2206 2207 2208 /**2209 * Report error according to configured debug_level2210 *2211 * @param array Named parameters2212 * @see self::raise_error()2213 */2214 public static function log_bug($arg_arr)2215 {2216 $rcmail = rcmail::get_instance();2217 $program = strtoupper($arg_arr['type']);2218 $level = $rcmail->config->get('debug_level');2219 2220 // disable errors for ajax requests, write to log instead (#1487831)2221 if (($level & 4) && !empty($_REQUEST['_remote'])) {2222 $level = ($level ^ 4) | 1;2223 }2224 2225 // write error to local log file2226 if ($level & 1) {2227 if ($_SERVER['REQUEST_METHOD'] == 'POST') {2228 $post_query = '?_task='.urlencode($_POST['_task']).'&_action='.urlencode($_POST['_action']);2229 }2230 else {2231 $post_query = '';2232 }2233 2234 $log_entry = sprintf("%s Error: %s%s (%s %s)",2235 $program,2236 $arg_arr['message'],2237 $arg_arr['file'] ? sprintf(' in %s on line %d', $arg_arr['file'], $arg_arr['line']) : '',2238 $_SERVER['REQUEST_METHOD'],2239 $_SERVER['REQUEST_URI'] . $post_query);2240 2241 if (!self::write_log('errors', $log_entry)) {2242 // send error to PHPs error handler if write_log didn't succeed2243 trigger_error($arg_arr['message']);2244 }2245 }2246 2247 // report the bug to the global bug reporting system2248 if ($level & 2) {2249 // TODO: Send error via HTTP2250 }2251 2252 // show error if debug_mode is on2253 if ($level & 4) {2254 print "<b>$program Error";2255 2256 if (!empty($arg_arr['file']) && !empty($arg_arr['line'])) {2257 print " in $arg_arr[file] ($arg_arr[line])";2258 }2259 2260 print ':</b> ';2261 print nl2br($arg_arr['message']);2262 print '<br />';2263 flush();2264 }2265 }2266 2267 2268 /**2269 1183 * Write login data (name, ID, IP address) to the 'userlogins' log file. 2270 1184 */ … … 2285 1199 sprintf('Successful login for %s (ID: %d) from %s in session %s', 2286 1200 $user_name, $user_id, self::remote_ip(), session_id())); 2287 }2288 2289 2290 /**2291 * Returns remote IP address and forwarded addresses if found2292 *2293 * @return string Remote IP address(es)2294 */2295 public static function remote_ip()2296 {2297 $address = $_SERVER['REMOTE_ADDR'];2298 2299 // append the NGINX X-Real-IP header, if set2300 if (!empty($_SERVER['HTTP_X_REAL_IP'])) {2301 $remote_ip[] = 'X-Real-IP: ' . $_SERVER['HTTP_X_REAL_IP'];2302 }2303 // append the X-Forwarded-For header, if set2304 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {2305 $remote_ip[] = 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR'];2306 }2307 2308 if (!empty($remote_ip)) {2309 $address .= '(' . implode(',', $remote_ip) . ')';2310 }2311 2312 return $address;2313 1201 } 2314 1202 … … 2328 1216 2329 1217 /** 2330 * Returns current time (with microseconds).2331 *2332 * @return float Current time in seconds since the Unix2333 */2334 public static function timer()2335 {2336 return microtime(true);2337 }2338 2339 2340 /**2341 * Logs time difference according to provided timer2342 *2343 * @param float $timer Timer (self::timer() result)2344 * @param string $label Log line prefix2345 * @param string $dest Log file name2346 *2347 * @see self::timer()2348 */2349 public static function print_timer($timer, $label = 'Timer', $dest = 'console')2350 {2351 static $print_count = 0;2352 2353 $print_count++;2354 $now = self::timer();2355 $diff = $now - $timer;2356 2357 if (empty($label)) {2358 $label = 'Timer '.$print_count;2359 }2360 2361 self::write_log($dest, sprintf("%s: %0.4f sec", $label, $diff));2362 }2363 2364 2365 /**2366 1218 * Garbage collector function for temp files. 2367 1219 * Remove temp files older than two days … … 2412 1264 } 2413 1265 2414 2415 /**2416 * Getter for logged user ID.2417 *2418 * @return mixed User identifier2419 */2420 public function get_user_id()2421 {2422 if (is_object($this->user)) {2423 return $this->user->ID;2424 }2425 }2426 2427 2428 /**2429 * Getter for logged user name.2430 *2431 * @return string User name2432 */2433 public function get_user_name()2434 {2435 if (is_object($this->user)) {2436 return $this->user->get_username();2437 }2438 }2439 1266 }
Note: See TracChangeset
for help on using the changeset viewer.
