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