source: github/program/include/main.inc @ e7dde1f

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