]> git.donarmstrong.com Git - roundcube.git/blob - program/include/main.inc
Imported Upstream version 0.2~alpha
[roundcube.git] / program / include / main.inc
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: main.inc 1459 2008-05-30 19:55:28Z alec $
19
20 */
21
22 /**
23  * RoundCube Webmail common functions
24  *
25  * @package Core
26  * @author Thomas Bruederli <roundcube@gmail.com>
27  */
28
29 require_once('lib/utf7.inc');
30 require_once('include/rcube_shared.inc');
31
32 // define constannts for input reading
33 define('RCUBE_INPUT_GET', 0x0101);
34 define('RCUBE_INPUT_POST', 0x0102);
35 define('RCUBE_INPUT_GPC', 0x0103);
36
37
38
39 /**
40  * Return correct name for a specific database table
41  *
42  * @param string Table name
43  * @return string Translated table name
44  */
45 function get_table_name($table)
46   {
47   global $CONFIG;
48
49   // return table name if configured
50   $config_key = 'db_table_'.$table;
51
52   if (strlen($CONFIG[$config_key]))
53     return $CONFIG[$config_key];
54
55   return $table;
56   }
57
58
59 /**
60  * Return correct name for a specific database sequence
61  * (used for Postgres only)
62  *
63  * @param string Secuence name
64  * @return string Translated sequence name
65  */
66 function get_sequence_name($sequence)
67   {
68   // return table name if configured
69   $config_key = 'db_sequence_'.$sequence;
70   $opt = rcmail::get_instance()->config->get($config_key);
71
72   if (!empty($opt))
73     {
74     $db = &rcmail::get_instance()->db;
75
76     if($db->db_provider=='pgsql') // just for sure
77       {
78       $db->db_handle->setOption('disable_smart_seqname', true);
79       $db->db_handle->setOption('seqname_format', '%s');
80       }       
81   
82     return $CONFIG[$opt];
83     }
84     
85   return $sequence;
86   }
87
88
89 /**
90  * Get localized text in the desired language
91  * It's a global wrapper for rcmail::gettext()
92  *
93  * @param mixed Named parameters array or label name
94  * @return string Localized text
95  * @see rcmail::gettext()
96  */
97 function rcube_label($p)
98 {
99   return rcmail::get_instance()->gettext($p);
100 }
101
102
103 /**
104  * Load virtuser table in array
105  *
106  * @return array Virtuser table entries
107  */
108 function rcmail_getvirtualfile()
109   {
110   global $CONFIG;
111   if (empty($CONFIG['virtuser_file']) || !is_file($CONFIG['virtuser_file']))
112     return FALSE;
113   
114   // read file 
115   $a_lines = file($CONFIG['virtuser_file']);
116   return $a_lines;
117   }
118
119
120 /**
121  * Find matches of the given pattern in virtuser table
122  * 
123  * @param string Regular expression to search for
124  * @return array Matching entries
125  */
126 function rcmail_findinvirtual($pattern)
127   {
128   $result = array();
129   $virtual = rcmail_getvirtualfile();
130   if ($virtual==FALSE)
131     return $result;
132
133   // check each line for matches
134   foreach ($virtual as $line)
135     {
136     $line = trim($line);
137     if (empty($line) || $line{0}=='#')
138       continue;
139       
140     if (eregi($pattern, $line))
141       $result[] = $line;
142     }
143
144   return $result;
145   }
146
147
148 /**
149  * Overwrite action variable
150  *
151  * @param string New action value
152  */
153 function rcmail_overwrite_action($action)
154   {
155   $app = rcmail::get_instance();
156   $app->action = $action;
157   $app->output->set_env('action', $action);
158   }
159
160
161 /**
162  * Compose an URL for a specific action
163  *
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
168  */
169 function rcmail_url($action, $p=array(), $task=null)
170 {
171   $app = rcmail::get_instance();
172   
173   $qstring = '';
174   $base = $app->comm_path;
175   
176   if ($task && in_array($task, rcmail::$main_tasks))
177     $base = ereg_replace('_task=[a-z]+', '_task='.$task, $app->comm_path);
178   
179   if (is_array($p))
180     foreach ($p as $key => $val)
181       $qstring .= '&'.urlencode($key).'='.urlencode($val);
182   
183   return $base . ($action ? '&_action='.$action : '') . $qstring;
184 }
185
186
187 /**
188  * Add a localized label to the client environment
189  * @deprecated
190  */
191 function rcube_add_label()
192   {
193   global $OUTPUT;
194   
195   $arg_list = func_get_args();
196   foreach ($arg_list as $i => $name)
197     $OUTPUT->add_label($name);
198   }
199
200
201 /**
202  * Garbage collector function for temp files.
203  * Remove temp files older than two days
204  */
205 function rcmail_temp_gc()
206   {
207   $tmp = unslashify($CONFIG['temp_dir']);
208   $expire = mktime() - 172800;  // expire in 48 hours
209
210   if ($dir = opendir($tmp))
211     {
212     while (($fname = readdir($dir)) !== false)
213       {
214       if ($fname{0} == '.')
215         continue;
216
217       if (filemtime($tmp.'/'.$fname) < $expire)
218         @unlink($tmp.'/'.$fname);
219       }
220
221     closedir($dir);
222     }
223   }
224
225
226 /**
227  * Garbage collector for cache entries.
228  * Remove all expired message cache records
229  */
230 function rcmail_message_cache_gc()
231   {
232   global $DB, $CONFIG;
233   
234   // no cache lifetime configured
235   if (empty($CONFIG['message_cache_lifetime']))
236     return;
237   
238   // get target timestamp
239   $ts = get_offset_time($CONFIG['message_cache_lifetime'], -1);
240   
241   $DB->query("DELETE FROM ".get_table_name('messages')."
242              WHERE  created < ".$DB->fromunixtime($ts));
243   }
244
245
246 /**
247  * Convert a string from one charset to another.
248  * Uses mbstring and iconv functions if possible
249  *
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
254  */
255 function rcube_charset_convert($str, $from, $to=NULL)
256   {
257   static $mbstring_loaded = null, $convert_warning = false;
258
259   $from = strtoupper($from);
260   $to = $to==NULL ? strtoupper(RCMAIL_CHARSET) : strtoupper($to);
261   $error = false; $conv = null;
262
263   if ($from==$to || $str=='' || empty($from))
264     return $str;
265     
266   $aliases = array(
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',
272   );
273
274   // convert charset using iconv module  
275   if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7')
276     {
277     $aliases['GB2312'] = 'GB18030';
278     $_iconv = iconv(($aliases[$from] ? $aliases[$from] : $from), ($aliases[$to] ? $aliases[$to] : $to) . "//IGNORE", $str);
279     if ($_iconv !== false)
280       {
281         return $_iconv;
282       }
283     }
284
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);
289   }
290
291   // convert charset using mbstring module
292   if ($mbstring_loaded)
293     {
294     $aliases['UTF-7'] = 'UTF7-IMAP';
295     $aliases['WINDOWS-1257'] = 'ISO-8859-13';
296     
297     // return if convert succeeded
298     if (($out = mb_convert_encoding($str, ($aliases[$to] ? $aliases[$to] : $to), ($aliases[$from] ? $aliases[$from] : $from))) != '')
299       return $out;
300     }
301     
302   
303   if (class_exists('utf8'))
304     $conv = new utf8();
305
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)
312     {
313     $conv->loadCharset($from);
314     $str = $conv->strToUtf8($str);
315     }
316   else if ($from != 'UTF-8')
317     $error = true;
318
319   // encode string for output
320   if ($to == 'UTF-7')
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)
325     {
326     $conv->loadCharset($to);
327     return $conv->utf8ToStr($str);
328     }
329   else if ($to != 'UTF-8')
330     $error = true;
331
332   // report error
333   if ($error && !$convert_warning)
334     {
335     raise_error(array(
336       'code' => 500,
337       'type' => 'php',
338       'file' => __FILE__,
339       'message' => "Could not convert string charset. Make sure iconv is installed or lib/utf8.class is available"
340       ), true, false);
341     
342     $convert_warning = true;
343     }
344   
345   // return UTF-8 string
346   return $str;
347   }
348
349
350 /**
351  * Replacing specials characters to a specific encoding type
352  *
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
358  */
359 function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
360   {
361   global $OUTPUT;
362   static $html_encode_arr = false;
363   static $js_rep_table = false;
364   static $xml_rep_table = false;
365
366   $charset = $OUTPUT->get_charset();
367   $is_iso_8859_1 = false;
368   if ($charset == 'ISO-8859-1') {
369     $is_iso_8859_1 = true;
370   }
371   if (!$enctype)
372     $enctype = $GLOBALS['OUTPUT_TYPE'];
373
374   // encode for plaintext
375   if ($enctype=='text')
376     return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str);
377
378   // encode for HTML output
379   if ($enctype=='html')
380     {
381     if (!$html_encode_arr)
382       {
383       $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);        
384       unset($html_encode_arr['?']);
385       }
386
387     $ltpos = strpos($str, '<');
388     $encode_arr = $html_encode_arr;
389
390     // don't replace quotes and html tags
391     if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false)
392       {
393       unset($encode_arr['"']);
394       unset($encode_arr['<']);
395       unset($encode_arr['>']);
396       unset($encode_arr['&']);
397       }
398     else if ($mode=='remove')
399       $str = strip_tags($str);
400     
401     // avoid douple quotation of &
402     $out = preg_replace('/&amp;([a-z]{2,5}|#[0-9]{2,4});/', '&\\1;', strtr($str, $encode_arr));
403       
404     return $newlines ? nl2br($out) : $out;
405     }
406
407   if ($enctype=='url')
408     return rawurlencode($str);
409
410   // if the replace tables for XML and JS are not yet defined
411   if ($js_rep_table===false)
412     {
413     $js_rep_table = $xml_rep_table = array();
414     $xml_rep_table['&'] = '&amp;';
415
416     for ($c=160; $c<256; $c++)  // can be increased to support more charsets
417       {
418       $xml_rep_table[Chr($c)] = "&#$c;";
419       
420       if ($is_iso_8859_1)
421         $js_rep_table[Chr($c)] = sprintf("\\u%04x", $c);
422       }
423
424     $xml_rep_table['"'] = '&quot;';
425     }
426
427   // encode for XML
428   if ($enctype=='xml')
429     return strtr($str, $xml_rep_table);
430
431   // encode for javascript use
432   if ($enctype=='js')
433     {
434     if ($charset!='UTF-8')
435       $str = rcube_charset_convert($str, RCMAIL_CHARSET,$charset);
436       
437     return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), addslashes(strtr($str, $js_rep_table)));
438     }
439
440   // no encoding given -> return original string
441   return $str;
442   }
443   
444 /**
445  * Quote a given string.
446  * Shortcut function for rep_specialchars_output
447  *
448  * @return string HTML-quoted string
449  * @see rep_specialchars_output()
450  */
451 function Q($str, $mode='strict', $newlines=TRUE)
452   {
453   return rep_specialchars_output($str, 'html', $mode, $newlines);
454   }
455
456 /**
457  * Quote a given string for javascript output.
458  * Shortcut function for rep_specialchars_output
459  * 
460  * @return string JS-quoted string
461  * @see rep_specialchars_output()
462  */
463 function JQ($str)
464   {
465   return rep_specialchars_output($str, 'js');
466   }
467
468
469 /**
470  * Read input value and convert it for internal use
471  * Performs stripslashes() and charset conversion if necessary
472  * 
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
478  */
479 function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
480   {
481   global $OUTPUT;
482   $value = NULL;
483   
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)
489     {
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];
496     }
497   
498   // strip slashes if magic_quotes enabled
499   if ((bool)get_magic_quotes_gpc())
500     $value = stripslashes($value);
501
502   // remove HTML tags if not allowed    
503   if (!$allow_html)
504     $value = strip_tags($value);
505   
506   // convert to internal charset
507   if (is_object($OUTPUT))
508     return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
509   else
510     return $value;
511   }
512
513 /**
514  * Remove all non-ascii and non-word chars
515  * except . and -
516  */
517 function asciiwords($str)
518 {
519   return preg_replace('/[^a-z0-9.-_]/i', '', $str);
520 }
521
522 /**
523  * Remove single and double quotes from given string
524  *
525  * @param string Input value
526  * @return string Dequoted string
527  */
528 function strip_quotes($str)
529 {
530   return preg_replace('/[\'"]/', '', $str);
531 }
532
533
534 /**
535  * Remove new lines characters from given string
536  *
537  * @param string Input value
538  * @return string Stripped string
539  */
540 function strip_newlines($str)
541 {
542   return preg_replace('/[\r\n]/', '', $str);
543 }
544
545
546 /**
547  * Check if a specific template exists
548  *
549  * @param string Template name
550  * @return boolean True if template exists
551  */
552 function template_exists($name)
553   {
554   global $CONFIG;
555   $skin_path = $CONFIG['skin_path'];
556
557   // check template file
558   return is_file("$skin_path/templates/$name.html");
559   }
560
561
562 /**
563  * Create a HTML table based on the given data
564  *
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
570  */
571 function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
572   {
573   global $DB;
574   
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'));
577   
578   $table = '<table' . $attrib_str . ">\n";
579     
580   // add table title
581   $table .= "<thead><tr>\n";
582
583   foreach ($a_show_cols as $col)
584     $table .= '<td class="'.$col.'">' . Q(rcube_label($col)) . "</td>\n";
585
586   $table .= "</tr></thead>\n<tbody>\n";
587   
588   $c = 0;
589   if (!is_array($table_data)) 
590     {
591     while ($table_data && ($sql_arr = $DB->fetch_assoc($table_data)))
592       {
593       $zebra_class = $c%2 ? 'even' : 'odd';
594
595       $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $sql_arr[$id_col]);
596
597       // format each col
598       foreach ($a_show_cols as $col)
599         {
600         $cont = Q($sql_arr[$col]);
601         $table .= '<td class="'.$col.'">' . $cont . "</td>\n";
602         }
603
604       $table .= "</tr>\n";
605       $c++;
606       }
607     }
608   else 
609     {
610     foreach ($table_data as $row_data)
611       {
612       $zebra_class = $c%2 ? 'even' : 'odd';
613
614       $table .= sprintf('<tr id="rcmrow%s" class="contact '.$zebra_class.'">'."\n", $row_data[$id_col]);
615
616       // format each col
617       foreach ($a_show_cols as $col)
618         {
619         $cont = Q($row_data[$col]);
620         $table .= '<td class="'.$col.'">' . $cont . "</td>\n";
621         }
622
623       $table .= "</tr>\n";
624       $c++;
625       }
626     }
627
628   // complete message table
629   $table .= "</tbody></table>\n";
630   
631   return $table;
632   }
633
634
635 /**
636  * Create an edit field for inclusion on a form
637  * 
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
643  */
644 function rcmail_get_edit_field($col, $value, $attrib, $type='text')
645   {
646   $fname = '_'.$col;
647   $attrib['name'] = $fname;
648   
649   if ($type=='checkbox')
650     {
651     $attrib['value'] = '1';
652     $input = new html_checkbox($attrib);
653     }
654   else if ($type=='textarea')
655     {
656     $attrib['cols'] = $attrib['size'];
657     $input = new html_textarea($attrib);
658     }
659   else
660     $input = new html_inputfield($attrib);
661
662   // use value from post
663   if (!empty($_POST[$fname]))
664     $value = get_input_value($fname, RCUBE_INPUT_POST);
665
666   $out = $input->show($value);
667          
668   return $out;
669   }
670
671
672 /**
673  * Return the mail domain configured for the given host
674  *
675  * @param string IMAP host
676  * @return string Resolved SMTP host
677  */
678 function rcmail_mail_domain($host)
679   {
680   global $CONFIG;
681
682   $domain = $host;
683   if (is_array($CONFIG['mail_domain']))
684     {
685     if (isset($CONFIG['mail_domain'][$host]))
686       $domain = $CONFIG['mail_domain'][$host];
687     }
688   else if (!empty($CONFIG['mail_domain']))
689     $domain = $CONFIG['mail_domain'];
690
691   return $domain;
692   }
693
694
695 /**
696  * Replace all css definitions with #container [def]
697  * and remove css-inlined scripting
698  *
699  * @param string CSS source code
700  * @param string Container ID to use as prefix
701  * @return string Modified CSS source
702  */
703 function rcmail_mod_css_styles($source, $container_id, $base_url = '')
704   {
705   $a_css_values = array();
706   $last_pos = 0;
707   
708   // ignore the whole block if evil styles are detected
709   if (stristr($source, 'expression') || stristr($source, 'behavior'))
710     return '';
711
712   // cut out all contents between { and }
713   while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
714   {
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);
718     $last_pos = $pos+2;
719   }
720
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(
724     array(
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"
730     ),
731     array(
732       '',
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"
737     ),
738     $source);
739
740   return $styles;
741   }
742
743 /**
744  * Try to autodetect operating system and find the correct line endings
745  *
746  * @return string The appropriate mail header delimiter
747  */
748 function rcmail_header_delm()
749 {
750   global $CONFIG;
751   
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')) 
756     return "\r\n";
757   else if (strtolower(substr(PHP_OS, 0, 3)=='mac'))
758     return "\r\n";
759   else    
760     return "\n";
761 }
762
763
764 /**
765  * Compose a valid attribute string for HTML tags
766  *
767  * @param array Named tag attributes
768  * @param array List of allowed attributes
769  * @return string HTML formatted attribute string
770  */
771 function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
772   {
773   // allow the following attributes to be added to the <iframe> tag
774   $attrib_str = '';
775   foreach ($allowed_attribs as $a)
776     if (isset($attrib[$a]))
777       $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '&quot;', $attrib[$a]));
778
779   return $attrib_str;
780   }
781
782
783 /**
784  * Convert a HTML attribute string attributes to an associative array (name => value)
785  *
786  * @param string Input string
787  * @return array Key-value pairs of parsed attributes
788  */
789 function parse_attrib_string($str)
790   {
791   $attrib = array();
792   preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]+)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
793
794   // convert attributes to an associative array (name => value)
795   if ($regs)
796     foreach ($regs as $attr)
797       {
798       $attrib[strtolower($attr[1])] = $attr[3] . $attr[4];
799       }
800
801   return $attrib;
802   }
803
804
805 /**
806  * Convert the given date to a human readable form
807  * This uses the date formatting properties from config
808  *
809  * @param mixed Date representation (string or timestamp)
810  * @param string Date format to use
811  * @return string Formatted date string
812  */
813 function format_date($date, $format=NULL)
814   {
815   global $CONFIG;
816   
817   $ts = NULL;
818
819   if (is_numeric($date))
820     $ts = $date;
821   else if (!empty($date))
822     {
823     while (($ts = @strtotime($date))===false)
824       {
825         // if we have a date in non-rfc format
826         // remove token from the end and try again
827         $d = explode(' ', $date);
828         array_pop($d);
829         if (!$d) break;
830         $date = implode(' ', $d);
831       }
832     }
833
834   if (empty($ts))
835     return '';
836    
837   // get user's timezone
838   $tz = $CONFIG['timezone'];
839   if ($CONFIG['dst_active'])
840     $tz++;
841
842   // convert time to user's timezone
843   $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
844   
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);
850
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']);
853
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';
859   else if (!$format)
860     $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
861
862
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()
865   $out = '';
866   for($i=0; $i<strlen($format); $i++)
867     {
868     if ($format{$i}=='\\')  // skip escape chars
869       continue;
870     
871     // write char "as-is"
872     if ($format{$i}==' ' || $format{$i-1}=='\\')
873       $out .= $format{$i};
874     // weekday (short)
875     else if ($format{$i}=='D')
876       $out .= rcube_label(strtolower(date('D', $timestamp)));
877     // weekday long
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)));
883     // month name (long)
884     else if ($format{$i}=='F')
885       $out .= rcube_label('long'.strtolower(date('M', $timestamp)));
886     else
887       $out .= date($format{$i}, $timestamp);
888     }
889   
890   return $out;
891   }
892
893
894 /**
895  * Compose a valid representaion of name and e-mail address
896  *
897  * @param string E-mail address
898  * @param string Person name
899  * @return string Formatted string
900  */
901 function format_email_recipient($email, $name='')
902   {
903   if ($name && $name != $email)
904     {
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);
907     }
908   else
909     return $email;
910   }
911
912
913
914 /****** debugging functions ********/
915
916
917 /**
918  * Print or write debug messages
919  *
920  * @param mixed Debug message or data
921  */
922 function console($msg)
923   {
924   if (!is_string($msg))
925     $msg = var_export($msg, true);
926
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";
931   else
932     {
933     print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>';
934     print $msg;
935     print "</pre></div>\n";
936     }
937   }
938
939
940 /**
941  * Append a line to a logfile in the logs directory.
942  * Date will be added automatically to the line.
943  *
944  * @param $name name of log file
945  * @param line Line to append
946  */
947 function write_log($name, $line)
948   {
949   global $CONFIG;
950
951   if (!is_string($line))
952     $line = var_export($line, true);
953   
954   $log_entry = sprintf("[%s]: %s\n",
955                  date("d-M-Y H:i:s O", mktime()),
956                  $line);
957                  
958   if (empty($CONFIG['log_dir']))
959     $CONFIG['log_dir'] = INSTALL_PATH.'logs';
960       
961   // try to open specific log file for writing
962   if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a'))    
963     {
964     fwrite($fp, $log_entry);
965     fclose($fp);
966     }
967   }
968
969
970 /**
971  * @access private
972  */
973 function rcube_timer()
974   {
975   list($usec, $sec) = explode(" ", microtime());
976   return ((float)$usec + (float)$sec);
977   }
978   
979
980 /**
981  * @access private
982  */
983 function rcube_print_time($timer, $label='Timer')
984   {
985   static $print_count = 0;
986   
987   $print_count++;
988   $now = rcube_timer();
989   $diff = $now-$timer;
990   
991   if (empty($label))
992     $label = 'Timer '.$print_count;
993   
994   console(sprintf("%s: %0.4f sec", $label, $diff));
995   }
996
997
998 /**
999  * Return the mailboxlist in HTML
1000  *
1001  * @param array Named parameters
1002  * @return string HTML code for the gui object
1003  */
1004 function rcmail_mailbox_list($attrib)
1005   {
1006   global $IMAP, $CONFIG, $OUTPUT, $COMM_PATH;
1007   static $s_added_script = FALSE;
1008   static $a_mailboxes;
1009
1010   // add some labels to client
1011   rcube_add_label('purgefolderconfirm');
1012   rcube_add_label('deletemessagesconfirm');
1013   
1014 // $mboxlist_start = rcube_timer();
1015   
1016   $type = $attrib['type'] ? $attrib['type'] : 'ul';
1017   $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
1018                                   array('style', 'class', 'id');
1019                                   
1020   if ($type=='ul' && !$attrib['id'])
1021     $attrib['id'] = 'rcmboxlist';
1022
1023   // allow the following attributes to be added to the <ul> tag
1024   $attrib_str = create_attrib_string($attrib, $add_attrib);
1025  
1026   $out = '<' . $type . $attrib_str . ">\n";
1027   
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']));
1032   
1033   // get mailbox list
1034   $mbox_name = $IMAP->get_mailbox_name();
1035   
1036   // build the folders tree
1037   if (empty($a_mailboxes))
1038     {
1039     // get mailbox list
1040     $a_folders = $IMAP->list_mailboxes();
1041     $delimiter = $IMAP->get_hierarchy_delimiter();
1042     $a_mailboxes = array();
1043
1044 // rcube_print_time($mboxlist_start, 'list_mailboxes()');
1045
1046     foreach ($a_folders as $folder)
1047       rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
1048     }
1049
1050 // var_dump($a_mailboxes);
1051
1052   if ($type=='select')
1053     $out .= rcmail_render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength']);
1054    else
1055     $out .= rcmail_render_folder_tree_html($a_mailboxes, $mbox_name, $attrib['maxlength']);
1056
1057 // rcube_print_time($mboxlist_start, 'render_folder_tree()');
1058
1059
1060   if ($type=='ul')
1061     $OUTPUT->add_gui_object('mailboxlist', $attrib['id']);
1062
1063   return $out . "</$type>";
1064   }
1065
1066
1067
1068
1069 /**
1070  * Create a hierarchical array of the mailbox list
1071  * @access private
1072  */
1073 function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
1074   {
1075   $pos = strpos($folder, $delm);
1076   if ($pos !== false)
1077     {
1078     $subFolders = substr($folder, $pos+1);
1079     $currentFolder = substr($folder, 0, $pos);
1080     }
1081   else
1082     {
1083     $subFolders = false;
1084     $currentFolder = $folder;
1085     }
1086
1087   $path .= $currentFolder;
1088
1089   if (!isset($arrFolders[$currentFolder]))
1090     {
1091     $arrFolders[$currentFolder] = array('id' => $path,
1092                                         'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
1093                                         'folders' => array());
1094     }
1095
1096   if (!empty($subFolders))
1097     rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
1098   }
1099   
1100
1101 /**
1102  * Return html for a structured list &lt;ul&gt; for the mailbox tree
1103  * @access private
1104  */
1105 function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, $maxlength, $nestLevel=0)
1106   {
1107   global $COMM_PATH, $IMAP, $CONFIG, $OUTPUT;
1108
1109   $idx = 0;
1110   $out = '';
1111   foreach ($arrFolders as $key => $folder)
1112     {
1113     $zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
1114     $title = '';
1115
1116     if ($folder_class = rcmail_folder_classname($folder['id']))
1117       $foldername = rcube_label($folder_class);
1118     else
1119       {
1120       $foldername = $folder['name'];
1121
1122       // shorten the folder name to a given length
1123       if ($maxlength && $maxlength>1)
1124         {
1125         $fname = abbreviate_string($foldername, $maxlength);
1126         if ($fname != $foldername)
1127           $title = ' title="'.Q($foldername).'"';
1128         $foldername = $fname;
1129         }
1130       }
1131
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']));
1135
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';
1145
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>',
1152                     $folder_id,
1153                     $class_name,
1154                     $zebra_class,
1155                     $folder['id']==$mbox_name ? ' selected' : '',
1156                     Q(rcmail_url('', array('_mbox' => $folder['id']))),
1157                     JS_OBJECT_NAME,
1158                     $js_name,
1159                     JS_OBJECT_NAME,
1160                     $js_name,
1161                     JS_OBJECT_NAME,
1162                     $js_name,
1163                     JS_OBJECT_NAME,
1164                     $js_name,
1165                     $title,
1166                     Q($foldername));
1167
1168     if (!empty($folder['folders']))
1169       $out .= "\n<ul>\n" . rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $maxlength, $nestLevel+1) . "</ul>\n";
1170
1171     $out .= "</li>\n";
1172     $idx++;
1173     }
1174
1175   return $out;
1176   }
1177
1178
1179 /**
1180  * Return html for a flat list <select> for the mailbox tree
1181  * @access private
1182  */
1183 function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, $nestLevel=0, $selected='')
1184   {
1185   global $IMAP, $OUTPUT;
1186
1187   $idx = 0;
1188   $out = '';
1189   foreach ($arrFolders as $key=>$folder)
1190     {
1191     if ($folder_class = rcmail_folder_classname($folder['id']))
1192       $foldername = rcube_label($folder_class);
1193     else
1194       {
1195       $foldername = $folder['name'];
1196       
1197       // shorten the folder name to a given length
1198       if ($maxlength && $maxlength>1)
1199         $foldername = abbreviate_string($foldername, $maxlength);
1200       }
1201
1202     $out .= sprintf('<option value="%s"%s>%s%s</option>'."\n",
1203                     htmlspecialchars($folder['id']),
1204                     ($selected == $foldername ? ' selected="selected"' : ''),
1205                     str_repeat('&nbsp;', $nestLevel*4),
1206                     Q($foldername));
1207
1208     if (!empty($folder['folders']))
1209       $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, $nestLevel+1, $selected);
1210
1211     $idx++;
1212     }
1213
1214   return $out;
1215   }
1216
1217
1218 /**
1219  * Return internal name for the given folder if it matches the configured special folders
1220  * @access private
1221  */
1222 function rcmail_folder_classname($folder_id)
1223 {
1224   global $CONFIG;
1225
1226   $cname = null;
1227   $folder_lc = strtolower($folder_id);
1228   
1229   // for these mailboxes we have localized labels and css classes
1230   foreach (array('inbox', 'sent', 'drafts', 'trash', 'junk') as $smbx)
1231   {
1232     if ($folder_lc == $smbx || $folder_id == $CONFIG[$smbx.'_mbox'])
1233       $cname = $smbx;
1234   }
1235   
1236   return $cname;
1237 }
1238
1239
1240 /**
1241  * Try to localize the given IMAP folder name.
1242  * UTF-7 decode it in case no localized text was found
1243  *
1244  * @param string Folder name
1245  * @return string Localized folder name in UTF-8 encoding
1246  */
1247 function rcmail_localize_foldername($name)
1248 {
1249   if ($folder_class = rcmail_folder_classname($name))
1250     return rcube_label($folder_class);
1251   else
1252     return rcube_charset_convert($name, 'UTF-7');
1253 }
1254
1255
1256 ?>