thomascube
2010-12-17 db1a87cd6c506f2afbd1a37c64cb56ae11120b49
commit | author | age
93e3ae 1 <?php
4e17e6 2
T 3 /*
4  +-----------------------------------------------------------------------+
5  | program/include/main.inc                                              |
6  |                                                                       |
e019f2 7  | This file is part of the Roundcube Webmail client                     |
A 8  | Copyright (C) 2005-2009, Roundcube Dev, - Switzerland                 |
30233b 9  | Licensed under the GNU GPL                                            |
4e17e6 10  |                                                                       |
T 11  | PURPOSE:                                                              |
12  |   Provide basic functions for the webmail package                     |
13  |                                                                       |
14  +-----------------------------------------------------------------------+
15  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16  +-----------------------------------------------------------------------+
17
18  $Id$
19
20 */
21
6d969b 22 /**
e019f2 23  * Roundcube Webmail common functions
6d969b 24  *
T 25  * @package Core
26  * @author Thomas Bruederli <roundcube@gmail.com>
27  */
28
0af7e8 29 require_once('lib/utf7.inc');
97bd2c 30 require_once('include/rcube_shared.inc');
4e17e6 31
1a7f99 32 // fallback if not PHP modules are available
T 33 @include_once('lib/utf8.class.php');
4e17e6 34
ea7c46 35 // define constannts for input reading
T 36 define('RCUBE_INPUT_GET', 0x0101);
37 define('RCUBE_INPUT_POST', 0x0102);
38 define('RCUBE_INPUT_GPC', 0x0103);
39
40
4e17e6 41
6d969b 42 /**
T 43  * Return correct name for a specific database table
44  *
45  * @param string Table name
46  * @return string Translated table name
47  */
4e17e6 48 function get_table_name($table)
T 49   {
50   global $CONFIG;
653242 51
4e17e6 52   // return table name if configured
T 53   $config_key = 'db_table_'.$table;
54
55   if (strlen($CONFIG[$config_key]))
56     return $CONFIG[$config_key];
653242 57
4e17e6 58   return $table;
T 59   }
60
61
6d969b 62 /**
T 63  * Return correct name for a specific database sequence
653242 64  * (used for Postgres only)
6d969b 65  *
T 66  * @param string Secuence name
67  * @return string Translated sequence name
68  */
1cded8 69 function get_sequence_name($sequence)
T 70   {
e1ac21 71   // return sequence name if configured
1cded8 72   $config_key = 'db_sequence_'.$sequence;
54dd42 73   $opt = rcmail::get_instance()->config->get($config_key);
1cded8 74
54dd42 75   if (!empty($opt))
3e8483 76     return $opt;
54dd42 77     
ae8f19 78   return $sequence;
1cded8 79   }
0af7e8 80
T 81
6d969b 82 /**
1854c4 83  * Get localized text in the desired language
T 84  * It's a global wrapper for rcmail::gettext()
6d969b 85  *
1854c4 86  * @param mixed Named parameters array or label name
T 87  * @return string Localized text
88  * @see rcmail::gettext()
6d969b 89  */
cc97ea 90 function rcube_label($p, $domain=null)
1854c4 91 {
cc97ea 92   return rcmail::get_instance()->gettext($p, $domain);
1854c4 93 }
4e17e6 94
T 95
6d969b 96 /**
T 97  * Overwrite action variable
98  *
99  * @param string New action value
100  */
10a699 101 function rcmail_overwrite_action($action)
T 102   {
197601 103   $app = rcmail::get_instance();
T 104   $app->action = $action;
105   $app->output->set_env('action', $action);
10a699 106   }
T 107
108
41bece 109 /**
T 110  * Compose an URL for a specific action
111  *
112  * @param string  Request action
113  * @param array   More URL parameters
114  * @param string  Request task (omit if the same)
115  * @return The application URL
116  */
117 function rcmail_url($action, $p=array(), $task=null)
f11541 118 {
197601 119   $app = rcmail::get_instance();
fde466 120   return $app->url((array)$p + array('_action' => $action, 'task' => $task));
f11541 121 }
9fee0e 122
4e17e6 123
6d969b 124 /**
T 125  * Garbage collector function for temp files.
126  * Remove temp files older than two days
127  */
70d4b9 128 function rcmail_temp_gc()
1cded8 129   {
c5ee03 130   $rcmail = rcmail::get_instance();
A 131
132   $tmp = unslashify($rcmail->config->get('temp_dir'));
70d4b9 133   $expire = mktime() - 172800;  // expire in 48 hours
1cded8 134
70d4b9 135   if ($dir = opendir($tmp))
1cded8 136     {
70d4b9 137     while (($fname = readdir($dir)) !== false)
T 138       {
139       if ($fname{0} == '.')
140         continue;
141
142       if (filemtime($tmp.'/'.$fname) < $expire)
143         @unlink($tmp.'/'.$fname);
144       }
145
146     closedir($dir);
147     }
1cded8 148   }
T 149
150
6d969b 151 /**
T 152  * Garbage collector for cache entries.
153  * Remove all expired message cache records
5c461b 154  * @return void
6d969b 155  */
29c64b 156 function rcmail_cache_gc()
cc9570 157   {
29c64b 158   $rcmail = rcmail::get_instance();
T 159   $db = $rcmail->get_dbh();
cc9570 160   
T 161   // get target timestamp
29c64b 162   $ts = get_offset_time($rcmail->config->get('message_cache_lifetime', '30d'), -1);
cc9570 163   
29c64b 164   $db->query("DELETE FROM ".get_table_name('messages')."
T 165              WHERE  created < " . $db->fromunixtime($ts));
166
167   $db->query("DELETE FROM ".get_table_name('cache')."
168               WHERE  created < " . $db->fromunixtime($ts));
cc9570 169   }
T 170
1cded8 171
2bca6e 172 /**
f52e7a 173  * Catch an error and throw an exception.
A 174  *
175  * @param  int    Level of the error
176  * @param  string Error message
177  */ 
178 function rcube_error_handler($errno, $errstr)
179   {
180   throw new ErrorException($errstr, 0, $errno);
181   }
182
183
184 /**
2bca6e 185  * Convert a string from one charset to another.
T 186  * Uses mbstring and iconv functions if possible
187  *
188  * @param  string Input string
189  * @param  string Suspected charset of the input string
f11541 190  * @param  string Target charset to convert to; defaults to RCMAIL_CHARSET
5c461b 191  * @return string Converted string
2bca6e 192  */
3f9edb 193 function rcube_charset_convert($str, $from, $to=NULL)
0af7e8 194   {
f52e7a 195   static $iconv_options = null;
77e232 196   static $mbstring_loaded = null;
A 197   static $mbstring_list = null;
198   static $convert_warning = false;
ce72e0 199   static $conv = null;
b8d8cb 200
dbe44c 201   $error = false;
f88d41 202
b77c9d 203   $to = empty($to) ? strtoupper(RCMAIL_CHARSET) : rcube_parse_charset($to);
dbe44c 204   $from = rcube_parse_charset($from);
65d710 205
c9a2fa 206   if ($from == $to || empty($str) || empty($from))
3f9edb 207     return $str;
ca85b1 208
acbf0f 209   // convert charset using iconv module
A 210   if (function_exists('iconv') && $from != 'UTF7-IMAP' && $to != 'UTF7-IMAP') {
f52e7a 211     if ($iconv_options === null) {
895d4e 212       // ignore characters not available in output charset
A 213       $iconv_options = '//IGNORE';
f52e7a 214       if (iconv('', $iconv_options, '') === false) {
A 215         // iconv implementation does not support options
216         $iconv_options = '';
217       }
218     }
b69560 219
f52e7a 220     // throw an exception if iconv reports an illegal character in input
A 221     // it means that input string has been truncated
222     set_error_handler('rcube_error_handler', E_NOTICE);
223     try {
224       $_iconv = iconv($from, $to . $iconv_options, $str);
225     } catch (ErrorException $e) {
226       $_iconv = false;
227     }
228     restore_error_handler();
ae8a60 229     if ($_iconv !== false) {
f52e7a 230       return $_iconv;
0393da 231     }
ae8a60 232   }
5f56a5 233
f52e7a 234   if ($mbstring_loaded === null)
d99b93 235     $mbstring_loaded = extension_loaded('mbstring');
A 236     
197601 237   // convert charset using mbstring module
ae8a60 238   if ($mbstring_loaded) {
b19536 239     $aliases['WINDOWS-1257'] = 'ISO-8859-13';
88f66e 240     
f52e7a 241     if ($mbstring_list === null) {
77e232 242       $mbstring_list = mb_list_encodings();
A 243       $mbstring_list = array_map('strtoupper', $mbstring_list);
244     }
dbe44c 245
77e232 246     $mb_from = $aliases[$from] ? $aliases[$from] : $from;
A 247     $mb_to = $aliases[$to] ? $aliases[$to] : $to;
248     
249     // return if encoding found, string matches encoding and convert succeeded
ae8a60 250     if (in_array($mb_from, $mbstring_list) && in_array($mb_to, $mbstring_list)) {
T 251       if (mb_check_encoding($str, $mb_from) && ($out = mb_convert_encoding($str, $mb_to, $mb_from)))
252         return $out;
83dbb7 253     }
ae8a60 254   }
65d710 255
ce72e0 256   // convert charset using bundled classes/functions
a5897a 257   if ($to == 'UTF-8') {
A 258     if ($from == 'UTF7-IMAP') {
259       if ($_str = utf7_to_utf8($str))
ce72e0 260         return $_str;
a5897a 261     }
A 262     else if ($from == 'UTF-7') {
263       if ($_str = rcube_utf7_to_utf8($str))
ce72e0 264         return $_str;
a5897a 265     }
A 266     else if (($from == 'ISO-8859-1') && function_exists('utf8_encode')) {
ce72e0 267       return utf8_encode($str);
a5897a 268     }
ce72e0 269     else if (class_exists('utf8')) {
A 270       if (!$conv)
271         $conv = new utf8($from);
272       else
273         $conv->loadCharset($from);
274
275       if($_str = $conv->strToUtf8($str))
276         return $_str;
a5897a 277     }
ce72e0 278     $error = true;
ae8a60 279   }
a5897a 280   
3f9edb 281   // encode string for output
a5897a 282   if ($from == 'UTF-8') {
A 283     // @TODO: we need a function for UTF-7 (RFC2152) conversion
284     if ($to == 'UTF7-IMAP' || $to == 'UTF-7') {
285       if ($_str = utf8_to_utf7($str))
ce72e0 286         return $_str;
a5897a 287     }
A 288     else if ($to == 'ISO-8859-1' && function_exists('utf8_decode')) {
289       return utf8_decode($str);
290     }
ce72e0 291     else if (class_exists('utf8')) {
A 292       if (!$conv)
293         $conv = new utf8($to);
294       else
295         $conv->loadCharset($from);
296
297       if ($_str = $conv->strToUtf8($str))
298         return $_str;
a5897a 299     }
ce72e0 300     $error = true;
ae8a60 301   }
T 302   
1a7f99 303   // report error
ce72e0 304   if ($error && !$convert_warning) {
1a7f99 305     raise_error(array(
T 306       'code' => 500,
307       'type' => 'php',
308       'file' => __FILE__,
ce72e0 309       'line' => __LINE__,
dbe44c 310       'message' => "Could not convert string from $from to $to. Make sure iconv/mbstring is installed or lib/utf8.class is available."
1a7f99 311       ), true, false);
T 312     
313     $convert_warning = true;
ae8a60 314   }
1a7f99 315   
ce72e0 316   // return UTF-8 or original string
3f9edb 317   return $str;
0af7e8 318   }
3f9edb 319
0af7e8 320
2bca6e 321 /**
dbe44c 322  * Parse and validate charset name string (see #1485758).
A 323  * Sometimes charset string is malformed, there are also charset aliases 
324  * but we need strict names for charset conversion (specially utf8 class)
325  *
5c461b 326  * @param  string Input charset name
A 327  * @return string The validated charset name
dbe44c 328  */
46a138 329 function rcube_parse_charset($input)
dbe44c 330   {
46a138 331   static $charsets = array();
A 332   $charset = strtoupper($input);
333
334   if (isset($charsets[$input]))
335     return $charsets[$input];
dbe44c 336
7d0b34 337   $charset = preg_replace(array(
4d7acb 338     '/^[^0-9A-Z]+/',    // e.g. _ISO-8859-JP$SIO
A 339     '/\$.*$/',          // e.g. _ISO-8859-JP$SIO
340     '/UNICODE-1-1-*/',  // RFC1641/1642
341     '/^X-/',            // X- prefix (e.g. X-ROMAN8 => ROMAN8)
7d0b34 342     ), '', $charset);
dbe44c 343
b8d8cb 344   if ($charset == 'BINARY')
A 345     return $charsets[$input] = null;
346
b1fb69 347   # Aliases: some of them from HTML5 spec.
dbe44c 348   $aliases = array(
ca85b1 349     'USASCII'       => 'WINDOWS-1252',
A 350     'ANSIX31101983' => 'WINDOWS-1252',
351     'ANSIX341968'   => 'WINDOWS-1252',
dbe44c 352     'UNKNOWN8BIT'   => 'ISO-8859-15',
5dc7c2 353     'UNKNOWN'       => 'ISO-8859-15',
A 354     'USERDEFINED'   => 'ISO-8859-15',
dbe44c 355     'KSC56011987'   => 'EUC-KR',
b1fb69 356     'GB2312'         => 'GBK',
A 357     'GB231280'        => 'GBK',
dbe44c 358     'UNICODE'        => 'UTF-8',
b1fb69 359     'UTF7IMAP'        => 'UTF7-IMAP',
A 360     'TIS620'        => 'WINDOWS-874',
361     'ISO88599'        => 'WINDOWS-1254',
362     'ISO885911'        => 'WINDOWS-874',
c8729e 363     'MACROMAN'        => 'MACINTOSH',
228075 364     '77'            => 'MAC',
A 365     '128'           => 'SHIFT-JIS',
366     '129'           => 'CP949',
367     '130'           => 'CP1361',
1e3271 368     '134'           => 'GBK',
228075 369     '136'           => 'BIG5',
A 370     '161'           => 'WINDOWS-1253',
371     '162'           => 'WINDOWS-1254',
372     '163'           => 'WINDOWS-1258',
373     '177'           => 'WINDOWS-1255',
374     '178'           => 'WINDOWS-1256',
375     '186'           => 'WINDOWS-1257',
376     '204'           => 'WINDOWS-1251',
377     '222'           => 'WINDOWS-874',
1e3271 378     '238'           => 'WINDOWS-1250',
4c6a61 379     'MS950'         => 'CP950',
03d3ba 380     'WINDOWS949'    => 'UHC',
dbe44c 381   );
A 382
4d7acb 383   // allow A-Z and 0-9 only
A 384   $str = preg_replace('/[^A-Z0-9]/', '', $charset);
dbe44c 385
A 386   if (isset($aliases[$str]))
46a138 387     $result = $aliases[$str];
A 388   // UTF
389   else if (preg_match('/U[A-Z][A-Z](7|8|16|32)(BE|LE)*/', $str, $m))
390     $result = 'UTF-' . $m[1] . $m[2];
391   // ISO-8859
392   else if (preg_match('/ISO8859([0-9]{0,2})/', $str, $m)) {
ca85b1 393     $iso = 'ISO-8859-' . ($m[1] ? $m[1] : 1);
46a138 394     // some clients sends windows-1252 text as latin1,
A 395     // it is safe to use windows-1252 for all latin1
396     $result = $iso == 'ISO-8859-1' ? 'WINDOWS-1252' : $iso;
ca85b1 397     }
1e3271 398   // handle broken charset names e.g. WINDOWS-1250HTTP-EQUIVCONTENT-TYPE
46a138 399   else if (preg_match('/(WIN|WINDOWS)([0-9]+)/', $str, $m)) {
A 400     $result = 'WINDOWS-' . $m[2];
401     }
4d7acb 402   // LATIN
1e3ba3 403   else if (preg_match('/LATIN(.*)/', $str, $m)) {
4d7acb 404     $aliases = array('2' => 2, '3' => 3, '4' => 4, '5' => 9, '6' => 10,
A 405         '7' => 13, '8' => 14, '9' => 15, '10' => 16,
1e3ba3 406         'ARABIC' => 6, 'CYRILLIC' => 5, 'GREEK' => 7, 'GREEK1' => 7, 'HEBREW' => 8);
4d7acb 407
A 408     // some clients sends windows-1252 text as latin1,
409     // it is safe to use windows-1252 for all latin1
1e3ba3 410     if ($m[1] == 1) {
4d7acb 411       $result = 'WINDOWS-1252';
A 412       }
413     // if iconv is not supported we need ISO labels, it's also safe for iconv
1e3ba3 414     else if (!empty($aliases[$m[1]])) {
A 415       $result = 'ISO-8859-'.$aliases[$m[1]];
4d7acb 416       }
A 417     // iconv requires convertion of e.g. LATIN-1 to LATIN1
418     else {
419       $result = $str;
420       }
421     }
46a138 422   else {
A 423     $result = $charset;
1e3271 424     }
A 425
46a138 426   $charsets[$input] = $result;
A 427
428   return $result;
dbe44c 429   }
a5897a 430
A 431
432 /**
433  * Converts string from standard UTF-7 (RFC 2152) to UTF-8.
434  *
435  * @param  string  Input string
5c461b 436  * @return string  The converted string
a5897a 437  */
A 438 function rcube_utf7_to_utf8($str)
439 {
440   $Index_64 = array(
441     0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
442     0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
443     0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,
444     1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,
445     0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
446     1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
447     0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
448     1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
449   );
450
451   $u7len = strlen($str);
452   $str = strval($str);
453   $res = '';
454
455   for ($i=0; $u7len > 0; $i++, $u7len--)
456   {
457     $u7 = $str[$i];
458     if ($u7 == '+')
459     {
460       $i++;
461       $u7len--;
462       $ch = '';
463
464       for (; $u7len > 0; $i++, $u7len--)
465       {
466         $u7 = $str[$i];
467
468         if (!$Index_64[ord($u7)])
469           break;
470
471     $ch .= $u7;
472       }
473
474       if ($ch == '') {
475         if ($u7 == '-')
476           $res .= '+';
477         continue;
478       }
479
480       $res .= rcube_utf16_to_utf8(base64_decode($ch));
481     }
482     else
483     {
484       $res .= $u7;
485     }
486   }
487
488   return $res;
489 }
490
491 /**
492  * Converts string from UTF-16 to UTF-8 (helper for utf-7 to utf-8 conversion)
493  *
494  * @param  string  Input string
5c461b 495  * @return string  The converted string
a5897a 496  */
A 497 function rcube_utf16_to_utf8($str)
498 {
499   $len = strlen($str);
500   $dec = '';
501
502   for ($i = 0; $i < $len; $i += 2) {
503     $c = ord($str[$i]) << 8 | ord($str[$i + 1]);
504     if ($c >= 0x0001 && $c <= 0x007F) {
505       $dec .= chr($c);
506     } else if ($c > 0x07FF) {
507       $dec .= chr(0xE0 | (($c >> 12) & 0x0F));
508       $dec .= chr(0x80 | (($c >>  6) & 0x3F));
509       $dec .= chr(0x80 | (($c >>  0) & 0x3F));
510     } else {
511       $dec .= chr(0xC0 | (($c >>  6) & 0x1F));
512       $dec .= chr(0x80 | (($c >>  0) & 0x3F));
513     }
514   }
515   return $dec;
516 }
dbe44c 517
A 518
519 /**
2bca6e 520  * Replacing specials characters to a specific encoding type
T 521  *
522  * @param  string  Input string
523  * @param  string  Encoding type: text|html|xml|js|url
524  * @param  string  Replace mode for tags: show|replace|remove
525  * @param  boolean Convert newlines
5c461b 526  * @return string  The quoted string
2bca6e 527  */
1cded8 528 function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
T 529   {
257782 530   static $html_encode_arr = false;
A 531   static $js_rep_table = false;
532   static $xml_rep_table = false;
1cded8 533
T 534   if (!$enctype)
c8a21d 535     $enctype = $OUTPUT->type;
1cded8 536
T 537   // encode for HTML output
538   if ($enctype=='html')
539     {
540     if (!$html_encode_arr)
541       {
b72e2f 542       $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);
1cded8 543       unset($html_encode_arr['?']);
T 544       }
545
546     $ltpos = strpos($str, '<');
547     $encode_arr = $html_encode_arr;
548
549     // don't replace quotes and html tags
550     if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false)
551       {
552       unset($encode_arr['"']);
553       unset($encode_arr['<']);
554       unset($encode_arr['>']);
10c92b 555       unset($encode_arr['&']);
1cded8 556       }
T 557     else if ($mode=='remove')
558       $str = strip_tags($str);
d2b27d 559
A 560     $out = strtr($str, $encode_arr);
561
674a0f 562     // avoid douple quotation of &
a4c970 563     $out = preg_replace('/&amp;([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', $out);
d2b27d 564
1cded8 565     return $newlines ? nl2br($out) : $out;
T 566     }
567
2bca6e 568   // if the replace tables for XML and JS are not yet defined
257782 569   if ($js_rep_table===false)
1cded8 570     {
f91a49 571     $js_rep_table = $xml_rep_table = array();
88375f 572     $xml_rep_table['&'] = '&amp;';
1cded8 573
T 574     for ($c=160; $c<256; $c++)  // can be increased to support more charsets
bc6ac4 575       $xml_rep_table[chr($c)] = "&#$c;";
1cded8 576
T 577     $xml_rep_table['"'] = '&quot;';
c21d6d 578     $js_rep_table['"'] = '\\"';
T 579     $js_rep_table["'"] = "\\'";
3d54e6 580     $js_rep_table["\\"] = "\\\\";
bc6ac4 581     // Unicode line and paragraph separators (#1486310)
A 582     $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A8))] = '&#8232;';
583     $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A9))] = '&#8233;';
1cded8 584     }
T 585
74eb6c 586   // encode for javascript use
A 587   if ($enctype=='js')
588     return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), strtr($str, $js_rep_table));
589
590   // encode for plaintext
591   if ($enctype=='text')
592     return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str);
593
594   if ($enctype=='url')
595     return rawurlencode($str);
596
2bca6e 597   // encode for XML
1cded8 598   if ($enctype=='xml')
T 599     return strtr($str, $xml_rep_table);
600
601   // no encoding given -> return original string
602   return $str;
2bca6e 603   }
f11541 604   
2bca6e 605 /**
6d969b 606  * Quote a given string.
T 607  * Shortcut function for rep_specialchars_output
608  *
609  * @return string HTML-quoted string
610  * @see rep_specialchars_output()
2bca6e 611  */
T 612 function Q($str, $mode='strict', $newlines=TRUE)
613   {
614   return rep_specialchars_output($str, 'html', $mode, $newlines);
615   }
616
617 /**
6d969b 618  * Quote a given string for javascript output.
T 619  * Shortcut function for rep_specialchars_output
620  * 
621  * @return string JS-quoted string
622  * @see rep_specialchars_output()
2bca6e 623  */
18e2a3 624 function JQ($str)
2bca6e 625   {
18e2a3 626   return rep_specialchars_output($str, 'js');
10a699 627   }
ea7c46 628
T 629
630 /**
631  * Read input value and convert it for internal use
632  * Performs stripslashes() and charset conversion if necessary
633  * 
634  * @param  string   Field name to read
635  * @param  int      Source to get value from (GPC)
636  * @param  boolean  Allow HTML tags in field value
637  * @param  string   Charset to convert into
638  * @return string   Field value or NULL if not available
639  */
640 function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
759696 641 {
ea7c46 642   $value = NULL;
T 643   
644   if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
645     $value = $_GET[$fname];
646   else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
647     $value = $_POST[$fname];
648   else if ($source==RCUBE_INPUT_GPC)
649     {
026d68 650     if (isset($_POST[$fname]))
ea7c46 651       $value = $_POST[$fname];
026d68 652     else if (isset($_GET[$fname]))
T 653       $value = $_GET[$fname];
ea7c46 654     else if (isset($_COOKIE[$fname]))
T 655       $value = $_COOKIE[$fname];
656     }
c5ee03 657
72b140 658   return parse_input_value($value, $allow_html, $charset);
A 659 }
660
661 /**
662  * Parse/validate input value. See get_input_value()
663  * Performs stripslashes() and charset conversion if necessary
664  * 
665  * @param  string   Input value
666  * @param  boolean  Allow HTML tags in field value
667  * @param  string   Charset to convert into
668  * @return string   Parsed value
669  */
670 function parse_input_value($value, $allow_html=FALSE, $charset=NULL)
671 {
672   global $OUTPUT;
673
c5ee03 674   if (empty($value))
A 675     return $value;
72b140 676
A 677   if (is_array($value)) {
678     foreach ($value as $idx => $val)
679       $value[$idx] = parse_input_value($val, $allow_html, $charset);
680     return $value;
681   }
c5ee03 682
6e47c0 683   // strip single quotes if magic_quotes_sybase is enabled
T 684   if (ini_get('magic_quotes_sybase'))
685     $value = str_replace("''", "'", $value);
ea7c46 686   // strip slashes if magic_quotes enabled
6e47c0 687   else if (get_magic_quotes_gpc() || get_magic_quotes_runtime())
ea7c46 688     $value = stripslashes($value);
T 689
690   // remove HTML tags if not allowed    
691   if (!$allow_html)
692     $value = strip_tags($value);
693   
694   // convert to internal charset
72b140 695   if (is_object($OUTPUT) && $charset)
026d68 696     return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
T 697   else
698     return $value;
759696 699 }
T 700
701 /**
702  * Convert array of request parameters (prefixed with _)
703  * to a regular array with non-prefixed keys.
704  *
705  * @param  int   Source to get value from (GPC)
706  * @return array Hash array with all request parameters
707  */
708 function request2param($mode = RCUBE_INPUT_GPC)
709 {
710   $out = array();
711   $src = $mode == RCUBE_INPUT_GET ? $_GET : ($mode == RCUBE_INPUT_POST ? $_POST : $_REQUEST);
712   foreach ($src as $key => $value) {
713     $fname = $key[0] == '_' ? substr($key, 1) : $key;
714     $out[$fname] = get_input_value($key, $mode);
ea7c46 715   }
759696 716   
T 717   return $out;
718 }
ea7c46 719
d5342a 720 /**
T 721  * Remove all non-ascii and non-word chars
8ca0c7 722  * except ., -, _
d5342a 723  */
8ca0c7 724 function asciiwords($str, $css_id = false, $replace_with = '')
d5342a 725 {
6d6e06 726   $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : '');
8ca0c7 727   return preg_replace("/[^$allowed]/i", $replace_with, $str);
d5342a 728 }
6d969b 729
e34ae1 730 /**
T 731  * Remove single and double quotes from given string
6d969b 732  *
T 733  * @param string Input value
734  * @return string Dequoted string
e34ae1 735  */
T 736 function strip_quotes($str)
737 {
2aa2b3 738   return str_replace(array("'", '"'), '', $str);
e34ae1 739 }
10a699 740
6d969b 741
3cf664 742 /**
T 743  * Remove new lines characters from given string
6d969b 744  *
T 745  * @param string Input value
746  * @return string Stripped string
3cf664 747  */
T 748 function strip_newlines($str)
749 {
750   return preg_replace('/[\r\n]/', '', $str);
f11541 751 }
4e17e6 752
T 753
6d969b 754 /**
T 755  * Create a HTML table based on the given data
756  *
757  * @param  array  Named table attributes
758  * @param  mixed  Table row data. Either a two-dimensional array or a valid SQL result set
759  * @param  array  List of cols to show
760  * @param  string Name of the identifier col
761  * @return string HTML table code
762  */
d1d2c4 763 function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
4e17e6 764   {
83a763 765   global $RCMAIL;
4e17e6 766   
83a763 767   $table = new html_table(/*array('cols' => count($a_show_cols))*/);
4e17e6 768     
83a763 769   // add table header
54759c 770   if (!$attrib['noheader'])
A 771     foreach ($a_show_cols as $col)
772       $table->add_header($col, Q(rcube_label($col)));
4e17e6 773   
T 774   $c = 0;
d1d2c4 775   if (!is_array($table_data)) 
83a763 776   {
T 777     $db = $RCMAIL->get_dbh();
778     while ($table_data && ($sql_arr = $db->fetch_assoc($table_data)))
4e17e6 779     {
83a763 780       $zebra_class = $c % 2 ? 'even' : 'odd';
1fcad1 781       $table->add_row(array('id' => 'rcmrow' . $sql_arr[$id_col], 'class' => $zebra_class));
d1d2c4 782
S 783       // format each col
784       foreach ($a_show_cols as $col)
83a763 785         $table->add($col, Q($sql_arr[$col]));
T 786       
d1d2c4 787       $c++;
S 788     }
83a763 789   }
d1d2c4 790   else 
83a763 791   {
d1d2c4 792     foreach ($table_data as $row_data)
83a763 793     {
T 794       $zebra_class = $c % 2 ? 'even' : 'odd';
b69560 795       if (!empty($row_data['class']))
A 796         $zebra_class .= ' '.$row_data['class'];
797
1fcad1 798       $table->add_row(array('id' => 'rcmrow' . $row_data[$id_col], 'class' => $zebra_class));
d1d2c4 799
S 800       // format each col
801       foreach ($a_show_cols as $col)
83a763 802         $table->add($col, Q($row_data[$col]));
T 803         
d1d2c4 804       $c++;
4e17e6 805     }
83a763 806   }
4e17e6 807
83a763 808   return $table->show($attrib);
4e17e6 809   }
T 810
811
a0109c 812 /**
S 813  * Create an edit field for inclusion on a form
814  * 
815  * @param string col field name
816  * @param string value field value
817  * @param array attrib HTML element attributes for field
818  * @param string type HTML element type (default 'text')
819  * @return string HTML field definition
820  */
4e17e6 821 function rcmail_get_edit_field($col, $value, $attrib, $type='text')
T 822   {
823   $fname = '_'.$col;
824   $attrib['name'] = $fname;
825   
826   if ($type=='checkbox')
827     {
828     $attrib['value'] = '1';
47124c 829     $input = new html_checkbox($attrib);
4e17e6 830     }
T 831   else if ($type=='textarea')
832     {
833     $attrib['cols'] = $attrib['size'];
47124c 834     $input = new html_textarea($attrib);
4e17e6 835     }
T 836   else
47124c 837     $input = new html_inputfield($attrib);
4e17e6 838
T 839   // use value from post
597170 840   if (!empty($_POST[$fname]))
407dcf 841     $value = get_input_value($fname, RCUBE_INPUT_POST,
A 842         $type == 'textarea' && strpos($attrib['class'], 'mce_editor')!==false ? true : false);
4e17e6 843
T 844   $out = $input->show($value);
845          
846   return $out;
f11541 847   }
T 848
849
6d969b 850 /**
97bd2c 851  * Replace all css definitions with #container [def]
a3e5b4 852  * and remove css-inlined scripting
97bd2c 853  *
T 854  * @param string CSS source code
855  * @param string Container ID to use as prefix
856  * @return string Modified CSS source
857  */
aa055c 858 function rcmail_mod_css_styles($source, $container_id)
97bd2c 859   {
T 860   $last_pos = 0;
aa055c 861   $replacements = new rcube_string_replacer;
cb3dfd 862
a3e5b4 863   // ignore the whole block if evil styles are detected
c5ee03 864   $stripped = preg_replace('/[^a-z\(:]/', '', rcmail_xss_entity_decode($source));
1c499a 865   if (preg_match('/expression|behavior|url\(|import/', $stripped))
aa055c 866     return '/* evil! */';
97bd2c 867
8e5ed7 868   // remove css comments (sometimes used for some ugly hacks)
T 869   $source = preg_replace('!/\*(.+)\*/!Ums', '', $source);
870
97bd2c 871   // cut out all contents between { and }
T 872   while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
873   {
aa055c 874     $key = $replacements->add(substr($source, $pos+1, $pos2-($pos+1)));
T 875     $source = substr($source, 0, $pos+1) . $replacements->get_replacement($key) . substr($source, $pos2, strlen($source)-$pos2);
97bd2c 876     $last_pos = $pos+2;
T 877   }
8e5ed7 878
a3e5b4 879   // remove html comments and add #container to each tag selector.
97bd2c 880   // also replace body definition because we also stripped off the <body> tag
T 881   $styles = preg_replace(
882     array(
883       '/(^\s*<!--)|(-->\s*$)/',
d0b981 884       '/(^\s*|,\s*|\}\s*)([a-z0-9\._#\*][a-z0-9\.\-_]*)/im',
cb3dfd 885       '/'.preg_quote($container_id, '/').'\s+body/i',
97bd2c 886     ),
T 887     array(
888       '',
889       "\\1#$container_id \\2",
cb3dfd 890       $container_id,
97bd2c 891     ),
T 892     $source);
cb3dfd 893
1608f4 894   // put block contents back in
aa055c 895   $styles = $replacements->resolve($styles);
97bd2c 896
T 897   return $styles;
898   }
fba1f5 899
97bd2c 900
T 901 /**
1c499a 902  * Decode escaped entities used by known XSS exploits.
T 903  * See http://downloads.securityfocus.com/vulnerabilities/exploits/26800.eml for examples
904  *
905  * @param string CSS content to decode
906  * @return string Decoded string
907  */
c5ee03 908 function rcmail_xss_entity_decode($content)
1c499a 909 {
T 910   $out = html_entity_decode(html_entity_decode($content));
c5ee03 911   $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', 'rcmail_xss_entity_decode_callback', $out);
85a913 912   $out = preg_replace('#/\*.*\*/#Um', '', $out);
1c499a 913   return $out;
T 914 }
915
aa055c 916
T 917 /**
c5ee03 918  * preg_replace_callback callback for rcmail_xss_entity_decode_callback
aa055c 919  *
T 920  * @param array matches result from preg_replace_callback
921  * @return string decoded entity
922  */ 
c5ee03 923 function rcmail_xss_entity_decode_callback($matches)
aa055c 924
T 925   return chr(hexdec($matches[1]));
926 }
1c499a 927
T 928 /**
6d969b 929  * Compose a valid attribute string for HTML tags
T 930  *
931  * @param array Named tag attributes
932  * @param array List of allowed attributes
933  * @return string HTML formatted attribute string
934  */
4e17e6 935 function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
T 936   {
937   // allow the following attributes to be added to the <iframe> tag
938   $attrib_str = '';
939   foreach ($allowed_attribs as $a)
940     if (isset($attrib[$a]))
fe79b1 941       $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '&quot;', $attrib[$a]));
4e17e6 942
T 943   return $attrib_str;
944   }
945
946
6d969b 947 /**
T 948  * Convert a HTML attribute string attributes to an associative array (name => value)
949  *
950  * @param string Input string
951  * @return array Key-value pairs of parsed attributes
952  */
fe79b1 953 function parse_attrib_string($str)
T 954   {
955   $attrib = array();
d59aaa 956   preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
fe79b1 957
T 958   // convert attributes to an associative array (name => value)
cc97ea 959   if ($regs) {
T 960     foreach ($regs as $attr) {
961       $attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]);
962     }
963   }
fe79b1 964
T 965   return $attrib;
966   }
967
4e17e6 968
6d969b 969 /**
T 970  * Convert the given date to a human readable form
971  * This uses the date formatting properties from config
972  *
973  * @param mixed Date representation (string or timestamp)
974  * @param string Date format to use
975  * @return string Formatted date string
976  */
4e17e6 977 function format_date($date, $format=NULL)
T 978   {
197601 979   global $CONFIG;
4e17e6 980   
4647e1 981   $ts = NULL;
ea090c 982
4e17e6 983   if (is_numeric($date))
T 984     $ts = $date;
b076a4 985   else if (!empty($date))
ea090c 986     {
33875d 987     // support non-standard "GMTXXXX" literal
A 988     $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date);
c9ca6a 989     // if date parsing fails, we have a date in non-rfc format.
S 990     // remove token from the end and try again
991     while ((($ts = @strtotime($date))===false) || ($ts < 0))
ea090c 992       {
A 993         $d = explode(' ', $date);
197601 994         array_pop($d);
T 995         if (!$d) break;
996         $date = implode(' ', $d);
ea090c 997       }
A 998     }
999
4647e1 1000   if (empty($ts))
b076a4 1001     return '';
4647e1 1002    
T 1003   // get user's timezone
62784a 1004   if ($CONFIG['timezone'] === 'auto')
T 1005     $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600;
c8ae24 1006   else {
T 1007     $tz = $CONFIG['timezone'];
1008     if ($CONFIG['dst_active'])
1009       $tz++;
1010   }
4e17e6 1011
T 1012   // convert time to user's timezone
4647e1 1013   $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
4e17e6 1014   
T 1015   // get current timestamp in user's timezone
1016   $now = time();  // local time
1017   $now -= (int)date('Z'); // make GMT time
4647e1 1018   $now += ($tz * 3600); // user's time
c45eb5 1019   $now_date = getdate($now);
4e17e6 1020
749b07 1021   $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
T 1022   $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
4e17e6 1023
539df6 1024   // define date format depending on current time
A 1025   if (!$format) {
1026     if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now)
1027       return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp));
1028     else if ($CONFIG['prettydate'] && $timestamp > $week_limit && $timestamp < $now)
1029       $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
1030     else
1031       $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
1032     }
4e17e6 1033
b6b593 1034   // strftime() format
A 1035   if (preg_match('/%[a-z]+/i', $format))
1036     return strftime($format, $timestamp);
4e17e6 1037
T 1038   // parse format string manually in order to provide localized weekday and month names
1039   // an alternative would be to convert the date() format string to fit with strftime()
1040   $out = '';
1041   for($i=0; $i<strlen($format); $i++)
1042     {
1043     if ($format{$i}=='\\')  // skip escape chars
1044       continue;
1045     
1046     // write char "as-is"
1047     if ($format{$i}==' ' || $format{$i-1}=='\\')
1048       $out .= $format{$i};
1049     // weekday (short)
1050     else if ($format{$i}=='D')
1051       $out .= rcube_label(strtolower(date('D', $timestamp)));
1052     // weekday long
1053     else if ($format{$i}=='l')
1054       $out .= rcube_label(strtolower(date('l', $timestamp)));
1055     // month name (short)
1056     else if ($format{$i}=='M')
1057       $out .= rcube_label(strtolower(date('M', $timestamp)));
1058     // month name (long)
1059     else if ($format{$i}=='F')
7479cc 1060       $out .= rcube_label('long'.strtolower(date('M', $timestamp)));
5b1de5 1061     else if ($format{$i}=='x')
A 1062       $out .= strftime('%x %X', $timestamp);
4e17e6 1063     else
T 1064       $out .= date($format{$i}, $timestamp);
1065     }
1066   
1067   return $out;
1068   }
1069
1070
6d969b 1071 /**
a9bfe2 1072  * Compose a valid representation of name and e-mail address
6d969b 1073  *
T 1074  * @param string E-mail address
1075  * @param string Person name
1076  * @return string Formatted string
1077  */
f11541 1078 function format_email_recipient($email, $name='')
T 1079   {
1080   if ($name && $name != $email)
0c6f4b 1081     {
T 1082     // Special chars as defined by RFC 822 need to in quoted string (or escaped).
a9bfe2 1083     return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, trim($email));
0c6f4b 1084     }
f11541 1085   else
a9bfe2 1086     return trim($email);
f11541 1087   }
T 1088
1089
1090
c39957 1091 /****** debugging functions ********/
T 1092
1093
1094 /**
1095  * Print or write debug messages
1096  *
1097  * @param mixed Debug message or data
5c461b 1098  * @return void
c39957 1099  */
ed132e 1100 function console()
c39957 1101   {
cc97ea 1102   $args = func_get_args();
76db10 1103
759696 1104   if (class_exists('rcmail', false)) {
T 1105     $rcmail = rcmail::get_instance();
1106     if (is_object($rcmail->plugins))
1107       $rcmail->plugins->exec_hook('console', $args);
1108   }
cc97ea 1109
ed132e 1110   $msg = array();
cc97ea 1111   foreach ($args as $arg)
76db10 1112     $msg[] = !is_string($arg) ? var_export($arg, true) : $arg;
c39957 1113
T 1114   if (!($GLOBALS['CONFIG']['debug_level'] & 4))
ed132e 1115     write_log('console', join(";\n", $msg));
197601 1116   else if ($GLOBALS['OUTPUT']->ajax_call)
ed132e 1117     print "/*\n " . join(";\n", $msg) . " \n*/\n";
c39957 1118   else
T 1119     {
1120     print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>';
ed132e 1121     print join(";<br/>\n", $msg);
c39957 1122     print "</pre></div>\n";
T 1123     }
1124   }
1125
1126
1127 /**
1128  * Append a line to a logfile in the logs directory.
1129  * Date will be added automatically to the line.
1130  *
653242 1131  * @param $name name of log file
S 1132  * @param line Line to append
5c461b 1133  * @return void
c39957 1134  */
T 1135 function write_log($name, $line)
1136   {
75fd64 1137   global $CONFIG, $RCMAIL;
e170b4 1138
T 1139   if (!is_string($line))
1140     $line = var_export($line, true);
0ad27c 1141  
A 1142   if (empty($CONFIG['log_date_format']))
1143     $CONFIG['log_date_format'] = 'd-M-Y H:i:s O';
75fd64 1144   
a0c4cb 1145   $date = date($CONFIG['log_date_format']);
T 1146   
75fd64 1147   // trigger logging hook
T 1148   if (is_object($RCMAIL) && is_object($RCMAIL->plugins)) {
a0c4cb 1149     $log = $RCMAIL->plugins->exec_hook('write_log', array('name' => $name, 'date' => $date, 'line' => $line));
75fd64 1150     $name = $log['name'];
T 1151     $line = $log['line'];
a0c4cb 1152     $date = $log['date'];
75fd64 1153     if ($log['abort'])
20e251 1154       return true;
75fd64 1155   }
0ad27c 1156  
b77d0d 1157   if ($CONFIG['log_driver'] == 'syslog') {
75fd64 1158     $prio = $name == 'errors' ? LOG_ERR : LOG_INFO;
5cf7aa 1159     syslog($prio, $line);
186938 1160     return true;
75fd64 1161   }
T 1162   else {
5cf7aa 1163     $line = sprintf("[%s]: %s\n", $date, $line);
A 1164
b77d0d 1165     // log_driver == 'file' is assumed here
A 1166     if (empty($CONFIG['log_dir']))
1167       $CONFIG['log_dir'] = INSTALL_PATH.'logs';
c9ca6a 1168
b77d0d 1169     // try to open specific log file for writing
cb8961 1170     $logfile = $CONFIG['log_dir'].'/'.$name;
T 1171     if ($fp = @fopen($logfile, 'a')) {
5cf7aa 1172       fwrite($fp, $line);
c9ca6a 1173       fflush($fp);
b77d0d 1174       fclose($fp);
186938 1175       return true;
c39957 1176     }
cb8961 1177     else
T 1178       trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING);
c39957 1179   }
186938 1180   return false;
b77d0d 1181 }
c39957 1182
cc9570 1183
6d969b 1184 /**
354455 1185  * Write login data (name, ID, IP address) to the 'userlogins' log file.
5c461b 1186  *
A 1187  * @return void
354455 1188  */
A 1189 function rcmail_log_login()
1190 {
1191   global $RCMAIL;
1192
1193   if (!$RCMAIL->config->get('log_logins') || !$RCMAIL->user)
1194     return;
1195
1196   write_log('userlogins', sprintf('Successful login for %s (ID: %d) from %s',
db1a87 1197     $RCMAIL->user->get_username(), $RCMAIL->user->ID, rcmail_remote_ip()));
T 1198 }
1199
1200
1201 /**
1202  * Returns remote IP address and forwarded addresses if found
1203  *
1204  * @return string Remote IP address(es)
1205  */
1206 function rcmail_remote_ip()
1207 {
1208     $address = $_SERVER['REMOTE_ADDR'];
1209
1210     // append the NGINX X-Real-IP header, if set
1211     if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
1212         $remote_ip[] = 'X-Real-IP: ' . $_SERVER['HTTP_X_REAL_IP'];
1213     }
1214     // append the X-Forwarded-For header, if set
1215     if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1216         $remote_ip[] = 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
1217     }
1218
1219     if (!empty($remote_ip))
1220         $address .= '(' . implode(',', $remote_ip) . ')';
1221
1222     return $address;
354455 1223 }
A 1224
1225
1226 /**
6d969b 1227  * @access private
5c461b 1228  * @return mixed
6d969b 1229  */
15a9d1 1230 function rcube_timer()
533e86 1231 {
T 1232   return microtime(true);
1233 }
db1a87 1234
15a9d1 1235
6d969b 1236 /**
T 1237  * @access private
5c461b 1238  * @return void
6d969b 1239  */
8bc018 1240 function rcube_print_time($timer, $label='Timer', $dest='console')
533e86 1241 {
15a9d1 1242   static $print_count = 0;
T 1243   
1244   $print_count++;
1245   $now = rcube_timer();
1246   $diff = $now-$timer;
1247   
1248   if (empty($label))
1249     $label = 'Timer '.$print_count;
1250   
8bc018 1251   write_log($dest, sprintf("%s: %0.4f sec", $label, $diff));
533e86 1252 }
15a9d1 1253
93be5b 1254
6d969b 1255 /**
T 1256  * Return the mailboxlist in HTML
1257  *
1258  * @param array Named parameters
1259  * @return string HTML code for the gui object
1260  */
93be5b 1261 function rcmail_mailbox_list($attrib)
62e542 1262 {
25f80d 1263   global $RCMAIL;
93be5b 1264   static $a_mailboxes;
64f20d 1265   
b822b6 1266   $attrib += array('maxlength' => 100, 'realnames' => false);
93be5b 1267
S 1268   // add some labels to client
112c91 1269   $RCMAIL->output->add_label('purgefolderconfirm', 'deletemessagesconfirm');
93be5b 1270   
S 1271   $type = $attrib['type'] ? $attrib['type'] : 'ul';
6d6e06 1272   unset($attrib['type']);
T 1273
93be5b 1274   if ($type=='ul' && !$attrib['id'])
S 1275     $attrib['id'] = 'rcmboxlist';
1276
1277   // get mailbox list
25f80d 1278   $mbox_name = $RCMAIL->imap->get_mailbox_name();
93be5b 1279   
S 1280   // build the folders tree
62e542 1281   if (empty($a_mailboxes)) {
93be5b 1282     // get mailbox list
25f80d 1283     $a_folders = $RCMAIL->imap->list_mailboxes();
T 1284     $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
93be5b 1285     $a_mailboxes = array();
S 1286
1287     foreach ($a_folders as $folder)
1288       rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
62e542 1289   }
10f08e 1290
f62d5f 1291   // allow plugins to alter the folder tree or to localize folder names
4fa127 1292   $hook = $RCMAIL->plugins->exec_hook('render_mailboxlist', array('list' => $a_mailboxes, 'delimiter' => $delimiter));
93be5b 1293
6d6e06 1294   if ($type=='select') {
T 1295     $select = new html_select($attrib);
1296     
1297     // add no-selection option
1298     if ($attrib['noselection'])
1299       $select->add(rcube_label($attrib['noselection']), '0');
1300     
f62d5f 1301     rcmail_render_folder_tree_select($hook['list'], $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']);
62e542 1302     $out = $select->show();
6d6e06 1303   }
T 1304   else {
f89f03 1305     $js_mailboxlist = array();
f62d5f 1306     $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($hook['list'], $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib);
f89f03 1307     
25f80d 1308     $RCMAIL->output->add_gui_object('mailboxlist', $attrib['id']);
f89f03 1309     $RCMAIL->output->set_env('mailboxes', $js_mailboxlist);
25f80d 1310     $RCMAIL->output->set_env('collapsed_folders', $RCMAIL->config->get('collapsed_folders'));
T 1311   }
93be5b 1312
6d6e06 1313   return $out;
62e542 1314 }
93be5b 1315
S 1316
cb3538 1317 /**
T 1318  * Return the mailboxlist as html_select object
1319  *
1320  * @param array Named parameters
5c461b 1321  * @return html_select HTML drop-down object
cb3538 1322  */
T 1323 function rcmail_mailbox_select($p = array())
1324 {
1325   global $RCMAIL;
1326   
93a88c 1327   $p += array('maxlength' => 100, 'realnames' => false);
cb3538 1328   $a_mailboxes = array();
db1a87 1329
T 1330   if ($p['unsubscribed'])
1331     $list = $RCMAIL->imap->list_unsubscribed();
1332   else
1333     $list = $RCMAIL->imap->list_mailboxes();
1334
1335   foreach ($list as $folder)
93a88c 1336     if (empty($p['exceptions']) || !in_array($folder, $p['exceptions']))
A 1337       rcmail_build_folder_tree($a_mailboxes, $folder, $RCMAIL->imap->get_hierarchy_delimiter());
cb3538 1338
T 1339   $select = new html_select($p);
1340   
1341   if ($p['noselection'])
1342     $select->add($p['noselection'], '');
1343     
64f20d 1344   rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames']);
cb3538 1345   
T 1346   return $select;
1347 }
93be5b 1348
S 1349
6d969b 1350 /**
T 1351  * Create a hierarchical array of the mailbox list
1352  * @access private
5c461b 1353  * @return void
6d969b 1354  */
93be5b 1355 function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
f89f03 1356 {
a5a4bf 1357   global $RCMAIL;
A 1358
93be5b 1359   $pos = strpos($folder, $delm);
10f08e 1360
f89f03 1361   if ($pos !== false) {
93be5b 1362     $subFolders = substr($folder, $pos+1);
S 1363     $currentFolder = substr($folder, 0, $pos);
10f08e 1364
A 1365     // sometimes folder has a delimiter as the last character
448409 1366     if (!strlen($subFolders))
10f08e 1367       $virtual = false;
A 1368     else if (!isset($arrFolders[$currentFolder]))
1369       $virtual = true;
1370     else
1371       $virtual = $arrFolders[$currentFolder]['virtual'];
f89f03 1372   }
T 1373   else {
93be5b 1374     $subFolders = false;
S 1375     $currentFolder = $folder;
f89f03 1376     $virtual = false;
T 1377   }
93be5b 1378
S 1379   $path .= $currentFolder;
1380
3870be 1381   // Check \Noselect option (if options are in cache)
A 1382   if (!$virtual && ($opts = $RCMAIL->imap->mailbox_options($path))) {
a5a4bf 1383     $virtual = in_array('\\Noselect', $opts);
A 1384   }
1385
f89f03 1386   if (!isset($arrFolders[$currentFolder])) {
6d6e06 1387     $arrFolders[$currentFolder] = array(
T 1388       'id' => $path,
a5897a 1389       'name' => rcube_charset_convert($currentFolder, 'UTF7-IMAP'),
f89f03 1390       'virtual' => $virtual,
6d6e06 1391       'folders' => array());
f89f03 1392   }
T 1393   else
1394     $arrFolders[$currentFolder]['virtual'] = $virtual;
93be5b 1395
448409 1396   if (strlen($subFolders))
93be5b 1397     rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
f89f03 1398 }
93be5b 1399   
S 1400
6d969b 1401 /**
T 1402  * Return html for a structured list &lt;ul&gt; for the mailbox tree
1403  * @access private
5c461b 1404  * @return string
6d969b 1405  */
f89f03 1406 function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel=0)
T 1407 {
25f80d 1408   global $RCMAIL, $CONFIG;
f89f03 1409   
T 1410   $maxlength = intval($attrib['maxlength']);
1411   $realnames = (bool)$attrib['realnames'];
1412   $msgcounts = $RCMAIL->imap->get_cache('messagecount');
93be5b 1413
S 1414   $idx = 0;
1415   $out = '';
f89f03 1416   foreach ($arrFolders as $key => $folder) {
cb3538 1417     $zebra_class = (($nestLevel+1)*$idx) % 2 == 0 ? 'even' : 'odd';
6d6e06 1418     $title = null;
93be5b 1419
f89f03 1420     if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames) {
cb3bad 1421       $foldername = rcube_label($folder_class);
f89f03 1422     }
T 1423     else {
93be5b 1424       $foldername = $folder['name'];
S 1425
1426       // shorten the folder name to a given length
f89f03 1427       if ($maxlength && $maxlength > 1) {
6f2f2d 1428         $fname = abbreviate_string($foldername, $maxlength);
93be5b 1429         if ($fname != $foldername)
6d6e06 1430           $title = $foldername;
93be5b 1431         $foldername = $fname;
S 1432       }
f89f03 1433     }
93be5b 1434
S 1435     // make folder name safe for ids and class names
8ca0c7 1436     $folder_id = asciiwords($folder['id'], true, '_');
6d6e06 1437     $classes = array('mailbox');
93be5b 1438
S 1439     // set special class for Sent, Drafts, Trash and Junk
1440     if ($folder['id']==$CONFIG['sent_mbox'])
6d6e06 1441       $classes[] = 'sent';
93be5b 1442     else if ($folder['id']==$CONFIG['drafts_mbox'])
6d6e06 1443       $classes[] = 'drafts';
93be5b 1444     else if ($folder['id']==$CONFIG['trash_mbox'])
6d6e06 1445       $classes[] = 'trash';
93be5b 1446     else if ($folder['id']==$CONFIG['junk_mbox'])
6d6e06 1447       $classes[] = 'junk';
d6497f 1448     else if ($folder['id']=='INBOX')
A 1449       $classes[] = 'inbox';
6d6e06 1450     else
d6497f 1451       $classes[] = '_'.asciiwords($folder_class ? $folder_class : strtolower($folder['id']), true);
6d6e06 1452       
T 1453     $classes[] = $zebra_class;
1454     
1455     if ($folder['id'] == $mbox_name)
1456       $classes[] = 'selected';
93be5b 1457
f5aa16 1458     $collapsed = preg_match('/&'.rawurlencode($folder['id']).'&/', $RCMAIL->config->get('collapsed_folders'));
f89f03 1459     $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
T 1460     
1461     if ($folder['virtual'])
1462       $classes[] = 'virtual';
1463     else if ($unread)
1464       $classes[] = 'unread';
f5aa16 1465
6d6e06 1466     $js_name = JQ($folder['id']);
f89f03 1467     $html_name = Q($foldername . ($unread ? " ($unread)" : ''));
T 1468     $link_attrib = $folder['virtual'] ? array() : array(
1469       'href' => rcmail_url('', array('_mbox' => $folder['id'])),
1470       'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name),
1471       'title' => $title,
1472     );
1473
6d6e06 1474     $out .= html::tag('li', array(
T 1475         'id' => "rcmli".$folder_id,
1476         'class' => join(' ', $classes),
1477         'noclose' => true),
f89f03 1478       html::a($link_attrib, $html_name) .
e1eb70 1479       (!empty($folder['folders']) ? html::div(array(
T 1480         'class' => ($collapsed ? 'collapsed' : 'expanded'),
1481         'style' => "position:absolute",
1482         'onclick' => sprintf("%s.command('collapse-folder', '%s')", JS_OBJECT_NAME, $js_name)
1483       ), '&nbsp;') : ''));
6d6e06 1484     
f89f03 1485     $jslist[$folder_id] = array('id' => $folder['id'], 'name' => $foldername, 'virtual' => $folder['virtual']);
T 1486     
1487     if (!empty($folder['folders'])) {
1488       $out .= html::tag('ul', array('style' => ($collapsed ? "display:none;" : null)),
1489         rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1));
1490     }
93be5b 1491
S 1492     $out .= "</li>\n";
1493     $idx++;
f89f03 1494   }
93be5b 1495
S 1496   return $out;
f89f03 1497 }
93be5b 1498
S 1499
6d969b 1500 /**
T 1501  * Return html for a flat list <select> for the mailbox tree
1502  * @access private
5c461b 1503  * @return string
6d969b 1504  */
64f20d 1505 function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0)
93be5b 1506   {
S 1507   $idx = 0;
1508   $out = '';
1509   foreach ($arrFolders as $key=>$folder)
1510     {
64f20d 1511     if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id'])))
cb3bad 1512       $foldername = rcube_label($folder_class);
93be5b 1513     else
S 1514       {
1515       $foldername = $folder['name'];
1516       
1517       // shorten the folder name to a given length
1518       if ($maxlength && $maxlength>1)
6f2f2d 1519         $foldername = abbreviate_string($foldername, $maxlength);
93be5b 1520       }
S 1521
6d6e06 1522     $select->add(str_repeat('&nbsp;', $nestLevel*4) . $foldername, $folder['id']);
93be5b 1523
S 1524     if (!empty($folder['folders']))
64f20d 1525       $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, $select, $realnames, $nestLevel+1);
93be5b 1526
S 1527     $idx++;
1528     }
1529
1530   return $out;
1531   }
1532
cb3bad 1533
T 1534 /**
1535  * Return internal name for the given folder if it matches the configured special folders
1536  * @access private
5c461b 1537  * @return string
cb3bad 1538  */
T 1539 function rcmail_folder_classname($folder_id)
1540 {
1541   global $CONFIG;
1542
0a1921 1543   if ($folder_id == 'INBOX')
A 1544     return 'inbox';
1545
cb3bad 1546   // for these mailboxes we have localized labels and css classes
64c9b5 1547   foreach (array('sent', 'drafts', 'trash', 'junk') as $smbx)
cb3bad 1548   {
f94629 1549     if ($folder_id == $CONFIG[$smbx.'_mbox'])
64c9b5 1550       return $smbx;
cb3bad 1551   }
T 1552 }
1553
1554
fed22f 1555 /**
T 1556  * Try to localize the given IMAP folder name.
1557  * UTF-7 decode it in case no localized text was found
1558  *
1559  * @param string Folder name
1560  * @return string Localized folder name in UTF-8 encoding
1561  */
1562 function rcmail_localize_foldername($name)
1563 {
1564   if ($folder_class = rcmail_folder_classname($name))
1565     return rcube_label($folder_class);
1566   else
a5897a 1567     return rcube_charset_convert($name, 'UTF7-IMAP');
fed22f 1568 }
T 1569
1570
db1a87 1571 function rcmail_quota_display($attrib)
T 1572 {
1573   global $OUTPUT;
1574
1575   if (!$attrib['id'])
1576     $attrib['id'] = 'rcmquotadisplay';
1577
1578   if(isset($attrib['display']))
1579     $_SESSION['quota_display'] = $attrib['display'];
1580
1581   $OUTPUT->add_gui_object('quotadisplay', $attrib['id']);
1582
1583   $quota = rcmail_quota_content($attrib);
1584
1585   $OUTPUT->add_script('$(document).ready(function(){
1586     rcmail.set_quota('.json_serialize($quota).')});', 'foot');
1587
1588   return html::span($attrib, '');
1589 }
1590
1591
1592 function rcmail_quota_content($attrib=NULL)
1593 {
1594   global $RCMAIL;
1595
1596   $quota = $RCMAIL->imap->get_quota();
1597   $quota = $RCMAIL->plugins->exec_hook('quota', $quota);
1598
1599   $quota_result = (array) $quota;
1600   $quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
1601
1602   if (!$quota['total'] && $RCMAIL->config->get('quota_zero_as_unlimited')) {
1603     $quota_result['title'] = rcube_label('unlimited');
1604     $quota_result['percent'] = 0;
1605   }
1606   else if ($quota['total']) {
1607     if (!isset($quota['percent']))
1608       $quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100));
1609
1610     $title = sprintf('%s / %s (%.0f%%)',
1611         show_bytes($quota['used'] * 1024), show_bytes($quota['total'] * 1024),
1612         $quota_result['percent']);
1613
1614     $quota_result['title'] = $title;
1615
1616     if ($attrib['width'])
1617       $quota_result['width'] = $attrib['width'];
1618     if ($attrib['height'])
1619       $quota_result['height']    = $attrib['height'];
1620   }
1621   else {
1622     $quota_result['title'] = rcube_label('unknown');
1623     $quota_result['percent'] = 0;
1624   }
1625
1626   return $quota_result;
1627 }
1628
1629
1630 /**
1631  * Outputs error message according to server error/response codes
1632  *
1633  * @param string Fallback message label
1634  * @param string Fallback message label arguments
1635  *
1636  * @return void
1637  */
1638 function rcmail_display_server_error($fallback=null, $fallback_args=null)
1639 {
1640     global $RCMAIL;
1641
1642     $err_code = $RCMAIL->imap->get_error_code();
1643     $res_code = $RCMAIL->imap->get_response_code();
1644
1645     if ($res_code == rcube_imap::NOPERM) {
1646         $RCMAIL->output->show_message('errornoperm', 'error');
1647     }
1648     else if ($res_code == rcube_imap::READONLY) {
1649         $RCMAIL->output->show_message('errorreadonly', 'error');
1650     }
1651     else if ($err_code && ($err_str = $RCMAIL->imap->get_error_str())) {
1652         $RCMAIL->output->show_message('servererrormsg', 'error', array('msg' => $err_str));
1653     }
1654     else if ($fallback) {
1655         $RCMAIL->output->show_message($fallback, 'error', $fallback_args);
1656     }
1657
1658     return true;
1659 }
1660
1661
b8ae50 1662 /**
A 1663  * Output HTML editor scripts
1664  *
be5d4a 1665  * @param string Editor mode
5c461b 1666  * @return void
b8ae50 1667  */
A 1668 function rcube_html_editor($mode='')
1669 {
9ab7bc 1670   global $RCMAIL, $CONFIG;
b8ae50 1671
dc0040 1672   $hook = $RCMAIL->plugins->exec_hook('hmtl_editor', array('mode' => $mode));
9ab7bc 1673
A 1674   if ($hook['abort'])
1675     return;  
1676
1fb718 1677   $lang = strtolower($_SESSION['language']);
A 1678
1679   // TinyMCE uses 'tw' for zh_TW (which is wrong, because tw is a code of Twi language)
1680   $lang = ($lang == 'zh_tw') ? 'tw' : substr($lang, 0, 2);
1681
dc0040 1682   if (!file_exists(INSTALL_PATH . 'program/js/tiny_mce/langs/'.$lang.'.js'))
A 1683     $lang = 'en';
1684
9ab7bc 1685   $RCMAIL->output->include_script('tiny_mce/tiny_mce.js');
A 1686   $RCMAIL->output->include_script('editor.js');
11a61a 1687   $RCMAIL->output->add_script(sprintf("rcmail_editor_init('\$__skin_path', '%s', %d, '%s');",
A 1688     JQ($lang), intval($CONFIG['enable_spellcheck']), $mode),
4b410e 1689     'foot');
db1a87 1690 }
T 1691
1692
1693 /**
1694  * Replaces TinyMCE's emoticon images with plain-text representation
1695  *
1696  * @param string HTML content
1697  * @return string HTML content
1698  */
1699 function rcmail_replace_emoticons($html)
1700 {
1701   $emoticons = array(
1702     '8-)' => 'smiley-cool',
1703     ':-#' => 'smiley-foot-in-mouth',
1704     ':-*' => 'smiley-kiss',
1705     ':-X' => 'smiley-sealed',
1706     ':-P' => 'smiley-tongue-out',
1707     ':-@' => 'smiley-yell',
1708     ":'(" => 'smiley-cry',
1709     ':-(' => 'smiley-frown',
1710     ':-D' => 'smiley-laughing',
1711     ':-)' => 'smiley-smile',
1712     ':-S' => 'smiley-undecided',
1713     ':-$' => 'smiley-embarassed',
1714     'O:-)' => 'smiley-innocent',
1715     ':-|' => 'smiley-money-mouth',
1716     ':-O' => 'smiley-surprised',
1717     ';-)' => 'smiley-wink',
1718   );
1719
1720   foreach ($emoticons as $idx => $file) {
1721     // <img title="Cry" src="http://.../program/js/tiny_mce/plugins/emotions/img/smiley-cry.gif" border="0" alt="Cry" />
1722     $search[]  = '/<img title="[a-z ]+" src="https?:\/\/[a-z0-9_.\/-]+\/tiny_mce\/plugins\/emotions\/img\/'.$file.'.gif"[^>]+\/>/i';
1723     $replace[] = $idx;
1724   }
1725
1726   return preg_replace($search, $replace, $html);
b8ae50 1727 }
aa055c 1728
T 1729
1730 /**
5818e4 1731  * Check if working in SSL mode
A 1732  *
1733  * @param integer HTTPS port number
1734  * @param boolean Enables 'use_https' option checking
5c461b 1735  * @return boolean
5818e4 1736  */
A 1737 function rcube_https_check($port=null, $use_https=true)
1738 {
1739   global $RCMAIL;
3a4c9f 1740
6c5aa6 1741   if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off')
5818e4 1742     return true;
3a4c9f 1743   if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https')
A 1744     return true;
5818e4 1745   if ($port && $_SERVER['SERVER_PORT'] == $port)
A 1746     return true;
929a50 1747   if ($use_https && isset($RCMAIL) && $RCMAIL->config->get('use_https'))
5818e4 1748     return true;
A 1749
1750   return false;
1751 }
1752
bb8721 1753
5c461b 1754 /**
A 1755  * For backward compatibility.
1756  *
1757  * @global rcmail $RCMAIL
1758  * @param string $var_name Variable name.
1759  * @return void
1760  */
929a50 1761 function rcube_sess_unset($var_name=null)
A 1762 {
1763   global $RCMAIL;
1764
1765   $RCMAIL->session->remove($var_name);
1766 }
1767
5818e4 1768
5c461b 1769
A 1770 /**
1771  * Replaces hostname variables
1772  *
1773  * @param string $name Hostname
1774  * @return string
1775  */
bb8721 1776 function rcube_parse_host($name)
A 1777 {
1778   // %n - host
1779   $n = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']);
1780   // %d - domain name without first part, e.g. %d=mail.domain.tld, %m=domain.tld
1781   $d = preg_replace('/^[^\.]+\./', '', $n);
1782   // %h - IMAP host
1783   $h = $_SESSION['imap_host'];
11be93 1784   // %z - IMAP domain without first part, e.g. %h=imap.domain.tld, %z=domain.tld
A 1785   $z = preg_replace('/^[^\.]+\./', '', $h);
bb8721 1786
11be93 1787   $name = str_replace(array('%n', '%d', '%h', '%z'), array($n, $d, $h, $z), $name);
bb8721 1788   return $name;
A 1789 }
1790
1791
5818e4 1792 /**
e4acbb 1793  * E-mail address validation
5c461b 1794  *
A 1795  * @param string $email Email address
1796  * @param boolean $dns_check True to check dns
1797  * @return boolean
e4acbb 1798  */
1baeb6 1799 function check_email($email, $dns_check=true)
e4acbb 1800 {
A 1801   // Check for invalid characters
1802   if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email))
1803     return false;
1804
aba092 1805   // Check for length limit specified by RFC 5321 (#1486453)
A 1806   if (strlen($email) > 254) 
1807     return false;
1808
1baeb6 1809   $email_array = explode('@', $email);
A 1810
aba092 1811   // Check that there's one @ symbol
1baeb6 1812   if (count($email_array) < 2)
e4acbb 1813     return false;
A 1814
1baeb6 1815   $domain_part = array_pop($email_array);
A 1816   $local_part = implode('@', $email_array);
1817
1818   // from PEAR::Validate
1819   $regexp = '&^(?:
1820     ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")|                  #1 quoted name
d7a5df 1821     ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*))     #2 OR dot-atom (RFC5322)
1baeb6 1822     $&xi';
A 1823
1824   if (!preg_match($regexp, $local_part))
1825     return false;
e4acbb 1826
A 1827   // Check domain part
1baeb6 1828   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))
A 1829     return true; // IP address
e4acbb 1830   else {
A 1831     // If not an IP address
1baeb6 1832     $domain_array = explode('.', $domain_part);
e4acbb 1833     if (sizeof($domain_array) < 2)
A 1834       return false; // Not enough parts to be a valid domain
1835
1baeb6 1836     foreach ($domain_array as $part)
A 1837       if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $part))
e4acbb 1838         return false;
A 1839
1baeb6 1840     if (!$dns_check || !rcmail::get_instance()->config->get('email_dns_check'))
e4acbb 1841       return true;
A 1842
0f3764 1843     if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && version_compare(PHP_VERSION, '5.3.0', '<')) {
A 1844       $lookup = array();
1845       @exec("nslookup -type=MX " . escapeshellarg($domain_part) . " 2>&1", $lookup);
1846       foreach ($lookup as $line) {
1847         if (strpos($line, 'MX preference'))
1848           return true;
1849       }
1850       return false;
1851     }
e4acbb 1852
A 1853     // find MX record(s)
1baeb6 1854     if (getmxrr($domain_part, $mx_records))
e4acbb 1855       return true;
A 1856
1857     // find any DNS record
1baeb6 1858     if (checkdnsrr($domain_part, 'ANY'))
e4acbb 1859       return true;
A 1860   }
1861
1862   return false;
1863 }
1864
1865
1866 /**
aa055c 1867  * Helper class to turn relative urls into absolute ones
T 1868  * using a predefined base
1869  */
1870 class rcube_base_replacer
1871 {
1872   private $base_url;
24c91e 1873
aa055c 1874   public function __construct($base)
T 1875   {
1876     $this->base_url = $base;
1877   }
24c91e 1878
aa055c 1879   public function callback($matches)
T 1880   {
1881     return $matches[1] . '="' . make_absolute_url($matches[3], $this->base_url) . '"';
1882   }
1883 }
1884
874ff4 1885
24c91e 1886 /**
A 1887  * Throw system error and show error page
1888  *
1889  * @param array Named parameters
1890  *  - code: Error code (required)
1891  *  - type: Error type [php|db|imap|javascript] (required)
1892  *  - message: Error message
1893  *  - file: File where error occured
1894  *  - line: Line where error occured
1895  * @param boolean True to log the error
1896  * @param boolean Terminate script execution
1897  */
874ff4 1898 // may be defined in Installer
A 1899 if (!function_exists('raise_error')) {
24c91e 1900 function raise_error($arg=array(), $log=false, $terminate=false)
A 1901 {
1902     global $__page_content, $CONFIG, $OUTPUT, $ERROR_CODE, $ERROR_MESSAGE;
1903
1904     // report bug (if not incompatible browser)
1905     if ($log && $arg['type'] && $arg['message'])
1906         log_bug($arg);
1907
1908     // display error page and terminate script
1909     if ($terminate) {
1910         $ERROR_CODE = $arg['code'];
1911         $ERROR_MESSAGE = $arg['message'];
1912         include('program/steps/utils/error.inc');
1913         exit;
1914     }
1915 }
874ff4 1916 }
24c91e 1917
A 1918
1919 /**
1920  * Report error according to configured debug_level
1921  *
1922  * @param array Named parameters
5c461b 1923  * @return void
24c91e 1924  * @see raise_error()
A 1925  */
1926 function log_bug($arg_arr)
1927 {
1928     global $CONFIG;
1929     $program = strtoupper($arg_arr['type']);
1930
1931     // write error to local log file
1932     if ($CONFIG['debug_level'] & 1) {
1933         $post_query = ($_SERVER['REQUEST_METHOD'] == 'POST' ? '?_task='.urlencode($_POST['_task']).'&_action='.urlencode($_POST['_action']) : '');
1934         $log_entry = sprintf("%s Error: %s%s (%s %s)",
1935             $program,
1936             $arg_arr['message'],
1937             $arg_arr['file'] ? sprintf(' in %s on line %d', $arg_arr['file'], $arg_arr['line']) : '',
1938             $_SERVER['REQUEST_METHOD'],
1939             $_SERVER['REQUEST_URI'] . $post_query);
1940
1941         if (!write_log('errors', $log_entry)) {
1942             // send error to PHPs error handler if write_log didn't succeed
1943             trigger_error($arg_arr['message']);
1944         }
1945     }
1946
1947     // resport the bug to the global bug reporting system
1948     if ($CONFIG['debug_level'] & 2) {
1949         // TODO: Send error via HTTP
1950     }
1951
1952     // show error if debug_mode is on
1953     if ($CONFIG['debug_level'] & 4) {
1954         print "<b>$program Error";
1955
1956         if (!empty($arg_arr['file']) && !empty($arg_arr['line']))
1957             print " in $arg_arr[file] ($arg_arr[line])";
1958
1959         print ':</b>&nbsp;';
1960         print nl2br($arg_arr['message']);
1961         print '<br />';
1962         flush();
1963     }
1964 }
db1a87 1965