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