4 +-----------------------------------------------------------------------+
5 | program/include/main.inc |
7 | This file is part of the RoundCube Webmail client |
8 | Copyright (C) 2005-2008, RoundCube Dev, - Switzerland |
9 | Licensed under the GNU GPL |
12 | Provide basic functions for the webmail package |
14 +-----------------------------------------------------------------------+
15 | Author: Thomas Bruederli <roundcube@gmail.com> |
16 +-----------------------------------------------------------------------+
18 $Id: main.inc 1459 2008-05-30 19:55:28Z alec $
23 * RoundCube Webmail common functions
26 * @author Thomas Bruederli <roundcube@gmail.com>
29 require_once('lib/utf7.inc');
30 require_once('include/rcube_shared.inc');
32 // define constannts for input reading
33 define('RCUBE_INPUT_GET', 0x0101);
34 define('RCUBE_INPUT_POST', 0x0102);
35 define('RCUBE_INPUT_GPC', 0x0103);
40 * Return correct name for a specific database table
42 * @param string Table name
43 * @return string Translated table name
45 function get_table_name($table)
49 // return table name if configured
50 $config_key = 'db_table_'.$table;
52 if (strlen($CONFIG[$config_key]))
53 return $CONFIG[$config_key];
60 * Return correct name for a specific database sequence
61 * (used for Postgres only)
63 * @param string Secuence name
64 * @return string Translated sequence name
66 function get_sequence_name($sequence)
68 // return table name if configured
69 $config_key = 'db_sequence_'.$sequence;
70 $opt = rcmail::get_instance()->config->get($config_key);
74 $db = &rcmail::get_instance()->db;
76 if($db->db_provider=='pgsql') // just for sure
78 $db->db_handle->setOption('disable_smart_seqname', true);
79 $db->db_handle->setOption('seqname_format', '%s');
90 * Get localized text in the desired language
91 * It's a global wrapper for rcmail::gettext()
93 * @param mixed Named parameters array or label name
94 * @return string Localized text
95 * @see rcmail::gettext()
97 function rcube_label($p)
99 return rcmail::get_instance()->gettext($p);
104 * Load virtuser table in array
106 * @return array Virtuser table entries
108 function rcmail_getvirtualfile()
111 if (empty($CONFIG['virtuser_file']) || !is_file($CONFIG['virtuser_file']))
115 $a_lines = file($CONFIG['virtuser_file']);
121 * Find matches of the given pattern in virtuser table
123 * @param string Regular expression to search for
124 * @return array Matching entries
126 function rcmail_findinvirtual($pattern)
129 $virtual = rcmail_getvirtualfile();
133 // check each line for matches
134 foreach ($virtual as $line)
137 if (empty($line) || $line{0}=='#')
140 if (eregi($pattern, $line))
149 * Overwrite action variable
151 * @param string New action value
153 function rcmail_overwrite_action($action)
155 $app = rcmail::get_instance();
156 $app->action = $action;
157 $app->output->set_env('action', $action);
162 * Compose an URL for a specific action
164 * @param string Request action
165 * @param array More URL parameters
166 * @param string Request task (omit if the same)
167 * @return The application URL
169 function rcmail_url($action, $p=array(), $task=null)
171 $app = rcmail::get_instance();
174 $base = $app->comm_path;
176 if ($task && in_array($task, rcmail::$main_tasks))
177 $base = ereg_replace('_task=[a-z]+', '_task='.$task, $app->comm_path);
180 foreach ($p as $key => $val)
181 $qstring .= '&'.urlencode($key).'='.urlencode($val);
183 return $base . ($action ? '&_action='.$action : '') . $qstring;
188 * Add a localized label to the client environment
191 function rcube_add_label()
195 $arg_list = func_get_args();
196 foreach ($arg_list as $i => $name)
197 $OUTPUT->add_label($name);
202 * Garbage collector function for temp files.
203 * Remove temp files older than two days
205 function rcmail_temp_gc()
207 $tmp = unslashify($CONFIG['temp_dir']);
208 $expire = mktime() - 172800; // expire in 48 hours
210 if ($dir = opendir($tmp))
212 while (($fname = readdir($dir)) !== false)
214 if ($fname{0} == '.')
217 if (filemtime($tmp.'/'.$fname) < $expire)
218 @unlink($tmp.'/'.$fname);
227 * Garbage collector for cache entries.
228 * Remove all expired message cache records
230 function rcmail_message_cache_gc()
234 // no cache lifetime configured
235 if (empty($CONFIG['message_cache_lifetime']))
238 // get target timestamp
239 $ts = get_offset_time($CONFIG['message_cache_lifetime'], -1);
241 $DB->query("DELETE FROM ".get_table_name('messages')."
242 WHERE created < ".$DB->fromunixtime($ts));
247 * Convert a string from one charset to another.
248 * Uses mbstring and iconv functions if possible
250 * @param string Input string
251 * @param string Suspected charset of the input string
252 * @param string Target charset to convert to; defaults to RCMAIL_CHARSET
253 * @return Converted string
255 function rcube_charset_convert($str, $from, $to=NULL)
257 static $mbstring_loaded = null, $convert_warning = false;
259 $from = strtoupper($from);
260 $to = $to==NULL ? strtoupper(RCMAIL_CHARSET) : strtoupper($to);
261 $error = false; $conv = null;
263 if ($from==$to || $str=='' || empty($from))
267 'UNKNOWN-8BIT' => 'ISO-8859-15',
268 'X-UNKNOWN' => 'ISO-8859-15',
269 'X-USER-DEFINED' => 'ISO-8859-15',
270 'ISO-8859-8-I' => 'ISO-8859-8',
271 'KS_C_5601-1987' => 'EUC-KR',
274 // convert charset using iconv module
275 if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7')
277 $aliases['GB2312'] = 'GB18030';
278 $_iconv = iconv(($aliases[$from] ? $aliases[$from] : $from), ($aliases[$to] ? $aliases[$to] : $to) . "//IGNORE", $str);
279 if ($_iconv !== false)
285 // settings for mbstring module (by Tadashi Jokagi)
286 if (is_null($mbstring_loaded)) {
287 if ($mbstring_loaded = extension_loaded("mbstring"))
288 mb_internal_encoding(RCMAIL_CHARSET);
291 // convert charset using mbstring module
292 if ($mbstring_loaded)
294 $aliases['UTF-7'] = 'UTF7-IMAP';
295 $aliases['WINDOWS-1257'] = 'ISO-8859-13';
297 // return if convert succeeded
298 if (($out = mb_convert_encoding($str, ($aliases[$to] ? $aliases[$to] : $to), ($aliases[$from] ? $aliases[$from] : $from))) != '')
303 if (class_exists('utf8'))
306 // convert string to UTF-8
307 if ($from == 'UTF-7')
308 $str = utf7_to_utf8($str);
309 else if (($from == 'ISO-8859-1') && function_exists('utf8_encode'))
310 $str = utf8_encode($str);
311 else if ($from != 'UTF-8' && $conv)
313 $conv->loadCharset($from);
314 $str = $conv->strToUtf8($str);
316 else if ($from != 'UTF-8')
319 // encode string for output
321 return utf8_to_utf7($str);
322 else if ($to == 'ISO-8859-1' && function_exists('utf8_decode'))
323 return utf8_decode($str);
324 else if ($to != 'UTF-8' && $conv)
326 $conv->loadCharset($to);
327 return $conv->utf8ToStr($str);
329 else if ($to != 'UTF-8')
333 if ($error && !$convert_warning)
339 'message' => "Could not convert string charset. Make sure iconv is installed or lib/utf8.class is available"
342 $convert_warning = true;
345 // return UTF-8 string
351 * Replacing specials characters to a specific encoding type
353 * @param string Input string
354 * @param string Encoding type: text|html|xml|js|url
355 * @param string Replace mode for tags: show|replace|remove
356 * @param boolean Convert newlines
357 * @return The quoted string
359 function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
362 static $html_encode_arr = false;
363 static $js_rep_table = false;
364 static $xml_rep_table = false;
366 $charset = $OUTPUT->get_charset();
367 $is_iso_8859_1 = false;
368 if ($charset == 'ISO-8859-1') {
369 $is_iso_8859_1 = true;
372 $enctype = $GLOBALS['OUTPUT_TYPE'];
374 // encode for plaintext
375 if ($enctype=='text')
376 return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str);
378 // encode for HTML output
379 if ($enctype=='html')
381 if (!$html_encode_arr)
383 $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);
384 unset($html_encode_arr['?']);
387 $ltpos = strpos($str, '<');
388 $encode_arr = $html_encode_arr;
390 // don't replace quotes and html tags
391 if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false)
393 unset($encode_arr['"']);
394 unset($encode_arr['<']);
395 unset($encode_arr['>']);
396 unset($encode_arr['&']);
398 else if ($mode=='remove')
399 $str = strip_tags($str);
401 // avoid douple quotation of &
402 $out = preg_replace('/&([a-z]{2,5}|#[0-9]{2,4});/', '&\\1;', strtr($str, $encode_arr));
404 return $newlines ? nl2br($out) : $out;
408 return rawurlencode($str);
410 // if the replace tables for XML and JS are not yet defined
411 if ($js_rep_table===false)
413 $js_rep_table = $xml_rep_table = array();
414 $xml_rep_table['&'] = '&';
416 for ($c=160; $c<256; $c++) // can be increased to support more charsets
418 $xml_rep_table[Chr($c)] = "&#$c;";
421 $js_rep_table[Chr($c)] = sprintf("\\u%04x", $c);
424 $xml_rep_table['"'] = '"';
429 return strtr($str, $xml_rep_table);
431 // encode for javascript use
434 if ($charset!='UTF-8')
435 $str = rcube_charset_convert($str, RCMAIL_CHARSET,$charset);
437 return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), addslashes(strtr($str, $js_rep_table)));
440 // no encoding given -> return original string
445 * Quote a given string.
446 * Shortcut function for rep_specialchars_output
448 * @return string HTML-quoted string
449 * @see rep_specialchars_output()
451 function Q($str, $mode='strict', $newlines=TRUE)
453 return rep_specialchars_output($str, 'html', $mode, $newlines);
457 * Quote a given string for javascript output.
458 * Shortcut function for rep_specialchars_output
460 * @return string JS-quoted string
461 * @see rep_specialchars_output()
465 return rep_specialchars_output($str, 'js');
470 * Read input value and convert it for internal use
471 * Performs stripslashes() and charset conversion if necessary
473 * @param string Field name to read
474 * @param int Source to get value from (GPC)
475 * @param boolean Allow HTML tags in field value
476 * @param string Charset to convert into
477 * @return string Field value or NULL if not available
479 function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
484 if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
485 $value = $_GET[$fname];
486 else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
487 $value = $_POST[$fname];
488 else if ($source==RCUBE_INPUT_GPC)
490 if (isset($_POST[$fname]))
491 $value = $_POST[$fname];
492 else if (isset($_GET[$fname]))
493 $value = $_GET[$fname];
494 else if (isset($_COOKIE[$fname]))
495 $value = $_COOKIE[$fname];
498 // strip slashes if magic_quotes enabled
499 if ((bool)get_magic_quotes_gpc())
500 $value = stripslashes($value);
502 // remove HTML tags if not allowed
504 $value = strip_tags($value);
506 // convert to internal charset
507 if (is_object($OUTPUT))
508 return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
514 * Remove all non-ascii and non-word chars
517 function asciiwords($str)
519 return preg_replace('/[^a-z0-9.-_]/i', '', $str);
523 * Remove single and double quotes from given string
525 * @param string Input value
526 * @return string Dequoted string
528 function strip_quotes($str)
530 return preg_replace('/[\'"]/', '', $str);
535 * Remove new lines characters from given string
537 * @param string Input value
538 * @return string Stripped string
540 function strip_newlines($str)
542 return preg_replace('/[\r\n]/', '', $str);
547 * Check if a specific template exists
549 * @param string Template name
550 * @return boolean True if template exists
552 function template_exists($name)
555 $skin_path = $CONFIG['skin_path'];
557 // check template file
558 return is_file("$skin_path/templates/$name.html");
563 * Create a HTML table based on the given data
565 * @param array Named table attributes
566 * @param mixed Table row data. Either a two-dimensional array or a valid SQL result set
567 * @param array List of cols to show
568 * @param string Name of the identifier col
569 * @return string HTML table code
571 function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
575 // allow the following attributes to be added to the <table> tag
576 $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
578 $table = '<table' . $attrib_str . ">\n";
581 $table .= "<thead><tr>\n";
583 foreach ($a_show_cols as $col)
584 $table .= '<td class="'.$col.'">' . Q(rcube_label($col)) . "</td>\n";
586 $table .= "</tr></thead>\n<tbody>\n";
589 if (!is_array($table_data))
591 while ($table_data && ($sql_arr = $DB->fetch_assoc($table_data)))
593 $zebra_class = $c%2 ? 'even' : 'odd';
595 $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $sql_arr[$id_col]);
598 foreach ($a_show_cols as $col)
600 $cont = Q($sql_arr[$col]);
601 $table .= '<td class="'.$col.'">' . $cont . "</td>\n";
610 foreach ($table_data as $row_data)
612 $zebra_class = $c%2 ? 'even' : 'odd';
614 $table .= sprintf('<tr id="rcmrow%s" class="contact '.$zebra_class.'">'."\n", $row_data[$id_col]);
617 foreach ($a_show_cols as $col)
619 $cont = Q($row_data[$col]);
620 $table .= '<td class="'.$col.'">' . $cont . "</td>\n";
628 // complete message table
629 $table .= "</tbody></table>\n";
636 * Create an edit field for inclusion on a form
638 * @param string col field name
639 * @param string value field value
640 * @param array attrib HTML element attributes for field
641 * @param string type HTML element type (default 'text')
642 * @return string HTML field definition
644 function rcmail_get_edit_field($col, $value, $attrib, $type='text')
647 $attrib['name'] = $fname;
649 if ($type=='checkbox')
651 $attrib['value'] = '1';
652 $input = new html_checkbox($attrib);
654 else if ($type=='textarea')
656 $attrib['cols'] = $attrib['size'];
657 $input = new html_textarea($attrib);
660 $input = new html_inputfield($attrib);
662 // use value from post
663 if (!empty($_POST[$fname]))
664 $value = get_input_value($fname, RCUBE_INPUT_POST);
666 $out = $input->show($value);
673 * Return the mail domain configured for the given host
675 * @param string IMAP host
676 * @return string Resolved SMTP host
678 function rcmail_mail_domain($host)
683 if (is_array($CONFIG['mail_domain']))
685 if (isset($CONFIG['mail_domain'][$host]))
686 $domain = $CONFIG['mail_domain'][$host];
688 else if (!empty($CONFIG['mail_domain']))
689 $domain = $CONFIG['mail_domain'];
696 * Replace all css definitions with #container [def]
697 * and remove css-inlined scripting
699 * @param string CSS source code
700 * @param string Container ID to use as prefix
701 * @return string Modified CSS source
703 function rcmail_mod_css_styles($source, $container_id, $base_url = '')
705 $a_css_values = array();
708 // ignore the whole block if evil styles are detected
709 if (stristr($source, 'expression') || stristr($source, 'behavior'))
712 // cut out all contents between { and }
713 while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
715 $key = sizeof($a_css_values);
716 $a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
717 $source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
721 // remove html comments and add #container to each tag selector.
722 // also replace body definition because we also stripped off the <body> tag
723 $styles = preg_replace(
725 '/(^\s*<!--)|(-->\s*$)/',
726 '/(^\s*|,\s*|\}\s*)([a-z0-9\._#][a-z0-9\.\-_]*)/im',
727 '/@import\s+(url\()?[\'"]?([^\)\'"]+)[\'"]?(\))?/ime',
728 '/<<str_replacement\[([0-9]+)\]>>/e',
729 "/$container_id\s+body/i"
733 "\\1#$container_id \\2",
734 "sprintf(\"@import url('./bin/modcss.php?u=%s&c=%s')\", urlencode(make_absolute_url('\\2','$base_url')), urlencode($container_id))",
735 "\$a_css_values[\\1]",
736 "$container_id div.rcmBody"
744 * Try to autodetect operating system and find the correct line endings
746 * @return string The appropriate mail header delimiter
748 function rcmail_header_delm()
752 // use the configured delimiter for headers
753 if (!empty($CONFIG['mail_header_delimiter']))
754 return $CONFIG['mail_header_delimiter'];
755 else if (strtolower(substr(PHP_OS, 0, 3)=='win'))
757 else if (strtolower(substr(PHP_OS, 0, 3)=='mac'))
765 * Compose a valid attribute string for HTML tags
767 * @param array Named tag attributes
768 * @param array List of allowed attributes
769 * @return string HTML formatted attribute string
771 function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
773 // allow the following attributes to be added to the <iframe> tag
775 foreach ($allowed_attribs as $a)
776 if (isset($attrib[$a]))
777 $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '"', $attrib[$a]));
784 * Convert a HTML attribute string attributes to an associative array (name => value)
786 * @param string Input string
787 * @return array Key-value pairs of parsed attributes
789 function parse_attrib_string($str)
792 preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]+)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
794 // convert attributes to an associative array (name => value)
796 foreach ($regs as $attr)
798 $attrib[strtolower($attr[1])] = $attr[3] . $attr[4];
806 * Convert the given date to a human readable form
807 * This uses the date formatting properties from config
809 * @param mixed Date representation (string or timestamp)
810 * @param string Date format to use
811 * @return string Formatted date string
813 function format_date($date, $format=NULL)
819 if (is_numeric($date))
821 else if (!empty($date))
823 while (($ts = @strtotime($date))===false)
825 // if we have a date in non-rfc format
826 // remove token from the end and try again
827 $d = explode(' ', $date);
830 $date = implode(' ', $d);
837 // get user's timezone
838 $tz = $CONFIG['timezone'];
839 if ($CONFIG['dst_active'])
842 // convert time to user's timezone
843 $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
845 // get current timestamp in user's timezone
846 $now = time(); // local time
847 $now -= (int)date('Z'); // make GMT time
848 $now += ($tz * 3600); // user's time
849 $now_date = getdate($now);
851 $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
852 $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
854 // define date format depending on current time
855 if ($CONFIG['prettydate'] && !$format && $timestamp > $today_limit && $timestamp < $now)
856 return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp));
857 else if ($CONFIG['prettydate'] && !$format && $timestamp > $week_limit && $timestamp < $now)
858 $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
860 $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
863 // parse format string manually in order to provide localized weekday and month names
864 // an alternative would be to convert the date() format string to fit with strftime()
866 for($i=0; $i<strlen($format); $i++)
868 if ($format{$i}=='\\') // skip escape chars
871 // write char "as-is"
872 if ($format{$i}==' ' || $format{$i-1}=='\\')
875 else if ($format{$i}=='D')
876 $out .= rcube_label(strtolower(date('D', $timestamp)));
878 else if ($format{$i}=='l')
879 $out .= rcube_label(strtolower(date('l', $timestamp)));
880 // month name (short)
881 else if ($format{$i}=='M')
882 $out .= rcube_label(strtolower(date('M', $timestamp)));
884 else if ($format{$i}=='F')
885 $out .= rcube_label('long'.strtolower(date('M', $timestamp)));
887 $out .= date($format{$i}, $timestamp);
895 * Compose a valid representaion of name and e-mail address
897 * @param string E-mail address
898 * @param string Person name
899 * @return string Formatted string
901 function format_email_recipient($email, $name='')
903 if ($name && $name != $email)
905 // Special chars as defined by RFC 822 need to in quoted string (or escaped).
906 return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, $email);
914 /****** debugging functions ********/
918 * Print or write debug messages
920 * @param mixed Debug message or data
922 function console($msg)
924 if (!is_string($msg))
925 $msg = var_export($msg, true);
927 if (!($GLOBALS['CONFIG']['debug_level'] & 4))
928 write_log('console', $msg);
929 else if ($GLOBALS['OUTPUT']->ajax_call)
930 print "/*\n $msg \n*/\n";
933 print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>';
935 print "</pre></div>\n";
941 * Append a line to a logfile in the logs directory.
942 * Date will be added automatically to the line.
944 * @param $name name of log file
945 * @param line Line to append
947 function write_log($name, $line)
951 if (!is_string($line))
952 $line = var_export($line, true);
954 $log_entry = sprintf("[%s]: %s\n",
955 date("d-M-Y H:i:s O", mktime()),
958 if (empty($CONFIG['log_dir']))
959 $CONFIG['log_dir'] = INSTALL_PATH.'logs';
961 // try to open specific log file for writing
962 if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a'))
964 fwrite($fp, $log_entry);
973 function rcube_timer()
975 list($usec, $sec) = explode(" ", microtime());
976 return ((float)$usec + (float)$sec);
983 function rcube_print_time($timer, $label='Timer')
985 static $print_count = 0;
988 $now = rcube_timer();
992 $label = 'Timer '.$print_count;
994 console(sprintf("%s: %0.4f sec", $label, $diff));
999 * Return the mailboxlist in HTML
1001 * @param array Named parameters
1002 * @return string HTML code for the gui object
1004 function rcmail_mailbox_list($attrib)
1006 global $IMAP, $CONFIG, $OUTPUT, $COMM_PATH;
1007 static $s_added_script = FALSE;
1008 static $a_mailboxes;
1010 // add some labels to client
1011 rcube_add_label('purgefolderconfirm');
1012 rcube_add_label('deletemessagesconfirm');
1014 // $mboxlist_start = rcube_timer();
1016 $type = $attrib['type'] ? $attrib['type'] : 'ul';
1017 $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
1018 array('style', 'class', 'id');
1020 if ($type=='ul' && !$attrib['id'])
1021 $attrib['id'] = 'rcmboxlist';
1023 // allow the following attributes to be added to the <ul> tag
1024 $attrib_str = create_attrib_string($attrib, $add_attrib);
1026 $out = '<' . $type . $attrib_str . ">\n";
1028 // add no-selection option
1029 if ($type=='select' && $attrib['noselection'])
1030 $out .= sprintf('<option value="0">%s</option>'."\n",
1031 rcube_label($attrib['noselection']));
1034 $mbox_name = $IMAP->get_mailbox_name();
1036 // build the folders tree
1037 if (empty($a_mailboxes))
1040 $a_folders = $IMAP->list_mailboxes();
1041 $delimiter = $IMAP->get_hierarchy_delimiter();
1042 $a_mailboxes = array();
1044 // rcube_print_time($mboxlist_start, 'list_mailboxes()');
1046 foreach ($a_folders as $folder)
1047 rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
1050 // var_dump($a_mailboxes);
1052 if ($type=='select')
1053 $out .= rcmail_render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength']);
1055 $out .= rcmail_render_folder_tree_html($a_mailboxes, $mbox_name, $attrib['maxlength']);
1057 // rcube_print_time($mboxlist_start, 'render_folder_tree()');
1061 $OUTPUT->add_gui_object('mailboxlist', $attrib['id']);
1063 return $out . "</$type>";
1070 * Create a hierarchical array of the mailbox list
1073 function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
1075 $pos = strpos($folder, $delm);
1078 $subFolders = substr($folder, $pos+1);
1079 $currentFolder = substr($folder, 0, $pos);
1083 $subFolders = false;
1084 $currentFolder = $folder;
1087 $path .= $currentFolder;
1089 if (!isset($arrFolders[$currentFolder]))
1091 $arrFolders[$currentFolder] = array('id' => $path,
1092 'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
1093 'folders' => array());
1096 if (!empty($subFolders))
1097 rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
1102 * Return html for a structured list <ul> for the mailbox tree
1105 function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, $maxlength, $nestLevel=0)
1107 global $COMM_PATH, $IMAP, $CONFIG, $OUTPUT;
1111 foreach ($arrFolders as $key => $folder)
1113 $zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
1116 if ($folder_class = rcmail_folder_classname($folder['id']))
1117 $foldername = rcube_label($folder_class);
1120 $foldername = $folder['name'];
1122 // shorten the folder name to a given length
1123 if ($maxlength && $maxlength>1)
1125 $fname = abbreviate_string($foldername, $maxlength);
1126 if ($fname != $foldername)
1127 $title = ' title="'.Q($foldername).'"';
1128 $foldername = $fname;
1132 // make folder name safe for ids and class names
1133 $folder_id = preg_replace('/[^A-Za-z0-9\-_]/', '', $folder['id']);
1134 $class_name = preg_replace('/[^a-z0-9\-_]/', '', $folder_class ? $folder_class : strtolower($folder['id']));
1136 // set special class for Sent, Drafts, Trash and Junk
1137 if ($folder['id']==$CONFIG['sent_mbox'])
1138 $class_name = 'sent';
1139 else if ($folder['id']==$CONFIG['drafts_mbox'])
1140 $class_name = 'drafts';
1141 else if ($folder['id']==$CONFIG['trash_mbox'])
1142 $class_name = 'trash';
1143 else if ($folder['id']==$CONFIG['junk_mbox'])
1144 $class_name = 'junk';
1146 $js_name = htmlspecialchars(JQ($folder['id']));
1147 $out .= sprintf('<li id="rcmli%s" class="mailbox %s %s%s"><a href="%s"'.
1148 ' onclick="return %s.command(\'list\',\'%s\',this)"'.
1149 ' onmouseover="return %s.focus_folder(\'%s\')"' .
1150 ' onmouseout="return %s.unfocus_folder(\'%s\')"' .
1151 ' onmouseup="return %s.folder_mouse_up(\'%s\')"%s>%s</a>',
1155 $folder['id']==$mbox_name ? ' selected' : '',
1156 Q(rcmail_url('', array('_mbox' => $folder['id']))),
1168 if (!empty($folder['folders']))
1169 $out .= "\n<ul>\n" . rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $maxlength, $nestLevel+1) . "</ul>\n";
1180 * Return html for a flat list <select> for the mailbox tree
1183 function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, $nestLevel=0, $selected='')
1185 global $IMAP, $OUTPUT;
1189 foreach ($arrFolders as $key=>$folder)
1191 if ($folder_class = rcmail_folder_classname($folder['id']))
1192 $foldername = rcube_label($folder_class);
1195 $foldername = $folder['name'];
1197 // shorten the folder name to a given length
1198 if ($maxlength && $maxlength>1)
1199 $foldername = abbreviate_string($foldername, $maxlength);
1202 $out .= sprintf('<option value="%s"%s>%s%s</option>'."\n",
1203 htmlspecialchars($folder['id']),
1204 ($selected == $foldername ? ' selected="selected"' : ''),
1205 str_repeat(' ', $nestLevel*4),
1208 if (!empty($folder['folders']))
1209 $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, $nestLevel+1, $selected);
1219 * Return internal name for the given folder if it matches the configured special folders
1222 function rcmail_folder_classname($folder_id)
1227 $folder_lc = strtolower($folder_id);
1229 // for these mailboxes we have localized labels and css classes
1230 foreach (array('inbox', 'sent', 'drafts', 'trash', 'junk') as $smbx)
1232 if ($folder_lc == $smbx || $folder_id == $CONFIG[$smbx.'_mbox'])
1241 * Try to localize the given IMAP folder name.
1242 * UTF-7 decode it in case no localized text was found
1244 * @param string Folder name
1245 * @return string Localized folder name in UTF-8 encoding
1247 function rcmail_localize_foldername($name)
1249 if ($folder_class = rcmail_folder_classname($name))
1250 return rcube_label($folder_class);
1252 return rcube_charset_convert($name, 'UTF-7');