source: github/program/include/main.inc @ 72b140d

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