source: github/program/include/main.inc @ 3ea30ef

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