thomascube
2007-11-25 b2ff3d44610e1836fe7080a7afffdf4f6ebd32da
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                     |
f11541 8  | Copyright (C) 2005-2007, 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
4e17e6 29 require_once('lib/des.inc');
0af7e8 30 require_once('lib/utf7.inc');
83dbb7 31 require_once('lib/utf8.class.php');
97bd2c 32 require_once('include/rcube_shared.inc');
f11541 33 require_once('include/rcmail_template.inc');
4e17e6 34
T 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
6d969b 42 /**
T 43  * Initial startup function
44  * to register session, create database and imap connections
45  *
46  * @param string Current task
47  */
4e17e6 48 function rcmail_startup($task='mail')
T 49   {
aad6e2 50   global $sess_id, $sess_user_lang;
f11541 51   global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB;
4e17e6 52
T 53   // check client
54   $BROWSER = rcube_browser();
de2e1e 55
e170b4 56   // load configuration
T 57   $CONFIG = rcmail_load_config();
bac7d1 58
7902df 59   // set session garbage collecting time according to session_lifetime
T 60   if (!empty($CONFIG['session_lifetime']))
e170b4 61     ini_set('session.gc_maxlifetime', ($CONFIG['session_lifetime']) * 120);
4e17e6 62
T 63   // prepare DB connection
6d969b 64   $dbwrapper = empty($CONFIG['db_backend']) ? 'db' : $CONFIG['db_backend'];
T 65   $dbclass = "rcube_" . $dbwrapper;
66   require_once("include/$dbclass.inc");
f45ec7 67   
6d969b 68   $DB = new $dbclass($CONFIG['db_dsnw'], $CONFIG['db_dsnr'], $CONFIG['db_persistent']);
42b113 69   $DB->sqlite_initials = $INSTALL_PATH.'SQL/sqlite.initial.sql';
8affba 70   $DB->db_connect('w');
e170b4 71
aad6e2 72   // use database for storing session data
T 73   include_once('include/session.inc');
4e17e6 74
T 75   // init session
76   session_start();
77   $sess_id = session_id();
de8c61 78
4e17e6 79   // create session and set session vars
bac7d1 80   if (!isset($_SESSION['auth_time']))
4e17e6 81     {
0af7e8 82     $_SESSION['user_lang'] = rcube_language_prop($CONFIG['locale_string']);
aad6e2 83     $_SESSION['auth_time'] = time();
T 84     $_SESSION['temp'] = true;
4e17e6 85     }
T 86
87   // set session vars global
0af7e8 88   $sess_user_lang = rcube_language_prop($_SESSION['user_lang']);
4e17e6 89
T 90
91   // overwrite config with user preferences
92   if (is_array($_SESSION['user_prefs']))
93     $CONFIG = array_merge($CONFIG, $_SESSION['user_prefs']);
94
95
96   // reset some session parameters when changing task
97   if ($_SESSION['task'] != $task)
98     unset($_SESSION['page']);
99
100   // set current task to session
101   $_SESSION['task'] = $task;
102
103   // create IMAP object
104   if ($task=='mail')
105     rcmail_imap_init();
106
107
108   // set localization
109   if ($CONFIG['locale_string'])
110     setlocale(LC_ALL, $CONFIG['locale_string']);
111   else if ($sess_user_lang)
112     setlocale(LC_ALL, $sess_user_lang);
113
114
115   register_shutdown_function('rcmail_shutdown');
116   }
9606ff 117
T 118
6d969b 119 /**
T 120  * Load roundcube configuration array
121  *
122  * @return array Named configuration parameters
123  */
e170b4 124 function rcmail_load_config()
T 125   {
f11541 126   global $INSTALL_PATH;
e170b4 127
T 128   // load config file
f11541 129   include_once('config/main.inc.php');
T 130   $conf = is_array($rcmail_config) ? $rcmail_config : array();
e170b4 131
T 132   // load host-specific configuration
133   rcmail_load_host_config($conf);
134
135   $conf['skin_path'] = $conf['skin_path'] ? unslashify($conf['skin_path']) : 'skins/default';
136
137   // load db conf
138   include_once('config/db.inc.php');
139   $conf = array_merge($conf, $rcmail_config);
140
141   if (empty($conf['log_dir']))
142     $conf['log_dir'] = $INSTALL_PATH.'logs';
143   else
144     $conf['log_dir'] = unslashify($conf['log_dir']);
145
146   // set PHP error logging according to config
147   if ($conf['debug_level'] & 1)
148     {
149     ini_set('log_errors', 1);
150     ini_set('error_log', $conf['log_dir'].'/errors');
151     }
152   if ($conf['debug_level'] & 4)
153     ini_set('display_errors', 1);
154   else
155     ini_set('display_errors', 0);
156
157   return $conf;
158   }
159
160
6d969b 161 /**
T 162  * Load a host-specific config file if configured
163  * This will merge the host specific configuration with the given one
164  *
165  * @param array Global configuration parameters
166  */
9606ff 167 function rcmail_load_host_config(&$config)
T 168   {
169   $fname = NULL;
170   
171   if (is_array($config['include_host_config']))
172     $fname = $config['include_host_config'][$_SERVER['HTTP_HOST']];
173   else if (!empty($config['include_host_config']))
174     $fname = preg_replace('/[^a-z0-9\.\-_]/i', '', $_SERVER['HTTP_HOST']) . '.inc.php';
175
176    if ($fname && is_file('config/'.$fname))
177      {
178      include('config/'.$fname);
179      $config = array_merge($config, $rcmail_config);
180      }
181   }
bac7d1 182
4e17e6 183
6d969b 184 /**
T 185  * Create unique authorization hash
186  *
187  * @param string Session ID
188  * @param int Timestamp
189  * @return string The generated auth hash
190  */
4e17e6 191 function rcmail_auth_hash($sess_id, $ts)
T 192   {
193   global $CONFIG;
194   
195   $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s',
196                          $sess_id,
197                          $ts,
198                          $CONFIG['ip_check'] ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***',
199                          $_SERVER['HTTP_USER_AGENT']);
200   
201   if (function_exists('sha1'))
202     return sha1($auth_string);
203   else
204     return md5($auth_string);
205   }
206
bac7d1 207
6d969b 208 /**
T 209  * Check the auth hash sent by the client against the local session credentials
210  *
211  * @return boolean True if valid, False if not
212  */
bac7d1 213 function rcmail_authenticate_session()
T 214   {
aad6e2 215   global $CONFIG, $SESS_CLIENT_IP, $SESS_CHANGED;
T 216   
217   // advanced session authentication
218   if ($CONFIG['double_auth'])
219   {
220     $now = time();
221     $valid = ($_COOKIE['sessauth'] == rcmail_auth_hash(session_id(), $_SESSION['auth_time']) ||
222               $_COOKIE['sessauth'] == rcmail_auth_hash(session_id(), $_SESSION['last_auth']));
aade7b 223
aad6e2 224     // renew auth cookie every 5 minutes (only for GET requests)
T 225     if (!$valid || ($_SERVER['REQUEST_METHOD']!='POST' && $now-$_SESSION['auth_time'] > 300))
bac7d1 226     {
aad6e2 227       $_SESSION['last_auth'] = $_SESSION['auth_time'];
T 228       $_SESSION['auth_time'] = $now;
229       setcookie('sessauth', rcmail_auth_hash(session_id(), $now));
bac7d1 230     }
aad6e2 231   }
T 232   else
233     $valid = $CONFIG['ip_check'] ? $_SERVER['REMOTE_ADDR'] == $SESS_CLIENT_IP : true;
234   
235   // check session filetime
236   if (!empty($CONFIG['session_lifetime']) && isset($SESS_CHANGED) && $SESS_CHANGED + $CONFIG['session_lifetime']*60 < time())
237     $valid = false;
31d9ef 238   
bac7d1 239   return $valid;
T 240   }
4e17e6 241
T 242
6d969b 243 /**
T 244  * Create global IMAP object and connect to server
245  *
246  * @param boolean True if connection should be established
247  */
4e17e6 248 function rcmail_imap_init($connect=FALSE)
T 249   {
f11541 250   global $CONFIG, $DB, $IMAP, $OUTPUT;
6dc026 251
1cded8 252   $IMAP = new rcube_imap($DB);
15a9d1 253   $IMAP->debug_level = $CONFIG['debug_level'];
T 254   $IMAP->skip_deleted = $CONFIG['skip_deleted'];
255
4e17e6 256
7902df 257   // connect with stored session data
T 258   if ($connect)
259     {
260     if (!($conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl'])))
f11541 261       $OUTPUT->show_message('imaperror', 'error');
7902df 262       
T 263     rcmail_set_imap_prop();
264     }
265
6dc026 266   // enable caching of imap data
T 267   if ($CONFIG['enable_caching']===TRUE)
268     $IMAP->set_caching(TRUE);
269
4e17e6 270   // set pagesize from config
T 271   if (isset($CONFIG['pagesize']))
272     $IMAP->set_pagesize($CONFIG['pagesize']);
7902df 273   }
4e17e6 274
T 275
6d969b 276 /**
T 277  * Set root dir and last stored mailbox
278  * This must be done AFTER connecting to the server!
279  */
7902df 280 function rcmail_set_imap_prop()
T 281   {
282   global $CONFIG, $IMAP;
283
284   // set root dir from config
620439 285   if (!empty($CONFIG['imap_root']))
7902df 286     $IMAP->set_rootdir($CONFIG['imap_root']);
fa4cd2 287
T 288   if (is_array($CONFIG['default_imap_folders']))
289     $IMAP->set_default_mailboxes($CONFIG['default_imap_folders']);
7902df 290
620439 291   if (!empty($_SESSION['mbox']))
7902df 292     $IMAP->set_mailbox($_SESSION['mbox']);
T 293   if (isset($_SESSION['page']))
294     $IMAP->set_page($_SESSION['page']);
4e17e6 295   }
T 296
297
6d969b 298 /**
T 299  * Do these things on script shutdown
300  */
4e17e6 301 function rcmail_shutdown()
T 302   {
6b603d 303   global $IMAP, $CONTACTS;
4e17e6 304   
T 305   if (is_object($IMAP))
306     {
307     $IMAP->close();
308     $IMAP->write_cache();
309     }
0af7e8 310     
6b603d 311   if (is_object($CONTACTS))
T 312     $CONTACTS->close();
313     
0af7e8 314   // before closing the database connection, write session data
T 315   session_write_close();
4e17e6 316   }
T 317
318
6d969b 319 /**
T 320  * Destroy session data and remove cookie
321  */
4e17e6 322 function rcmail_kill_session()
T 323   {
86f172 324   // save user preferences
T 325   $a_user_prefs = $_SESSION['user_prefs'];
326   if (!is_array($a_user_prefs))
327     $a_user_prefs = array();
328     
329   if ((isset($_SESSION['sort_col']) && $_SESSION['sort_col']!=$a_user_prefs['message_sort_col']) ||
330       (isset($_SESSION['sort_order']) && $_SESSION['sort_order']!=$a_user_prefs['message_sort_order']))
331     {
332     $a_user_prefs['message_sort_col'] = $_SESSION['sort_col'];
333     $a_user_prefs['message_sort_order'] = $_SESSION['sort_order'];
334     rcmail_save_user_prefs($a_user_prefs);
335     }
336
aad6e2 337   $_SESSION = array('user_lang' => $GLOBALS['sess_user_lang'], 'auth_time' => time(), 'temp' => true);
T 338   setcookie('sessauth', '-del-', time()-60);
4e17e6 339   }
T 340
341
6d969b 342 /**
T 343  * Return correct name for a specific database table
344  *
345  * @param string Table name
346  * @return string Translated table name
347  */
4e17e6 348 function get_table_name($table)
T 349   {
350   global $CONFIG;
351   
352   // return table name if configured
353   $config_key = 'db_table_'.$table;
354
355   if (strlen($CONFIG[$config_key]))
356     return $CONFIG[$config_key];
357   
358   return $table;
359   }
360
361
6d969b 362 /**
T 363  * Return correct name for a specific database sequence
364  * (used for Postres only)
365  *
366  * @param string Secuence name
367  * @return string Translated sequence name
368  */
1cded8 369 function get_sequence_name($sequence)
T 370   {
371   global $CONFIG;
372   
373   // return table name if configured
374   $config_key = 'db_sequence_'.$sequence;
375
376   if (strlen($CONFIG[$config_key]))
377     return $CONFIG[$config_key];
378   
379   return $table;
380   }
0af7e8 381
T 382
6d969b 383 /**
T 384  * Check the given string and returns language properties
385  *
386  * @param string Language code
387  * @param string Peropert name
388  * @return string Property value
389  */
0af7e8 390 function rcube_language_prop($lang, $prop='lang')
T 391   {
c8c1e0 392   global $INSTALL_PATH;
0af7e8 393   static $rcube_languages, $rcube_language_aliases, $rcube_charsets;
T 394
395   if (empty($rcube_languages))
c8c1e0 396     @include($INSTALL_PATH.'program/localization/index.inc');
0af7e8 397     
T 398   // check if we have an alias for that language
399   if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang]))
400     $lang = $rcube_language_aliases[$lang];
401     
402   // try the first two chars
f88d41 403   if (!isset($rcube_languages[$lang]) && strlen($lang)>2)
0af7e8 404     {
T 405     $lang = substr($lang, 0, 2);
406     $lang = rcube_language_prop($lang);
407     }
408
409   if (!isset($rcube_languages[$lang]))
410     $lang = 'en_US';
411
412   // language has special charset configured
413   if (isset($rcube_charsets[$lang]))
414     $charset = $rcube_charsets[$lang];
415   else
416     $charset = 'UTF-8';    
f88d41 417
0af7e8 418
T 419   if ($prop=='charset')
420     return $charset;
421   else
422     return $lang;
423   }
1cded8 424   
4e17e6 425
6d969b 426 /**
T 427  * Init output object for GUI and add common scripts.
428  * This will instantiate a rcmail_template object and set
429  * environment vars according to the current session and configuration
430  */
f11541 431 function rcmail_load_gui()
4e17e6 432   {
f11541 433   global $CONFIG, $OUTPUT, $sess_user_lang;
4e17e6 434
T 435   // init output page
f11541 436   $OUTPUT = new rcmail_template($CONFIG, $GLOBALS['_task']);
T 437   $OUTPUT->set_env('comm_path', $GLOBALS['COMM_PATH']);
4e17e6 438
f11541 439   if (is_array($CONFIG['javascript_config']))
T 440   {
441     foreach ($CONFIG['javascript_config'] as $js_config_var)
442       $OUTPUT->set_env($js_config_var, $CONFIG[$js_config_var]);
de8c61 443   }
e170b4 444
597170 445   if (!empty($GLOBALS['_framed']))
f11541 446     $OUTPUT->set_env('framed', true);
7cc38e 447
13c1af 448   // set locale setting
T 449   rcmail_set_locale($sess_user_lang);
450
7cc38e 451   // set user-selected charset
5bc8cb 452   if (!empty($CONFIG['charset']))
7cc38e 453     $OUTPUT->set_charset($CONFIG['charset']);
f11541 454     
T 455   // register common UI objects
456   $OUTPUT->add_handlers(array(
457     'loginform' => 'rcmail_login_form',
458     'username'  => 'rcmail_current_username',
459     'message' => 'rcmail_message_container',
460     'charsetselector' => 'rcmail_charset_selector',
461   ));
0af7e8 462
10a699 463   // add some basic label to client
f11541 464   if (!$OUTPUT->ajax_call)
737b6a 465     rcube_add_label('loading', 'movingmessage');
0af7e8 466   }
7cc38e 467
T 468
6d969b 469 /**
T 470  * Set localization charset based on the given language.
471  * This also creates a global property for mbstring usage.
472  */
7cc38e 473 function rcmail_set_locale($lang)
T 474   {
f11541 475   global $OUTPUT, $MBSTRING;
f88d41 476   static $s_mbstring_loaded = NULL;
T 477   
478   // settings for mbstring module (by Tadashi Jokagi)
88f66e 479   if (is_null($s_mbstring_loaded)) 
T 480     $MBSTRING = $s_mbstring_loaded = extension_loaded("mbstring"); 
481   else
5f56a5 482     $MBSTRING = $s_mbstring_loaded = FALSE;
88f66e 483   
86df15 484   if ($MBSTRING)
f11541 485     mb_internal_encoding(RCMAIL_CHARSET);
f88d41 486
3f9edb 487   $OUTPUT->set_charset(rcube_language_prop($lang, 'charset'));
7cc38e 488   }
4e17e6 489
T 490
6d969b 491 /**
T 492  * Auto-select IMAP host based on the posted login information
493  *
494  * @return string Selected IMAP host
495  */
0a020c 496 function rcmail_autoselect_host()
T 497   {
498   global $CONFIG;
499   
500   $host = isset($_POST['_host']) ? get_input_value('_host', RCUBE_INPUT_POST) : $CONFIG['default_host'];
501   if (is_array($host))
502     {
503     list($user, $domain) = explode('@', get_input_value('_user', RCUBE_INPUT_POST));
504     if (!empty($domain))
505       {
506       foreach ($host as $imap_host => $mail_domains)
507         if (is_array($mail_domains) && in_array($domain, $mail_domains))
508           {
509           $host = $imap_host;
510           break;
511           }
512       }
513
514     // take the first entry if $host is still an array
515     if (is_array($host))
516       $host = array_shift($host);
517     }
518   
519   return $host;
520   }
521
522
6d969b 523 /**
T 524  * Perfom login to the IMAP server and to the webmail service.
525  * This will also create a new user entry if auto_create_user is configured.
526  *
527  * @param string IMAP user name
528  * @param string IMAP password
529  * @param string IMAP host
530  * @return boolean True on success, False on failure
531  */
4e17e6 532 function rcmail_login($user, $pass, $host=NULL)
T 533   {
534   global $CONFIG, $IMAP, $DB, $sess_user_lang;
42b113 535   $user_id = NULL;
4e17e6 536   
T 537   if (!$host)
538     $host = $CONFIG['default_host'];
539
ee883a 540   // Validate that selected host is in the list of configured hosts
T 541   if (is_array($CONFIG['default_host']))
542     {
543     $allowed = FALSE;
544     foreach ($CONFIG['default_host'] as $key => $host_allowed)
545       {
546       if (!is_numeric($key))
547         $host_allowed = $key;
548       if ($host == $host_allowed)
549         {
550         $allowed = TRUE;
551         break;
552         }
553       }
554     if (!$allowed)
555       return FALSE;
556     }
557   else if (!empty($CONFIG['default_host']) && $host != $CONFIG['default_host'])
558     return FALSE;
559
f619de 560   // parse $host URL
T 561   $a_host = parse_url($host);
562   if ($a_host['host'])
563     {
564     $host = $a_host['host'];
565     $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? TRUE : FALSE;
566     $imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : $CONFIG['default_port']);
567     }
ea7c46 568   else
T 569     $imap_port = $CONFIG['default_port'];
f619de 570
026d68 571
T 572   /* Modify username with domain if required  
573      Inspired by Marco <P0L0_notspam_binware.org>
574   */
575   // Check if we need to add domain
0d1dd7 576   if (!empty($CONFIG['username_domain']) && !strpos($user, '@'))
026d68 577     {
T 578     if (is_array($CONFIG['username_domain']) && isset($CONFIG['username_domain'][$host]))
579       $user .= '@'.$CONFIG['username_domain'][$host];
996066 580     else if (is_string($CONFIG['username_domain']))
T 581       $user .= '@'.$CONFIG['username_domain'];
026d68 582     }
T 583
532844 584   // try to resolve email address from virtuser table    
T 585   if (!empty($CONFIG['virtuser_file']) && strpos($user, '@'))
586     $user = rcmail_email2user($user);
587
0d1dd7 588   // lowercase username if it's an e-mail address (#1484473)
T 589   if (strpos($user, '@'))
590     $user = strtolower($user);
026d68 591
4e17e6 592   // query if user already registered
532844 593   $sql_result = $DB->query(
T 594     "SELECT user_id, username, language, preferences
595      FROM ".get_table_name('users')."
596      WHERE  mail_host=? AND (username=? OR alias=?)",
597     $host,
598     $user,
599     $user);
4e17e6 600
42b113 601   // user already registered -> overwrite username
4e17e6 602   if ($sql_arr = $DB->fetch_assoc($sql_result))
T 603     {
604     $user_id = $sql_arr['user_id'];
42b113 605     $user = $sql_arr['username'];
T 606     }
977a29 607
42b113 608   // exit if IMAP login failed
T 609   if (!($imap_login  = $IMAP->connect($host, $user, $pass, $imap_port, $imap_ssl)))
610     return FALSE;
611
612   // user already registered
613   if ($user_id && !empty($sql_arr))
614     {
4e17e6 615     // get user prefs
T 616     if (strlen($sql_arr['preferences']))
617       {
618       $user_prefs = unserialize($sql_arr['preferences']);
619       $_SESSION['user_prefs'] = $user_prefs;
620       array_merge($CONFIG, $user_prefs);
621       }
622
f3b659 623
4e17e6 624     // set user specific language
T 625     if (strlen($sql_arr['language']))
626       $sess_user_lang = $_SESSION['user_lang'] = $sql_arr['language'];
f3b659 627       
4e17e6 628     // update user's record
d7cb77 629     $DB->query("UPDATE ".get_table_name('users')."
107bde 630                 SET    last_login=".$DB->now()."
d7cb77 631                 WHERE  user_id=?",
S 632                 $user_id);
4e17e6 633     }
T 634   // create new system user
635   else if ($CONFIG['auto_create_user'])
636     {
637     $user_id = rcmail_create_user($user, $host);
638     }
352aef 639   else
T 640     {
641     raise_error(array(
642       'code' => 600,
643       'type' => 'php',
644       'file' => "config/main.inc.php",
645       'message' => "Acces denied for new user $user. 'auto_create_user' is disabled"
646       ), true, false);
647     }
4e17e6 648
T 649   if ($user_id)
650     {
651     $_SESSION['user_id']   = $user_id;
652     $_SESSION['imap_host'] = $host;
7902df 653     $_SESSION['imap_port'] = $imap_port;
T 654     $_SESSION['imap_ssl']  = $imap_ssl;
4e17e6 655     $_SESSION['username']  = $user;
f3b659 656     $_SESSION['user_lang'] = $sess_user_lang;
4e17e6 657     $_SESSION['password']  = encrypt_passwd($pass);
ff52be 658     $_SESSION['login_time'] = mktime();
4e17e6 659
fa4cd2 660     // force reloading complete list of subscribed mailboxes
T 661     rcmail_set_imap_prop();
4e17e6 662     $IMAP->clear_cache('mailboxes');
b2ff3d 663
T 664     if ($CONFIG['create_default_folders'])
665         $IMAP->create_default_folders();
4e17e6 666
T 667     return TRUE;
668     }
669
670   return FALSE;
671   }
672
673
6d969b 674 /**
T 675  * Create new entry in users and identities table
676  *
677  * @param string User name
678  * @param string IMAP host
679  * @return mixed New user ID or False on failure
680  */
4e17e6 681 function rcmail_create_user($user, $host)
95609c 682 {
4e17e6 683   global $DB, $CONFIG, $IMAP;
977a29 684
T 685   $user_email = '';
686
687   // try to resolve user in virtusertable
0d1dd7 688   if (!empty($CONFIG['virtuser_file']) && !strpos($user, '@'))
977a29 689     $user_email = rcmail_user2email($user);
T 690
d7cb77 691   $DB->query("INSERT INTO ".get_table_name('users')."
977a29 692               (created, last_login, username, mail_host, alias, language)
107bde 693               VALUES (".$DB->now().", ".$DB->now().", ?, ?, ?, ?)",
3cf664 694               strip_newlines($user),
T 695               strip_newlines($host),
696               strip_newlines($user_email),
697               $_SESSION['user_lang']);
4e17e6 698
1cded8 699   if ($user_id = $DB->insert_id(get_sequence_name('users')))
95609c 700   {
8cb245 701     $mail_domain = rcmail_mail_domain($host);
977a29 702    
T 703     if ($user_email=='')
0d1dd7 704       $user_email = strpos($user, '@') ? $user : sprintf('%s@%s', $user, $mail_domain);
977a29 705
52c1f2 706     $user_name = $user!=$user_email ? $user : '';
977a29 707
f88d41 708     // try to resolve the e-mail address from the virtuser table
3cf664 709     if (!empty($CONFIG['virtuser_query']) &&
fe89f8 710         ($sql_result = $DB->query(preg_replace('/%u/', $DB->escapeSimple($user), $CONFIG['virtuser_query']))) &&
e61145 711         ($DB->num_rows()>0))
95609c 712     {
e61145 713       while ($sql_arr = $DB->fetch_array($sql_result))
95609c 714       {
e61145 715         $DB->query("INSERT INTO ".get_table_name('identities')."
S 716                    (user_id, del, standard, name, email)
717                    VALUES (?, 0, 1, ?, ?)",
718                    $user_id,
3cf664 719                    strip_newlines($user_name),
e61145 720                    preg_replace('/^@/', $user . '@', $sql_arr[0]));
95609c 721       }
T 722     }
e61145 723     else
95609c 724     {
e61145 725       // also create new identity records
S 726       $DB->query("INSERT INTO ".get_table_name('identities')."
727                   (user_id, del, standard, name, email)
728                   VALUES (?, 0, 1, ?, ?)",
729                   $user_id,
3cf664 730                   strip_newlines($user_name),
T 731                   strip_newlines($user_email));
95609c 732     }
4e17e6 733                        
T 734     // get existing mailboxes
735     $a_mailboxes = $IMAP->list_mailboxes();
95609c 736   }
42b113 737   else
95609c 738   {
T 739     raise_error(array(
740       'code' => 500,
741       'type' => 'php',
742       'line' => __LINE__,
743       'file' => __FILE__,
744       'message' => "Failed to create new user"), TRUE, FALSE);
745   }
4e17e6 746     
T 747   return $user_id;
95609c 748 }
4e17e6 749
T 750
6d969b 751 /**
T 752  * Load virtuser table in array
753  *
754  * @return array Virtuser table entries
755  */
977a29 756 function rcmail_getvirtualfile()
T 757   {
758   global $CONFIG;
759   if (empty($CONFIG['virtuser_file']) || !is_file($CONFIG['virtuser_file']))
760     return FALSE;
761   
762   // read file 
763   $a_lines = file($CONFIG['virtuser_file']);
764   return $a_lines;
765   }
766
767
6d969b 768 /**
T 769  * Find matches of the given pattern in virtuser table
770  * 
771  * @param string Regular expression to search for
772  * @return array Matching entries
773  */
977a29 774 function rcmail_findinvirtual($pattern)
T 775   {
776   $result = array();
777   $virtual = rcmail_getvirtualfile();
778   if ($virtual==FALSE)
779     return $result;
780
781   // check each line for matches
782   foreach ($virtual as $line)
783     {
784     $line = trim($line);
785     if (empty($line) || $line{0}=='#')
786       continue;
787       
788     if (eregi($pattern, $line))
789       $result[] = $line;
790     }
791
792   return $result;
793   }
794
795
6d969b 796 /**
T 797  * Resolve username using a virtuser table
798  *
799  * @param string E-mail address to resolve
800  * @return string Resolved IMAP username
801  */
977a29 802 function rcmail_email2user($email)
T 803   {
804   $user = $email;
805   $r = rcmail_findinvirtual("^$email");
806
807   for ($i=0; $i<count($r); $i++)
808     {
809     $data = $r[$i];
810     $arr = preg_split('/\s+/', $data);
811     if(count($arr)>0)
812       {
813       $user = trim($arr[count($arr)-1]);
814       break;
815       }
816     }
817
818   return $user;
819   }
820
821
6d969b 822 /**
T 823  * Resolve e-mail address from virtuser table
824  *
825  * @param string User name
826  * @return string Resolved e-mail address
827  */
977a29 828 function rcmail_user2email($user)
T 829   {
830   $email = "";
831   $r = rcmail_findinvirtual("$user$");
832
833   for ($i=0; $i<count($r); $i++)
834     {
835     $data=$r[$i];
836     $arr = preg_split('/\s+/', $data);
837     if (count($arr)>0)
838       {
839       $email = trim($arr[0]);
840       break;
841       }
842     }
843
844   return $email;
845   } 
846
847
6d969b 848 /**
T 849  * Write the given user prefs to the user's record
850  *
851  * @param mixed User prefs to save
852  * @return boolean True on success, False on failure
853  */
86f172 854 function rcmail_save_user_prefs($a_user_prefs)
T 855   {
856   global $DB, $CONFIG, $sess_user_lang;
857   
5e8045 858   // merge (partial) prefs array with existing settings
T 859   $a_user_prefs += (array)$_SESSION['user_prefs'];
860   
86f172 861   $DB->query("UPDATE ".get_table_name('users')."
T 862               SET    preferences=?,
863                      language=?
864               WHERE  user_id=?",
865               serialize($a_user_prefs),
866               $sess_user_lang,
867               $_SESSION['user_id']);
868
869   if ($DB->affected_rows())
870     {
871     $_SESSION['user_prefs'] = $a_user_prefs;  
872     $CONFIG = array_merge($CONFIG, $a_user_prefs);
873     return TRUE;
874     }
875     
876   return FALSE;
877   }
878
879
6d969b 880 /**
T 881  * Overwrite action variable
882  *
883  * @param string New action value
884  */
10a699 885 function rcmail_overwrite_action($action)
T 886   {
f11541 887   global $OUTPUT;
10a699 888   $GLOBALS['_action'] = $action;
f11541 889   $OUTPUT->set_env('action', $action);
10a699 890   }
T 891
892
41bece 893 /**
T 894  * Compose an URL for a specific action
895  *
896  * @param string  Request action
897  * @param array   More URL parameters
898  * @param string  Request task (omit if the same)
899  * @return The application URL
900  */
901 function rcmail_url($action, $p=array(), $task=null)
f11541 902 {
T 903   global $MAIN_TASKS, $COMM_PATH;
904   $qstring = '';
905   $base = $COMM_PATH;
906   
907   if ($task && in_array($task, $MAIN_TASKS))
908     $base = ereg_replace('_task=[a-z]+', '_task='.$task, $COMM_PATH);
909   
910   if (is_array($p))
911     foreach ($p as $key => $val)
912       $qstring .= '&'.urlencode($key).'='.urlencode($val);
913   
914   return $base . ($action ? '&_action='.$action : '') . $qstring;
915 }
916
917
918 // @deprecated
4647e1 919 function show_message($message, $type='notice', $vars=NULL)
4e17e6 920   {
f11541 921   global $OUTPUT;
T 922   $OUTPUT->show_message($message, $type, $vars);
4e17e6 923   }
T 924
925
6d969b 926 /**
T 927  * Encrypt IMAP password using DES encryption
928  *
929  * @param string Password to encrypt
930  * @return string Encryprted string
931  */
4e17e6 932 function encrypt_passwd($pass)
T 933   {
bac7d1 934   $cypher = des(get_des_key(), $pass, 1, 0, NULL);
4e17e6 935   return base64_encode($cypher);
T 936   }
937
938
6d969b 939 /**
T 940  * Decrypt IMAP password using DES encryption
941  *
942  * @param string Encrypted password
943  * @return string Plain password
944  */
4e17e6 945 function decrypt_passwd($cypher)
T 946   {
bac7d1 947   $pass = des(get_des_key(), base64_decode($cypher), 0, 0, NULL);
T 948   return preg_replace('/\x00/', '', $pass);
949   }
950
951
6d969b 952 /**
T 953  * Return a 24 byte key for the DES encryption
954  *
955  * @return string DES encryption key
956  */
bac7d1 957 function get_des_key()
T 958   {
959   $key = !empty($GLOBALS['CONFIG']['des_key']) ? $GLOBALS['CONFIG']['des_key'] : 'rcmail?24BitPwDkeyF**ECB';
960   $len = strlen($key);
961   
962   // make sure the key is exactly 24 chars long
963   if ($len<24)
964     $key .= str_repeat('_', 24-$len);
965   else if ($len>24)
966     substr($key, 0, 24);
967   
968   return $key;
4e17e6 969   }
T 970
971
6d969b 972 /**
T 973  * Read directory program/localization and return a list of available languages
974  *
975  * @return array List of available localizations
976  */
9fee0e 977 function rcube_list_languages()
T 978   {
979   global $CONFIG, $INSTALL_PATH;
980   static $sa_languages = array();
981
982   if (!sizeof($sa_languages))
983     {
c8c1e0 984     @include($INSTALL_PATH.'program/localization/index.inc');
9fee0e 985
c8c1e0 986     if ($dh = @opendir($INSTALL_PATH.'program/localization'))
9fee0e 987       {
T 988       while (($name = readdir($dh)) !== false)
989         {
c8c1e0 990         if ($name{0}=='.' || !is_dir($INSTALL_PATH.'program/localization/'.$name))
9fee0e 991           continue;
T 992
993         if ($label = $rcube_languages[$name])
994           $sa_languages[$name] = $label ? $label : $name;
995         }
996       closedir($dh);
997       }
998     }
999   return $sa_languages;
1000   }
1001
4e17e6 1002
6d969b 1003 /**
T 1004  * Add a localized label to the client environment
1005  */
10a699 1006 function rcube_add_label()
T 1007   {
f11541 1008   global $OUTPUT;
10a699 1009   
T 1010   $arg_list = func_get_args();
1011   foreach ($arg_list as $i => $name)
f11541 1012     $OUTPUT->command('add_label', $name, rcube_label($name));
1cded8 1013   }
T 1014
1015
6d969b 1016 /**
T 1017  * Garbage collector function for temp files.
1018  * Remove temp files older than two days
1019  */
70d4b9 1020 function rcmail_temp_gc()
1cded8 1021   {
70d4b9 1022   $tmp = unslashify($CONFIG['temp_dir']);
T 1023   $expire = mktime() - 172800;  // expire in 48 hours
1cded8 1024
70d4b9 1025   if ($dir = opendir($tmp))
1cded8 1026     {
70d4b9 1027     while (($fname = readdir($dir)) !== false)
T 1028       {
1029       if ($fname{0} == '.')
1030         continue;
1031
1032       if (filemtime($tmp.'/'.$fname) < $expire)
1033         @unlink($tmp.'/'.$fname);
1034       }
1035
1036     closedir($dir);
1037     }
1cded8 1038   }
T 1039
1040
6d969b 1041 /**
T 1042  * Garbage collector for cache entries.
1043  * Remove all expired message cache records
1044  */
cc9570 1045 function rcmail_message_cache_gc()
T 1046   {
1047   global $DB, $CONFIG;
1048   
1049   // no cache lifetime configured
1050   if (empty($CONFIG['message_cache_lifetime']))
1051     return;
1052   
1053   // get target timestamp
1054   $ts = get_offset_time($CONFIG['message_cache_lifetime'], -1);
1055   
1056   $DB->query("DELETE FROM ".get_table_name('messages')."
1057              WHERE  created < ".$DB->fromunixtime($ts));
1058   }
1059
1cded8 1060
2bca6e 1061 /**
T 1062  * Convert a string from one charset to another.
1063  * Uses mbstring and iconv functions if possible
1064  *
1065  * @param  string Input string
1066  * @param  string Suspected charset of the input string
f11541 1067  * @param  string Target charset to convert to; defaults to RCMAIL_CHARSET
2bca6e 1068  * @return Converted string
T 1069  */
3f9edb 1070 function rcube_charset_convert($str, $from, $to=NULL)
0af7e8 1071   {
5f56a5 1072   global $MBSTRING;
f88d41 1073
83dbb7 1074   $from = strtoupper($from);
f11541 1075   $to = $to==NULL ? strtoupper(RCMAIL_CHARSET) : strtoupper($to);
f88d41 1076
2f2f15 1077   if ($from==$to || $str=='' || empty($from))
3f9edb 1078     return $str;
5f56a5 1079
b8e65c 1080   // convert charset using iconv module  
T 1081   if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7')
0393da 1082     {
T 1083     $iconv_map = array('KS_C_5601-1987' => 'EUC-KR');
1084     return iconv(($iconv_map[$from] ? $iconv_map[$from] : $from), ($iconv_map[$to] ? $iconv_map[$to] : $to) . "//IGNORE", $str);
1085     }
5f56a5 1086
b8e65c 1087   // convert charset using mbstring module  
88f66e 1088   if ($MBSTRING)
b8e65c 1089     {
0393da 1090     $mb_map = array('UTF-7' => 'UTF7-IMAP', 'KS_C_5601-1987' => 'EUC-KR');
88f66e 1091     
5f56a5 1092     // return if convert succeeded
0393da 1093     if (($out = mb_convert_encoding($str, ($mb_map[$to] ? $mb_map[$to] : $to), ($mb_map[$from] ? $mb_map[$from] : $from))) != '')
5f56a5 1094       return $out;
83dbb7 1095     }
58e360 1096
T 1097   $conv = new utf8();
1098
83dbb7 1099   // convert string to UTF-8
T 1100   if ($from=='UTF-7')
c8c1a3 1101     $str = utf7_to_utf8($str);
4d4264 1102   else if (($from=='ISO-8859-1') && function_exists('utf8_encode'))
83dbb7 1103     $str = utf8_encode($str);
T 1104   else if ($from!='UTF-8')
1105     {
58e360 1106     $conv->loadCharset($from);
83dbb7 1107     $str = $conv->strToUtf8($str);
T 1108     }
0af7e8 1109
3f9edb 1110   // encode string for output
83dbb7 1111   if ($to=='UTF-7')
c8c1a3 1112     return utf8_to_utf7($str);
83dbb7 1113   else if ($to=='ISO-8859-1' && function_exists('utf8_decode'))
T 1114     return utf8_decode($str);
1115   else if ($to!='UTF-8')
1116     {
58e360 1117     $conv->loadCharset($to);
83dbb7 1118     return $conv->utf8ToStr($str);
T 1119     }
3f9edb 1120
83dbb7 1121   // return UTF-8 string
3f9edb 1122   return $str;
0af7e8 1123   }
3f9edb 1124
0af7e8 1125
2bca6e 1126 /**
T 1127  * Replacing specials characters to a specific encoding type
1128  *
1129  * @param  string  Input string
1130  * @param  string  Encoding type: text|html|xml|js|url
1131  * @param  string  Replace mode for tags: show|replace|remove
1132  * @param  boolean Convert newlines
1133  * @return The quoted string
1134  */
1cded8 1135 function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
T 1136   {
3f9edb 1137   global $OUTPUT_TYPE, $OUTPUT;
2bca6e 1138   static $html_encode_arr, $js_rep_table, $xml_rep_table;
1cded8 1139
T 1140   if (!$enctype)
1141     $enctype = $GLOBALS['OUTPUT_TYPE'];
1142
1143   // encode for plaintext
1144   if ($enctype=='text')
1145     return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str);
1146
1147   // encode for HTML output
1148   if ($enctype=='html')
1149     {
1150     if (!$html_encode_arr)
1151       {
0af7e8 1152       $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);        
1cded8 1153       unset($html_encode_arr['?']);
T 1154       }
1155
1156     $ltpos = strpos($str, '<');
1157     $encode_arr = $html_encode_arr;
1158
1159     // don't replace quotes and html tags
1160     if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false)
1161       {
1162       unset($encode_arr['"']);
1163       unset($encode_arr['<']);
1164       unset($encode_arr['>']);
10c92b 1165       unset($encode_arr['&']);
1cded8 1166       }
T 1167     else if ($mode=='remove')
1168       $str = strip_tags($str);
674a0f 1169     
T 1170     // avoid douple quotation of &
e6a406 1171     $out = preg_replace('/&amp;([a-z]{2,5}|#[0-9]{2,4});/', '&\\1;', strtr($str, $encode_arr));
0af7e8 1172       
1cded8 1173     return $newlines ? nl2br($out) : $out;
T 1174     }
1175
1176   if ($enctype=='url')
1177     return rawurlencode($str);
1178
2bca6e 1179   // if the replace tables for XML and JS are not yet defined
1cded8 1180   if (!$js_rep_table)
T 1181     {
f91a49 1182     $js_rep_table = $xml_rep_table = array();
88375f 1183     $xml_rep_table['&'] = '&amp;';
1cded8 1184
T 1185     for ($c=160; $c<256; $c++)  // can be increased to support more charsets
1186       {
1187       $xml_rep_table[Chr($c)] = "&#$c;";
1188       
3f9edb 1189       if ($OUTPUT->get_charset()=='ISO-8859-1')
74ae88 1190         $js_rep_table[Chr($c)] = sprintf("\\u%04x", $c);
1cded8 1191       }
T 1192
1193     $xml_rep_table['"'] = '&quot;';
1194     }
1195
2bca6e 1196   // encode for XML
1cded8 1197   if ($enctype=='xml')
T 1198     return strtr($str, $xml_rep_table);
1199
1200   // encode for javascript use
1201   if ($enctype=='js')
13c1af 1202     {
T 1203     if ($OUTPUT->get_charset()!='UTF-8')
f11541 1204       $str = rcube_charset_convert($str, RCMAIL_CHARSET, $OUTPUT->get_charset());
13c1af 1205       
f11541 1206     return preg_replace(array("/\r?\n/", "/\r/"), array('\n', '\n'), addslashes(strtr($str, $js_rep_table)));
13c1af 1207     }
1cded8 1208
T 1209   // no encoding given -> return original string
1210   return $str;
2bca6e 1211   }
f11541 1212   
2bca6e 1213 /**
6d969b 1214  * Quote a given string.
T 1215  * Shortcut function for rep_specialchars_output
1216  *
1217  * @return string HTML-quoted string
1218  * @see rep_specialchars_output()
2bca6e 1219  */
T 1220 function Q($str, $mode='strict', $newlines=TRUE)
1221   {
1222   return rep_specialchars_output($str, 'html', $mode, $newlines);
1223   }
1224
1225 /**
6d969b 1226  * Quote a given string for javascript output.
T 1227  * Shortcut function for rep_specialchars_output
1228  * 
1229  * @return string JS-quoted string
1230  * @see rep_specialchars_output()
2bca6e 1231  */
18e2a3 1232 function JQ($str)
2bca6e 1233   {
18e2a3 1234   return rep_specialchars_output($str, 'js');
10a699 1235   }
ea7c46 1236
T 1237
1238 /**
1239  * Read input value and convert it for internal use
1240  * Performs stripslashes() and charset conversion if necessary
1241  * 
1242  * @param  string   Field name to read
1243  * @param  int      Source to get value from (GPC)
1244  * @param  boolean  Allow HTML tags in field value
1245  * @param  string   Charset to convert into
1246  * @return string   Field value or NULL if not available
1247  */
1248 function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
1249   {
1250   global $OUTPUT;
1251   $value = NULL;
1252   
1253   if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
1254     $value = $_GET[$fname];
1255   else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
1256     $value = $_POST[$fname];
1257   else if ($source==RCUBE_INPUT_GPC)
1258     {
026d68 1259     if (isset($_POST[$fname]))
ea7c46 1260       $value = $_POST[$fname];
026d68 1261     else if (isset($_GET[$fname]))
T 1262       $value = $_GET[$fname];
ea7c46 1263     else if (isset($_COOKIE[$fname]))
T 1264       $value = $_COOKIE[$fname];
1265     }
1266   
1267   // strip slashes if magic_quotes enabled
1268   if ((bool)get_magic_quotes_gpc())
1269     $value = stripslashes($value);
1270
1271   // remove HTML tags if not allowed    
1272   if (!$allow_html)
1273     $value = strip_tags($value);
1274   
1275   // convert to internal charset
026d68 1276   if (is_object($OUTPUT))
T 1277     return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
1278   else
1279     return $value;
ea7c46 1280   }
T 1281
6d969b 1282
e34ae1 1283 /**
T 1284  * Remove single and double quotes from given string
6d969b 1285  *
T 1286  * @param string Input value
1287  * @return string Dequoted string
e34ae1 1288  */
T 1289 function strip_quotes($str)
1290 {
1291   return preg_replace('/[\'"]/', '', $str);
1292 }
10a699 1293
6d969b 1294
3cf664 1295 /**
T 1296  * Remove new lines characters from given string
6d969b 1297  *
T 1298  * @param string Input value
1299  * @return string Stripped string
3cf664 1300  */
T 1301 function strip_newlines($str)
1302 {
1303   return preg_replace('/[\r\n]/', '', $str);
f11541 1304 }
4e17e6 1305
T 1306
6d969b 1307 /**
T 1308  * Check if a specific template exists
1309  *
1310  * @param string Template name
1311  * @return boolean True if template exists
1312  */
4e17e6 1313 function template_exists($name)
T 1314   {
f11541 1315   global $CONFIG;
4e17e6 1316   $skin_path = $CONFIG['skin_path'];
T 1317
1318   // check template file
1319   return is_file("$skin_path/templates/$name.html");
1320   }
1321
1322
6d969b 1323 /**
T 1324  * Wrapper for rcmail_template::parse()
1325  * @deprecated
1326  */
f11541 1327 function parse_template($name='main', $exit=true)
4e17e6 1328   {
f11541 1329   $GLOBALS['OUTPUT']->parse($name, $exit);
4e17e6 1330   }
T 1331
1332
6d969b 1333 /**
T 1334  * Create a HTML table based on the given data
1335  *
1336  * @param  array  Named table attributes
1337  * @param  mixed  Table row data. Either a two-dimensional array or a valid SQL result set
1338  * @param  array  List of cols to show
1339  * @param  string Name of the identifier col
1340  * @return string HTML table code
1341  */
d1d2c4 1342 function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
4e17e6 1343   {
T 1344   global $DB;
1345   
1346   // allow the following attributes to be added to the <table> tag
1347   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
1348   
1349   $table = '<table' . $attrib_str . ">\n";
1350     
1351   // add table title
1352   $table .= "<thead><tr>\n";
1353
1354   foreach ($a_show_cols as $col)
2bca6e 1355     $table .= '<td class="'.$col.'">' . Q(rcube_label($col)) . "</td>\n";
4e17e6 1356
T 1357   $table .= "</tr></thead>\n<tbody>\n";
1358   
1359   $c = 0;
d1d2c4 1360   if (!is_array($table_data)) 
4e17e6 1361     {
d1d2c4 1362     while ($table_data && ($sql_arr = $DB->fetch_assoc($table_data)))
4e17e6 1363       {
d1d2c4 1364       $zebra_class = $c%2 ? 'even' : 'odd';
4e17e6 1365
d1d2c4 1366       $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $sql_arr[$id_col]);
S 1367
1368       // format each col
1369       foreach ($a_show_cols as $col)
1370         {
2bca6e 1371         $cont = Q($sql_arr[$col]);
T 1372         $table .= '<td class="'.$col.'">' . $cont . "</td>\n";
d1d2c4 1373         }
S 1374
1375       $table .= "</tr>\n";
1376       $c++;
1377       }
1378     }
1379   else 
1380     {
1381     foreach ($table_data as $row_data)
1382       {
1383       $zebra_class = $c%2 ? 'even' : 'odd';
1384
1385       $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $row_data[$id_col]);
1386
1387       // format each col
1388       foreach ($a_show_cols as $col)
1389         {
2bca6e 1390         $cont = Q($row_data[$col]);
T 1391         $table .= '<td class="'.$col.'">' . $cont . "</td>\n";
d1d2c4 1392         }
S 1393
1394       $table .= "</tr>\n";
1395       $c++;
1396       }
4e17e6 1397     }
T 1398
1399   // complete message table
1400   $table .= "</tbody></table>\n";
1401   
1402   return $table;
1403   }
1404
1405
a0109c 1406 /**
S 1407  * Create an edit field for inclusion on a form
1408  * 
1409  * @param string col field name
1410  * @param string value field value
1411  * @param array attrib HTML element attributes for field
1412  * @param string type HTML element type (default 'text')
1413  * @return string HTML field definition
1414  */
4e17e6 1415 function rcmail_get_edit_field($col, $value, $attrib, $type='text')
T 1416   {
1417   $fname = '_'.$col;
1418   $attrib['name'] = $fname;
1419   
1420   if ($type=='checkbox')
1421     {
1422     $attrib['value'] = '1';
1423     $input = new checkbox($attrib);
1424     }
1425   else if ($type=='textarea')
1426     {
1427     $attrib['cols'] = $attrib['size'];
1428     $input = new textarea($attrib);
1429     }
1430   else
1431     $input = new textfield($attrib);
1432
1433   // use value from post
597170 1434   if (!empty($_POST[$fname]))
4e17e6 1435     $value = $_POST[$fname];
T 1436
1437   $out = $input->show($value);
1438          
1439   return $out;
1440   }
1441
1442
6d969b 1443 /**
T 1444  * Return the mail domain configured for the given host
1445  *
1446  * @param string IMAP host
1447  * @return string Resolved SMTP host
1448  */
f11541 1449 function rcmail_mail_domain($host)
T 1450   {
1451   global $CONFIG;
1452
1453   $domain = $host;
1454   if (is_array($CONFIG['mail_domain']))
1455     {
1456     if (isset($CONFIG['mail_domain'][$host]))
1457       $domain = $CONFIG['mail_domain'][$host];
1458     }
1459   else if (!empty($CONFIG['mail_domain']))
1460     $domain = $CONFIG['mail_domain'];
1461
1462   return $domain;
1463   }
1464
1465
6d969b 1466 /**
97bd2c 1467  * Replace all css definitions with #container [def]
T 1468  *
1469  * @param string CSS source code
1470  * @param string Container ID to use as prefix
1471  * @return string Modified CSS source
1472  */
1473 function rcmail_mod_css_styles($source, $container_id, $base_url = '')
1474   {
1475   $a_css_values = array();
1476   $last_pos = 0;
1477
1478   // cut out all contents between { and }
1479   while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
1480   {
1481     $key = sizeof($a_css_values);
1482     $a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
1483     $source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
1484     $last_pos = $pos+2;
1485   }
1486
1487   // remove html commends and add #container to each tag selector.
1488   // also replace body definition because we also stripped off the <body> tag
1489   $styles = preg_replace(
1490     array(
1491       '/(^\s*<!--)|(-->\s*$)/',
1492       '/(^\s*|,\s*|\}\s*)([a-z0-9\._#][a-z0-9\.\-_]*)/im',
1493       '/@import\s+(url\()?[\'"]?([^\)\'"]+)[\'"]?(\))?/ime',
1494       '/<<str_replacement\[([0-9]+)\]>>/e',
1495       "/$container_id\s+body/i"
1496     ),
1497     array(
1498       '',
1499       "\\1#$container_id \\2",
1500       "sprintf(\"@import url('./bin/modcss.php?u=%s&c=%s')\", urlencode(make_absolute_url('\\2','$base_url')), urlencode($container_id))",
1501       "\$a_css_values[\\1]",
1502       "$container_id div.rcmBody"
1503     ),
1504     $source);
1505
1506   return $styles;
1507   }
1508
1509
1510 /**
6d969b 1511  * Compose a valid attribute string for HTML tags
T 1512  *
1513  * @param array Named tag attributes
1514  * @param array List of allowed attributes
1515  * @return string HTML formatted attribute string
1516  */
4e17e6 1517 function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
T 1518   {
1519   // allow the following attributes to be added to the <iframe> tag
1520   $attrib_str = '';
1521   foreach ($allowed_attribs as $a)
1522     if (isset($attrib[$a]))
fe79b1 1523       $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '&quot;', $attrib[$a]));
4e17e6 1524
T 1525   return $attrib_str;
1526   }
1527
1528
6d969b 1529 /**
T 1530  * Convert a HTML attribute string attributes to an associative array (name => value)
1531  *
1532  * @param string Input string
1533  * @return array Key-value pairs of parsed attributes
1534  */
fe79b1 1535 function parse_attrib_string($str)
T 1536   {
1537   $attrib = array();
b3ce79 1538   preg_match_all('/\s*([-_a-z]+)=(["\'])([^"]+)\2/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
fe79b1 1539
T 1540   // convert attributes to an associative array (name => value)
1541   if ($regs)
1542     foreach ($regs as $attr)
b3ce79 1543       $attrib[strtolower($attr[1])] = $attr[3];
fe79b1 1544
T 1545   return $attrib;
1546   }
1547
4e17e6 1548
6d969b 1549 /**
T 1550  * Convert the given date to a human readable form
1551  * This uses the date formatting properties from config
1552  *
1553  * @param mixed Date representation (string or timestamp)
1554  * @param string Date format to use
1555  * @return string Formatted date string
1556  */
4e17e6 1557 function format_date($date, $format=NULL)
T 1558   {
1559   global $CONFIG, $sess_user_lang;
1560   
4647e1 1561   $ts = NULL;
T 1562   
4e17e6 1563   if (is_numeric($date))
T 1564     $ts = $date;
b076a4 1565   else if (!empty($date))
4647e1 1566     $ts = @strtotime($date);
T 1567     
1568   if (empty($ts))
b076a4 1569     return '';
4647e1 1570    
T 1571   // get user's timezone
1572   $tz = $CONFIG['timezone'];
1573   if ($CONFIG['dst_active'])
1574     $tz++;
4e17e6 1575
T 1576   // convert time to user's timezone
4647e1 1577   $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
4e17e6 1578   
T 1579   // get current timestamp in user's timezone
1580   $now = time();  // local time
1581   $now -= (int)date('Z'); // make GMT time
4647e1 1582   $now += ($tz * 3600); // user's time
c45eb5 1583   $now_date = getdate($now);
4e17e6 1584
749b07 1585   $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
T 1586   $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
4e17e6 1587
30233b 1588   // define date format depending on current time  
87b280 1589   if ($CONFIG['prettydate'] && !$format && $timestamp > $today_limit && $timestamp < $now)
8c8b2a 1590     return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp));
87b280 1591   else if ($CONFIG['prettydate'] && !$format && $timestamp > $week_limit && $timestamp < $now)
4e17e6 1592     $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
T 1593   else if (!$format)
1594     $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
1595
1596
1597   // parse format string manually in order to provide localized weekday and month names
1598   // an alternative would be to convert the date() format string to fit with strftime()
1599   $out = '';
1600   for($i=0; $i<strlen($format); $i++)
1601     {
1602     if ($format{$i}=='\\')  // skip escape chars
1603       continue;
1604     
1605     // write char "as-is"
1606     if ($format{$i}==' ' || $format{$i-1}=='\\')
1607       $out .= $format{$i};
1608     // weekday (short)
1609     else if ($format{$i}=='D')
1610       $out .= rcube_label(strtolower(date('D', $timestamp)));
1611     // weekday long
1612     else if ($format{$i}=='l')
1613       $out .= rcube_label(strtolower(date('l', $timestamp)));
1614     // month name (short)
1615     else if ($format{$i}=='M')
1616       $out .= rcube_label(strtolower(date('M', $timestamp)));
1617     // month name (long)
1618     else if ($format{$i}=='F')
1619       $out .= rcube_label(strtolower(date('F', $timestamp)));
1620     else
1621       $out .= date($format{$i}, $timestamp);
1622     }
1623   
1624   return $out;
1625   }
1626
1627
6d969b 1628 /**
T 1629  * Compose a valid representaion of name and e-mail address
1630  *
1631  * @param string E-mail address
1632  * @param string Person name
1633  * @return string Formatted string
1634  */
f11541 1635 function format_email_recipient($email, $name='')
T 1636   {
1637   if ($name && $name != $email)
1638     return sprintf('%s <%s>', strpos($name, ",") ? '"'.$name.'"' : $name, $email);
1639   else
1640     return $email;
1641   }
1642
1643
1644
c39957 1645 /****** debugging functions ********/
T 1646
1647
1648 /**
1649  * Print or write debug messages
1650  *
1651  * @param mixed Debug message or data
1652  */
1653 function console($msg)
1654   {
8d4bcd 1655   if (!is_string($msg))
c39957 1656     $msg = var_export($msg, true);
T 1657
1658   if (!($GLOBALS['CONFIG']['debug_level'] & 4))
1659     write_log('console', $msg);
1660   else if ($GLOBALS['REMOTE_REQUEST'])
1661     print "/*\n $msg \n*/\n";
1662   else
1663     {
1664     print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>';
1665     print $msg;
1666     print "</pre></div>\n";
1667     }
1668   }
1669
1670
1671 /**
1672  * Append a line to a logfile in the logs directory.
1673  * Date will be added automatically to the line.
1674  *
1675  * @param $name Name of logfile
1676  * @param $line Line to append
1677  */
1678 function write_log($name, $line)
1679   {
2c6337 1680   global $CONFIG, $INSTALL_PATH;
e170b4 1681
T 1682   if (!is_string($line))
1683     $line = var_export($line, true);
c39957 1684   
T 1685   $log_entry = sprintf("[%s]: %s\n",
1686                  date("d-M-Y H:i:s O", mktime()),
1687                  $line);
1688                  
1689   if (empty($CONFIG['log_dir']))
1690     $CONFIG['log_dir'] = $INSTALL_PATH.'logs';
1691       
1692   // try to open specific log file for writing
1693   if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a'))    
1694     {
1695     fwrite($fp, $log_entry);
1696     fclose($fp);
1697     }
1698   }
1699
cc9570 1700
6d969b 1701 /**
T 1702  * @access private
1703  */
15a9d1 1704 function rcube_timer()
T 1705   {
1706   list($usec, $sec) = explode(" ", microtime());
1707   return ((float)$usec + (float)$sec);
1708   }
1709   
1710
6d969b 1711 /**
T 1712  * @access private
1713  */
15a9d1 1714 function rcube_print_time($timer, $label='Timer')
T 1715   {
1716   static $print_count = 0;
1717   
1718   $print_count++;
1719   $now = rcube_timer();
1720   $diff = $now-$timer;
1721   
1722   if (empty($label))
1723     $label = 'Timer '.$print_count;
1724   
1725   console(sprintf("%s: %0.4f sec", $label, $diff));
1726   }
1727
93be5b 1728
6d969b 1729 /**
T 1730  * Return the mailboxlist in HTML
1731  *
1732  * @param array Named parameters
1733  * @return string HTML code for the gui object
1734  */
93be5b 1735 function rcmail_mailbox_list($attrib)
S 1736   {
1737   global $IMAP, $CONFIG, $OUTPUT, $COMM_PATH;
1738   static $s_added_script = FALSE;
1739   static $a_mailboxes;
1740
1741   // add some labels to client
1742   rcube_add_label('purgefolderconfirm');
1743   rcube_add_label('deletemessagesconfirm');
1744   
1745 // $mboxlist_start = rcube_timer();
1746   
1747   $type = $attrib['type'] ? $attrib['type'] : 'ul';
1748   $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
1749                                   array('style', 'class', 'id');
1750                                   
1751   if ($type=='ul' && !$attrib['id'])
1752     $attrib['id'] = 'rcmboxlist';
1753
1754   // allow the following attributes to be added to the <ul> tag
1755   $attrib_str = create_attrib_string($attrib, $add_attrib);
1756  
1757   $out = '<' . $type . $attrib_str . ">\n";
1758   
1759   // add no-selection option
1760   if ($type=='select' && $attrib['noselection'])
1761     $out .= sprintf('<option value="0">%s</option>'."\n",
1762                     rcube_label($attrib['noselection']));
1763   
1764   // get mailbox list
1765   $mbox_name = $IMAP->get_mailbox_name();
1766   
1767   // for these mailboxes we have localized labels
1768   $special_mailboxes = array('inbox', 'sent', 'drafts', 'trash', 'junk');
1769
1770
1771   // build the folders tree
1772   if (empty($a_mailboxes))
1773     {
1774     // get mailbox list
1775     $a_folders = $IMAP->list_mailboxes();
1776     $delimiter = $IMAP->get_hierarchy_delimiter();
1777     $a_mailboxes = array();
1778
1779 // rcube_print_time($mboxlist_start, 'list_mailboxes()');
1780
1781     foreach ($a_folders as $folder)
1782       rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
1783     }
1784
1785 // var_dump($a_mailboxes);
1786
1787   if ($type=='select')
1788     $out .= rcmail_render_folder_tree_select($a_mailboxes, $special_mailboxes, $mbox_name, $attrib['maxlength']);
1789    else
1790     $out .= rcmail_render_folder_tree_html($a_mailboxes, $special_mailboxes, $mbox_name, $attrib['maxlength']);
1791
1792 // rcube_print_time($mboxlist_start, 'render_folder_tree()');
1793
1794
1795   if ($type=='ul')
1796     $OUTPUT->add_gui_object('mailboxlist', $attrib['id']);
1797
1798   return $out . "</$type>";
1799   }
1800
1801
1802
1803
6d969b 1804 /**
T 1805  * Create a hierarchical array of the mailbox list
1806  * @access private
1807  */
93be5b 1808 function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
S 1809   {
1810   $pos = strpos($folder, $delm);
1811   if ($pos !== false)
1812     {
1813     $subFolders = substr($folder, $pos+1);
1814     $currentFolder = substr($folder, 0, $pos);
1815     }
1816   else
1817     {
1818     $subFolders = false;
1819     $currentFolder = $folder;
1820     }
1821
1822   $path .= $currentFolder;
1823
1824   if (!isset($arrFolders[$currentFolder]))
1825     {
1826     $arrFolders[$currentFolder] = array('id' => $path,
1827                                         'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
1828                                         'folders' => array());
1829     }
1830
1831   if (!empty($subFolders))
1832     rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
1833   }
1834   
1835
6d969b 1836 /**
T 1837  * Return html for a structured list &lt;ul&gt; for the mailbox tree
1838  * @access private
1839  */
93be5b 1840 function rcmail_render_folder_tree_html(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
S 1841   {
1842   global $COMM_PATH, $IMAP, $CONFIG, $OUTPUT;
1843
1844   $idx = 0;
1845   $out = '';
1846   foreach ($arrFolders as $key => $folder)
1847     {
1848     $zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
1849     $title = '';
1850
1851     $folder_lc = strtolower($folder['id']);
1852     if (in_array($folder_lc, $special))
1853       $foldername = rcube_label($folder_lc);
1854     else
1855       {
1856       $foldername = $folder['name'];
1857
1858       // shorten the folder name to a given length
1859       if ($maxlength && $maxlength>1)
1860         {
1861         $fname = abbrevate_string($foldername, $maxlength);
1862         if ($fname != $foldername)
1863           $title = ' title="'.Q($foldername).'"';
1864         $foldername = $fname;
1865         }
1866       }
1867
1868     // add unread message count display
1869     if ($unread_count = $IMAP->messagecount($folder['id'], 'RECENT', ($folder['id']==$mbox_name)))
1870       $foldername .= sprintf(' (%d)', $unread_count);
1871
1872     // make folder name safe for ids and class names
1873     $folder_id = preg_replace('/[^A-Za-z0-9\-_]/', '', $folder['id']);
1874     $class_name = preg_replace('/[^a-z0-9\-_]/', '', $folder_lc);
1875
1876     // set special class for Sent, Drafts, Trash and Junk
1877     if ($folder['id']==$CONFIG['sent_mbox'])
1878       $class_name = 'sent';
1879     else if ($folder['id']==$CONFIG['drafts_mbox'])
1880       $class_name = 'drafts';
1881     else if ($folder['id']==$CONFIG['trash_mbox'])
1882       $class_name = 'trash';
1883     else if ($folder['id']==$CONFIG['junk_mbox'])
1884       $class_name = 'junk';
1885
1886     $js_name = htmlspecialchars(JQ($folder['id']));
1887     $out .= sprintf('<li id="rcmli%s" class="mailbox %s %s%s%s"><a href="%s"'.
1888                     ' onclick="return %s.command(\'list\',\'%s\',this)"'.
1889                     ' onmouseover="return %s.focus_folder(\'%s\')"' .
1890                     ' onmouseout="return %s.unfocus_folder(\'%s\')"' .
1891                     ' onmouseup="return %s.folder_mouse_up(\'%s\')"%s>%s</a>',
1892                     $folder_id,
1893                     $class_name,
1894                     $zebra_class,
1895                     $unread_count ? ' unread' : '',
1896                     $folder['id']==$mbox_name ? ' selected' : '',
1897                     Q(rcmail_url('', array('_mbox' => $folder['id']))),
1898                     JS_OBJECT_NAME,
1899                     $js_name,
1900                     JS_OBJECT_NAME,
1901                     $js_name,
1902                     JS_OBJECT_NAME,
1903                     $js_name,
1904                     JS_OBJECT_NAME,
1905                     $js_name,
1906                     $title,
1907                     Q($foldername));
1908
1909     if (!empty($folder['folders']))
1910       $out .= "\n<ul>\n" . rcmail_render_folder_tree_html($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1) . "</ul>\n";
1911
1912     $out .= "</li>\n";
1913     $idx++;
1914     }
1915
1916   return $out;
1917   }
1918
1919
6d969b 1920 /**
T 1921  * Return html for a flat list <select> for the mailbox tree
1922  * @access private
1923  */
93be5b 1924 function rcmail_render_folder_tree_select(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
S 1925   {
1926   global $IMAP, $OUTPUT;
1927
1928   $idx = 0;
1929   $out = '';
1930   foreach ($arrFolders as $key=>$folder)
1931     {
1932     $folder_lc = strtolower($folder['id']);
1933     if (in_array($folder_lc, $special))
1934       $foldername = rcube_label($folder_lc);
1935     else
1936       {
1937       $foldername = $folder['name'];
1938       
1939       // shorten the folder name to a given length
1940       if ($maxlength && $maxlength>1)
1941         $foldername = abbrevate_string($foldername, $maxlength);
1942       }
1943
1944     $out .= sprintf('<option value="%s">%s%s</option>'."\n",
1945                     htmlspecialchars($folder['id']),
1946                     str_repeat('&nbsp;', $nestLevel*4),
1947                     Q($foldername));
1948
1949     if (!empty($folder['folders']))
1950       $out .= rcmail_render_folder_tree_select($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1);
1951
1952     $idx++;
1953     }
1954
1955   return $out;
1956   }
1957
d1d2c4 1958 ?>