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

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