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

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