1<?php 2 3/** 4 +-----------------------------------------------------------------------+ 5 | This file is part of the Roundcube webmail client | 6 | | 7 | Copyright (C) The Roundcube Dev Team | 8 | | 9 | Licensed under the GNU General Public License version 3 or | 10 | any later version with exceptions for skins & plugins. | 11 | See the README file for a full license statement. | 12 | | 13 | CONTENTS: | 14 | Roundcube Framework Initialization | 15 +-----------------------------------------------------------------------+ 16 | Author: Thomas Bruederli <roundcube@gmail.com> | 17 | Author: Aleksander Machniak <alec@alec.pl> | 18 +-----------------------------------------------------------------------+ 19*/ 20 21/** 22 * Roundcube Framework Initialization 23 * 24 * @package Framework 25 * @subpackage Core 26 */ 27 28$config = [ 29 'error_reporting' => E_ALL & ~E_NOTICE & ~E_STRICT, 30 'display_errors' => false, 31 'log_errors' => true, 32 // Some users are not using Installer, so we'll check some 33 // critical PHP settings here. Only these, which doesn't provide 34 // an error/warning in the logs later. See (#1486307). 35 'mbstring.func_overload' => 0, 36]; 37 38// check these additional ini settings if not called via CLI 39if (php_sapi_name() != 'cli') { 40 $config += [ 41 'suhosin.session.encrypt' => false, 42 'file_uploads' => true, 43 'session.auto_start' => false, 44 'zlib.output_compression' => false, 45 ]; 46} 47 48foreach ($config as $optname => $optval) { 49 $ini_optval = filter_var(ini_get($optname), is_bool($optval) ? FILTER_VALIDATE_BOOLEAN : FILTER_VALIDATE_INT); 50 if ($optval != $ini_optval && @ini_set($optname, $optval) === false) { 51 $optval = !is_bool($optval) ? $optval : ($optval ? 'On' : 'Off'); 52 $error = "ERROR: Wrong '$optname' option value and it wasn't possible to set it to required value ($optval).\n" 53 . "Check your PHP configuration (including php_admin_flag)."; 54 55 if (defined('STDERR')) fwrite(STDERR, $error); else echo $error; 56 exit(1); 57 } 58} 59 60// framework constants 61define('RCUBE_VERSION', '1.5.1'); 62define('RCUBE_CHARSET', 'UTF-8'); 63define('RCUBE_TEMP_FILE_PREFIX', 'RCMTEMP'); 64 65if (!defined('RCUBE_LIB_DIR')) { 66 define('RCUBE_LIB_DIR', __DIR__ . '/'); 67} 68 69if (!defined('RCUBE_INSTALL_PATH')) { 70 define('RCUBE_INSTALL_PATH', RCUBE_LIB_DIR); 71} 72 73if (!defined('RCUBE_CONFIG_DIR')) { 74 define('RCUBE_CONFIG_DIR', RCUBE_INSTALL_PATH . 'config/'); 75} 76 77if (!defined('RCUBE_PLUGINS_DIR')) { 78 define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'plugins/'); 79} 80 81if (!defined('RCUBE_LOCALIZATION_DIR')) { 82 define('RCUBE_LOCALIZATION_DIR', RCUBE_INSTALL_PATH . 'localization/'); 83} 84 85// set internal encoding for mbstring extension 86mb_internal_encoding(RCUBE_CHARSET); 87mb_regex_encoding(RCUBE_CHARSET); 88 89// make sure the Roundcube lib directory is in the include_path 90$rcube_path = realpath(RCUBE_LIB_DIR . '..'); 91$sep = PATH_SEPARATOR; 92$regexp = "!(^|$sep)" . preg_quote($rcube_path, '!') . "($sep|\$)!"; 93$path = ini_get('include_path'); 94 95if (!preg_match($regexp, $path)) { 96 set_include_path($path . PATH_SEPARATOR . $rcube_path); 97} 98 99// Register autoloader 100spl_autoload_register('rcube_autoload'); 101 102// set PEAR error handling (will also load the PEAR main class) 103PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, function($err) { rcube::raise_error($err, true); }); 104 105 106/** 107 * Similar function as in_array() but case-insensitive with multibyte support. 108 * 109 * @param string $needle Needle value 110 * @param array $heystack Array to search in 111 * 112 * @return bool True if found, False if not 113 */ 114function in_array_nocase($needle, $haystack) 115{ 116 // use much faster method for ascii 117 if (is_ascii($needle)) { 118 foreach ((array) $haystack as $value) { 119 if (strcasecmp($value, $needle) === 0) { 120 return true; 121 } 122 } 123 } 124 else { 125 $needle = mb_strtolower($needle); 126 foreach ((array) $haystack as $value) { 127 if ($needle === mb_strtolower($value)) { 128 return true; 129 } 130 } 131 } 132 133 return false; 134} 135 136/** 137 * Parse a human readable string for a number of bytes. 138 * 139 * @param string $str Input string 140 * 141 * @return float Number of bytes 142 */ 143function parse_bytes($str) 144{ 145 if (is_numeric($str)) { 146 return floatval($str); 147 } 148 149 $bytes = 0; 150 151 if (preg_match('/([0-9\.]+)\s*([a-z]*)/i', $str, $regs)) { 152 $bytes = floatval($regs[1]); 153 switch (strtolower($regs[2])) { 154 case 'g': 155 case 'gb': 156 $bytes *= 1073741824; 157 break; 158 case 'm': 159 case 'mb': 160 $bytes *= 1048576; 161 break; 162 case 'k': 163 case 'kb': 164 $bytes *= 1024; 165 break; 166 } 167 } 168 169 return floatval($bytes); 170} 171 172/** 173 * Make sure the string ends with a slash 174 * 175 * @param string $str A string 176 * 177 * @return string A string ending with a slash 178 */ 179function slashify($str) 180{ 181 return unslashify($str) . '/'; 182} 183 184/** 185 * Remove slashes at the end of the string 186 * 187 * @param string $str A string 188 * 189 * @return string A string ending with no slash 190 */ 191function unslashify($str) 192{ 193 return rtrim($str, '/'); 194} 195 196/** 197 * Returns number of seconds for a specified offset string. 198 * 199 * @param string $str String representation of the offset (e.g. 20min, 5h, 2days, 1week) 200 * 201 * @return int Number of seconds 202 */ 203function get_offset_sec($str) 204{ 205 if (preg_match('/^([0-9]+)\s*([smhdw])/i', $str, $regs)) { 206 $amount = (int) $regs[1]; 207 $unit = strtolower($regs[2]); 208 } 209 else { 210 $amount = (int) $str; 211 $unit = 's'; 212 } 213 214 switch ($unit) { 215 case 'w': 216 $amount *= 7; 217 case 'd': 218 $amount *= 24; 219 case 'h': 220 $amount *= 60; 221 case 'm': 222 $amount *= 60; 223 } 224 225 return $amount; 226} 227 228/** 229 * Create a unix timestamp with a specified offset from now. 230 * 231 * @param string $offset_str String representation of the offset (e.g. 20min, 5h, 2days) 232 * @param int $factor Factor to multiply with the offset 233 * 234 * @return int Unix timestamp 235 */ 236function get_offset_time($offset_str, $factor = 1) 237{ 238 return time() + get_offset_sec($offset_str) * $factor; 239} 240 241/** 242 * Truncate string if it is longer than the allowed length. 243 * Replace the middle or the ending part of a string with a placeholder. 244 * 245 * @param string $str Input string 246 * @param int $maxlength Max. length 247 * @param string $placeholder Replace removed chars with this 248 * @param bool $ending Set to True if string should be truncated from the end 249 * 250 * @return string Abbreviated string 251 */ 252function abbreviate_string($str, $maxlength, $placeholder = '...', $ending = false) 253{ 254 $length = mb_strlen($str); 255 256 if ($length > $maxlength) { 257 if ($ending) { 258 return mb_substr($str, 0, $maxlength) . $placeholder; 259 } 260 261 $placeholder_length = mb_strlen($placeholder); 262 $first_part_length = floor(($maxlength - $placeholder_length)/2); 263 $second_starting_location = $length - $maxlength + $first_part_length + $placeholder_length; 264 265 $prefix = mb_substr($str, 0, $first_part_length); 266 $suffix = mb_substr($str, $second_starting_location); 267 $str = $prefix . $placeholder . $suffix; 268 } 269 270 return $str; 271} 272 273/** 274 * Get all keys from array (recursive). 275 * 276 * @param array $array Input array 277 * 278 * @return array List of array keys 279 */ 280function array_keys_recursive($array) 281{ 282 $keys = []; 283 284 if (!empty($array) && is_array($array)) { 285 foreach ($array as $key => $child) { 286 $keys[] = $key; 287 foreach (array_keys_recursive($child) as $val) { 288 $keys[] = $val; 289 } 290 } 291 } 292 293 return $keys; 294} 295 296/** 297 * Get first element from an array 298 * 299 * @param array $array Input array 300 * 301 * @return mixed First element if found, Null otherwise 302 */ 303function array_first($array) 304{ 305 if (is_array($array)) { 306 reset($array); 307 foreach ($array as $element) { 308 return $element; 309 } 310 } 311} 312 313/** 314 * Remove all non-ascii and non-word chars except ., -, _ 315 * 316 * @param string $str A string 317 * @param bool $css_id The result may be used as CSS identifier 318 * @param string $replace_with Replacement character 319 * 320 * @return string Clean string 321 */ 322function asciiwords($str, $css_id = false, $replace_with = '') 323{ 324 $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : ''); 325 return preg_replace("/[^$allowed]+/i", $replace_with, $str); 326} 327 328/** 329 * Check if a string contains only ascii characters 330 * 331 * @param string $str String to check 332 * @param bool $control_chars Includes control characters 333 * 334 * @return bool True if the string contains ASCII-only, False otherwise 335 */ 336function is_ascii($str, $control_chars = true) 337{ 338 $regexp = $control_chars ? '/[^\x00-\x7F]/' : '/[^\x20-\x7E]/'; 339 return preg_match($regexp, $str) ? false : true; 340} 341 342/** 343 * Compose a valid representation of name and e-mail address 344 * 345 * @param string $email E-mail address 346 * @param string $name Person name 347 * 348 * @return string Formatted string 349 */ 350function format_email_recipient($email, $name = '') 351{ 352 $email = trim($email); 353 354 if ($name && $name != $email) { 355 // Special chars as defined by RFC 822 need to in quoted string (or escaped). 356 if (preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name)) { 357 $name = '"'.addcslashes($name, '"').'"'; 358 } 359 360 return "$name <$email>"; 361 } 362 363 return $email; 364} 365 366/** 367 * Format e-mail address 368 * 369 * @param string $email E-mail address 370 * 371 * @return string Formatted e-mail address 372 */ 373function format_email($email) 374{ 375 $email = trim($email); 376 $parts = explode('@', $email); 377 $count = count($parts); 378 379 if ($count > 1) { 380 $parts[$count-1] = mb_strtolower($parts[$count-1]); 381 382 $email = implode('@', $parts); 383 } 384 385 return $email; 386} 387 388/** 389 * Fix version number so it can be used correctly in version_compare() 390 * 391 * @param string $version Version number string 392 * 393 * @param return Version number string 394 */ 395function version_parse($version) 396{ 397 return str_replace( 398 ['-stable', '-git'], 399 ['.0', '.99'], 400 $version 401 ); 402} 403 404/** 405 * Use PHP5 autoload for dynamic class loading 406 * 407 * @param string $classname Class name 408 * 409 * @return bool True when the class file has been found 410 * 411 * @todo Make Zend, PEAR etc play with this 412 * @todo Make our classes conform to a more straight forward CS. 413 */ 414function rcube_autoload($classname) 415{ 416 if (strpos($classname, 'rcube') === 0) { 417 $classname = preg_replace('/^rcube_(cache|db|session|spellchecker)_/', '\\1/', $classname); 418 $classname = 'Roundcube/' . $classname; 419 } 420 else if (strpos($classname, 'html_') === 0 || $classname === 'html') { 421 $classname = 'Roundcube/html'; 422 } 423 else if (strpos($classname, 'Mail_') === 0) { 424 $classname = 'Mail/' . substr($classname, 5); 425 } 426 else if (strpos($classname, 'Net_') === 0) { 427 $classname = 'Net/' . substr($classname, 4); 428 } 429 else if (strpos($classname, 'Auth_') === 0) { 430 $classname = 'Auth/' . substr($classname, 5); 431 } 432 433 // Translate PHP namespaces into directories, 434 // i.e. use \Sabre\VObject; $vcf = VObject\Reader::read(...) 435 // -> Sabre/VObject/Reader.php 436 $classname = str_replace('\\', '/', $classname); 437 438 if ($fp = @fopen("$classname.php", 'r', true)) { 439 fclose($fp); 440 include_once "$classname.php"; 441 return true; 442 } 443 444 return false; 445} 446