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

Last change on this file since 1532 was 1532, checked in by thomasb, 5 years ago

More code cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.7 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 * Check if a specific template exists
507 *
508 * @param string Template name
509 * @return boolean True if template exists
510 */
511function template_exists($name)
512  {
513  global $CONFIG;
514  $skin_path = $CONFIG['skin_path'];
515
516  // check template file
517  return is_file("$skin_path/templates/$name.html");
518  }
519
520
521/**
522 * Create a HTML table based on the given data
523 *
524 * @param  array  Named table attributes
525 * @param  mixed  Table row data. Either a two-dimensional array or a valid SQL result set
526 * @param  array  List of cols to show
527 * @param  string Name of the identifier col
528 * @return string HTML table code
529 */
530function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
531  {
532  global $RCMAIL;
533 
534  $table = new html_table(/*array('cols' => count($a_show_cols))*/);
535   
536  // add table header
537  foreach ($a_show_cols as $col)
538    $table->add_header($col, Q(rcube_label($col)));
539 
540  $c = 0;
541  if (!is_array($table_data))
542  {
543    $db = $RCMAIL->get_dbh();
544    while ($table_data && ($sql_arr = $db->fetch_assoc($table_data)))
545    {
546      $zebra_class = $c % 2 ? 'even' : 'odd';
547      $table->add_row(array('id' => 'rcmrow' . $sql_arr[$id_col], 'class' => "contact $zebra_class"));
548
549      // format each col
550      foreach ($a_show_cols as $col)
551        $table->add($col, Q($sql_arr[$col]));
552     
553      $c++;
554    }
555  }
556  else
557  {
558    foreach ($table_data as $row_data)
559    {
560      $zebra_class = $c % 2 ? 'even' : 'odd';
561      $table->add_row(array('id' => 'rcmrow' . $row_data[$id_col], 'class' => "contact $zebra_class"));
562
563      // format each col
564      foreach ($a_show_cols as $col)
565        $table->add($col, Q($row_data[$col]));
566       
567      $c++;
568    }
569  }
570
571  return $table->show($attrib);
572  }
573
574
575/**
576 * Create an edit field for inclusion on a form
577 *
578 * @param string col field name
579 * @param string value field value
580 * @param array attrib HTML element attributes for field
581 * @param string type HTML element type (default 'text')
582 * @return string HTML field definition
583 */
584function rcmail_get_edit_field($col, $value, $attrib, $type='text')
585  {
586  $fname = '_'.$col;
587  $attrib['name'] = $fname;
588 
589  if ($type=='checkbox')
590    {
591    $attrib['value'] = '1';
592    $input = new html_checkbox($attrib);
593    }
594  else if ($type=='textarea')
595    {
596    $attrib['cols'] = $attrib['size'];
597    $input = new html_textarea($attrib);
598    }
599  else
600    $input = new html_inputfield($attrib);
601
602  // use value from post
603  if (!empty($_POST[$fname]))
604    $value = get_input_value($fname, RCUBE_INPUT_POST);
605
606  $out = $input->show($value);
607         
608  return $out;
609  }
610
611
612/**
613 * Replace all css definitions with #container [def]
614 * and remove css-inlined scripting
615 *
616 * @param string CSS source code
617 * @param string Container ID to use as prefix
618 * @return string Modified CSS source
619 */
620function rcmail_mod_css_styles($source, $container_id, $base_url = '')
621  {
622  $a_css_values = array();
623  $last_pos = 0;
624 
625  // ignore the whole block if evil styles are detected
626  if (stristr($source, 'expression') || stristr($source, 'behavior'))
627    return '';
628
629  // cut out all contents between { and }
630  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
631  {
632    $key = sizeof($a_css_values);
633    $a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
634    $source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
635    $last_pos = $pos+2;
636  }
637
638  // remove html comments and add #container to each tag selector.
639  // also replace body definition because we also stripped off the <body> tag
640  $styles = preg_replace(
641    array(
642      '/(^\s*<!--)|(-->\s*$)/',
643      '/(^\s*|,\s*|\}\s*)([a-z0-9\._#][a-z0-9\.\-_]*)/im',
644      '/@import\s+(url\()?[\'"]?([^\)\'"]+)[\'"]?(\))?/ime',
645      '/<<str_replacement\[([0-9]+)\]>>/e',
646      "/$container_id\s+body/i"
647    ),
648    array(
649      '',
650      "\\1#$container_id \\2",
651      "sprintf(\"@import url('./bin/modcss.php?u=%s&c=%s')\", urlencode(make_absolute_url('\\2','$base_url')), urlencode($container_id))",
652      "\$a_css_values[\\1]",
653      "$container_id div.rcmBody"
654    ),
655    $source);
656
657  return $styles;
658  }
659
660
661/**
662 * Compose a valid attribute string for HTML tags
663 *
664 * @param array Named tag attributes
665 * @param array List of allowed attributes
666 * @return string HTML formatted attribute string
667 */
668function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
669  {
670  // allow the following attributes to be added to the <iframe> tag
671  $attrib_str = '';
672  foreach ($allowed_attribs as $a)
673    if (isset($attrib[$a]))
674      $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '&quot;', $attrib[$a]));
675
676  return $attrib_str;
677  }
678
679
680/**
681 * Convert a HTML attribute string attributes to an associative array (name => value)
682 *
683 * @param string Input string
684 * @return array Key-value pairs of parsed attributes
685 */
686function parse_attrib_string($str)
687  {
688  $attrib = array();
689  preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]+)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
690
691  // convert attributes to an associative array (name => value)
692  if ($regs)
693    foreach ($regs as $attr)
694      {
695      $attrib[strtolower($attr[1])] = $attr[3] . $attr[4];
696      }
697
698  return $attrib;
699  }
700
701
702/**
703 * Convert the given date to a human readable form
704 * This uses the date formatting properties from config
705 *
706 * @param mixed Date representation (string or timestamp)
707 * @param string Date format to use
708 * @return string Formatted date string
709 */
710function format_date($date, $format=NULL)
711  {
712  global $CONFIG;
713 
714  $ts = NULL;
715
716  if (is_numeric($date))
717    $ts = $date;
718  else if (!empty($date))
719    {
720    while (($ts = @strtotime($date))===false)
721      {
722        // if we have a date in non-rfc format
723        // remove token from the end and try again
724        $d = explode(' ', $date);
725        array_pop($d);
726        if (!$d) break;
727        $date = implode(' ', $d);
728      }
729    }
730
731  if (empty($ts))
732    return '';
733   
734  // get user's timezone
735  $tz = $CONFIG['timezone'];
736  if ($CONFIG['dst_active'])
737    $tz++;
738
739  // convert time to user's timezone
740  $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
741 
742  // get current timestamp in user's timezone
743  $now = time();  // local time
744  $now -= (int)date('Z'); // make GMT time
745  $now += ($tz * 3600); // user's time
746  $now_date = getdate($now);
747
748  $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
749  $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
750
751  // define date format depending on current time 
752  if ($CONFIG['prettydate'] && !$format && $timestamp > $today_limit && $timestamp < $now)
753    return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp));
754  else if ($CONFIG['prettydate'] && !$format && $timestamp > $week_limit && $timestamp < $now)
755    $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
756  else if (!$format)
757    $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
758
759
760  // parse format string manually in order to provide localized weekday and month names
761  // an alternative would be to convert the date() format string to fit with strftime()
762  $out = '';
763  for($i=0; $i<strlen($format); $i++)
764    {
765    if ($format{$i}=='\\')  // skip escape chars
766      continue;
767   
768    // write char "as-is"
769    if ($format{$i}==' ' || $format{$i-1}=='\\')
770      $out .= $format{$i};
771    // weekday (short)
772    else if ($format{$i}=='D')
773      $out .= rcube_label(strtolower(date('D', $timestamp)));
774    // weekday long
775    else if ($format{$i}=='l')
776      $out .= rcube_label(strtolower(date('l', $timestamp)));
777    // month name (short)
778    else if ($format{$i}=='M')
779      $out .= rcube_label(strtolower(date('M', $timestamp)));
780    // month name (long)
781    else if ($format{$i}=='F')
782      $out .= rcube_label('long'.strtolower(date('M', $timestamp)));
783    else
784      $out .= date($format{$i}, $timestamp);
785    }
786 
787  return $out;
788  }
789
790
791/**
792 * Compose a valid representaion of name and e-mail address
793 *
794 * @param string E-mail address
795 * @param string Person name
796 * @return string Formatted string
797 */
798function format_email_recipient($email, $name='')
799  {
800  if ($name && $name != $email)
801    {
802    // Special chars as defined by RFC 822 need to in quoted string (or escaped).
803    return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, $email);
804    }
805  else
806    return $email;
807  }
808
809
810
811/****** debugging functions ********/
812
813
814/**
815 * Print or write debug messages
816 *
817 * @param mixed Debug message or data
818 */
819function console($msg)
820  {
821  if (!is_string($msg))
822    $msg = var_export($msg, true);
823
824  if (!($GLOBALS['CONFIG']['debug_level'] & 4))
825    write_log('console', $msg);
826  else if ($GLOBALS['OUTPUT']->ajax_call)
827    print "/*\n $msg \n*/\n";
828  else
829    {
830    print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>';
831    print $msg;
832    print "</pre></div>\n";
833    }
834  }
835
836
837/**
838 * Append a line to a logfile in the logs directory.
839 * Date will be added automatically to the line.
840 *
841 * @param $name name of log file
842 * @param line Line to append
843 */
844function write_log($name, $line)
845  {
846  global $CONFIG;
847
848  if (!is_string($line))
849    $line = var_export($line, true);
850 
851  $log_entry = sprintf("[%s]: %s\n",
852                 date("d-M-Y H:i:s O", mktime()),
853                 $line);
854                 
855  if (empty($CONFIG['log_dir']))
856    $CONFIG['log_dir'] = INSTALL_PATH.'logs';
857     
858  // try to open specific log file for writing
859  if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a'))   
860    {
861    fwrite($fp, $log_entry);
862    fclose($fp);
863    }
864  }
865
866
867/**
868 * @access private
869 */
870function rcube_timer()
871  {
872  list($usec, $sec) = explode(" ", microtime());
873  return ((float)$usec + (float)$sec);
874  }
875 
876
877/**
878 * @access private
879 */
880function rcube_print_time($timer, $label='Timer')
881  {
882  static $print_count = 0;
883 
884  $print_count++;
885  $now = rcube_timer();
886  $diff = $now-$timer;
887 
888  if (empty($label))
889    $label = 'Timer '.$print_count;
890 
891  console(sprintf("%s: %0.4f sec", $label, $diff));
892  }
893
894
895/**
896 * Return the mailboxlist in HTML
897 *
898 * @param array Named parameters
899 * @return string HTML code for the gui object
900 */
901function rcmail_mailbox_list($attrib)
902  {
903  global $IMAP, $CONFIG, $OUTPUT, $COMM_PATH;
904  static $s_added_script = FALSE;
905  static $a_mailboxes;
906
907  // add some labels to client
908  rcube_add_label('purgefolderconfirm');
909  rcube_add_label('deletemessagesconfirm');
910 
911// $mboxlist_start = rcube_timer();
912 
913  $type = $attrib['type'] ? $attrib['type'] : 'ul';
914  $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
915                                  array('style', 'class', 'id');
916                                 
917  if ($type=='ul' && !$attrib['id'])
918    $attrib['id'] = 'rcmboxlist';
919
920  // allow the following attributes to be added to the <ul> tag
921  $attrib_str = create_attrib_string($attrib, $add_attrib);
922 
923  $out = '<' . $type . $attrib_str . ">\n";
924 
925  // add no-selection option
926  if ($type=='select' && $attrib['noselection'])
927    $out .= sprintf('<option value="0">%s</option>'."\n",
928                    rcube_label($attrib['noselection']));
929 
930  // get mailbox list
931  $mbox_name = $IMAP->get_mailbox_name();
932 
933  // build the folders tree
934  if (empty($a_mailboxes))
935    {
936    // get mailbox list
937    $a_folders = $IMAP->list_mailboxes();
938    $delimiter = $IMAP->get_hierarchy_delimiter();
939    $a_mailboxes = array();
940
941// rcube_print_time($mboxlist_start, 'list_mailboxes()');
942
943    foreach ($a_folders as $folder)
944      rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
945    }
946
947// var_dump($a_mailboxes);
948
949  if ($type=='select')
950    $out .= rcmail_render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength']);
951   else
952    $out .= rcmail_render_folder_tree_html($a_mailboxes, $mbox_name, $attrib['maxlength']);
953
954// rcube_print_time($mboxlist_start, 'render_folder_tree()');
955
956
957  if ($type=='ul')
958    $OUTPUT->add_gui_object('mailboxlist', $attrib['id']);
959
960  return $out . "</$type>";
961  }
962
963
964
965
966/**
967 * Create a hierarchical array of the mailbox list
968 * @access private
969 */
970function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
971  {
972  $pos = strpos($folder, $delm);
973  if ($pos !== false)
974    {
975    $subFolders = substr($folder, $pos+1);
976    $currentFolder = substr($folder, 0, $pos);
977    }
978  else
979    {
980    $subFolders = false;
981    $currentFolder = $folder;
982    }
983
984  $path .= $currentFolder;
985
986  if (!isset($arrFolders[$currentFolder]))
987    {
988    $arrFolders[$currentFolder] = array('id' => $path,
989                                        'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
990                                        'folders' => array());
991    }
992
993  if (!empty($subFolders))
994    rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
995  }
996 
997
998/**
999 * Return html for a structured list &lt;ul&gt; for the mailbox tree
1000 * @access private
1001 */
1002function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, $maxlength, $nestLevel=0)
1003  {
1004  global $COMM_PATH, $IMAP, $CONFIG, $OUTPUT;
1005
1006  $idx = 0;
1007  $out = '';
1008  foreach ($arrFolders as $key => $folder)
1009    {
1010    $zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
1011    $title = '';
1012
1013    if ($folder_class = rcmail_folder_classname($folder['id']))
1014      $foldername = rcube_label($folder_class);
1015    else
1016      {
1017      $foldername = $folder['name'];
1018
1019      // shorten the folder name to a given length
1020      if ($maxlength && $maxlength>1)
1021        {
1022        $fname = abbreviate_string($foldername, $maxlength);
1023        if ($fname != $foldername)
1024          $title = ' title="'.Q($foldername).'"';
1025        $foldername = $fname;
1026        }
1027      }
1028
1029    // make folder name safe for ids and class names
1030    $folder_id = preg_replace('/[^A-Za-z0-9\-_]/', '', $folder['id']);
1031    $class_name = preg_replace('/[^a-z0-9\-_]/', '', $folder_class ? $folder_class : strtolower($folder['id']));
1032
1033    // set special class for Sent, Drafts, Trash and Junk
1034    if ($folder['id']==$CONFIG['sent_mbox'])
1035      $class_name = 'sent';
1036    else if ($folder['id']==$CONFIG['drafts_mbox'])
1037      $class_name = 'drafts';
1038    else if ($folder['id']==$CONFIG['trash_mbox'])
1039      $class_name = 'trash';
1040    else if ($folder['id']==$CONFIG['junk_mbox'])
1041      $class_name = 'junk';
1042
1043    $js_name = htmlspecialchars(JQ($folder['id']));
1044    $out .= sprintf('<li id="rcmli%s" class="mailbox %s %s%s"><a href="%s"'.
1045                    ' onclick="return %s.command(\'list\',\'%s\',this)"'.
1046                    ' onmouseover="return %s.focus_folder(\'%s\')"' .
1047                    ' onmouseout="return %s.unfocus_folder(\'%s\')"' .
1048                    ' onmouseup="return %s.folder_mouse_up(\'%s\')"%s>%s</a>',
1049                    $folder_id,
1050                    $class_name,
1051                    $zebra_class,
1052                    $folder['id']==$mbox_name ? ' selected' : '',
1053                    Q(rcmail_url('', array('_mbox' => $folder['id']))),
1054                    JS_OBJECT_NAME,
1055                    $js_name,
1056                    JS_OBJECT_NAME,
1057                    $js_name,
1058                    JS_OBJECT_NAME,
1059                    $js_name,
1060                    JS_OBJECT_NAME,
1061                    $js_name,
1062                    $title,
1063                    Q($foldername));
1064
1065    if (!empty($folder['folders']))
1066      $out .= "\n<ul>\n" . rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $maxlength, $nestLevel+1) . "</ul>\n";
1067
1068    $out .= "</li>\n";
1069    $idx++;
1070    }
1071
1072  return $out;
1073  }
1074
1075
1076/**
1077 * Return html for a flat list <select> for the mailbox tree
1078 * @access private
1079 */
1080function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, $nestLevel=0, $selected='')
1081  {
1082  global $IMAP, $OUTPUT;
1083
1084  $idx = 0;
1085  $out = '';
1086  foreach ($arrFolders as $key=>$folder)
1087    {
1088    if ($folder_class = rcmail_folder_classname($folder['id']))
1089      $foldername = rcube_label($folder_class);
1090    else
1091      {
1092      $foldername = $folder['name'];
1093     
1094      // shorten the folder name to a given length
1095      if ($maxlength && $maxlength>1)
1096        $foldername = abbreviate_string($foldername, $maxlength);
1097      }
1098
1099    $out .= sprintf('<option value="%s"%s>%s%s</option>'."\n",
1100                    htmlspecialchars($folder['id']),
1101                    ($selected == $foldername ? ' selected="selected"' : ''),
1102                    str_repeat('&nbsp;', $nestLevel*4),
1103                    Q($foldername));
1104
1105    if (!empty($folder['folders']))
1106      $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, $nestLevel+1, $selected);
1107
1108    $idx++;
1109    }
1110
1111  return $out;
1112  }
1113
1114
1115/**
1116 * Return internal name for the given folder if it matches the configured special folders
1117 * @access private
1118 */
1119function rcmail_folder_classname($folder_id)
1120{
1121  global $CONFIG;
1122
1123  $cname = null;
1124  $folder_lc = strtolower($folder_id);
1125 
1126  // for these mailboxes we have localized labels and css classes
1127  foreach (array('inbox', 'sent', 'drafts', 'trash', 'junk') as $smbx)
1128  {
1129    if ($folder_lc == $smbx || $folder_id == $CONFIG[$smbx.'_mbox'])
1130      $cname = $smbx;
1131  }
1132 
1133  return $cname;
1134}
1135
1136
1137/**
1138 * Try to localize the given IMAP folder name.
1139 * UTF-7 decode it in case no localized text was found
1140 *
1141 * @param string Folder name
1142 * @return string Localized folder name in UTF-8 encoding
1143 */
1144function rcmail_localize_foldername($name)
1145{
1146  if ($folder_class = rcmail_folder_classname($name))
1147    return rcube_label($folder_class);
1148  else
1149    return rcube_charset_convert($name, 'UTF-7');
1150}
1151
1152
1153?>
Note: See TracBrowser for help on using the repository browser.