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

Last change on this file since 1539 was 1539, checked in by alec, 5 years ago
  • Added option to select skin in user preferences (#1485031)
  • template_exists() moved to rcube_template class
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.4 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-2008, 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 table name if configured
73  $config_key = 'db_sequence_'.$sequence;
74  $opt = rcmail::get_instance()->config->get($config_key);
75
76  if (!empty($opt))
77    {
78    $db = &rcmail::get_instance()->db;
79
80    if($db->db_provider=='pgsql') // just for sure
81      {
82      $db->db_handle->setOption('disable_smart_seqname', true);
83      $db->db_handle->setOption('seqname_format', '%s');
84      }       
85 
86    return $CONFIG[$opt];
87    }
88   
89  return $sequence;
90  }
91
92
93/**
94 * Get localized text in the desired language
95 * It's a global wrapper for rcmail::gettext()
96 *
97 * @param mixed Named parameters array or label name
98 * @return string Localized text
99 * @see rcmail::gettext()
100 */
101function rcube_label($p)
102{
103  return rcmail::get_instance()->gettext($p);
104}
105
106
107/**
108 * Overwrite action variable
109 *
110 * @param string New action value
111 */
112function rcmail_overwrite_action($action)
113  {
114  $app = rcmail::get_instance();
115  $app->action = $action;
116  $app->output->set_env('action', $action);
117  }
118
119
120/**
121 * Compose an URL for a specific action
122 *
123 * @param string  Request action
124 * @param array   More URL parameters
125 * @param string  Request task (omit if the same)
126 * @return The application URL
127 */
128function rcmail_url($action, $p=array(), $task=null)
129{
130  $app = rcmail::get_instance();
131 
132  $qstring = '';
133  $base = $app->comm_path;
134 
135  if ($task && in_array($task, rcmail::$main_tasks))
136    $base = ereg_replace('_task=[a-z]+', '_task='.$task, $app->comm_path);
137 
138  if (is_array($p))
139    foreach ($p as $key => $val)
140      $qstring .= '&'.urlencode($key).'='.urlencode($val);
141 
142  return $base . ($action ? '&_action='.$action : '') . $qstring;
143}
144
145
146/**
147 * Add a localized label to the client environment
148 * @deprecated
149 */
150function rcube_add_label()
151  {
152  global $OUTPUT;
153 
154  $arg_list = func_get_args();
155  foreach ($arg_list as $i => $name)
156    $OUTPUT->add_label($name);
157  }
158
159
160/**
161 * Garbage collector function for temp files.
162 * Remove temp files older than two days
163 */
164function rcmail_temp_gc()
165  {
166  $tmp = unslashify($CONFIG['temp_dir']);
167  $expire = mktime() - 172800;  // expire in 48 hours
168
169  if ($dir = opendir($tmp))
170    {
171    while (($fname = readdir($dir)) !== false)
172      {
173      if ($fname{0} == '.')
174        continue;
175
176      if (filemtime($tmp.'/'.$fname) < $expire)
177        @unlink($tmp.'/'.$fname);
178      }
179
180    closedir($dir);
181    }
182  }
183
184
185/**
186 * Garbage collector for cache entries.
187 * Remove all expired message cache records
188 */
189function rcmail_message_cache_gc()
190  {
191  global $DB, $CONFIG;
192 
193  // no cache lifetime configured
194  if (empty($CONFIG['message_cache_lifetime']))
195    return;
196 
197  // get target timestamp
198  $ts = get_offset_time($CONFIG['message_cache_lifetime'], -1);
199 
200  $DB->query("DELETE FROM ".get_table_name('messages')."
201             WHERE  created < ".$DB->fromunixtime($ts));
202  }
203
204
205/**
206 * Convert a string from one charset to another.
207 * Uses mbstring and iconv functions if possible
208 *
209 * @param  string Input string
210 * @param  string Suspected charset of the input string
211 * @param  string Target charset to convert to; defaults to RCMAIL_CHARSET
212 * @return Converted string
213 */
214function rcube_charset_convert($str, $from, $to=NULL)
215  {
216  static $mbstring_loaded = null, $convert_warning = false;
217
218  $from = strtoupper($from);
219  $to = $to==NULL ? strtoupper(RCMAIL_CHARSET) : strtoupper($to);
220  $error = false; $conv = null;
221
222  if ($from==$to || $str=='' || empty($from))
223    return $str;
224   
225  $aliases = array(
226    'UNKNOWN-8BIT'   => 'ISO-8859-15',
227    'X-UNKNOWN'      => 'ISO-8859-15',
228    'X-USER-DEFINED' => 'ISO-8859-15',
229    'ISO-8859-8-I'   => 'ISO-8859-8',
230    'KS_C_5601-1987' => 'EUC-KR',
231  );
232
233  // convert charset using iconv module 
234  if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7')
235    {
236    $aliases['GB2312'] = 'GB18030';
237    $_iconv = iconv(($aliases[$from] ? $aliases[$from] : $from), ($aliases[$to] ? $aliases[$to] : $to) . "//IGNORE", $str);
238    if ($_iconv !== false)
239      {
240        return $_iconv;
241      }
242    }
243
244  // settings for mbstring module (by Tadashi Jokagi)
245  if (is_null($mbstring_loaded)) {
246    if ($mbstring_loaded = extension_loaded("mbstring"))
247      mb_internal_encoding(RCMAIL_CHARSET);
248  }
249
250  // convert charset using mbstring module
251  if ($mbstring_loaded)
252    {
253    $aliases['UTF-7'] = 'UTF7-IMAP';
254    $aliases['WINDOWS-1257'] = 'ISO-8859-13';
255   
256    // return if convert succeeded
257    if (($out = mb_convert_encoding($str, ($aliases[$to] ? $aliases[$to] : $to), ($aliases[$from] ? $aliases[$from] : $from))) != '')
258      return $out;
259    }
260   
261 
262  if (class_exists('utf8'))
263    $conv = new utf8();
264
265  // convert string to UTF-8
266  if ($from == 'UTF-7')
267    $str = utf7_to_utf8($str);
268  else if (($from == 'ISO-8859-1') && function_exists('utf8_encode'))
269    $str = utf8_encode($str);
270  else if ($from != 'UTF-8' && $conv)
271    {
272    $conv->loadCharset($from);
273    $str = $conv->strToUtf8($str);
274    }
275  else if ($from != 'UTF-8')
276    $error = true;
277
278  // encode string for output
279  if ($to == 'UTF-7')
280    return utf8_to_utf7($str);
281  else if ($to == 'ISO-8859-1' && function_exists('utf8_decode'))
282    return utf8_decode($str);
283  else if ($to != 'UTF-8' && $conv)
284    {
285    $conv->loadCharset($to);
286    return $conv->utf8ToStr($str);
287    }
288  else if ($to != 'UTF-8')
289    $error = true;
290
291  // report error
292  if ($error && !$convert_warning)
293    {
294    raise_error(array(
295      'code' => 500,
296      'type' => 'php',
297      'file' => __FILE__,
298      'message' => "Could not convert string charset. Make sure iconv is installed or lib/utf8.class is available"
299      ), true, false);
300   
301    $convert_warning = true;
302    }
303 
304  // return UTF-8 string
305  return $str;
306  }
307
308
309/**
310 * Replacing specials characters to a specific encoding type
311 *
312 * @param  string  Input string
313 * @param  string  Encoding type: text|html|xml|js|url
314 * @param  string  Replace mode for tags: show|replace|remove
315 * @param  boolean Convert newlines
316 * @return The quoted string
317 */
318function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
319  {
320  global $OUTPUT;
321  static $html_encode_arr = false;
322  static $js_rep_table = false;
323  static $xml_rep_table = false;
324
325  $charset = $OUTPUT->get_charset();
326  $is_iso_8859_1 = false;
327  if ($charset == 'ISO-8859-1') {
328    $is_iso_8859_1 = true;
329  }
330  if (!$enctype)
331    $enctype = $GLOBALS['OUTPUT_TYPE'];
332
333  // encode for plaintext
334  if ($enctype=='text')
335    return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str);
336
337  // encode for HTML output
338  if ($enctype=='html')
339    {
340    if (!$html_encode_arr)
341      {
342      $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);       
343      unset($html_encode_arr['?']);
344      }
345
346    $ltpos = strpos($str, '<');
347    $encode_arr = $html_encode_arr;
348
349    // don't replace quotes and html tags
350    if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false)
351      {
352      unset($encode_arr['"']);
353      unset($encode_arr['<']);
354      unset($encode_arr['>']);
355      unset($encode_arr['&']);
356      }
357    else if ($mode=='remove')
358      $str = strip_tags($str);
359   
360    // avoid douple quotation of &
361    $out = preg_replace('/&amp;([a-z]{2,5}|#[0-9]{2,4});/', '&\\1;', strtr($str, $encode_arr));
362     
363    return $newlines ? nl2br($out) : $out;
364    }
365
366  if ($enctype=='url')
367    return rawurlencode($str);
368
369  // if the replace tables for XML and JS are not yet defined
370  if ($js_rep_table===false)
371    {
372    $js_rep_table = $xml_rep_table = array();
373    $xml_rep_table['&'] = '&amp;';
374
375    for ($c=160; $c<256; $c++)  // can be increased to support more charsets
376      {
377      $xml_rep_table[Chr($c)] = "&#$c;";
378     
379      if ($is_iso_8859_1)
380        $js_rep_table[Chr($c)] = sprintf("\\u%04x", $c);
381      }
382
383    $xml_rep_table['"'] = '&quot;';
384    }
385
386  // encode for XML
387  if ($enctype=='xml')
388    return strtr($str, $xml_rep_table);
389
390  // encode for javascript use
391  if ($enctype=='js')
392    {
393    if ($charset!='UTF-8')
394      $str = rcube_charset_convert($str, RCMAIL_CHARSET,$charset);
395     
396    return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), addslashes(strtr($str, $js_rep_table)));
397    }
398
399  // no encoding given -> return original string
400  return $str;
401  }
402 
403/**
404 * Quote a given string.
405 * Shortcut function for rep_specialchars_output
406 *
407 * @return string HTML-quoted string
408 * @see rep_specialchars_output()
409 */
410function Q($str, $mode='strict', $newlines=TRUE)
411  {
412  return rep_specialchars_output($str, 'html', $mode, $newlines);
413  }
414
415/**
416 * Quote a given string for javascript output.
417 * Shortcut function for rep_specialchars_output
418 *
419 * @return string JS-quoted string
420 * @see rep_specialchars_output()
421 */
422function JQ($str)
423  {
424  return rep_specialchars_output($str, 'js');
425  }
426
427
428/**
429 * Read input value and convert it for internal use
430 * Performs stripslashes() and charset conversion if necessary
431 *
432 * @param  string   Field name to read
433 * @param  int      Source to get value from (GPC)
434 * @param  boolean  Allow HTML tags in field value
435 * @param  string   Charset to convert into
436 * @return string   Field value or NULL if not available
437 */
438function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
439  {
440  global $OUTPUT;
441  $value = NULL;
442 
443  if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
444    $value = $_GET[$fname];
445  else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
446    $value = $_POST[$fname];
447  else if ($source==RCUBE_INPUT_GPC)
448    {
449    if (isset($_POST[$fname]))
450      $value = $_POST[$fname];
451    else if (isset($_GET[$fname]))
452      $value = $_GET[$fname];
453    else if (isset($_COOKIE[$fname]))
454      $value = $_COOKIE[$fname];
455    }
456 
457  // strip slashes if magic_quotes enabled
458  if ((bool)get_magic_quotes_gpc())
459    $value = stripslashes($value);
460
461  // remove HTML tags if not allowed   
462  if (!$allow_html)
463    $value = strip_tags($value);
464 
465  // convert to internal charset
466  if (is_object($OUTPUT))
467    return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
468  else
469    return $value;
470  }
471
472/**
473 * Remove all non-ascii and non-word chars
474 * except . and -
475 */
476function asciiwords($str)
477{
478  return preg_replace('/[^a-z0-9.-_]/i', '', $str);
479}
480
481/**
482 * Remove single and double quotes from given string
483 *
484 * @param string Input value
485 * @return string Dequoted string
486 */
487function strip_quotes($str)
488{
489  return preg_replace('/[\'"]/', '', $str);
490}
491
492
493/**
494 * Remove new lines characters from given string
495 *
496 * @param string Input value
497 * @return string Stripped string
498 */
499function strip_newlines($str)
500{
501  return preg_replace('/[\r\n]/', '', $str);
502}
503
504
505/**
506 * Create a HTML table based on the given data
507 *
508 * @param  array  Named table attributes
509 * @param  mixed  Table row data. Either a two-dimensional array or a valid SQL result set
510 * @param  array  List of cols to show
511 * @param  string Name of the identifier col
512 * @return string HTML table code
513 */
514function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
515  {
516  global $RCMAIL;
517 
518  $table = new html_table(/*array('cols' => count($a_show_cols))*/);
519   
520  // add table header
521  foreach ($a_show_cols as $col)
522    $table->add_header($col, Q(rcube_label($col)));
523 
524  $c = 0;
525  if (!is_array($table_data))
526  {
527    $db = $RCMAIL->get_dbh();
528    while ($table_data && ($sql_arr = $db->fetch_assoc($table_data)))
529    {
530      $zebra_class = $c % 2 ? 'even' : 'odd';
531      $table->add_row(array('id' => 'rcmrow' . $sql_arr[$id_col], 'class' => "contact $zebra_class"));
532
533      // format each col
534      foreach ($a_show_cols as $col)
535        $table->add($col, Q($sql_arr[$col]));
536     
537      $c++;
538    }
539  }
540  else
541  {
542    foreach ($table_data as $row_data)
543    {
544      $zebra_class = $c % 2 ? 'even' : 'odd';
545      $table->add_row(array('id' => 'rcmrow' . $row_data[$id_col], 'class' => "contact $zebra_class"));
546
547      // format each col
548      foreach ($a_show_cols as $col)
549        $table->add($col, Q($row_data[$col]));
550       
551      $c++;
552    }
553  }
554
555  return $table->show($attrib);
556  }
557
558
559/**
560 * Create an edit field for inclusion on a form
561 *
562 * @param string col field name
563 * @param string value field value
564 * @param array attrib HTML element attributes for field
565 * @param string type HTML element type (default 'text')
566 * @return string HTML field definition
567 */
568function rcmail_get_edit_field($col, $value, $attrib, $type='text')
569  {
570  $fname = '_'.$col;
571  $attrib['name'] = $fname;
572 
573  if ($type=='checkbox')
574    {
575    $attrib['value'] = '1';
576    $input = new html_checkbox($attrib);
577    }
578  else if ($type=='textarea')
579    {
580    $attrib['cols'] = $attrib['size'];
581    $input = new html_textarea($attrib);
582    }
583  else
584    $input = new html_inputfield($attrib);
585
586  // use value from post
587  if (!empty($_POST[$fname]))
588    $value = get_input_value($fname, RCUBE_INPUT_POST);
589
590  $out = $input->show($value);
591         
592  return $out;
593  }
594
595
596/**
597 * Replace all css definitions with #container [def]
598 * and remove css-inlined scripting
599 *
600 * @param string CSS source code
601 * @param string Container ID to use as prefix
602 * @return string Modified CSS source
603 */
604function rcmail_mod_css_styles($source, $container_id, $base_url = '')
605  {
606  $a_css_values = array();
607  $last_pos = 0;
608 
609  // ignore the whole block if evil styles are detected
610  if (stristr($source, 'expression') || stristr($source, 'behavior'))
611    return '';
612
613  // cut out all contents between { and }
614  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
615  {
616    $key = sizeof($a_css_values);
617    $a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
618    $source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
619    $last_pos = $pos+2;
620  }
621
622  // remove html comments and add #container to each tag selector.
623  // also replace body definition because we also stripped off the <body> tag
624  $styles = preg_replace(
625    array(
626      '/(^\s*<!--)|(-->\s*$)/',
627      '/(^\s*|,\s*|\}\s*)([a-z0-9\._#][a-z0-9\.\-_]*)/im',
628      '/@import\s+(url\()?[\'"]?([^\)\'"]+)[\'"]?(\))?/ime',
629      '/<<str_replacement\[([0-9]+)\]>>/e',
630      "/$container_id\s+body/i"
631    ),
632    array(
633      '',
634      "\\1#$container_id \\2",
635      "sprintf(\"@import url('./bin/modcss.php?u=%s&c=%s')\", urlencode(make_absolute_url('\\2','$base_url')), urlencode($container_id))",
636      "\$a_css_values[\\1]",
637      "$container_id div.rcmBody"
638    ),
639    $source);
640
641  return $styles;
642  }
643
644
645/**
646 * Compose a valid attribute string for HTML tags
647 *
648 * @param array Named tag attributes
649 * @param array List of allowed attributes
650 * @return string HTML formatted attribute string
651 */
652function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
653  {
654  // allow the following attributes to be added to the <iframe> tag
655  $attrib_str = '';
656  foreach ($allowed_attribs as $a)
657    if (isset($attrib[$a]))
658      $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '&quot;', $attrib[$a]));
659
660  return $attrib_str;
661  }
662
663
664/**
665 * Convert a HTML attribute string attributes to an associative array (name => value)
666 *
667 * @param string Input string
668 * @return array Key-value pairs of parsed attributes
669 */
670function parse_attrib_string($str)
671  {
672  $attrib = array();
673  preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]+)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
674
675  // convert attributes to an associative array (name => value)
676  if ($regs)
677    foreach ($regs as $attr)
678      {
679      $attrib[strtolower($attr[1])] = $attr[3] . $attr[4];
680      }
681
682  return $attrib;
683  }
684
685
686/**
687 * Convert the given date to a human readable form
688 * This uses the date formatting properties from config
689 *
690 * @param mixed Date representation (string or timestamp)
691 * @param string Date format to use
692 * @return string Formatted date string
693 */
694function format_date($date, $format=NULL)
695  {
696  global $CONFIG;
697 
698  $ts = NULL;
699
700  if (is_numeric($date))
701    $ts = $date;
702  else if (!empty($date))
703    {
704    while (($ts = @strtotime($date))===false)
705      {
706        // if we have a date in non-rfc format
707        // remove token from the end and try again
708        $d = explode(' ', $date);
709        array_pop($d);
710        if (!$d) break;
711        $date = implode(' ', $d);
712      }
713    }
714
715  if (empty($ts))
716    return '';
717   
718  // get user's timezone
719  $tz = $CONFIG['timezone'];
720  if ($CONFIG['dst_active'])
721    $tz++;
722
723  // convert time to user's timezone
724  $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
725 
726  // get current timestamp in user's timezone
727  $now = time();  // local time
728  $now -= (int)date('Z'); // make GMT time
729  $now += ($tz * 3600); // user's time
730  $now_date = getdate($now);
731
732  $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
733  $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
734
735  // define date format depending on current time 
736  if ($CONFIG['prettydate'] && !$format && $timestamp > $today_limit && $timestamp < $now)
737    return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp));
738  else if ($CONFIG['prettydate'] && !$format && $timestamp > $week_limit && $timestamp < $now)
739    $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
740  else if (!$format)
741    $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
742
743
744  // parse format string manually in order to provide localized weekday and month names
745  // an alternative would be to convert the date() format string to fit with strftime()
746  $out = '';
747  for($i=0; $i<strlen($format); $i++)
748    {
749    if ($format{$i}=='\\')  // skip escape chars
750      continue;
751   
752    // write char "as-is"
753    if ($format{$i}==' ' || $format{$i-1}=='\\')
754      $out .= $format{$i};
755    // weekday (short)
756    else if ($format{$i}=='D')
757      $out .= rcube_label(strtolower(date('D', $timestamp)));
758    // weekday long
759    else if ($format{$i}=='l')
760      $out .= rcube_label(strtolower(date('l', $timestamp)));
761    // month name (short)
762    else if ($format{$i}=='M')
763      $out .= rcube_label(strtolower(date('M', $timestamp)));
764    // month name (long)
765    else if ($format{$i}=='F')
766      $out .= rcube_label('long'.strtolower(date('M', $timestamp)));
767    else
768      $out .= date($format{$i}, $timestamp);
769    }
770 
771  return $out;
772  }
773
774
775/**
776 * Compose a valid representaion of name and e-mail address
777 *
778 * @param string E-mail address
779 * @param string Person name
780 * @return string Formatted string
781 */
782function format_email_recipient($email, $name='')
783  {
784  if ($name && $name != $email)
785    {
786    // Special chars as defined by RFC 822 need to in quoted string (or escaped).
787    return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, $email);
788    }
789  else
790    return $email;
791  }
792
793
794
795/****** debugging functions ********/
796
797
798/**
799 * Print or write debug messages
800 *
801 * @param mixed Debug message or data
802 */
803function console($msg)
804  {
805  if (!is_string($msg))
806    $msg = var_export($msg, true);
807
808  if (!($GLOBALS['CONFIG']['debug_level'] & 4))
809    write_log('console', $msg);
810  else if ($GLOBALS['OUTPUT']->ajax_call)
811    print "/*\n $msg \n*/\n";
812  else
813    {
814    print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>';
815    print $msg;
816    print "</pre></div>\n";
817    }
818  }
819
820
821/**
822 * Append a line to a logfile in the logs directory.
823 * Date will be added automatically to the line.
824 *
825 * @param $name name of log file
826 * @param line Line to append
827 */
828function write_log($name, $line)
829  {
830  global $CONFIG;
831
832  if (!is_string($line))
833    $line = var_export($line, true);
834 
835  $log_entry = sprintf("[%s]: %s\n",
836                 date("d-M-Y H:i:s O", mktime()),
837                 $line);
838                 
839  if (empty($CONFIG['log_dir']))
840    $CONFIG['log_dir'] = INSTALL_PATH.'logs';
841     
842  // try to open specific log file for writing
843  if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a'))   
844    {
845    fwrite($fp, $log_entry);
846    fclose($fp);
847    }
848  }
849
850
851/**
852 * @access private
853 */
854function rcube_timer()
855  {
856  list($usec, $sec) = explode(" ", microtime());
857  return ((float)$usec + (float)$sec);
858  }
859 
860
861/**
862 * @access private
863 */
864function rcube_print_time($timer, $label='Timer')
865  {
866  static $print_count = 0;
867 
868  $print_count++;
869  $now = rcube_timer();
870  $diff = $now-$timer;
871 
872  if (empty($label))
873    $label = 'Timer '.$print_count;
874 
875  console(sprintf("%s: %0.4f sec", $label, $diff));
876  }
877
878
879/**
880 * Return the mailboxlist in HTML
881 *
882 * @param array Named parameters
883 * @return string HTML code for the gui object
884 */
885function rcmail_mailbox_list($attrib)
886  {
887  global $IMAP, $CONFIG, $OUTPUT, $COMM_PATH;
888  static $s_added_script = FALSE;
889  static $a_mailboxes;
890
891  // add some labels to client
892  rcube_add_label('purgefolderconfirm');
893  rcube_add_label('deletemessagesconfirm');
894 
895// $mboxlist_start = rcube_timer();
896 
897  $type = $attrib['type'] ? $attrib['type'] : 'ul';
898  $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
899                                  array('style', 'class', 'id');
900                                 
901  if ($type=='ul' && !$attrib['id'])
902    $attrib['id'] = 'rcmboxlist';
903
904  // allow the following attributes to be added to the <ul> tag
905  $attrib_str = create_attrib_string($attrib, $add_attrib);
906 
907  $out = '<' . $type . $attrib_str . ">\n";
908 
909  // add no-selection option
910  if ($type=='select' && $attrib['noselection'])
911    $out .= sprintf('<option value="0">%s</option>'."\n",
912                    rcube_label($attrib['noselection']));
913 
914  // get mailbox list
915  $mbox_name = $IMAP->get_mailbox_name();
916 
917  // build the folders tree
918  if (empty($a_mailboxes))
919    {
920    // get mailbox list
921    $a_folders = $IMAP->list_mailboxes();
922    $delimiter = $IMAP->get_hierarchy_delimiter();
923    $a_mailboxes = array();
924
925// rcube_print_time($mboxlist_start, 'list_mailboxes()');
926
927    foreach ($a_folders as $folder)
928      rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
929    }
930
931// var_dump($a_mailboxes);
932
933  if ($type=='select')
934    $out .= rcmail_render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength']);
935   else
936    $out .= rcmail_render_folder_tree_html($a_mailboxes, $mbox_name, $attrib['maxlength']);
937
938// rcube_print_time($mboxlist_start, 'render_folder_tree()');
939
940
941  if ($type=='ul')
942    $OUTPUT->add_gui_object('mailboxlist', $attrib['id']);
943
944  return $out . "</$type>";
945  }
946
947
948
949
950/**
951 * Create a hierarchical array of the mailbox list
952 * @access private
953 */
954function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
955  {
956  $pos = strpos($folder, $delm);
957  if ($pos !== false)
958    {
959    $subFolders = substr($folder, $pos+1);
960    $currentFolder = substr($folder, 0, $pos);
961    }
962  else
963    {
964    $subFolders = false;
965    $currentFolder = $folder;
966    }
967
968  $path .= $currentFolder;
969
970  if (!isset($arrFolders[$currentFolder]))
971    {
972    $arrFolders[$currentFolder] = array('id' => $path,
973                                        'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
974                                        'folders' => array());
975    }
976
977  if (!empty($subFolders))
978    rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
979  }
980 
981
982/**
983 * Return html for a structured list &lt;ul&gt; for the mailbox tree
984 * @access private
985 */
986function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, $maxlength, $nestLevel=0)
987  {
988  global $COMM_PATH, $IMAP, $CONFIG, $OUTPUT;
989
990  $idx = 0;
991  $out = '';
992  foreach ($arrFolders as $key => $folder)
993    {
994    $zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
995    $title = '';
996
997    if ($folder_class = rcmail_folder_classname($folder['id']))
998      $foldername = rcube_label($folder_class);
999    else
1000      {
1001      $foldername = $folder['name'];
1002
1003      // shorten the folder name to a given length
1004      if ($maxlength && $maxlength>1)
1005        {
1006        $fname = abbreviate_string($foldername, $maxlength);
1007        if ($fname != $foldername)
1008          $title = ' title="'.Q($foldername).'"';
1009        $foldername = $fname;
1010        }
1011      }
1012
1013    // make folder name safe for ids and class names
1014    $folder_id = preg_replace('/[^A-Za-z0-9\-_]/', '', $folder['id']);
1015    $class_name = preg_replace('/[^a-z0-9\-_]/', '', $folder_class ? $folder_class : strtolower($folder['id']));
1016
1017    // set special class for Sent, Drafts, Trash and Junk
1018    if ($folder['id']==$CONFIG['sent_mbox'])
1019      $class_name = 'sent';
1020    else if ($folder['id']==$CONFIG['drafts_mbox'])
1021      $class_name = 'drafts';
1022    else if ($folder['id']==$CONFIG['trash_mbox'])
1023      $class_name = 'trash';
1024    else if ($folder['id']==$CONFIG['junk_mbox'])
1025      $class_name = 'junk';
1026
1027    $js_name = htmlspecialchars(JQ($folder['id']));
1028    $out .= sprintf('<li id="rcmli%s" class="mailbox %s %s%s"><a href="%s"'.
1029                    ' onclick="return %s.command(\'list\',\'%s\',this)"'.
1030                    ' onmouseover="return %s.focus_folder(\'%s\')"' .
1031                    ' onmouseout="return %s.unfocus_folder(\'%s\')"' .
1032                    ' onmouseup="return %s.folder_mouse_up(\'%s\')"%s>%s</a>',
1033                    $folder_id,
1034                    $class_name,
1035                    $zebra_class,
1036                    $folder['id']==$mbox_name ? ' selected' : '',
1037                    Q(rcmail_url('', array('_mbox' => $folder['id']))),
1038                    JS_OBJECT_NAME,
1039                    $js_name,
1040                    JS_OBJECT_NAME,
1041                    $js_name,
1042                    JS_OBJECT_NAME,
1043                    $js_name,
1044                    JS_OBJECT_NAME,
1045                    $js_name,
1046                    $title,
1047                    Q($foldername));
1048
1049    if (!empty($folder['folders']))
1050      $out .= "\n<ul>\n" . rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $maxlength, $nestLevel+1) . "</ul>\n";
1051
1052    $out .= "</li>\n";
1053    $idx++;
1054    }
1055
1056  return $out;
1057  }
1058
1059
1060/**
1061 * Return html for a flat list <select> for the mailbox tree
1062 * @access private
1063 */
1064function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, $nestLevel=0, $selected='')
1065  {
1066  global $IMAP, $OUTPUT;
1067
1068  $idx = 0;
1069  $out = '';
1070  foreach ($arrFolders as $key=>$folder)
1071    {
1072    if ($folder_class = rcmail_folder_classname($folder['id']))
1073      $foldername = rcube_label($folder_class);
1074    else
1075      {
1076      $foldername = $folder['name'];
1077     
1078      // shorten the folder name to a given length
1079      if ($maxlength && $maxlength>1)
1080        $foldername = abbreviate_string($foldername, $maxlength);
1081      }
1082
1083    $out .= sprintf('<option value="%s"%s>%s%s</option>'."\n",
1084                    htmlspecialchars($folder['id']),
1085                    ($selected == $foldername ? ' selected="selected"' : ''),
1086                    str_repeat('&nbsp;', $nestLevel*4),
1087                    Q($foldername));
1088
1089    if (!empty($folder['folders']))
1090      $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, $nestLevel+1, $selected);
1091
1092    $idx++;
1093    }
1094
1095  return $out;
1096  }
1097
1098
1099/**
1100 * Return internal name for the given folder if it matches the configured special folders
1101 * @access private
1102 */
1103function rcmail_folder_classname($folder_id)
1104{
1105  global $CONFIG;
1106
1107  $cname = null;
1108  $folder_lc = strtolower($folder_id);
1109 
1110  // for these mailboxes we have localized labels and css classes
1111  foreach (array('inbox', 'sent', 'drafts', 'trash', 'junk') as $smbx)
1112  {
1113    if ($folder_lc == $smbx || $folder_id == $CONFIG[$smbx.'_mbox'])
1114      $cname = $smbx;
1115  }
1116 
1117  return $cname;
1118}
1119
1120
1121/**
1122 * Try to localize the given IMAP folder name.
1123 * UTF-7 decode it in case no localized text was found
1124 *
1125 * @param string Folder name
1126 * @return string Localized folder name in UTF-8 encoding
1127 */
1128function rcmail_localize_foldername($name)
1129{
1130  if ($folder_class = rcmail_folder_classname($name))
1131    return rcube_label($folder_class);
1132  else
1133    return rcube_charset_convert($name, 'UTF-7');
1134}
1135
1136
1137?>
Note: See TracBrowser for help on using the repository browser.