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

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