source: subversion/trunk/roundcubemail/program/include/main.inc @ 3200

Last change on this file since 3200 was 3200, checked in by alec, 3 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.8 KB
Line 
1<?php
2
3/*
4 +-----------------------------------------------------------------------+
5 | program/include/main.inc                                              |
6 |                                                                       |
7 | This file is part of the RoundCube Webmail client                     |
8 | Copyright (C) 2005-2009, RoundCube Dev, - Switzerland                 |
9 | Licensed under the GNU GPL                                            |
10 |                                                                       |
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
22/**
23 * RoundCube Webmail common functions
24 *
25 * @package Core
26 * @author Thomas Bruederli <roundcube@gmail.com>
27 */
28
29require_once('lib/utf7.inc');
30require_once('include/rcube_shared.inc');
31
32// fallback if not PHP modules are available
33@include_once('lib/des.inc');
34@include_once('lib/utf8.class.php');
35
36// define constannts for input reading
37define('RCUBE_INPUT_GET', 0x0101);
38define('RCUBE_INPUT_POST', 0x0102);
39define('RCUBE_INPUT_GPC', 0x0103);
40
41
42
43/**
44 * Return correct name for a specific database table
45 *
46 * @param string Table name
47 * @return string Translated table name
48 */
49function get_table_name($table)
50  {
51  global $CONFIG;
52
53  // return table name if configured
54  $config_key = 'db_table_'.$table;
55
56  if (strlen($CONFIG[$config_key]))
57    return $CONFIG[$config_key];
58
59  return $table;
60  }
61
62
63/**
64 * Return correct name for a specific database sequence
65 * (used for Postgres only)
66 *
67 * @param string Secuence name
68 * @return string Translated sequence name
69 */
70function get_sequence_name($sequence)
71  {
72  // return table name if configured
73  $config_key = 'db_sequence_'.$sequence;
74  $opt = rcmail::get_instance()->config->get($config_key);
75
76  if (!empty($opt))
77    return $opt;
78   
79  return $sequence;
80  }
81
82
83/**
84 * Get localized text in the desired language
85 * It's a global wrapper for rcmail::gettext()
86 *
87 * @param mixed Named parameters array or label name
88 * @return string Localized text
89 * @see rcmail::gettext()
90 */
91function rcube_label($p, $domain=null)
92{
93  return rcmail::get_instance()->gettext($p, $domain);
94}
95
96
97/**
98 * Overwrite action variable
99 *
100 * @param string New action value
101 */
102function rcmail_overwrite_action($action)
103  {
104  $app = rcmail::get_instance();
105  $app->action = $action;
106  $app->output->set_env('action', $action);
107  }
108
109
110/**
111 * Compose an URL for a specific action
112 *
113 * @param string  Request action
114 * @param array   More URL parameters
115 * @param string  Request task (omit if the same)
116 * @return The application URL
117 */
118function rcmail_url($action, $p=array(), $task=null)
119{
120  $app = rcmail::get_instance();
121  return $app->url((array)$p + array('_action' => $action, 'task' => $task));
122}
123
124
125/**
126 * Garbage collector function for temp files.
127 * Remove temp files older than two days
128 */
129function rcmail_temp_gc()
130  {
131  $rcmail = rcmail::get_instance();
132
133  $tmp = unslashify($rcmail->config->get('temp_dir'));
134  $expire = mktime() - 172800;  // expire in 48 hours
135
136  if ($dir = opendir($tmp))
137    {
138    while (($fname = readdir($dir)) !== false)
139      {
140      if ($fname{0} == '.')
141        continue;
142
143      if (filemtime($tmp.'/'.$fname) < $expire)
144        @unlink($tmp.'/'.$fname);
145      }
146
147    closedir($dir);
148    }
149  }
150
151
152/**
153 * Garbage collector for cache entries.
154 * Remove all expired message cache records
155 */
156function rcmail_cache_gc()
157  {
158  $rcmail = rcmail::get_instance();
159  $db = $rcmail->get_dbh();
160 
161  // get target timestamp
162  $ts = get_offset_time($rcmail->config->get('message_cache_lifetime', '30d'), -1);
163 
164  $db->query("DELETE FROM ".get_table_name('messages')."
165             WHERE  created < " . $db->fromunixtime($ts));
166
167  $db->query("DELETE FROM ".get_table_name('cache')."
168              WHERE  created < " . $db->fromunixtime($ts));
169  }
170
171
172/**
173 * Convert a string from one charset to another.
174 * Uses mbstring and iconv functions if possible
175 *
176 * @param  string Input string
177 * @param  string Suspected charset of the input string
178 * @param  string Target charset to convert to; defaults to RCMAIL_CHARSET
179 * @return Converted string
180 */
181function rcube_charset_convert($str, $from, $to=NULL)
182  {
183  static $mbstring_loaded = null;
184  static $mbstring_list = null;
185  static $convert_warning = false;
186  static $conv = null;
187 
188  $error = false;
189
190  $to = empty($to) ? $to = strtoupper(RCMAIL_CHARSET) : rcube_parse_charset($to);
191  $from = rcube_parse_charset($from);
192
193  if ($from == $to || empty($str) || empty($from))
194    return $str;
195
196  // convert charset using iconv module 
197  if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
198    $_iconv = iconv($from, $to . '//IGNORE', $str);
199    if ($_iconv !== false) {
200        return $_iconv;
201    }
202  }
203
204  if (is_null($mbstring_loaded))
205    $mbstring_loaded = extension_loaded('mbstring');
206   
207  // convert charset using mbstring module
208  if ($mbstring_loaded) {
209    $aliases['WINDOWS-1257'] = 'ISO-8859-13';
210   
211    if (is_null($mbstring_list)) {
212      $mbstring_list = mb_list_encodings();
213      $mbstring_list = array_map('strtoupper', $mbstring_list);
214    }
215
216    $mb_from = $aliases[$from] ? $aliases[$from] : $from;
217    $mb_to = $aliases[$to] ? $aliases[$to] : $to;
218   
219    // return if encoding found, string matches encoding and convert succeeded
220    if (in_array($mb_from, $mbstring_list) && in_array($mb_to, $mbstring_list)) {
221      if (mb_check_encoding($str, $mb_from) && ($out = mb_convert_encoding($str, $mb_to, $mb_from)))
222        return $out;
223    }
224  }
225
226  // convert charset using bundled classes/functions
227  if ($to == 'UTF-8') {
228    if ($from == 'UTF7-IMAP') {
229      if ($_str = utf7_to_utf8($str))
230        return $_str;
231    }
232    else if ($from == 'UTF-7') {
233      if ($_str = rcube_utf7_to_utf8($str))
234        return $_str;
235    }
236    else if (($from == 'ISO-8859-1') && function_exists('utf8_encode')) {
237      return utf8_encode($str);
238    }
239    else if (class_exists('utf8')) {
240      if (!$conv)
241        $conv = new utf8($from);
242      else
243        $conv->loadCharset($from);
244
245      if($_str = $conv->strToUtf8($str))
246        return $_str;
247    }
248    $error = true;
249  }
250 
251  // encode string for output
252  if ($from == 'UTF-8') {
253    // @TODO: we need a function for UTF-7 (RFC2152) conversion
254    if ($to == 'UTF7-IMAP' || $to == 'UTF-7') {
255      if ($_str = utf8_to_utf7($str))
256        return $_str;
257    }
258    else if ($to == 'ISO-8859-1' && function_exists('utf8_decode')) {
259      return utf8_decode($str);
260    }
261    else if (class_exists('utf8')) {
262      if (!$conv)
263        $conv = new utf8($to);
264      else
265        $conv->loadCharset($from);
266
267      if ($_str = $conv->strToUtf8($str))
268        return $_str;
269    }
270    $error = true;
271  }
272 
273  // report error
274  if ($error && !$convert_warning) {
275    raise_error(array(
276      'code' => 500,
277      'type' => 'php',
278      'file' => __FILE__,
279      'line' => __LINE__,
280      'message' => "Could not convert string from $from to $to. Make sure iconv/mbstring is installed or lib/utf8.class is available."
281      ), true, false);
282   
283    $convert_warning = true;
284  }
285 
286  // return UTF-8 or original string
287  return $str;
288  }
289
290
291/**
292 * Parse and validate charset name string (see #1485758).
293 * Sometimes charset string is malformed, there are also charset aliases
294 * but we need strict names for charset conversion (specially utf8 class)
295 *
296 * @param  string  Input charset name
297 * @return The validated charset name
298 */
299function rcube_parse_charset($charset)
300  {
301  $charset = strtoupper($charset);
302
303  $charset = preg_replace(array(
304    '/^[^0-9A-Z]+/',    // e.g. _ISO-8859-JP$SIO
305    '/\$.*$/',          // e.g. _ISO-8859-JP$SIO
306    '/UNICODE-1-1-/',   // RFC1642
307    ), '', $charset);
308
309  # Aliases: some of them from HTML5 spec.
310  $aliases = array(
311    'USASCII'       => 'WINDOWS-1252',
312    'ANSIX31101983' => 'WINDOWS-1252',
313    'ANSIX341968'   => 'WINDOWS-1252',
314    'UNKNOWN8BIT'   => 'ISO-8859-15',
315    'UNKNOWN'       => 'ISO-8859-15',
316    'USERDEFINED'   => 'ISO-8859-15',
317    'KSC56011987'   => 'EUC-KR',
318    'GB2312'        => 'GBK',
319    'GB231280'      => 'GBK',
320    'UNICODE'       => 'UTF-8',
321    'UTF7IMAP'      => 'UTF7-IMAP',
322    'TIS620'        => 'WINDOWS-874',
323    'ISO88599'      => 'WINDOWS-1254',
324    'ISO885911'     => 'WINDOWS-874',
325  );
326
327  // allow a-z and 0-9 only and remove X- prefix (e.g. X-ROMAN8 => ROMAN8)
328  $str = preg_replace(array('/[^a-z0-9]/i', '/^x+/i'), '', $charset);
329
330  if (isset($aliases[$str]))
331    return $aliases[$str];
332
333  if (preg_match('/UTF(7|8|16|32)(BE|LE)*/', $str, $m))
334    return 'UTF-' . $m[1] . $m[2];
335
336  if (preg_match('/ISO8859([0-9]{0,2})/', $str, $m)) {
337    $iso = 'ISO-8859-' . ($m[1] ? $m[1] : 1);
338    # some clients sends windows-1252 text as latin1,
339    # it is safe to use windows-1252 for all latin1
340    return $iso == 'ISO-8859-1' ? 'WINDOWS-1252' : $iso;
341    }
342
343  return $charset;
344  }
345
346
347/**
348 * Converts string from standard UTF-7 (RFC 2152) to UTF-8.
349 *
350 * @param  string  Input string
351 * @return The converted string
352 */
353function rcube_utf7_to_utf8($str)
354{
355  $Index_64 = array(
356    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
357    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
358    0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,
359    1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,
360    0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
361    1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
362    0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
363    1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
364  );
365
366  $u7len = strlen($str);
367  $str = strval($str);
368  $res = '';
369
370  for ($i=0; $u7len > 0; $i++, $u7len--)
371  {
372    $u7 = $str[$i];
373    if ($u7 == '+')
374    {
375      $i++;
376      $u7len--;
377      $ch = '';
378
379      for (; $u7len > 0; $i++, $u7len--)
380      {
381        $u7 = $str[$i];
382
383        if (!$Index_64[ord($u7)])
384          break;
385
386        $ch .= $u7;
387      }
388
389      if ($ch == '') {
390        if ($u7 == '-')
391          $res .= '+';
392        continue;
393      }
394
395      $res .= rcube_utf16_to_utf8(base64_decode($ch));
396    }
397    else
398    {
399      $res .= $u7;
400    }
401  }
402
403  return $res;
404}
405
406/**
407 * Converts string from UTF-16 to UTF-8 (helper for utf-7 to utf-8 conversion)
408 *
409 * @param  string  Input string
410 * @return The converted string
411 */
412function rcube_utf16_to_utf8($str)
413{
414  $len = strlen($str);
415  $dec = '';
416
417  for ($i = 0; $i < $len; $i += 2) {
418    $c = ord($str[$i]) << 8 | ord($str[$i + 1]);
419    if ($c >= 0x0001 && $c <= 0x007F) {
420      $dec .= chr($c);
421    } else if ($c > 0x07FF) {
422      $dec .= chr(0xE0 | (($c >> 12) & 0x0F));
423      $dec .= chr(0x80 | (($c >>  6) & 0x3F));
424      $dec .= chr(0x80 | (($c >>  0) & 0x3F));
425    } else {
426      $dec .= chr(0xC0 | (($c >>  6) & 0x1F));
427      $dec .= chr(0x80 | (($c >>  0) & 0x3F));
428    }
429  }
430  return $dec;
431}
432
433
434/**
435 * Replacing specials characters to a specific encoding type
436 *
437 * @param  string  Input string
438 * @param  string  Encoding type: text|html|xml|js|url
439 * @param  string  Replace mode for tags: show|replace|remove
440 * @param  boolean Convert newlines
441 * @return The quoted string
442 */
443function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
444  {
445  static $html_encode_arr = false;
446  static $js_rep_table = false;
447  static $xml_rep_table = false;
448
449  if (!$enctype)
450    $enctype = $OUTPUT->type;
451
452  // encode for HTML output
453  if ($enctype=='html')
454    {
455    if (!$html_encode_arr)
456      {
457      $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);       
458      unset($html_encode_arr['?']);
459      }
460
461    $ltpos = strpos($str, '<');
462    $encode_arr = $html_encode_arr;
463
464    // don't replace quotes and html tags
465    if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false)
466      {
467      unset($encode_arr['"']);
468      unset($encode_arr['<']);
469      unset($encode_arr['>']);
470      unset($encode_arr['&']);
471      }
472    else if ($mode=='remove')
473      $str = strip_tags($str);
474   
475    // avoid douple quotation of &
476    $out = preg_replace('/&amp;([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', strtr($str, $encode_arr));
477     
478    return $newlines ? nl2br($out) : $out;
479    }
480
481  // if the replace tables for XML and JS are not yet defined
482  if ($js_rep_table===false)
483    {
484    $js_rep_table = $xml_rep_table = array();
485    $xml_rep_table['&'] = '&amp;';
486
487    for ($c=160; $c<256; $c++)  // can be increased to support more charsets
488      $xml_rep_table[chr($c)] = "&#$c;";
489
490    $xml_rep_table['"'] = '&quot;';
491    $js_rep_table['"'] = '\\"';
492    $js_rep_table["'"] = "\\'";
493    $js_rep_table["\\"] = "\\\\";
494    // Unicode line and paragraph separators (#1486310)
495    $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A8))] = '&#8232;';
496    $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A9))] = '&#8233;';
497    }
498
499  // encode for javascript use
500  if ($enctype=='js')
501    return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), strtr($str, $js_rep_table));
502
503  // encode for plaintext
504  if ($enctype=='text')
505    return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str);
506
507  if ($enctype=='url')
508    return rawurlencode($str);
509
510  // encode for XML
511  if ($enctype=='xml')
512    return strtr($str, $xml_rep_table);
513
514  // no encoding given -> return original string
515  return $str;
516  }
517 
518/**
519 * Quote a given string.
520 * Shortcut function for rep_specialchars_output
521 *
522 * @return string HTML-quoted string
523 * @see rep_specialchars_output()
524 */
525function Q($str, $mode='strict', $newlines=TRUE)
526  {
527  return rep_specialchars_output($str, 'html', $mode, $newlines);
528  }
529
530/**
531 * Quote a given string for javascript output.
532 * Shortcut function for rep_specialchars_output
533 *
534 * @return string JS-quoted string
535 * @see rep_specialchars_output()
536 */
537function JQ($str)
538  {
539  return rep_specialchars_output($str, 'js');
540  }
541
542
543/**
544 * Read input value and convert it for internal use
545 * Performs stripslashes() and charset conversion if necessary
546 *
547 * @param  string   Field name to read
548 * @param  int      Source to get value from (GPC)
549 * @param  boolean  Allow HTML tags in field value
550 * @param  string   Charset to convert into
551 * @return string   Field value or NULL if not available
552 */
553function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
554{
555  global $OUTPUT;
556  $value = NULL;
557 
558  if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
559    $value = $_GET[$fname];
560  else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
561    $value = $_POST[$fname];
562  else if ($source==RCUBE_INPUT_GPC)
563    {
564    if (isset($_POST[$fname]))
565      $value = $_POST[$fname];
566    else if (isset($_GET[$fname]))
567      $value = $_GET[$fname];
568    else if (isset($_COOKIE[$fname]))
569      $value = $_COOKIE[$fname];
570    }
571
572  if (empty($value))
573    return $value;
574
575  // strip single quotes if magic_quotes_sybase is enabled
576  if (ini_get('magic_quotes_sybase'))
577    $value = str_replace("''", "'", $value);
578  // strip slashes if magic_quotes enabled
579  else if (get_magic_quotes_gpc() || get_magic_quotes_runtime())
580    $value = stripslashes($value);
581
582  // remove HTML tags if not allowed   
583  if (!$allow_html)
584    $value = strip_tags($value);
585 
586  // convert to internal charset
587  if (is_object($OUTPUT))
588    return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
589  else
590    return $value;
591}
592
593/**
594 * Convert array of request parameters (prefixed with _)
595 * to a regular array with non-prefixed keys.
596 *
597 * @param  int   Source to get value from (GPC)
598 * @return array Hash array with all request parameters
599 */
600function request2param($mode = RCUBE_INPUT_GPC)
601{
602  $out = array();
603  $src = $mode == RCUBE_INPUT_GET ? $_GET : ($mode == RCUBE_INPUT_POST ? $_POST : $_REQUEST);
604  foreach ($src as $key => $value) {
605    $fname = $key[0] == '_' ? substr($key, 1) : $key;
606    $out[$fname] = get_input_value($key, $mode);
607  }
608 
609  return $out;
610}
611
612/**
613 * Remove all non-ascii and non-word chars
614 * except ., -, _
615 */
616function asciiwords($str, $css_id = false, $replace_with = '')
617{
618  $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : '');
619  return preg_replace("/[^$allowed]/i", $replace_with, $str);
620}
621
622/**
623 * Remove single and double quotes from given string
624 *
625 * @param string Input value
626 * @return string Dequoted string
627 */
628function strip_quotes($str)
629{
630  return preg_replace('/[\'"]/', '', $str);
631}
632
633
634/**
635 * Remove new lines characters from given string
636 *
637 * @param string Input value
638 * @return string Stripped string
639 */
640function strip_newlines($str)
641{
642  return preg_replace('/[\r\n]/', '', $str);
643}
644
645
646/**
647 * Create a HTML table based on the given data
648 *
649 * @param  array  Named table attributes
650 * @param  mixed  Table row data. Either a two-dimensional array or a valid SQL result set
651 * @param  array  List of cols to show
652 * @param  string Name of the identifier col
653 * @return string HTML table code
654 */
655function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
656  {
657  global $RCMAIL;
658 
659  $table = new html_table(/*array('cols' => count($a_show_cols))*/);
660   
661  // add table header
662  foreach ($a_show_cols as $col)
663    $table->add_header($col, Q(rcube_label($col)));
664 
665  $c = 0;
666  if (!is_array($table_data))
667  {
668    $db = $RCMAIL->get_dbh();
669    while ($table_data && ($sql_arr = $db->fetch_assoc($table_data)))
670    {
671      $zebra_class = $c % 2 ? 'even' : 'odd';
672      $table->add_row(array('id' => 'rcmrow' . $sql_arr[$id_col], 'class' => $zebra_class));
673
674      // format each col
675      foreach ($a_show_cols as $col)
676        $table->add($col, Q($sql_arr[$col]));
677     
678      $c++;
679    }
680  }
681  else
682  {
683    foreach ($table_data as $row_data)
684    {
685      $zebra_class = $c % 2 ? 'even' : 'odd';
686      $table->add_row(array('id' => 'rcmrow' . $row_data[$id_col], 'class' => $zebra_class));
687
688      // format each col
689      foreach ($a_show_cols as $col)
690        $table->add($col, Q($row_data[$col]));
691       
692      $c++;
693    }
694  }
695
696  return $table->show($attrib);
697  }
698
699
700/**
701 * Create an edit field for inclusion on a form
702 *
703 * @param string col field name
704 * @param string value field value
705 * @param array attrib HTML element attributes for field
706 * @param string type HTML element type (default 'text')
707 * @return string HTML field definition
708 */
709function rcmail_get_edit_field($col, $value, $attrib, $type='text')
710  {
711  $fname = '_'.$col;
712  $attrib['name'] = $fname;
713 
714  if ($type=='checkbox')
715    {
716    $attrib['value'] = '1';
717    $input = new html_checkbox($attrib);
718    }
719  else if ($type=='textarea')
720    {
721    $attrib['cols'] = $attrib['size'];
722    $input = new html_textarea($attrib);
723    }
724  else
725    $input = new html_inputfield($attrib);
726
727  // use value from post
728  if (!empty($_POST[$fname]))
729    $value = get_input_value($fname, RCUBE_INPUT_POST,
730            $type == 'textarea' && strpos($attrib['class'], 'mce_editor')!==false ? true : false);
731
732  $out = $input->show($value);
733         
734  return $out;
735  }
736
737
738/**
739 * Replace all css definitions with #container [def]
740 * and remove css-inlined scripting
741 *
742 * @param string CSS source code
743 * @param string Container ID to use as prefix
744 * @return string Modified CSS source
745 */
746function rcmail_mod_css_styles($source, $container_id)
747  {
748  $last_pos = 0;
749  $replacements = new rcube_string_replacer;
750 
751  // ignore the whole block if evil styles are detected
752  $stripped = preg_replace('/[^a-z\(:]/', '', rcmail_xss_entity_decode($source));
753  if (preg_match('/expression|behavior|url\(|import/', $stripped))
754    return '/* evil! */';
755
756  // cut out all contents between { and }
757  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
758  {
759    $key = $replacements->add(substr($source, $pos+1, $pos2-($pos+1)));
760    $source = substr($source, 0, $pos+1) . $replacements->get_replacement($key) . substr($source, $pos2, strlen($source)-$pos2);
761    $last_pos = $pos+2;
762  }
763 
764  // remove html comments and add #container to each tag selector.
765  // also replace body definition because we also stripped off the <body> tag
766  $styles = preg_replace(
767    array(
768      '/(^\s*<!--)|(-->\s*$)/',
769      '/(^\s*|,\s*|\}\s*)([a-z0-9\._#][a-z0-9\.\-_]*)/im',
770      "/$container_id\s+body/i",
771    ),
772    array(
773      '',
774      "\\1#$container_id \\2",
775      "$container_id div.rcmBody",
776    ),
777    $source);
778 
779  // put block contents back in
780  $styles = $replacements->resolve($styles);
781
782  return $styles;
783  }
784
785
786/**
787 * Decode escaped entities used by known XSS exploits.
788 * See http://downloads.securityfocus.com/vulnerabilities/exploits/26800.eml for examples
789 *
790 * @param string CSS content to decode
791 * @return string Decoded string
792 */
793function rcmail_xss_entity_decode($content)
794{
795  $out = html_entity_decode(html_entity_decode($content));
796  $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', 'rcmail_xss_entity_decode_callback', $out);
797  $out = preg_replace('#/\*.*\*/#Um', '', $out);
798  return $out;
799}
800
801
802/**
803 * preg_replace_callback callback for rcmail_xss_entity_decode_callback
804 *
805 * @param array matches result from preg_replace_callback
806 * @return string decoded entity
807 */
808function rcmail_xss_entity_decode_callback($matches)
809{
810  return chr(hexdec($matches[1]));
811}
812
813/**
814 * Compose a valid attribute string for HTML tags
815 *
816 * @param array Named tag attributes
817 * @param array List of allowed attributes
818 * @return string HTML formatted attribute string
819 */
820function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
821  {
822  // allow the following attributes to be added to the <iframe> tag
823  $attrib_str = '';
824  foreach ($allowed_attribs as $a)
825    if (isset($attrib[$a]))
826      $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '&quot;', $attrib[$a]));
827
828  return $attrib_str;
829  }
830
831
832/**
833 * Convert a HTML attribute string attributes to an associative array (name => value)
834 *
835 * @param string Input string
836 * @return array Key-value pairs of parsed attributes
837 */
838function parse_attrib_string($str)
839  {
840  $attrib = array();
841  preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
842
843  // convert attributes to an associative array (name => value)
844  if ($regs) {
845    foreach ($regs as $attr) {
846      $attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]);
847    }
848  }
849
850  return $attrib;
851  }
852
853
854/**
855 * Convert the given date to a human readable form
856 * This uses the date formatting properties from config
857 *
858 * @param mixed Date representation (string or timestamp)
859 * @param string Date format to use
860 * @return string Formatted date string
861 */
862function format_date($date, $format=NULL)
863  {
864  global $CONFIG;
865 
866  $ts = NULL;
867
868  if (is_numeric($date))
869    $ts = $date;
870  else if (!empty($date))
871    {
872    // support non-standard "GMTXXXX" literal
873    $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date);
874    // if date parsing fails, we have a date in non-rfc format.
875    // remove token from the end and try again
876    while ((($ts = @strtotime($date))===false) || ($ts < 0))
877      {
878        $d = explode(' ', $date);
879        array_pop($d);
880        if (!$d) break;
881        $date = implode(' ', $d);
882      }
883    }
884
885  if (empty($ts))
886    return '';
887   
888  // get user's timezone
889  if ($CONFIG['timezone'] === 'auto')
890    $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600;
891  else {
892    $tz = $CONFIG['timezone'];
893    if ($CONFIG['dst_active'])
894      $tz++;
895  }
896
897  // convert time to user's timezone
898  $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
899 
900  // get current timestamp in user's timezone
901  $now = time();  // local time
902  $now -= (int)date('Z'); // make GMT time
903  $now += ($tz * 3600); // user's time
904  $now_date = getdate($now);
905
906  $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
907  $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
908
909  // define date format depending on current time
910  if (!$format) {
911    if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now)
912      return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp));
913    else if ($CONFIG['prettydate'] && $timestamp > $week_limit && $timestamp < $now)
914      $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
915    else
916      $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
917    }
918
919  // strftime() format
920  if (preg_match('/%[a-z]+/i', $format))
921    return strftime($format, $timestamp);
922
923  // parse format string manually in order to provide localized weekday and month names
924  // an alternative would be to convert the date() format string to fit with strftime()
925  $out = '';
926  for($i=0; $i<strlen($format); $i++)
927    {
928    if ($format{$i}=='\\')  // skip escape chars
929      continue;
930   
931    // write char "as-is"
932    if ($format{$i}==' ' || $format{$i-1}=='\\')
933      $out .= $format{$i};
934    // weekday (short)
935    else if ($format{$i}=='D')
936      $out .= rcube_label(strtolower(date('D', $timestamp)));
937    // weekday long
938    else if ($format{$i}=='l')
939      $out .= rcube_label(strtolower(date('l', $timestamp)));
940    // month name (short)
941    else if ($format{$i}=='M')
942      $out .= rcube_label(strtolower(date('M', $timestamp)));
943    // month name (long)
944    else if ($format{$i}=='F')
945      $out .= rcube_label('long'.strtolower(date('M', $timestamp)));
946    else if ($format{$i}=='x')
947      $out .= strftime('%x %X', $timestamp);
948    else
949      $out .= date($format{$i}, $timestamp);
950    }
951 
952  return $out;
953  }
954
955
956/**
957 * Compose a valid representation of name and e-mail address
958 *
959 * @param string E-mail address
960 * @param string Person name
961 * @return string Formatted string
962 */
963function format_email_recipient($email, $name='')
964  {
965  if ($name && $name != $email)
966    {
967    // Special chars as defined by RFC 822 need to in quoted string (or escaped).
968    return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, trim($email));
969    }
970  else
971    return trim($email);
972  }
973
974
975
976/****** debugging functions ********/
977
978
979/**
980 * Print or write debug messages
981 *
982 * @param mixed Debug message or data
983 */
984function console()
985  {
986  $args = func_get_args();
987
988  if (class_exists('rcmail', false)) {
989    $rcmail = rcmail::get_instance();
990    if (is_object($rcmail->plugins))
991      $rcmail->plugins->exec_hook('console', $args);
992  }
993
994  $msg = array();
995  foreach ($args as $arg)
996    $msg[] = !is_string($arg) ? var_export($arg, true) : $arg;
997
998  if (!($GLOBALS['CONFIG']['debug_level'] & 4))
999    write_log('console', join(";\n", $msg));
1000  else if ($GLOBALS['OUTPUT']->ajax_call)
1001    print "/*\n " . join(";\n", $msg) . " \n*/\n";
1002  else
1003    {
1004    print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>';
1005    print join(";<br/>\n", $msg);
1006    print "</pre></div>\n";
1007    }
1008  }
1009
1010
1011/**
1012 * Append a line to a logfile in the logs directory.
1013 * Date will be added automatically to the line.
1014 *
1015 * @param $name name of log file
1016 * @param line Line to append
1017 */
1018function write_log($name, $line)
1019  {
1020  global $CONFIG, $RCMAIL;
1021
1022  if (!is_string($line))
1023    $line = var_export($line, true);
1024 
1025  if (empty($CONFIG['log_date_format']))
1026    $CONFIG['log_date_format'] = 'd-M-Y H:i:s O';
1027 
1028  $date = date($CONFIG['log_date_format']);
1029 
1030  // trigger logging hook
1031  if (is_object($RCMAIL) && is_object($RCMAIL->plugins)) {
1032    $log = $RCMAIL->plugins->exec_hook('write_log', array('name' => $name, 'date' => $date, 'line' => $line));
1033    $name = $log['name'];
1034    $line = $log['line'];
1035    $date = $log['date'];
1036    if ($log['abort'])
1037      return true;
1038  }
1039 
1040  $log_entry = sprintf("[%s]: %s\n", $date, $line);
1041
1042  if ($CONFIG['log_driver'] == 'syslog') {
1043    $prio = $name == 'errors' ? LOG_ERR : LOG_INFO;
1044    syslog($prio, $log_entry);
1045    return true;
1046  }
1047  else {
1048    // log_driver == 'file' is assumed here
1049    if (empty($CONFIG['log_dir']))
1050      $CONFIG['log_dir'] = INSTALL_PATH.'logs';
1051
1052    // try to open specific log file for writing
1053    $logfile = $CONFIG['log_dir'].'/'.$name;
1054    if ($fp = @fopen($logfile, 'a')) {
1055      fwrite($fp, $log_entry);
1056      fflush($fp);
1057      fclose($fp);
1058      return true;
1059    }
1060    else
1061      trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING);
1062  }
1063  return false;
1064}
1065
1066
1067/**
1068 * @access private
1069 */
1070function rcube_timer()
1071{
1072  return microtime(true);
1073}
1074 
1075
1076/**
1077 * @access private
1078 */
1079function rcube_print_time($timer, $label='Timer', $dest='console')
1080{
1081  static $print_count = 0;
1082 
1083  $print_count++;
1084  $now = rcube_timer();
1085  $diff = $now-$timer;
1086 
1087  if (empty($label))
1088    $label = 'Timer '.$print_count;
1089 
1090  write_log($dest, sprintf("%s: %0.4f sec", $label, $diff));
1091}
1092
1093
1094/**
1095 * Return the mailboxlist in HTML
1096 *
1097 * @param array Named parameters
1098 * @return string HTML code for the gui object
1099 */
1100function rcmail_mailbox_list($attrib)
1101{
1102  global $RCMAIL;
1103  static $a_mailboxes;
1104 
1105  $attrib += array('maxlength' => 100, 'realnames' => false);
1106
1107  // add some labels to client
1108  $RCMAIL->output->add_label('purgefolderconfirm', 'deletemessagesconfirm');
1109 
1110  $type = $attrib['type'] ? $attrib['type'] : 'ul';
1111  unset($attrib['type']);
1112
1113  if ($type=='ul' && !$attrib['id'])
1114    $attrib['id'] = 'rcmboxlist';
1115
1116  // get mailbox list
1117  $mbox_name = $RCMAIL->imap->get_mailbox_name();
1118 
1119  // build the folders tree
1120  if (empty($a_mailboxes)) {
1121    // get mailbox list
1122    $a_folders = $RCMAIL->imap->list_mailboxes();
1123    $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
1124    $a_mailboxes = array();
1125
1126    foreach ($a_folders as $folder)
1127      rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
1128  }
1129 
1130  // allow plugins to alter the folder tree or to localize folder names
1131  $hook = $RCMAIL->plugins->exec_hook('render_mailboxlist', array('list' => $a_mailboxes, 'delimiter' => $delimiter));
1132
1133  if ($type=='select') {
1134    $select = new html_select($attrib);
1135   
1136    // add no-selection option
1137    if ($attrib['noselection'])
1138      $select->add(rcube_label($attrib['noselection']), '0');
1139   
1140    rcmail_render_folder_tree_select($hook['list'], $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']);
1141    $out = $select->show();
1142  }
1143  else {
1144    $js_mailboxlist = array();
1145    $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($hook['list'], $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib);
1146   
1147    $RCMAIL->output->add_gui_object('mailboxlist', $attrib['id']);
1148    $RCMAIL->output->set_env('mailboxes', $js_mailboxlist);
1149    $RCMAIL->output->set_env('collapsed_folders', $RCMAIL->config->get('collapsed_folders'));
1150  }
1151
1152  return $out;
1153}
1154
1155
1156/**
1157 * Return the mailboxlist as html_select object
1158 *
1159 * @param array Named parameters
1160 * @return object html_select HTML drop-down object
1161 */
1162function rcmail_mailbox_select($p = array())
1163{
1164  global $RCMAIL;
1165 
1166  $p += array('maxlength' => 100, 'realnames' => false);
1167  $a_mailboxes = array();
1168 
1169  foreach ($RCMAIL->imap->list_mailboxes() as $folder)
1170    if (empty($p['exceptions']) || !in_array($folder, $p['exceptions']))
1171      rcmail_build_folder_tree($a_mailboxes, $folder, $RCMAIL->imap->get_hierarchy_delimiter());
1172
1173  $select = new html_select($p);
1174 
1175  if ($p['noselection'])
1176    $select->add($p['noselection'], '');
1177   
1178  rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames']);
1179 
1180  return $select;
1181}
1182
1183
1184/**
1185 * Create a hierarchical array of the mailbox list
1186 * @access private
1187 */
1188function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
1189{
1190  $pos = strpos($folder, $delm);
1191  if ($pos !== false) {
1192    $subFolders = substr($folder, $pos+1);
1193    $currentFolder = substr($folder, 0, $pos);
1194    $virtual = !isset($arrFolders[$currentFolder]);
1195  }
1196  else {
1197    $subFolders = false;
1198    $currentFolder = $folder;
1199    $virtual = false;
1200  }
1201
1202  $path .= $currentFolder;
1203
1204  if (!isset($arrFolders[$currentFolder])) {
1205    $arrFolders[$currentFolder] = array(
1206      'id' => $path,
1207      'name' => rcube_charset_convert($currentFolder, 'UTF7-IMAP'),
1208      'virtual' => $virtual,
1209      'folders' => array());
1210  }
1211  else
1212    $arrFolders[$currentFolder]['virtual'] = $virtual;
1213
1214  if (!empty($subFolders))
1215    rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
1216}
1217 
1218
1219/**
1220 * Return html for a structured list &lt;ul&gt; for the mailbox tree
1221 * @access private
1222 */
1223function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel=0)
1224{
1225  global $RCMAIL, $CONFIG;
1226 
1227  $maxlength = intval($attrib['maxlength']);
1228  $realnames = (bool)$attrib['realnames'];
1229  $msgcounts = $RCMAIL->imap->get_cache('messagecount');
1230
1231  $idx = 0;
1232  $out = '';
1233  foreach ($arrFolders as $key => $folder) {
1234    $zebra_class = (($nestLevel+1)*$idx) % 2 == 0 ? 'even' : 'odd';
1235    $title = null;
1236
1237    if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames) {
1238      $foldername = rcube_label($folder_class);
1239    }
1240    else {
1241      $foldername = $folder['name'];
1242
1243      // shorten the folder name to a given length
1244      if ($maxlength && $maxlength > 1) {
1245        $fname = abbreviate_string($foldername, $maxlength);
1246        if ($fname != $foldername)
1247          $title = $foldername;
1248        $foldername = $fname;
1249      }
1250    }
1251
1252    // make folder name safe for ids and class names
1253    $folder_id = asciiwords($folder['id'], true, '_');
1254    $classes = array('mailbox');
1255
1256    // set special class for Sent, Drafts, Trash and Junk
1257    if ($folder['id']==$CONFIG['sent_mbox'])
1258      $classes[] = 'sent';
1259    else if ($folder['id']==$CONFIG['drafts_mbox'])
1260      $classes[] = 'drafts';
1261    else if ($folder['id']==$CONFIG['trash_mbox'])
1262      $classes[] = 'trash';
1263    else if ($folder['id']==$CONFIG['junk_mbox'])
1264      $classes[] = 'junk';
1265    else if ($folder['id']=='INBOX')
1266      $classes[] = 'inbox';
1267    else
1268      $classes[] = '_'.asciiwords($folder_class ? $folder_class : strtolower($folder['id']), true);
1269     
1270    $classes[] = $zebra_class;
1271   
1272    if ($folder['id'] == $mbox_name)
1273      $classes[] = 'selected';
1274
1275    $collapsed = preg_match('/&'.rawurlencode($folder['id']).'&/', $RCMAIL->config->get('collapsed_folders'));
1276    $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
1277   
1278    if ($folder['virtual'])
1279      $classes[] = 'virtual';
1280    else if ($unread)
1281      $classes[] = 'unread';
1282
1283    $js_name = JQ($folder['id']);
1284    $html_name = Q($foldername . ($unread ? " ($unread)" : ''));
1285    $link_attrib = $folder['virtual'] ? array() : array(
1286      'href' => rcmail_url('', array('_mbox' => $folder['id'])),
1287      'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name),
1288      'title' => $title,
1289    );
1290
1291    $out .= html::tag('li', array(
1292        'id' => "rcmli".$folder_id,
1293        'class' => join(' ', $classes),
1294        'noclose' => true),
1295      html::a($link_attrib, $html_name) .
1296      (!empty($folder['folders']) ? html::div(array(
1297        'class' => ($collapsed ? 'collapsed' : 'expanded'),
1298        'style' => "position:absolute",
1299        'onclick' => sprintf("%s.command('collapse-folder', '%s')", JS_OBJECT_NAME, $js_name)
1300      ), '&nbsp;') : ''));
1301   
1302    $jslist[$folder_id] = array('id' => $folder['id'], 'name' => $foldername, 'virtual' => $folder['virtual']);
1303   
1304    if (!empty($folder['folders'])) {
1305      $out .= html::tag('ul', array('style' => ($collapsed ? "display:none;" : null)),
1306        rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1));
1307    }
1308
1309    $out .= "</li>\n";
1310    $idx++;
1311  }
1312
1313  return $out;
1314}
1315
1316
1317/**
1318 * Return html for a flat list <select> for the mailbox tree
1319 * @access private
1320 */
1321function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0)
1322  {
1323  $idx = 0;
1324  $out = '';
1325  foreach ($arrFolders as $key=>$folder)
1326    {
1327    if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id'])))
1328      $foldername = rcube_label($folder_class);
1329    else
1330      {
1331      $foldername = $folder['name'];
1332     
1333      // shorten the folder name to a given length
1334      if ($maxlength && $maxlength>1)
1335        $foldername = abbreviate_string($foldername, $maxlength);
1336      }
1337
1338    $select->add(str_repeat('&nbsp;', $nestLevel*4) . $foldername, $folder['id']);
1339
1340    if (!empty($folder['folders']))
1341      $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, $select, $realnames, $nestLevel+1);
1342
1343    $idx++;
1344    }
1345
1346  return $out;
1347  }
1348
1349
1350/**
1351 * Return internal name for the given folder if it matches the configured special folders
1352 * @access private
1353 */
1354function rcmail_folder_classname($folder_id)
1355{
1356  global $CONFIG;
1357
1358  // for these mailboxes we have localized labels and css classes
1359  foreach (array('sent', 'drafts', 'trash', 'junk') as $smbx)
1360  {
1361    if ($folder_id == $CONFIG[$smbx.'_mbox'])
1362      return $smbx;
1363  }
1364
1365  if ($folder_id == 'INBOX')
1366    return 'inbox';
1367}
1368
1369
1370/**
1371 * Try to localize the given IMAP folder name.
1372 * UTF-7 decode it in case no localized text was found
1373 *
1374 * @param string Folder name
1375 * @return string Localized folder name in UTF-8 encoding
1376 */
1377function rcmail_localize_foldername($name)
1378{
1379  if ($folder_class = rcmail_folder_classname($name))
1380    return rcube_label($folder_class);
1381  else
1382    return rcube_charset_convert($name, 'UTF7-IMAP');
1383}
1384
1385
1386/**
1387 * Output HTML editor scripts
1388 *
1389 * @param string Editor mode
1390 */
1391function rcube_html_editor($mode='')
1392{
1393  global $RCMAIL, $CONFIG;
1394
1395  $hook = $RCMAIL->plugins->exec_hook('hmtl_editor', array('mode' => $mode));
1396
1397  if ($hook['abort'])
1398    return; 
1399
1400  $lang = strtolower(substr($_SESSION['language'], 0, 2));
1401  if (!file_exists(INSTALL_PATH . 'program/js/tiny_mce/langs/'.$lang.'.js'))
1402    $lang = 'en';
1403
1404  $RCMAIL->output->include_script('tiny_mce/tiny_mce.js');
1405  $RCMAIL->output->include_script('editor.js');
1406  $RCMAIL->output->add_script('rcmail_editor_init("$__skin_path",
1407    "'.JQ($lang).'", '.intval($CONFIG['enable_spellcheck']).', "'.$mode.'");');
1408}
1409
1410
1411/**
1412 * Check if working in SSL mode
1413 *
1414 * @param integer HTTPS port number
1415 * @param boolean Enables 'use_https' option checking
1416 */
1417function rcube_https_check($port=null, $use_https=true)
1418{
1419  global $RCMAIL;
1420 
1421  if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off')
1422    return true;
1423  if ($port && $_SERVER['SERVER_PORT'] == $port)
1424    return true;
1425  if ($use_https && $RCMAIL->config->get('use_https'))
1426    return true;
1427
1428  return false;
1429}
1430
1431
1432/**
1433 * E-mail address validation
1434 */
1435function check_email($email)
1436{
1437  // Check for invalid characters
1438  if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email))
1439    return false;
1440
1441  // Check that there's one @ symbol, and that the lengths are right
1442  if (!preg_match('/^([^@]{1,64})@([^@]{1,255})$/', $email, $email_array))
1443    return false;
1444
1445  // Check local part
1446  $local_array = explode('.', $email_array[1]);
1447  foreach ($local_array as $local_part)
1448    if (!preg_match('/^(([A-Za-z0-9!#$%&\'*+\/=?^_`{|}~-]+)|("[^"]+"))$/', $local_part))
1449      return false;
1450
1451  // Check domain part
1452  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}$/', $email_array[2])
1453      || 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}\]$/', $email_array[2]))
1454    return true; // If an IP address
1455  else {
1456    // If not an IP address
1457    $domain_array = explode('.', $email_array[2]);
1458    if (sizeof($domain_array) < 2)
1459      return false; // Not enough parts to be a valid domain
1460
1461    foreach ($domain_array as $domain_part)
1462      if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $domain_part))
1463        return false;
1464
1465    if (!rcmail::get_instance()->config->get('email_dns_check'))
1466      return true;
1467
1468    if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && version_compare(PHP_VERSION, '5.3.0', '<'))
1469      return true;
1470
1471    // find MX record(s)
1472    if (getmxrr($email_array[2], $mx_records))
1473      return true;
1474
1475    // find any DNS record
1476    if (checkdnsrr($email_array[2], 'ANY'))
1477      return true;
1478  }
1479
1480  return false;
1481}
1482
1483
1484/**
1485 * Helper class to turn relative urls into absolute ones
1486 * using a predefined base
1487 */
1488class rcube_base_replacer
1489{
1490  private $base_url;
1491 
1492  public function __construct($base)
1493  {
1494    $this->base_url = $base;
1495  }
1496 
1497  public function callback($matches)
1498  {
1499    return $matches[1] . '="' . make_absolute_url($matches[3], $this->base_url) . '"';
1500  }
1501}
1502
1503?>
Note: See TracBrowser for help on using the repository browser.