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