source: github/program/include/main.inc @ 1e3271ed

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