Aleksander Machniak
2012-06-30 7c8fd8031038e7958ef4dbb059e86decd6fefa28
commit | author | age
197601 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/include/rcmail.php                                            |
6  |                                                                       |
e019f2 7  | This file is part of the Roundcube Webmail client                     |
0c2596 8  | Copyright (C) 2008-2012, The Roundcube Dev Team                       |
A 9  | Copyright (C) 2011-2012, Kolab Systems AG                             |
7fe381 10  |                                                                       |
T 11  | Licensed under the GNU General Public License version 3 or            |
12  | any later version with exceptions for skins & plugins.                |
13  | See the README file for a full license statement.                     |
197601 14  |                                                                       |
T 15  | PURPOSE:                                                              |
16  |   Application class providing core functions and holding              |
17  |   instances of all 'global' objects like db- and imap-connections     |
18  +-----------------------------------------------------------------------+
19  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
1aceb9 20  | Author: Aleksander Machniak <alec@alec.pl>                            |
197601 21  +-----------------------------------------------------------------------+
T 22 */
23
24
25 /**
e019f2 26  * Application class of Roundcube Webmail
197601 27  * implemented as singleton
T 28  *
29  * @package Core
30  */
0c2596 31 class rcmail extends rcube
197601 32 {
5c461b 33   /**
A 34    * Main tasks.
35    *
36    * @var array
37    */
677e1f 38   static public $main_tasks = array('mail','settings','addressbook','login','logout','utils','dummy');
5c461b 39
A 40   /**
41    * Current task.
42    *
43    * @var string
44    */
9b94eb 45   public $task;
5c461b 46
A 47   /**
48    * Current action.
49    *
50    * @var string
51    */
197601 52   public $action = '';
T 53   public $comm_path = './';
677e1f 54
0501b6 55   private $address_books = array();
68d2d5 56   private $action_map = array();
677e1f 57
A 58
1aceb9 59   const JS_OBJECT_NAME = 'rcmail';
A 60
7c8fd8 61   const ERROR_STORAGE          = -2;
AM 62   const ERROR_INVALID_REQUEST  = 1;
63   const ERROR_INVALID_HOST     = 2;
64   const ERROR_COOKIES_DISABLED = 3;
65
66
197601 67   /**
T 68    * This implements the 'singleton' design pattern
69    *
5c461b 70    * @return rcmail The one and only instance
197601 71    */
T 72   static function get_instance()
73   {
0c2596 74     if (!self::$instance || !is_a(self::$instance, 'rcmail')) {
197601 75       self::$instance = new rcmail();
T 76       self::$instance->startup();  // init AFTER object was linked with self::$instance
77     }
78
79     return self::$instance;
80   }
b62a0d 81
A 82
197601 83   /**
T 84    * Initial startup function
85    * to register session, create database and imap connections
86    */
0c2596 87   protected function startup()
197601 88   {
0c2596 89     $this->init(self::INIT_WITH_DB | self::INIT_WITH_PLUGINS);
a90ad2 90
929a50 91     // start session
A 92     $this->session_init();
197601 93
T 94     // create user object
95     $this->set_user(new rcube_user($_SESSION['user_id']));
929a50 96
A 97     // configure session (after user config merge!)
98     $this->session_configure();
197601 99
9b94eb 100     // set task and action properties
1aceb9 101     $this->set_task(rcube_utils::get_input_value('_task', rcube_utils::INPUT_GPC));
A 102     $this->action = asciiwords(rcube_utils::get_input_value('_action', rcube_utils::INPUT_GPC));
9b94eb 103
197601 104     // reset some session parameters when changing task
677e1f 105     if ($this->task != 'utils') {
A 106       if ($this->session && $_SESSION['task'] != $this->task)
107         $this->session->remove('page');
108       // set current task to session
109       $_SESSION['task'] = $this->task;
110     }
197601 111
48bc52 112     // init output class
A 113     if (!empty($_REQUEST['_remote']))
929a50 114       $GLOBALS['OUTPUT'] = $this->json_init();
48bc52 115     else
A 116       $GLOBALS['OUTPUT'] = $this->load_gui(!empty($_REQUEST['_framed']));
117
0c2596 118     // load plugins
A 119     $this->plugins->init($this, $this->task);
120     $this->plugins->load_plugins((array)$this->config->get('plugins', array()), array('filesystem_attachments', 'jqueryui'));
197601 121   }
b62a0d 122
A 123
197601 124   /**
T 125    * Setter for application task
126    *
127    * @param string Task to set
128    */
129   public function set_task($task)
130   {
1c932d 131     $task = asciiwords($task);
9b94eb 132
A 133     if ($this->user && $this->user->ID)
c3be8e 134       $task = !$task ? 'mail' : $task;
9b94eb 135     else
A 136       $task = 'login';
137
138     $this->task = $task;
1c932d 139     $this->comm_path = $this->url(array('task' => $this->task));
b62a0d 140
197601 141     if ($this->output)
1c932d 142       $this->output->set_env('task', $this->task);
197601 143   }
b62a0d 144
A 145
197601 146   /**
T 147    * Setter for system user object
148    *
5c461b 149    * @param rcube_user Current user instance
197601 150    */
T 151   public function set_user($user)
152   {
153     if (is_object($user)) {
154       $this->user = $user;
b62a0d 155
197601 156       // overwrite config with user preferences
b545d3 157       $this->config->set_user_prefs((array)$this->user->get_prefs());
197601 158     }
b62a0d 159
c8ae24 160     $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language']));
531abb 161
197601 162     // set localization
e80f50 163     setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8');
14de18 164
b62a0d 165     // workaround for http://bugs.php.net/bug.php?id=18556
A 166     if (in_array($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ')))
167       setlocale(LC_CTYPE, 'en_US' . '.utf8');
197601 168   }
b62a0d 169
A 170
197601 171   /**
ade8e1 172    * Return instance of the internal address book class
T 173    *
3704b7 174    * @param string  Address book identifier
ade8e1 175    * @param boolean True if the address book needs to be writeable
7f7ed2 176    *
5c461b 177    * @return rcube_contacts Address book object
ade8e1 178    */
T 179   public function get_address_book($id, $writeable = false)
180   {
b896b1 181     $contacts    = null;
ade8e1 182     $ldap_config = (array)$this->config->get('ldap_public');
b896b1 183     $abook_type  = strtolower($this->config->get('address_book_type'));
cc97ea 184
f03d89 185     // 'sql' is the alias for '0' used by autocomplete
A 186     if ($id == 'sql')
187         $id = '0';
188
0501b6 189     // use existing instance
9a835c 190     if (isset($this->address_books[$id]) && is_object($this->address_books[$id])
A 191       && is_a($this->address_books[$id], 'rcube_addressbook')
192       && (!$writeable || !$this->address_books[$id]->readonly)
193     ) {
0501b6 194       $contacts = $this->address_books[$id];
T 195     }
cc97ea 196     else if ($id && $ldap_config[$id]) {
c321a9 197       $contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['storage_host']));
cc97ea 198     }
T 199     else if ($id === '0') {
0c2596 200       $contacts = new rcube_contacts($this->db, $this->get_user_id());
ade8e1 201     }
b896b1 202     else {
A 203       $plugin = $this->plugins->exec_hook('addressbook_get', array('id' => $id, 'writeable' => $writeable));
204
205       // plugin returned instance of a rcube_addressbook
206       if ($plugin['instance'] instanceof rcube_addressbook) {
207         $contacts = $plugin['instance'];
208       }
7f7ed2 209       // get first source from the list
5ed119 210       else if (!$id) {
7f7ed2 211         $source = reset($this->get_address_sources($writeable));
A 212         if (!empty($source)) {
213           $contacts = $this->get_address_book($source['id']);
214           if ($contacts)
215             $id = $source['id'];
5ed119 216         }
ade8e1 217       }
5ed119 218     }
A 219
220     if (!$contacts) {
0c2596 221       self::raise_error(array(
782d85 222         'code' => 700, 'type' => 'php',
5ed119 223         'file' => __FILE__, 'line' => __LINE__,
A 224         'message' => "Addressbook source ($id) not found!"),
225         true, true);
ade8e1 226     }
457373 227
438753 228     // set configured sort order
T 229     if ($sort_col = $this->config->get('addressbook_sort_col'))
230         $contacts->set_sort_order($sort_col);
231
457373 232     // add to the 'books' array for shutdown function
14b342 233     $this->address_books[$id] = $contacts;
b62a0d 234
ade8e1 235     return $contacts;
T 236   }
3704b7 237
A 238
239   /**
240    * Return address books list
241    *
242    * @param boolean True if the address book needs to be writeable
5c9d1f 243    *
3704b7 244    * @return array  Address books array
A 245    */
246   public function get_address_sources($writeable = false)
247   {
248     $abook_type = strtolower($this->config->get('address_book_type'));
7fdb9d 249     $ldap_config = $this->config->get('ldap_public');
A 250     $autocomplete = (array) $this->config->get('autocomplete_addressbooks');
3704b7 251     $list = array();
A 252
253     // We are using the DB address book
254     if ($abook_type != 'ldap') {
0501b6 255       if (!isset($this->address_books['0']))
0c2596 256         $this->address_books['0'] = new rcube_contacts($this->db, $this->get_user_id());
3704b7 257       $list['0'] = array(
5c9d1f 258         'id'       => '0',
0c2596 259         'name'     => $this->gettext('personaladrbook'),
5c9d1f 260         'groups'   => $this->address_books['0']->groups,
8c263e 261         'readonly' => $this->address_books['0']->readonly,
d06e57 262         'autocomplete' => in_array('sql', $autocomplete),
T 263         'undelete' => $this->address_books['0']->undelete && $this->config->get('undo_timeout'),
3704b7 264       );
A 265     }
266
7fdb9d 267     if ($ldap_config) {
A 268       $ldap_config = (array) $ldap_config;
08b7b6 269       foreach ($ldap_config as $id => $prop) {
A 270         // handle misconfiguration
271         if (empty($prop) || !is_array($prop)) {
272           continue;
273         }
3704b7 274         $list[$id] = array(
5c9d1f 275           'id'       => $id,
A 276           'name'     => $prop['name'],
277           'groups'   => is_array($prop['groups']),
a61bbb 278           'readonly' => !$prop['writable'],
5c9d1f 279           'hidden'   => $prop['hidden'],
A 280           'autocomplete' => in_array($id, $autocomplete)
3704b7 281         );
08b7b6 282       }
3704b7 283     }
A 284
e6ce00 285     $plugin = $this->plugins->exec_hook('addressbooks_list', array('sources' => $list));
3704b7 286     $list = $plugin['sources'];
A 287
0501b6 288     foreach ($list as $idx => $item) {
T 289       // register source for shutdown function
290       if (!is_object($this->address_books[$item['id']]))
291         $this->address_books[$item['id']] = $item;
292       // remove from list if not writeable as requested
293       if ($writeable && $item['readonly'])
c0297f 294           unset($list[$idx]);
3704b7 295     }
8c263e 296
3704b7 297     return $list;
A 298   }
b62a0d 299
A 300
ade8e1 301   /**
197601 302    * Init output object for GUI and add common scripts.
T 303    * This will instantiate a rcmail_template object and set
304    * environment vars according to the current session and configuration
0ece58 305    *
T 306    * @param boolean True if this request is loaded in a (i)frame
0c2596 307    * @return rcube_output_html Reference to HTML output object
197601 308    */
T 309   public function load_gui($framed = false)
310   {
311     // init output page
0c2596 312     if (!($this->output instanceof rcube_output_html))
A 313       $this->output = new rcube_output_html($this->task, $framed);
197601 314
95d90f 315     // set keep-alive/check-recent interval
bf67d6 316     if ($this->session && ($keep_alive = $this->session->get_keep_alive())) {
929a50 317       $this->output->set_env('keep_alive', $keep_alive);
95d90f 318     }
197601 319
T 320     if ($framed) {
321       $this->comm_path .= '&_framed=1';
322       $this->output->set_env('framed', true);
323     }
324
325     $this->output->set_env('task', $this->task);
326     $this->output->set_env('action', $this->action);
327     $this->output->set_env('comm_path', $this->comm_path);
79c45f 328     $this->output->set_charset(RCMAIL_CHARSET);
197601 329
7f5a84 330     // add some basic labels to client
110360 331     $this->output->add_label('loading', 'servererror', 'requesttimedout');
b62a0d 332
197601 333     return $this->output;
T 334   }
b62a0d 335
A 336
197601 337   /**
T 338    * Create an output object for JSON responses
0ece58 339    *
0c2596 340    * @return rcube_output_json Reference to JSON output object
197601 341    */
929a50 342   public function json_init()
197601 343   {
0c2596 344     if (!($this->output instanceof rcube_output_json))
A 345       $this->output = new rcube_output_json($this->task);
b62a0d 346
197601 347     return $this->output;
929a50 348   }
A 349
350
351   /**
352    * Create session object and start the session.
353    */
354   public function session_init()
355   {
963a10 356     parent::session_init();
929a50 357
A 358     // set initial session vars
cf2da2 359     if (!$_SESSION['user_id'])
929a50 360       $_SESSION['temp'] = true;
40d246 361
T 362     // restore skin selection after logout
363     if ($_SESSION['temp'] && !empty($_SESSION['skin']))
364       $this->config->set('skin', $_SESSION['skin']);
197601 365   }
T 366
367
368   /**
c321a9 369    * Perfom login to the mail server and to the webmail service.
197601 370    * This will also create a new user entry if auto_create_user is configured.
T 371    *
c321a9 372    * @param string Mail storage (IMAP) user name
T 373    * @param string Mail storage (IMAP) password
374    * @param string Mail storage (IMAP) host
7c8fd8 375    * @param bool   Enables cookie check
fdff34 376    *
197601 377    * @return boolean True on success, False on failure
T 378    */
7c8fd8 379   function login($username, $pass, $host = null, $cookiecheck = false)
197601 380   {
7c8fd8 381     $this->login_error = null;
AM 382
fdff34 383     if (empty($username)) {
7c8fd8 384       return false;
AM 385     }
386
387     if ($cookiecheck && empty($_COOKIE)) {
388       $this->login_error = self::ERROR_COOKIES_DISABLED;
fdff34 389       return false;
A 390     }
391
197601 392     $config = $this->config->all();
T 393
394     if (!$host)
395       $host = $config['default_host'];
396
397     // Validate that selected host is in the list of configured hosts
398     if (is_array($config['default_host'])) {
399       $allowed = false;
400       foreach ($config['default_host'] as $key => $host_allowed) {
401         if (!is_numeric($key))
402           $host_allowed = $key;
403         if ($host == $host_allowed) {
404           $allowed = true;
405           break;
406         }
407       }
7c8fd8 408       if (!$allowed) {
AM 409         $host = null;
197601 410       }
7c8fd8 411     }
AM 412     else if (!empty($config['default_host']) && $host != rcube_utils::parse_host($config['default_host'])) {
413       $host = null;
414     }
415
416     if (!$host) {
417       $this->login_error = self::ERROR_INVALID_HOST;
197601 418       return false;
7c8fd8 419     }
197601 420
T 421     // parse $host URL
422     $a_host = parse_url($host);
423     if ($a_host['host']) {
424       $host = $a_host['host'];
c321a9 425       $ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
e99991 426       if (!empty($a_host['port']))
c321a9 427         $port = $a_host['port'];
T 428       else if ($ssl && $ssl != 'tls' && (!$config['default_port'] || $config['default_port'] == 143))
429         $port = 993;
197601 430     }
b62a0d 431
c321a9 432     if (!$port) {
T 433         $port = $config['default_port'];
434     }
197601 435
b62a0d 436     /* Modify username with domain if required
197601 437        Inspired by Marco <P0L0_notspam_binware.org>
T 438     */
439     // Check if we need to add domain
c16fab 440     if (!empty($config['username_domain']) && strpos($username, '@') === false) {
197601 441       if (is_array($config['username_domain']) && isset($config['username_domain'][$host]))
1aceb9 442         $username .= '@'.rcube_utils::parse_host($config['username_domain'][$host], $host);
197601 443       else if (is_string($config['username_domain']))
1aceb9 444         $username .= '@'.rcube_utils::parse_host($config['username_domain'], $host);
197601 445     }
T 446
c321a9 447     // Convert username to lowercase. If storage backend
e17553 448     // is case-insensitive we need to store always the same username (#1487113)
A 449     if ($config['login_lc']) {
450       $username = mb_strtolower($username);
451     }
452
942069 453     // try to resolve email address from virtuser table
e17553 454     if (strpos($username, '@') && ($virtuser = rcube_user::email2user($username))) {
A 455       $username = $virtuser;
456     }
197601 457
f1adbf 458     // Here we need IDNA ASCII
A 459     // Only rcube_contacts class is using domain names in Unicode
1aceb9 460     $host = rcube_utils::idn_to_ascii($host);
f1adbf 461     if (strpos($username, '@')) {
8f94b1 462       // lowercase domain name
A 463       list($local, $domain) = explode('@', $username);
464       $username = $local . '@' . mb_strtolower($domain);
1aceb9 465       $username = rcube_utils::idn_to_ascii($username);
f1adbf 466     }
A 467
197601 468     // user already registered -> overwrite username
T 469     if ($user = rcube_user::query($username, $host))
470       $username = $user->data['username'];
471
1aceb9 472     $storage = $this->get_storage();
48bc52 473
c321a9 474     // try to log in
1aceb9 475     if (!($login = $storage->connect($host, $username, $pass, $port, $ssl))) {
f1adbf 476       // try with lowercase
6d94ab 477       $username_lc = mb_strtolower($username);
e17553 478       if ($username_lc != $username) {
A 479         // try to find user record again -> overwrite username
480         if (!$user && ($user = rcube_user::query($username_lc, $host)))
481           $username_lc = $user->data['username'];
482
1aceb9 483         if ($login = $storage->connect($host, $username_lc, $pass, $port, $ssl))
e17553 484           $username = $username_lc;
A 485       }
6d94ab 486     }
T 487
c321a9 488     // exit if login failed
T 489     if (!$login) {
197601 490       return false;
c321a9 491     }
197601 492
T 493     // user already registered -> update user's record
494     if (is_object($user)) {
d08333 495       // update last login timestamp
197601 496       $user->touch();
T 497     }
498     // create new system user
499     else if ($config['auto_create_user']) {
500       if ($created = rcube_user::create($username, $host)) {
501         $user = $created;
502       }
f879f4 503       else {
0c2596 504         self::raise_error(array(
782d85 505           'code' => 620, 'type' => 'php',
6d94ab 506           'file' => __FILE__, 'line' => __LINE__,
f879f4 507           'message' => "Failed to create a user record. Maybe aborted by a plugin?"
10eedb 508           ), true, false);
f879f4 509       }
197601 510     }
T 511     else {
0c2596 512       self::raise_error(array(
782d85 513         'code' => 621, 'type' => 'php',
10eedb 514         'file' => __FILE__, 'line' => __LINE__,
782d85 515         'message' => "Access denied for new user $username. 'auto_create_user' is disabled"
197601 516         ), true, false);
T 517     }
518
519     // login succeeded
520     if (is_object($user) && $user->ID) {
f53750 521       // Configure environment
197601 522       $this->set_user($user);
c321a9 523       $this->set_storage_prop();
88ca38 524       $this->session_configure();
f53750 525
A 526       // fix some old settings according to namespace prefix
527       $this->fix_namespace_settings($user);
528
529       // create default folders on first login
530       if ($config['create_default_folders'] && (!empty($created) || empty($user->data['last_login']))) {
1aceb9 531         $storage->create_default_folders();
f53750 532       }
197601 533
T 534       // set session vars
c321a9 535       $_SESSION['user_id']      = $user->ID;
T 536       $_SESSION['username']     = $user->data['username'];
537       $_SESSION['storage_host'] = $host;
538       $_SESSION['storage_port'] = $port;
539       $_SESSION['storage_ssl']  = $ssl;
540       $_SESSION['password']     = $this->encrypt($pass);
6a8b4c 541       $_SESSION['login_time']   = time();
f53750 542
b62a0d 543       if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_')
c8ae24 544         $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
65082b 545       if (isset($_REQUEST['_dstactive']) && $_REQUEST['_dstactive'] != '_default_')
T 546         $_SESSION['dst_active'] = intval($_REQUEST['_dstactive']);
197601 547
T 548       // force reloading complete list of subscribed mailboxes
1aceb9 549       $storage->clear_cache('mailboxes', true);
197601 550
T 551       return true;
552     }
553
554     return false;
555   }
556
557
7c8fd8 558     /**
AM 559      * Returns error code of last login operation
560      *
561      * @return int Error code
562      */
563     public function login_error()
564     {
565         if ($this->login_error) {
566             return $this->login_error;
567         }
568
569         if ($this->storage && $this->storage->get_error_code() < -1) {
570             return self::ERROR_STORAGE;
571         }
572     }
573
574
197601 575   /**
1854c4 576    * Auto-select IMAP host based on the posted login information
T 577    *
578    * @return string Selected IMAP host
579    */
580   public function autoselect_host()
581   {
582     $default_host = $this->config->get('default_host');
257f88 583     $host = null;
b62a0d 584
257f88 585     if (is_array($default_host)) {
1aceb9 586       $post_host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST);
45dd7c 587       $post_user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST);
AM 588
589       list($user, $domain) = explode('@', $post_user);
b62a0d 590
257f88 591       // direct match in default_host array
T 592       if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) {
593         $host = $post_host;
594       }
595       // try to select host by mail domain
45dd7c 596       else if (!empty($domain)) {
c321a9 597         foreach ($default_host as $storage_host => $mail_domains) {
48f04d 598           if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) {
c321a9 599             $host = $storage_host;
48f04d 600             break;
T 601           }
602           else if (stripos($storage_host, $domain) !== false || stripos(strval($mail_domains), $domain) !== false) {
603             $host = is_numeric($storage_host) ? $mail_domains : $storage_host;
1854c4 604             break;
T 605           }
606         }
607       }
608
48f04d 609       // take the first entry if $host is still not set
257f88 610       if (empty($host)) {
48f04d 611         list($key, $val) = each($default_host);
T 612         $host = is_numeric($key) ? $val : $key;
257f88 613       }
T 614     }
615     else if (empty($default_host)) {
1aceb9 616       $host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST);
1854c4 617     }
eec34e 618     else
1aceb9 619       $host = rcube_utils::parse_host($default_host);
1854c4 620
T 621     return $host;
622   }
623
624
625   /**
626    * Destroy session data and remove cookie
627    */
628   public function kill_session()
629   {
e6ce00 630     $this->plugins->exec_hook('session_destroy');
b62a0d 631
cf2da2 632     $this->session->kill();
40d246 633     $_SESSION = array('language' => $this->user->language, 'temp' => true, 'skin' => $this->config->get('skin'));
1854c4 634     $this->user->reset();
T 635   }
636
637
638   /**
639    * Do server side actions on logout
640    */
641   public function logout_actions()
642   {
1aceb9 643     $config  = $this->config->all();
A 644     $storage = $this->get_storage();
1854c4 645
T 646     if ($config['logout_purge'] && !empty($config['trash_mbox'])) {
1aceb9 647       $storage->clear_folder($config['trash_mbox']);
1854c4 648     }
T 649
650     if ($config['logout_expunge']) {
1aceb9 651       $storage->expunge_folder('INBOX');
1854c4 652     }
40a186 653
A 654     // Try to save unsaved user preferences
655     if (!empty($_SESSION['preferences'])) {
656       $this->user->save_prefs(unserialize($_SESSION['preferences']));
657     }
fec2d8 658   }
T 659
660
661   /**
57f0c8 662    * Generate a unique token to be used in a form request
T 663    *
664    * @return string The request token
665    */
549933 666   public function get_request_token()
57f0c8 667   {
ec045b 668     $sess_id = $_COOKIE[ini_get('session.name')];
c9f2c4 669     if (!$sess_id) $sess_id = session_id();
1aceb9 670
A 671     $plugin = $this->plugins->exec_hook('request_token', array(
672         'value' => md5('RT' . $this->get_user_id() . $this->config->get('des_key') . $sess_id)));
673
ef27a6 674     return $plugin['value'];
57f0c8 675   }
b62a0d 676
A 677
57f0c8 678   /**
T 679    * Check if the current request contains a valid token
680    *
549933 681    * @param int Request method
57f0c8 682    * @return boolean True if request token is valid false if not
T 683    */
1aceb9 684   public function check_request($mode = rcube_utils::INPUT_POST)
57f0c8 685   {
1aceb9 686     $token = rcube_utils::get_input_value('_token', $mode);
ec045b 687     $sess_id = $_COOKIE[ini_get('session.name')];
T 688     return !empty($sess_id) && $token == $this->get_request_token();
57f0c8 689   }
b62a0d 690
A 691
57f0c8 692   /**
1854c4 693    * Create unique authorization hash
T 694    *
695    * @param string Session ID
696    * @param int Timestamp
697    * @return string The generated auth hash
698    */
699   private function get_auth_hash($sess_id, $ts)
700   {
701     $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s',
702       $sess_id,
703       $ts,
704       $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***',
705       $_SERVER['HTTP_USER_AGENT']);
706
707     if (function_exists('sha1'))
708       return sha1($auth_string);
709     else
710       return md5($auth_string);
711   }
712
564741 713
A 714   /**
e019f2 715    * Build a valid URL to this instance of Roundcube
c719f3 716    *
T 717    * @param mixed Either a string with the action or url parameters as key-value pairs
1aceb9 718    *
c719f3 719    * @return string Valid application URL
T 720    */
721   public function url($p)
722   {
723     if (!is_array($p))
fde466 724       $p = array('_action' => @func_get_arg(0));
b62a0d 725
1c932d 726     $task = $p['_task'] ? $p['_task'] : ($p['task'] ? $p['task'] : $this->task);
cc97ea 727     $p['_task'] = $task;
1038a6 728     unset($p['task']);
A 729
cf1777 730     $url = './';
T 731     $delm = '?';
77406b 732     foreach (array_reverse($p) as $key => $val) {
a7321e 733       if ($val !== '' && $val !== null) {
cc97ea 734         $par = $key[0] == '_' ? $key : '_'.$key;
cf1777 735         $url .= $delm.urlencode($par).'='.urlencode($val);
T 736         $delm = '&';
737       }
738     }
c719f3 739     return $url;
T 740   }
cefd1d 741
T 742
743   /**
0c2596 744    * Function to be executed in script shutdown
0501b6 745    */
0c2596 746   public function shutdown()
0501b6 747   {
0c2596 748     parent::shutdown();
0501b6 749
0c2596 750     foreach ($this->address_books as $book) {
A 751       if (is_object($book) && is_a($book, 'rcube_addressbook'))
752         $book->close();
0501b6 753     }
T 754
0c2596 755     // before closing the database connection, write session data
A 756     if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
757       session_write_close();
758     }
f53750 759
0c2596 760     // write performance stats to logs/console
A 761     if ($this->config->get('devel_mode')) {
762       if (function_exists('memory_get_usage'))
1aceb9 763         $mem = $this->show_bytes(memory_get_usage());
0c2596 764       if (function_exists('memory_get_peak_usage'))
1aceb9 765         $mem .= '/'.$this->show_bytes(memory_get_peak_usage());
0c2596 766
A 767       $log = $this->task . ($this->action ? '/'.$this->action : '') . ($mem ? " [$mem]" : '');
768       if (defined('RCMAIL_START'))
769         self::print_timer(RCMAIL_START, $log);
770       else
771         self::console($log);
772     }
cefd1d 773   }
68d2d5 774
A 775   /**
776    * Registers action aliases for current task
777    *
778    * @param array $map Alias-to-filename hash array
779    */
780   public function register_action_map($map)
781   {
782     if (is_array($map)) {
783       foreach ($map as $idx => $val) {
784         $this->action_map[$idx] = $val;
785       }
786     }
787   }
f53750 788
68d2d5 789   /**
A 790    * Returns current action filename
791    *
792    * @param array $map Alias-to-filename hash array
793    */
794   public function get_action_file()
795   {
796     if (!empty($this->action_map[$this->action])) {
797       return $this->action_map[$this->action];
798     }
799
800     return strtr($this->action, '-', '_') . '.inc';
801   }
802
d08333 803   /**
A 804    * Fixes some user preferences according to namespace handling change.
805    * Old Roundcube versions were using folder names with removed namespace prefix.
806    * Now we need to add the prefix on servers where personal namespace has prefix.
807    *
808    * @param rcube_user $user User object
809    */
810   private function fix_namespace_settings($user)
811   {
c321a9 812     $prefix     = $this->storage->get_namespace('prefix');
d08333 813     $prefix_len = strlen($prefix);
A 814
815     if (!$prefix_len)
816       return;
817
f53750 818     $prefs = $this->config->all();
A 819     if (!empty($prefs['namespace_fixed']))
d08333 820       return;
A 821
822     // Build namespace prefix regexp
c321a9 823     $ns     = $this->storage->get_namespace();
d08333 824     $regexp = array();
A 825
826     foreach ($ns as $entry) {
827       if (!empty($entry)) {
828         foreach ($entry as $item) {
829           if (strlen($item[0])) {
830             $regexp[] = preg_quote($item[0], '/');
831           }
832         }
833       }
834     }
835     $regexp = '/^('. implode('|', $regexp).')/';
836
837     // Fix preferences
838     $opts = array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox', 'archive_mbox');
839     foreach ($opts as $opt) {
840       if ($value = $prefs[$opt]) {
841         if ($value != 'INBOX' && !preg_match($regexp, $value)) {
842           $prefs[$opt] = $prefix.$value;
843         }
844       }
845     }
846
c321a9 847     if (!empty($prefs['default_folders'])) {
T 848       foreach ($prefs['default_folders'] as $idx => $name) {
d08333 849         if ($name != 'INBOX' && !preg_match($regexp, $name)) {
c321a9 850           $prefs['default_folders'][$idx] = $prefix.$name;
d08333 851         }
A 852       }
853     }
854
855     if (!empty($prefs['search_mods'])) {
856       $folders = array();
857       foreach ($prefs['search_mods'] as $idx => $value) {
858         if ($idx != 'INBOX' && $idx != '*' && !preg_match($regexp, $idx)) {
859           $idx = $prefix.$idx;
860         }
861         $folders[$idx] = $value;
862       }
863       $prefs['search_mods'] = $folders;
864     }
865
866     if (!empty($prefs['message_threading'])) {
867       $folders = array();
868       foreach ($prefs['message_threading'] as $idx => $value) {
869         if ($idx != 'INBOX' && !preg_match($regexp, $idx)) {
870           $idx = $prefix.$idx;
871         }
872         $folders[$prefix.$idx] = $value;
873       }
874       $prefs['message_threading'] = $folders;
875     }
876
877     if (!empty($prefs['collapsed_folders'])) {
878       $folders     = explode('&&', $prefs['collapsed_folders']);
879       $count       = count($folders);
880       $folders_str = '';
881
882       if ($count) {
883           $folders[0]        = substr($folders[0], 1);
884           $folders[$count-1] = substr($folders[$count-1], 0, -1);
885       }
886
887       foreach ($folders as $value) {
888         if ($value != 'INBOX' && !preg_match($regexp, $value)) {
889           $value = $prefix.$value;
890         }
891         $folders_str .= '&'.$value.'&';
892       }
893       $prefs['collapsed_folders'] = $folders_str;
894     }
895
896     $prefs['namespace_fixed'] = true;
897
898     // save updated preferences and reset imap settings (default folders)
899     $user->save_prefs($prefs);
c321a9 900     $this->set_storage_prop();
d08333 901   }
A 902
0c2596 903
A 904     /**
905      * Overwrite action variable
906      *
907      * @param string New action value
908      */
909     public function overwrite_action($action)
910     {
911         $this->action = $action;
912         $this->output->set_env('action', $action);
913     }
914
915
916     /**
917      * Send the given message using the configured method.
918      *
919      * @param object $message    Reference to Mail_MIME object
920      * @param string $from       Sender address string
921      * @param array  $mailto     Array of recipient address strings
922      * @param array  $smtp_error SMTP error array (reference)
923      * @param string $body_file  Location of file with saved message body (reference),
924      *                           used when delay_file_io is enabled
925      * @param array  $smtp_opts  SMTP options (e.g. DSN request)
926      *
927      * @return boolean Send status.
928      */
929     public function deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file = null, $smtp_opts = null)
930     {
931         $headers = $message->headers();
932
933         // send thru SMTP server using custom SMTP library
934         if ($this->config->get('smtp_server')) {
935             // generate list of recipients
936             $a_recipients = array($mailto);
937
938             if (strlen($headers['Cc']))
939                 $a_recipients[] = $headers['Cc'];
940             if (strlen($headers['Bcc']))
941                 $a_recipients[] = $headers['Bcc'];
942
943             // clean Bcc from header for recipients
944             $send_headers = $headers;
945             unset($send_headers['Bcc']);
946             // here too, it because txtHeaders() below use $message->_headers not only $send_headers
947             unset($message->_headers['Bcc']);
948
949             $smtp_headers = $message->txtHeaders($send_headers, true);
950
951             if ($message->getParam('delay_file_io')) {
952                 // use common temp dir
953                 $temp_dir = $this->config->get('temp_dir');
954                 $body_file = tempnam($temp_dir, 'rcmMsg');
955                 if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) {
956                     self::raise_error(array('code' => 650, 'type' => 'php',
957                         'file' => __FILE__, 'line' => __LINE__,
958                         'message' => "Could not create message: ".$mime_result->getMessage()),
959                         TRUE, FALSE);
960                     return false;
961                 }
962                 $msg_body = fopen($body_file, 'r');
963             }
964             else {
965                 $msg_body = $message->get();
966             }
967
968             // send message
969             if (!is_object($this->smtp)) {
970                 $this->smtp_init(true);
971             }
972
973             $sent = $this->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $smtp_opts);
974             $smtp_response = $this->smtp->get_response();
975             $smtp_error = $this->smtp->get_error();
976
977             // log error
978             if (!$sent) {
979                 self::raise_error(array('code' => 800, 'type' => 'smtp',
980                     'line' => __LINE__, 'file' => __FILE__,
981                     'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE);
982             }
983         }
984         // send mail using PHP's mail() function
985         else {
986             // unset some headers because they will be added by the mail() function
987             $headers_enc = $message->headers($headers);
988             $headers_php = $message->_headers;
989             unset($headers_php['To'], $headers_php['Subject']);
990
991             // reset stored headers and overwrite
992             $message->_headers = array();
993             $header_str = $message->txtHeaders($headers_php);
994
995             // #1485779
996             if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
997                 if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) {
998                     $headers_enc['To'] = implode(', ', $m[1]);
999                 }
1000             }
1001
1002             $msg_body = $message->get();
1003
1004             if (PEAR::isError($msg_body)) {
1005                 self::raise_error(array('code' => 650, 'type' => 'php',
1006                     'file' => __FILE__, 'line' => __LINE__,
1007                     'message' => "Could not create message: ".$msg_body->getMessage()),
1008                     TRUE, FALSE);
1009             }
1010             else {
1011                 $delim   = $this->config->header_delimiter();
1012                 $to      = $headers_enc['To'];
1013                 $subject = $headers_enc['Subject'];
1014                 $header_str = rtrim($header_str);
1015
1016                 if ($delim != "\r\n") {
1017                     $header_str = str_replace("\r\n", $delim, $header_str);
1018                     $msg_body   = str_replace("\r\n", $delim, $msg_body);
1019                     $to         = str_replace("\r\n", $delim, $to);
1020                     $subject    = str_replace("\r\n", $delim, $subject);
1021                 }
1022
1023                 if (ini_get('safe_mode'))
1024                     $sent = mail($to, $subject, $msg_body, $header_str);
1025                 else
1026                     $sent = mail($to, $subject, $msg_body, $header_str, "-f$from");
1027             }
1028         }
1029
1030         if ($sent) {
1031             $this->plugins->exec_hook('message_sent', array('headers' => $headers, 'body' => $msg_body));
1032
1033             // remove MDN headers after sending
1034             unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
1035
1036             // get all recipients
1037             if ($headers['Cc'])
1038                 $mailto .= $headers['Cc'];
1039             if ($headers['Bcc'])
1040                 $mailto .= $headers['Bcc'];
1041             if (preg_match_all('/<([^@]+@[^>]+)>/', $mailto, $m))
1042                 $mailto = implode(', ', array_unique($m[1]));
1043
1044             if ($this->config->get('smtp_log')) {
1045                 self::write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s",
1046                     $this->user->get_username(),
1047                     $_SERVER['REMOTE_ADDR'],
1048                     $mailto,
1049                     !empty($smtp_response) ? join('; ', $smtp_response) : ''));
1050             }
1051         }
1052
1053         if (is_resource($msg_body)) {
1054             fclose($msg_body);
1055         }
1056
1057         $message->_headers = array();
1058         $message->headers($headers);
1059
1060         return $sent;
1061     }
1062
1063
1064     /**
1065      * Unique Message-ID generator.
1066      *
1067      * @return string Message-ID
1068      */
1069     public function gen_message_id()
1070     {
1071         $local_part  = md5(uniqid('rcmail'.mt_rand(),true));
1072         $domain_part = $this->user->get_username('domain');
1073
1074         // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924)
1075         if (!preg_match('/\.[a-z]+$/i', $domain_part)) {
1076             foreach (array($_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']) as $host) {
1077                 $host = preg_replace('/:[0-9]+$/', '', $host);
1078                 if ($host && preg_match('/\.[a-z]+$/i', $host)) {
1079                     $domain_part = $host;
1080                 }
1081             }
1082         }
1083
1084         return sprintf('<%s@%s>', $local_part, $domain_part);
1085     }
1086
1087
1088     /**
1089      * Returns RFC2822 formatted current date in user's timezone
1090      *
1091      * @return string Date
1092      */
1093     public function user_date()
1094     {
1095         // get user's timezone
1096         try {
1097             $tz   = new DateTimeZone($this->config->get('timezone'));
1098             $date = new DateTime('now', $tz);
1099         }
1100         catch (Exception $e) {
1101             $date = new DateTime();
1102         }
1103
1104         return $date->format('r');
1105     }
1106
1107
1108     /**
1109      * Write login data (name, ID, IP address) to the 'userlogins' log file.
1110      */
1111     public function log_login()
1112     {
1113         if (!$this->config->get('log_logins')) {
1114             return;
1115         }
1116
1117         $user_name = $this->get_user_name();
1118         $user_id   = $this->get_user_id();
1119
1120         if (!$user_id) {
1121             return;
1122         }
1123
1124         self::write_log('userlogins',
1125             sprintf('Successful login for %s (ID: %d) from %s in session %s',
1aceb9 1126                 $user_name, $user_id, rcube_utils::remote_ip(), session_id()));
0c2596 1127     }
A 1128
1aceb9 1129
A 1130     /**
1131      * Create a HTML table based on the given data
1132      *
1133      * @param  array  Named table attributes
1134      * @param  mixed  Table row data. Either a two-dimensional array or a valid SQL result set
1135      * @param  array  List of cols to show
1136      * @param  string Name of the identifier col
1137      *
1138      * @return string HTML table code
1139      */
1140     public function table_output($attrib, $table_data, $a_show_cols, $id_col)
1141     {
1142         $table = new html_table(/*array('cols' => count($a_show_cols))*/);
1143
1144         // add table header
1145         if (!$attrib['noheader']) {
1146             foreach ($a_show_cols as $col) {
1147                 $table->add_header($col, $this->Q($this->gettext($col)));
1148             }
1149         }
1150
1151         if (!is_array($table_data)) {
1152             $db = $this->get_dbh();
1153             while ($table_data && ($sql_arr = $db->fetch_assoc($table_data))) {
1154                 $table->add_row(array('id' => 'rcmrow' . rcube_utils::html_identifier($sql_arr[$id_col])));
1155
1156                 // format each col
1157                 foreach ($a_show_cols as $col) {
1158                     $table->add($col, $this->Q($sql_arr[$col]));
1159                 }
1160             }
1161         }
1162         else {
1163             foreach ($table_data as $row_data) {
1164                 $class = !empty($row_data['class']) ? $row_data['class'] : '';
1165                 $rowid = 'rcmrow' . rcube_utils::html_identifier($row_data[$id_col]);
1166
1167                 $table->add_row(array('id' => $rowid, 'class' => $class));
1168
1169                 // format each col
1170                 foreach ($a_show_cols as $col) {
1171                     $table->add($col, $this->Q(is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col]));
1172                 }
1173             }
1174         }
1175
1176         return $table->show($attrib);
1177     }
1178
1179
1180     /**
1181      * Convert the given date to a human readable form
1182      * This uses the date formatting properties from config
1183      *
1184      * @param mixed  Date representation (string, timestamp or DateTime object)
1185      * @param string Date format to use
1186      * @param bool   Enables date convertion according to user timezone
1187      *
1188      * @return string Formatted date string
1189      */
1190     public function format_date($date, $format = null, $convert = true)
1191     {
1192         if (is_object($date) && is_a($date, 'DateTime')) {
1193             $timestamp = $date->format('U');
1194         }
1195         else {
1196             if (!empty($date)) {
1197                 $timestamp = rcube_strtotime($date);
1198             }
1199
1200             if (empty($timestamp)) {
1201                 return '';
1202             }
1203
1204             try {
1205                 $date = new DateTime("@".$timestamp);
1206             }
1207             catch (Exception $e) {
1208                 return '';
1209             }
1210         }
1211
1212         if ($convert) {
1213             try {
1214                 // convert to the right timezone
1215                 $stz = date_default_timezone_get();
1216                 $tz = new DateTimeZone($this->config->get('timezone'));
1217                 $date->setTimezone($tz);
1218                 date_default_timezone_set($tz->getName());
1219
1220                 $timestamp = $date->format('U');
1221             }
1222             catch (Exception $e) {
1223             }
1224         }
1225
1226         // define date format depending on current time
1227         if (!$format) {
1228             $now         = time();
1229             $now_date    = getdate($now);
1230             $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
1231             $week_limit  = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
1232             $pretty_date = $this->config->get('prettydate');
1233
1234             if ($pretty_date && $timestamp > $today_limit && $timestamp < $now) {
1235                 $format = $this->config->get('date_today', $this->config->get('time_format', 'H:i'));
1236                 $today  = true;
1237             }
1238             else if ($pretty_date && $timestamp > $week_limit && $timestamp < $now) {
1239                 $format = $this->config->get('date_short', 'D H:i');
1240             }
1241             else {
1242                 $format = $this->config->get('date_long', 'Y-m-d H:i');
1243             }
1244         }
1245
1246         // strftime() format
1247         if (preg_match('/%[a-z]+/i', $format)) {
1248             $format = strftime($format, $timestamp);
1249             if ($stz) {
1250                 date_default_timezone_set($stz);
1251             }
1252             return $today ? ($this->gettext('today') . ' ' . $format) : $format;
1253         }
1254
1255         // parse format string manually in order to provide localized weekday and month names
1256         // an alternative would be to convert the date() format string to fit with strftime()
1257         $out = '';
1258         for ($i=0; $i<strlen($format); $i++) {
1259             if ($format[$i] == "\\") {  // skip escape chars
1260                 continue;
1261             }
1262
1263             // write char "as-is"
1264             if ($format[$i] == ' ' || $format[$i-1] == "\\") {
1265                 $out .= $format[$i];
1266             }
1267             // weekday (short)
1268             else if ($format[$i] == 'D') {
1269                 $out .= $this->gettext(strtolower(date('D', $timestamp)));
1270             }
1271             // weekday long
1272             else if ($format[$i] == 'l') {
1273                 $out .= $this->gettext(strtolower(date('l', $timestamp)));
1274             }
1275             // month name (short)
1276             else if ($format[$i] == 'M') {
1277                 $out .= $this->gettext(strtolower(date('M', $timestamp)));
1278             }
1279             // month name (long)
1280             else if ($format[$i] == 'F') {
1281                 $out .= $this->gettext('long'.strtolower(date('M', $timestamp)));
1282             }
1283             else if ($format[$i] == 'x') {
1284                 $out .= strftime('%x %X', $timestamp);
1285             }
1286             else {
1287                 $out .= date($format[$i], $timestamp);
1288             }
1289         }
1290
1291         if ($today) {
1292             $label = $this->gettext('today');
1293             // replcae $ character with "Today" label (#1486120)
1294             if (strpos($out, '$') !== false) {
1295                 $out = preg_replace('/\$/', $label, $out, 1);
1296             }
1297             else {
1298                 $out = $label . ' ' . $out;
1299             }
1300         }
1301
1302         if ($stz) {
1303             date_default_timezone_set($stz);
1304         }
1305
1306         return $out;
1307     }
1308
1309
1310     /**
1311      * Return folders list in HTML
1312      *
1313      * @param array $attrib Named parameters
1314      *
1315      * @return string HTML code for the gui object
1316      */
1317     public function folder_list($attrib)
1318     {
1319         static $a_mailboxes;
1320
1321         $attrib += array('maxlength' => 100, 'realnames' => false, 'unreadwrap' => ' (%s)');
1322
1323         $rcmail  = rcmail::get_instance();
1324         $storage = $rcmail->get_storage();
1325
1326         // add some labels to client
1327         $rcmail->output->add_label('purgefolderconfirm', 'deletemessagesconfirm');
1328
1329         $type = $attrib['type'] ? $attrib['type'] : 'ul';
1330         unset($attrib['type']);
1331
1332         if ($type == 'ul' && !$attrib['id']) {
1333             $attrib['id'] = 'rcmboxlist';
1334         }
1335
1336         if (empty($attrib['folder_name'])) {
1337             $attrib['folder_name'] = '*';
1338         }
1339
1340         // get current folder
1341         $mbox_name = $storage->get_folder();
1342
1343         // build the folders tree
1344         if (empty($a_mailboxes)) {
1345             // get mailbox list
1346             $a_folders = $storage->list_folders_subscribed(
1347                 '', $attrib['folder_name'], $attrib['folder_filter']);
1348             $delimiter = $storage->get_hierarchy_delimiter();
1349             $a_mailboxes = array();
1350
1351             foreach ($a_folders as $folder) {
1352                 $rcmail->build_folder_tree($a_mailboxes, $folder, $delimiter);
1353             }
1354         }
1355
1356         // allow plugins to alter the folder tree or to localize folder names
1357         $hook = $rcmail->plugins->exec_hook('render_mailboxlist', array(
1358             'list'      => $a_mailboxes,
1359             'delimiter' => $delimiter,
1360             'type'      => $type,
1361             'attribs'   => $attrib,
1362         ));
1363
1364         $a_mailboxes = $hook['list'];
1365         $attrib      = $hook['attribs'];
1366
1367         if ($type == 'select') {
0a1dd5 1368             $attrib['is_escaped'] = true;
1aceb9 1369             $select = new html_select($attrib);
A 1370
1371             // add no-selection option
1372             if ($attrib['noselection']) {
0a1dd5 1373                 $select->add(html::quote($rcmail->gettext($attrib['noselection'])), '');
1aceb9 1374             }
A 1375
1376             $rcmail->render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']);
1377             $out = $select->show($attrib['default']);
1378         }
1379         else {
1380             $js_mailboxlist = array();
1381             $out = html::tag('ul', $attrib, $rcmail->render_folder_tree_html($a_mailboxes, $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib);
1382
1383             $rcmail->output->add_gui_object('mailboxlist', $attrib['id']);
1384             $rcmail->output->set_env('mailboxes', $js_mailboxlist);
1385             $rcmail->output->set_env('unreadwrap', $attrib['unreadwrap']);
1386             $rcmail->output->set_env('collapsed_folders', (string)$rcmail->config->get('collapsed_folders'));
1387         }
1388
1389         return $out;
1390     }
1391
1392
1393     /**
1394      * Return folders list as html_select object
1395      *
1396      * @param array $p  Named parameters
1397      *
1398      * @return html_select HTML drop-down object
1399      */
1400     public function folder_selector($p = array())
1401     {
0a1dd5 1402         $p += array('maxlength' => 100, 'realnames' => false, 'is_escaped' => true);
1aceb9 1403         $a_mailboxes = array();
A 1404         $storage = $this->get_storage();
1405
1406         if (empty($p['folder_name'])) {
1407             $p['folder_name'] = '*';
1408         }
1409
1410         if ($p['unsubscribed']) {
1411             $list = $storage->list_folders('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
1412         }
1413         else {
1414             $list = $storage->list_folders_subscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
1415         }
1416
1417         $delimiter = $storage->get_hierarchy_delimiter();
1418
1419         foreach ($list as $folder) {
1420             if (empty($p['exceptions']) || !in_array($folder, $p['exceptions'])) {
1421                 $this->build_folder_tree($a_mailboxes, $folder, $delimiter);
1422             }
1423         }
1424
1425         $select = new html_select($p);
1426
1427         if ($p['noselection']) {
0a1dd5 1428             $select->add(html::quote($p['noselection']), '');
1aceb9 1429         }
A 1430
1431         $this->render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p);
1432
1433         return $select;
1434     }
1435
1436
1437     /**
1438      * Create a hierarchical array of the mailbox list
1439      */
1440     public function build_folder_tree(&$arrFolders, $folder, $delm = '/', $path = '')
1441     {
1442         // Handle namespace prefix
1443         $prefix = '';
1444         if (!$path) {
1445             $n_folder = $folder;
1446             $folder = $this->storage->mod_folder($folder);
1447
1448             if ($n_folder != $folder) {
1449                 $prefix = substr($n_folder, 0, -strlen($folder));
1450             }
1451         }
1452
1453         $pos = strpos($folder, $delm);
1454
1455         if ($pos !== false) {
1456             $subFolders    = substr($folder, $pos+1);
1457             $currentFolder = substr($folder, 0, $pos);
1458
1459             // sometimes folder has a delimiter as the last character
1460             if (!strlen($subFolders)) {
1461                 $virtual = false;
1462             }
1463             else if (!isset($arrFolders[$currentFolder])) {
1464                 $virtual = true;
1465             }
1466             else {
1467                 $virtual = $arrFolders[$currentFolder]['virtual'];
1468             }
1469         }
1470         else {
1471             $subFolders    = false;
1472             $currentFolder = $folder;
1473             $virtual       = false;
1474         }
1475
1476         $path .= $prefix . $currentFolder;
1477
1478         if (!isset($arrFolders[$currentFolder])) {
1479             $arrFolders[$currentFolder] = array(
1480                 'id' => $path,
1481                 'name' => rcube_charset::convert($currentFolder, 'UTF7-IMAP'),
1482                 'virtual' => $virtual,
1483                 'folders' => array());
1484         }
1485         else {
1486             $arrFolders[$currentFolder]['virtual'] = $virtual;
1487         }
1488
1489         if (strlen($subFolders)) {
1490             $this->build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
1491         }
1492     }
1493
1494
1495     /**
1496      * Return html for a structured list &lt;ul&gt; for the mailbox tree
1497      */
1498     public function render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel = 0)
1499     {
1500         $maxlength = intval($attrib['maxlength']);
1501         $realnames = (bool)$attrib['realnames'];
1502         $msgcounts = $this->storage->get_cache('messagecount');
1503         $collapsed = $this->config->get('collapsed_folders');
1504
1505         $out = '';
1506         foreach ($arrFolders as $key => $folder) {
1507             $title        = null;
1508             $folder_class = $this->folder_classname($folder['id']);
1509             $is_collapsed = strpos($collapsed, '&'.rawurlencode($folder['id']).'&') !== false;
1510             $unread       = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
1511
1512             if ($folder_class && !$realnames) {
1513                 $foldername = $this->gettext($folder_class);
1514             }
1515             else {
1516                 $foldername = $folder['name'];
1517
1518                 // shorten the folder name to a given length
1519                 if ($maxlength && $maxlength > 1) {
1520                     $fname = abbreviate_string($foldername, $maxlength);
1521                     if ($fname != $foldername) {
1522                         $title = $foldername;
1523                     }
1524                     $foldername = $fname;
1525                 }
1526             }
1527
1528             // make folder name safe for ids and class names
1529             $folder_id = rcube_utils::html_identifier($folder['id'], true);
1530             $classes   = array('mailbox');
1531
1532             // set special class for Sent, Drafts, Trash and Junk
1533             if ($folder_class) {
1534                 $classes[] = $folder_class;
1535             }
1536
1537             if ($folder['id'] == $mbox_name) {
1538                 $classes[] = 'selected';
1539             }
1540
1541             if ($folder['virtual']) {
1542                 $classes[] = 'virtual';
1543             }
1544             else if ($unread) {
1545                 $classes[] = 'unread';
1546             }
1547
1548             $js_name = $this->JQ($folder['id']);
1549             $html_name = $this->Q($foldername) . ($unread ? html::span('unreadcount', sprintf($attrib['unreadwrap'], $unread)) : '');
1550             $link_attrib = $folder['virtual'] ? array() : array(
1551                 'href' => $this->url(array('_mbox' => $folder['id'])),
1552                 'onclick' => sprintf("return %s.command('list','%s',this)", rcmail::JS_OBJECT_NAME, $js_name),
1553                 'rel' => $folder['id'],
1554                 'title' => $title,
1555             );
1556
1557             $out .= html::tag('li', array(
1558                 'id' => "rcmli".$folder_id,
1559                 'class' => join(' ', $classes),
1560                 'noclose' => true),
1561                 html::a($link_attrib, $html_name) .
1562                 (!empty($folder['folders']) ? html::div(array(
1563                     'class' => ($is_collapsed ? 'collapsed' : 'expanded'),
1564                     'style' => "position:absolute",
1565                     'onclick' => sprintf("%s.command('collapse-folder', '%s')", rcmail::JS_OBJECT_NAME, $js_name)
1566                 ), '&nbsp;') : ''));
1567
1568             $jslist[$folder_id] = array(
1569                 'id'      => $folder['id'],
1570                 'name'    => $foldername,
1571                 'virtual' => $folder['virtual']
1572             );
1573
1574             if (!empty($folder['folders'])) {
1575                 $out .= html::tag('ul', array('style' => ($is_collapsed ? "display:none;" : null)),
1576                     $this->render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1));
1577             }
1578
1579             $out .= "</li>\n";
1580         }
1581
1582         return $out;
1583     }
1584
1585
1586     /**
1587      * Return html for a flat list <select> for the mailbox tree
1588      */
1589     public function render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames = false, $nestLevel = 0, $opts = array())
1590     {
1591         $out = '';
1592
1593         foreach ($arrFolders as $key => $folder) {
1594             // skip exceptions (and its subfolders)
1595             if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) {
1596                 continue;
1597             }
1598
1599             // skip folders in which it isn't possible to create subfolders
1600             if (!empty($opts['skip_noinferiors'])) {
1601                 $attrs = $this->storage->folder_attributes($folder['id']);
1602                 if ($attrs && in_array('\\Noinferiors', $attrs)) {
1603                     continue;
1604                 }
1605             }
1606
1607             if (!$realnames && ($folder_class = $this->folder_classname($folder['id']))) {
1608                 $foldername = $this->gettext($folder_class);
1609             }
1610             else {
1611                 $foldername = $folder['name'];
1612
1613                 // shorten the folder name to a given length
1614                 if ($maxlength && $maxlength > 1) {
1615                     $foldername = abbreviate_string($foldername, $maxlength);
1616                 }
e7ca04 1617             }
1aceb9 1618
0a1dd5 1619             $select->add(str_repeat('&nbsp;', $nestLevel*4) . html::quote($foldername), $folder['id']);
1aceb9 1620
e7ca04 1621             if (!empty($folder['folders'])) {
A 1622                 $out .= $this->render_folder_tree_select($folder['folders'], $mbox_name, $maxlength,
1623                     $select, $realnames, $nestLevel+1, $opts);
1aceb9 1624             }
A 1625         }
1626
1627         return $out;
1628     }
1629
1630
1631     /**
1632      * Return internal name for the given folder if it matches the configured special folders
1633      */
1634     public function folder_classname($folder_id)
1635     {
1636         if ($folder_id == 'INBOX') {
1637             return 'inbox';
1638         }
1639
1640         // for these mailboxes we have localized labels and css classes
1641         foreach (array('sent', 'drafts', 'trash', 'junk') as $smbx)
1642         {
1643             if ($folder_id === $this->config->get($smbx.'_mbox')) {
1644                 return $smbx;
1645             }
1646         }
1647     }
1648
1649
1650     /**
1651      * Try to localize the given IMAP folder name.
1652      * UTF-7 decode it in case no localized text was found
1653      *
1654      * @param string $name  Folder name
1655      *
1656      * @return string Localized folder name in UTF-8 encoding
1657      */
1658     public function localize_foldername($name)
1659     {
1660         if ($folder_class = $this->folder_classname($name)) {
1661             return $this->gettext($folder_class);
1662         }
1663         else {
1664             return rcube_charset::convert($name, 'UTF7-IMAP');
1665         }
1666     }
1667
1668
1669     public function localize_folderpath($path)
1670     {
1671         $protect_folders = $this->config->get('protect_default_folders');
1672         $default_folders = (array) $this->config->get('default_folders');
1673         $delimiter       = $this->storage->get_hierarchy_delimiter();
1674         $path            = explode($delimiter, $path);
1675         $result          = array();
1676
1677         foreach ($path as $idx => $dir) {
1678             $directory = implode($delimiter, array_slice($path, 0, $idx+1));
1679             if ($protect_folders && in_array($directory, $default_folders)) {
1680                 unset($result);
1681                 $result[] = $this->localize_foldername($directory);
1682             }
1683             else {
1684                 $result[] = rcube_charset::convert($dir, 'UTF7-IMAP');
1685             }
1686         }
1687
1688         return implode($delimiter, $result);
1689     }
1690
1691
1692     public static function quota_display($attrib)
1693     {
1694         $rcmail = rcmail::get_instance();
1695
1696         if (!$attrib['id']) {
1697             $attrib['id'] = 'rcmquotadisplay';
1698         }
1699
1700         $_SESSION['quota_display'] = !empty($attrib['display']) ? $attrib['display'] : 'text';
1701
1702         $rcmail->output->add_gui_object('quotadisplay', $attrib['id']);
1703
1704         $quota = $rcmail->quota_content($attrib);
1705
1706         $rcmail->output->add_script('rcmail.set_quota('.rcube_output::json_serialize($quota).');', 'docready');
1707
1708         return html::span($attrib, '');
1709     }
1710
1711
1712     public function quota_content($attrib = null)
1713     {
1714         $quota = $this->storage->get_quota();
1715         $quota = $this->plugins->exec_hook('quota', $quota);
1716
1717         $quota_result = (array) $quota;
1718         $quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
1719
1720         if (!$quota['total'] && $this->config->get('quota_zero_as_unlimited')) {
1721             $quota_result['title']   = $this->gettext('unlimited');
1722             $quota_result['percent'] = 0;
1723         }
1724         else if ($quota['total']) {
1725             if (!isset($quota['percent'])) {
1726                 $quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100));
1727             }
1728
1729             $title = sprintf('%s / %s (%.0f%%)',
1730                 $this->show_bytes($quota['used'] * 1024), $this->show_bytes($quota['total'] * 1024),
1731                 $quota_result['percent']);
1732
1733             $quota_result['title'] = $title;
1734
1735             if ($attrib['width']) {
1736                 $quota_result['width'] = $attrib['width'];
1737             }
1738             if ($attrib['height']) {
1739                 $quota_result['height']    = $attrib['height'];
1740             }
1741         }
1742         else {
1743             $quota_result['title']   = $this->gettext('unknown');
1744             $quota_result['percent'] = 0;
1745         }
1746
1747         return $quota_result;
1748     }
1749
1750
1751     /**
1752      * Outputs error message according to server error/response codes
1753      *
1754      * @param string $fallback       Fallback message label
1755      * @param array  $fallback_args  Fallback message label arguments
1756      */
1757     public function display_server_error($fallback = null, $fallback_args = null)
1758     {
1759         $err_code = $this->storage->get_error_code();
1760         $res_code = $this->storage->get_response_code();
1761
1762         if ($err_code < 0) {
1763             $this->output->show_message('storageerror', 'error');
1764         }
1765         else if ($res_code == rcube_storage::NOPERM) {
1766             $this->output->show_message('errornoperm', 'error');
1767         }
1768         else if ($res_code == rcube_storage::READONLY) {
1769             $this->output->show_message('errorreadonly', 'error');
1770         }
1771         else if ($err_code && ($err_str = $this->storage->get_error_str())) {
1772             // try to detect access rights problem and display appropriate message
1773             if (stripos($err_str, 'Permission denied') !== false) {
1774                 $this->output->show_message('errornoperm', 'error');
1775             }
1776             else {
1777                 $this->output->show_message('servererrormsg', 'error', array('msg' => $err_str));
1778             }
1779         }
1780         else if ($fallback) {
1781             $this->output->show_message($fallback, 'error', $fallback_args);
1782         }
1783     }
1784
1785
1786     /**
1787      * Output HTML editor scripts
1788      *
1789      * @param string $mode  Editor mode
1790      */
1791     public function html_editor($mode = '')
1792     {
1793         $hook = $this->plugins->exec_hook('html_editor', array('mode' => $mode));
1794
1795         if ($hook['abort']) {
1796             return;
1797         }
1798
1799         $lang = strtolower($_SESSION['language']);
1800
1801         // TinyMCE uses two-letter lang codes, with exception of Chinese
1802         if (strpos($lang, 'zh_') === 0) {
1803             $lang = str_replace('_', '-', $lang);
1804         }
1805         else {
1806             $lang = substr($lang, 0, 2);
1807         }
1808
1809         if (!file_exists(INSTALL_PATH . 'program/js/tiny_mce/langs/'.$lang.'.js')) {
1810             $lang = 'en';
1811         }
1812
1813         $script = json_encode(array(
1814             'mode'       => $mode,
1815             'lang'       => $lang,
1816             'skin_path'  => $this->output->get_skin_path(),
1817             'spellcheck' => intval($this->config->get('enable_spellcheck')),
1818             'spelldict'  => intval($this->config->get('spellcheck_dictionary'))
1819         ));
1820
1821         $this->output->include_script('tiny_mce/tiny_mce.js');
1822         $this->output->include_script('editor.js');
1823         $this->output->add_script("rcmail_editor_init($script)", 'docready');
1824     }
1825
1826
1827     /**
1828      * Replaces TinyMCE's emoticon images with plain-text representation
1829      *
1830      * @param string $html  HTML content
1831      *
1832      * @return string HTML content
1833      */
1834     public static function replace_emoticons($html)
1835     {
1836         $emoticons = array(
1837             '8-)' => 'smiley-cool',
1838             ':-#' => 'smiley-foot-in-mouth',
1839             ':-*' => 'smiley-kiss',
1840             ':-X' => 'smiley-sealed',
1841             ':-P' => 'smiley-tongue-out',
1842             ':-@' => 'smiley-yell',
1843             ":'(" => 'smiley-cry',
1844             ':-(' => 'smiley-frown',
1845             ':-D' => 'smiley-laughing',
1846             ':-)' => 'smiley-smile',
1847             ':-S' => 'smiley-undecided',
1848             ':-$' => 'smiley-embarassed',
1849             'O:-)' => 'smiley-innocent',
1850             ':-|' => 'smiley-money-mouth',
1851             ':-O' => 'smiley-surprised',
1852             ';-)' => 'smiley-wink',
1853         );
1854
1855         foreach ($emoticons as $idx => $file) {
1856             // <img title="Cry" src="http://.../program/js/tiny_mce/plugins/emotions/img/smiley-cry.gif" border="0" alt="Cry" />
1857             $search[]  = '/<img title="[a-z ]+" src="https?:\/\/[a-z0-9_.\/-]+\/tiny_mce\/plugins\/emotions\/img\/'.$file.'.gif"[^>]+\/>/i';
1858             $replace[] = $idx;
1859         }
1860
1861         return preg_replace($search, $replace, $html);
1862     }
1863
1864
1865     /**
1866      * File upload progress handler.
1867      */
1868     public function upload_progress()
1869     {
1870         $prefix = ini_get('apc.rfc1867_prefix');
1871         $params = array(
1872             'action' => $this->action,
1873             'name' => rcube_utils::get_input_value('_progress', rcube_utils::INPUT_GET),
1874         );
1875
1876         if (function_exists('apc_fetch')) {
1877             $status = apc_fetch($prefix . $params['name']);
1878
1879             if (!empty($status)) {
1880                 $status['percent'] = round($status['current']/$status['total']*100);
1881                 $params = array_merge($status, $params);
1882             }
1883         }
1884
1885         if (isset($params['percent']))
1886             $params['text'] = $this->gettext(array('name' => 'uploadprogress', 'vars' => array(
1887                 'percent' => $params['percent'] . '%',
1888                 'current' => $this->show_bytes($params['current']),
1889                 'total'   => $this->show_bytes($params['total'])
1890         )));
1891
1892         $this->output->command('upload_progress_update', $params);
1893         $this->output->send();
1894     }
1895
1896
1897     /**
1898      * Initializes file uploading interface.
1899      */
1900     public function upload_init()
1901     {
1902         // Enable upload progress bar
1903         if (($seconds = $this->config->get('upload_progress')) && ini_get('apc.rfc1867')) {
1904             if ($field_name = ini_get('apc.rfc1867_name')) {
1905                 $this->output->set_env('upload_progress_name', $field_name);
1906                 $this->output->set_env('upload_progress_time', (int) $seconds);
1907             }
1908         }
1909
1910         // find max filesize value
1911         $max_filesize = parse_bytes(ini_get('upload_max_filesize'));
1912         $max_postsize = parse_bytes(ini_get('post_max_size'));
1913         if ($max_postsize && $max_postsize < $max_filesize) {
1914             $max_filesize = $max_postsize;
1915         }
1916
1917         $this->output->set_env('max_filesize', $max_filesize);
1918         $max_filesize = self::show_bytes($max_filesize);
1919         $this->output->set_env('filesizeerror', $this->gettext(array(
1920             'name' => 'filesizeerror', 'vars' => array('size' => $max_filesize))));
1921
1922         return $max_filesize;
1923     }
1924
1925
1926     /**
1927      * Initializes client-side autocompletion.
1928      */
1929     public function autocomplete_init()
1930     {
1931         static $init;
1932
1933         if ($init) {
1934             return;
1935         }
1936
1937         $init = 1;
1938
1939         if (($threads = (int)$this->config->get('autocomplete_threads')) > 0) {
1940             $book_types = (array) $this->config->get('autocomplete_addressbooks', 'sql');
1941             if (count($book_types) > 1) {
1942                 $this->output->set_env('autocomplete_threads', $threads);
1943                 $this->output->set_env('autocomplete_sources', $book_types);
1944             }
1945         }
1946
1947         $this->output->set_env('autocomplete_max', (int)$this->config->get('autocomplete_max', 15));
1948         $this->output->set_env('autocomplete_min_length', $this->config->get('autocomplete_min_length'));
1949         $this->output->add_label('autocompletechars', 'autocompletemore');
1950     }
1951
1952
1953     /**
1954      * Returns supported font-family specifications
1955      *
1956      * @param string $font  Font name
1957      *
1958      * @param string|array Font-family specification array or string (if $font is used)
1959      */
1960     public static function font_defs($font = null)
1961     {
1962         $fonts = array(
1963             'Andale Mono'   => '"Andale Mono",Times,monospace',
1964             'Arial'         => 'Arial,Helvetica,sans-serif',
1965             'Arial Black'   => '"Arial Black","Avant Garde",sans-serif',
1966             'Book Antiqua'  => '"Book Antiqua",Palatino,serif',
1967             'Courier New'   => '"Courier New",Courier,monospace',
1968             'Georgia'       => 'Georgia,Palatino,serif',
1969             'Helvetica'     => 'Helvetica,Arial,sans-serif',
1970             'Impact'        => 'Impact,Chicago,sans-serif',
1971             'Tahoma'        => 'Tahoma,Arial,Helvetica,sans-serif',
1972             'Terminal'      => 'Terminal,Monaco,monospace',
1973             'Times New Roman' => '"Times New Roman",Times,serif',
1974             'Trebuchet MS'  => '"Trebuchet MS",Geneva,sans-serif',
1975             'Verdana'       => 'Verdana,Geneva,sans-serif',
1976         );
1977
1978         if ($font) {
1979             return $fonts[$font];
1980         }
1981
1982         return $fonts;
1983     }
1984
1985
1986     /**
1987      * Create a human readable string for a number of bytes
1988      *
1989      * @param int Number of bytes
1990      *
1991      * @return string Byte string
1992      */
1993     public function show_bytes($bytes)
1994     {
1995         if ($bytes >= 1073741824) {
1996             $gb  = $bytes/1073741824;
1997             $str = sprintf($gb>=10 ? "%d " : "%.1f ", $gb) . $this->gettext('GB');
1998         }
1999         else if ($bytes >= 1048576) {
2000             $mb  = $bytes/1048576;
2001             $str = sprintf($mb>=10 ? "%d " : "%.1f ", $mb) . $this->gettext('MB');
2002         }
2003         else if ($bytes >= 1024) {
2004             $str = sprintf("%d ",  round($bytes/1024)) . $this->gettext('KB');
2005         }
2006         else {
2007             $str = sprintf('%d ', $bytes) . $this->gettext('B');
2008         }
2009
2010         return $str;
2011     }
2012
2013
2014     /**
2015      * Quote a given string.
2016      * Shortcut function for rcube_utils::rep_specialchars_output()
2017      *
2018      * @return string HTML-quoted string
2019      */
2020     public static function Q($str, $mode = 'strict', $newlines = true)
2021     {
2022         return rcube_utils::rep_specialchars_output($str, 'html', $mode, $newlines);
2023     }
2024
2025
2026     /**
2027      * Quote a given string for javascript output.
2028      * Shortcut function for rcube_utils::rep_specialchars_output()
2029      *
2030      * @return string JS-quoted string
2031      */
2032     public static function JQ($str)
2033     {
2034         return rcube_utils::rep_specialchars_output($str, 'js');
2035     }
2036
2037
8749e9 2038     /**
AM 2039      * Returns real size (calculated) of the message part
2040      *
2041      * @param rcube_message_part  Message part
2042      *
2043      * @return string Part size (and unit)
2044      */
2045     public function message_part_size($part)
2046     {
2047         if (isset($part->d_parameters['size'])) {
2048             $size = $this->show_bytes((int)$part->d_parameters['size']);
2049         }
2050         else {
2051           $size = $part->size;
2052           if ($part->encoding == 'base64') {
2053             $size = $size / 1.33;
2054           }
2055
2056           $size = '~' . $this->show_bytes($size);
2057         }
2058
2059         return $size;
2060     }
2061
2062
1aceb9 2063     /************************************************************************
A 2064      *********          Deprecated methods (to be removed)          *********
2065      ***********************************************************************/
2066
2067     public static function setcookie($name, $value, $exp = 0)
2068     {
2069         rcube_utils::setcookie($name, $value, $exp);
2070     }
38a08c 2071
AM 2072     public function imap_connect()
2073     {
2074         return $this->storage_connect();
2075     }
5a575b 2076
b97f21 2077     public function imap_init()
TB 2078     {
2079         return $this->storage_init();
2080     }
2081
5a575b 2082     /**
AM 2083      * Connect to the mail storage server with stored session data
2084      *
2085      * @return bool True on success, False on error
2086      */
2087     public function storage_connect()
2088     {
2089         $storage = $this->get_storage();
2090
2091         if ($_SESSION['storage_host'] && !$storage->is_connected()) {
2092             $host = $_SESSION['storage_host'];
2093             $user = $_SESSION['username'];
2094             $port = $_SESSION['storage_port'];
2095             $ssl  = $_SESSION['storage_ssl'];
2096             $pass = $this->decrypt($_SESSION['password']);
2097
2098             if (!$storage->connect($host, $user, $pass, $port, $ssl)) {
2099                 if (is_object($this->output)) {
2100                     $error = $storage->get_error_code() == -1 ? 'storageerror' : 'sessionerror';
2101                     $this->output->show_message($error, 'error');
2102                 }
2103             }
2104             else {
2105                 $this->set_storage_prop();
2106                 return $storage->is_connected();
2107             }
2108         }
2109
2110         return false;
2111     }
197601 2112 }