source: github/program/include/rcmail.php @ 9b94eb6

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since 9b94eb6 was 9b94eb6, checked in by alecpl <alec@…>, 3 years ago
  • Fix setting task name according to auth state. So, any action before user is authenticated is assigned to 'login' task instead of 'mail'. Now binding plugins to 'login' task is possible and realy usefull. It's also possible to bind to all tasks excluding 'login'.
  • Property mode set to 100644
File size: 31.2 KB
Line 
1<?php
2
3/*
4 +-----------------------------------------------------------------------+
5 | program/include/rcmail.php                                            |
6 |                                                                       |
7 | This file is part of the RoundCube Webmail client                     |
8 | Copyright (C) 2008-2009, RoundCube Dev. - Switzerland                 |
9 | Licensed under the GNU GPL                                            |
10 |                                                                       |
11 | PURPOSE:                                                              |
12 |   Application class providing core functions and holding              |
13 |   instances of all 'global' objects like db- and imap-connections     |
14 +-----------------------------------------------------------------------+
15 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16 +-----------------------------------------------------------------------+
17
18 $Id$
19
20*/
21
22
23/**
24 * Application class of RoundCube Webmail
25 * implemented as singleton
26 *
27 * @package Core
28 */
29class rcmail
30{
31  static public $main_tasks = array('mail','settings','addressbook','login','logout','dummy');
32 
33  static private $instance;
34 
35  public $config;
36  public $user;
37  public $db;
38  public $smtp;
39  public $imap;
40  public $output;
41  public $plugins;
42  public $task;
43  public $action = '';
44  public $comm_path = './';
45 
46  private $texts;
47 
48 
49  /**
50   * This implements the 'singleton' design pattern
51   *
52   * @return object rcmail The one and only instance
53   */
54  static function get_instance()
55  {
56    if (!self::$instance) {
57      self::$instance = new rcmail();
58      self::$instance->startup();  // init AFTER object was linked with self::$instance
59    }
60
61    return self::$instance;
62  }
63 
64 
65  /**
66   * Private constructor
67   */
68  private function __construct()
69  {
70    // load configuration
71    $this->config = new rcube_config();
72   
73    register_shutdown_function(array($this, 'shutdown'));
74  }
75 
76 
77  /**
78   * Initial startup function
79   * to register session, create database and imap connections
80   *
81   * @todo Remove global vars $DB, $USER
82   */
83  private function startup()
84  {
85    $config_all = $this->config->all();
86
87    // initialize syslog
88    if ($this->config->get('log_driver') == 'syslog') {
89      $syslog_id = $this->config->get('syslog_id', 'roundcube');
90      $syslog_facility = $this->config->get('syslog_facility', LOG_USER);
91      openlog($syslog_id, LOG_ODELAY, $syslog_facility);
92    }
93
94    // connect to database
95    $GLOBALS['DB'] = $this->get_dbh();
96
97    // use database for storing session data
98    include_once('include/session.inc');
99
100    // set session domain
101    if (!empty($config_all['session_domain'])) {
102      ini_set('session.cookie_domain', $config_all['session_domain']);
103    }
104    // set session garbage collecting time according to session_lifetime
105    if (!empty($config_all['session_lifetime'])) {
106      ini_set('session.gc_maxlifetime', ($config_all['session_lifetime']) * 120);
107    }
108
109    // start PHP session (if not in CLI mode)
110    if ($_SERVER['REMOTE_ADDR'])
111      session_start();
112
113    // set initial session vars
114    if (!isset($_SESSION['auth_time'])) {
115      $_SESSION['auth_time'] = time();
116      $_SESSION['temp'] = true;
117    }
118
119    // create user object
120    $this->set_user(new rcube_user($_SESSION['user_id']));
121
122    // set task and action properties
123    $this->set_task(get_input_value('_task', RCUBE_INPUT_GPC));
124    $this->action = asciiwords(get_input_value('_action', RCUBE_INPUT_GPC));
125
126    // reset some session parameters when changing task
127    if ($_SESSION['task'] != $this->task)
128      rcube_sess_unset('page');
129
130    // set current task to session
131    $_SESSION['task'] = $this->task;
132
133    // create IMAP object
134    if ($this->task == 'login')
135      $this->imap_init();
136     
137    // create plugin API and load plugins
138    $this->plugins = rcube_plugin_api::get_instance();
139  }
140 
141 
142  /**
143   * Setter for application task
144   *
145   * @param string Task to set
146   */
147  public function set_task($task)
148  {
149    $task = asciiwords($task);
150
151    if ($this->user && $this->user->ID)
152      $task = !$task || $task == 'login' ? 'mail' : $task;
153    else
154      $task = 'login';
155
156    $this->task = $task;
157    $this->comm_path = $this->url(array('task' => $this->task));
158   
159    if ($this->output)
160      $this->output->set_env('task', $this->task);
161  }
162 
163 
164  /**
165   * Setter for system user object
166   *
167   * @param object rcube_user Current user instance
168   */
169  public function set_user($user)
170  {
171    if (is_object($user)) {
172      $this->user = $user;
173      $GLOBALS['USER'] = $this->user;
174     
175      // overwrite config with user preferences
176      $this->config->merge((array)$this->user->get_prefs());
177    }
178   
179    $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language']));
180
181    // set localization
182    setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8');
183
184    // workaround for http://bugs.php.net/bug.php?id=18556
185    if (in_array($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ'))) 
186      setlocale(LC_CTYPE, 'en_US' . '.utf8'); 
187  }
188 
189 
190  /**
191   * Check the given string and return a valid language code
192   *
193   * @param string Language code
194   * @return string Valid language code
195   */
196  private function language_prop($lang)
197  {
198    static $rcube_languages, $rcube_language_aliases;
199   
200    // user HTTP_ACCEPT_LANGUAGE if no language is specified
201    if (empty($lang) || $lang == 'auto') {
202       $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
203       $lang = str_replace('-', '_', $accept_langs[0]);
204     }
205     
206    if (empty($rcube_languages)) {
207      @include(INSTALL_PATH . 'program/localization/index.inc');
208    }
209   
210    // check if we have an alias for that language
211    if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
212      $lang = $rcube_language_aliases[$lang];
213    }
214    // try the first two chars
215    else if (!isset($rcube_languages[$lang])) {
216      $short = substr($lang, 0, 2);
217     
218      // check if we have an alias for the short language code
219      if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {
220        $lang = $rcube_language_aliases[$short];
221      }
222      // expand 'nn' to 'nn_NN'
223      else if (!isset($rcube_languages[$short])) {
224        $lang = $short.'_'.strtoupper($short);
225      }
226    }
227
228    if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
229      $lang = 'en_US';
230    }
231
232    return $lang;
233  }
234 
235 
236  /**
237   * Get the current database connection
238   *
239   * @return object rcube_mdb2  Database connection object
240   */
241  public function get_dbh()
242  {
243    if (!$this->db) {
244      $config_all = $this->config->all();
245
246      $this->db = new rcube_mdb2($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']);
247      $this->db->sqlite_initials = INSTALL_PATH . 'SQL/sqlite.initial.sql';
248      $this->db->set_debug((bool)$config_all['sql_debug']);
249      $this->db->db_connect('w');
250    }
251
252    return $this->db;
253  }
254 
255 
256  /**
257   * Return instance of the internal address book class
258   *
259   * @param boolean True if the address book needs to be writeable
260   * @return object rcube_contacts Address book object
261   */
262  public function get_address_book($id, $writeable = false)
263  {
264    $contacts = null;
265    $ldap_config = (array)$this->config->get('ldap_public');
266    $abook_type = strtolower($this->config->get('address_book_type'));
267
268    $plugin = $this->plugins->exec_hook('get_address_book', array('id' => $id, 'writeable' => $writeable));
269   
270    // plugin returned instance of a rcube_addressbook
271    if ($plugin['instance'] instanceof rcube_addressbook) {
272      $contacts = $plugin['instance'];
273    }
274    else if ($id && $ldap_config[$id]) {
275      $contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['imap_host']));
276    }
277    else if ($id === '0') {
278      $contacts = new rcube_contacts($this->db, $this->user->ID);
279    }
280    else if ($abook_type == 'ldap') {
281      // Use the first writable LDAP address book.
282      foreach ($ldap_config as $id => $prop) {
283        if (!$writeable || $prop['writable']) {
284          $contacts = new rcube_ldap($prop, $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['imap_host']));
285          break;
286        }
287      }
288    }
289    else {
290      $contacts = new rcube_contacts($this->db, $this->user->ID);
291    }
292   
293    return $contacts;
294  }
295 
296 
297  /**
298   * Init output object for GUI and add common scripts.
299   * This will instantiate a rcmail_template object and set
300   * environment vars according to the current session and configuration
301   *
302   * @param boolean True if this request is loaded in a (i)frame
303   * @return object rcube_template Reference to HTML output object
304   */
305  public function load_gui($framed = false)
306  {
307    // init output page
308    if (!($this->output instanceof rcube_template))
309      $this->output = new rcube_template($this->task, $framed);
310
311    // set keep-alive/check-recent interval
312    if ($keep_alive = $this->config->get('keep_alive')) {
313      // be sure that it's less than session lifetime
314      if ($session_lifetime = $this->config->get('session_lifetime'))
315        $keep_alive = min($keep_alive, $session_lifetime * 60 - 30);
316      $this->output->set_env('keep_alive', max(60, $keep_alive));
317    }
318
319    if ($framed) {
320      $this->comm_path .= '&_framed=1';
321      $this->output->set_env('framed', true);
322    }
323
324    $this->output->set_env('task', $this->task);
325    $this->output->set_env('action', $this->action);
326    $this->output->set_env('comm_path', $this->comm_path);
327    $this->output->set_charset(RCMAIL_CHARSET);
328
329    // add some basic label to client
330    $this->output->add_label('loading', 'servererror');
331   
332    return $this->output;
333  }
334 
335 
336  /**
337   * Create an output object for JSON responses
338   *
339   * @return object rcube_json_output Reference to JSON output object
340   */
341  public function init_json()
342  {
343    if (!($this->output instanceof rcube_json_output))
344      $this->output = new rcube_json_output($this->task);
345   
346    return $this->output;
347  }
348
349
350  /**
351   * Create SMTP object and connect to server
352   *
353   * @param boolean True if connection should be established
354   */
355  public function smtp_init($connect = false)
356  {
357    $this->smtp = new rcube_smtp();
358 
359    if ($connect)
360      $this->smtp->connect();
361  }
362 
363 
364  /**
365   * Create global IMAP object and connect to server
366   *
367   * @param boolean True if connection should be established
368   * @todo Remove global $IMAP
369   */
370  public function imap_init($connect = false)
371  {
372    $this->imap = new rcube_imap($this->db);
373    $this->imap->debug_level = $this->config->get('debug_level');
374    $this->imap->skip_deleted = $this->config->get('skip_deleted');
375    $this->imap->index_sort = $this->config->get('index_sort', true);
376
377    // enable caching of imap data
378    if ($this->config->get('enable_caching')) {
379      $this->imap->set_caching(true);
380    }
381
382    // set pagesize from config
383    $this->imap->set_pagesize($this->config->get('pagesize', 50));
384   
385    // Setting root and delimiter before iil_Connect can save time detecting them
386    // using NAMESPACE and LIST
387    $options = array(
388      'auth_method' => $this->config->get('imap_auth_type', 'check'),
389      'delimiter' => isset($_SESSION['imap_delimiter']) ? $_SESSION['imap_delimiter'] : $this->config->get('imap_delimiter'),
390      'rootdir' => isset($_SESSION['imap_root']) ? $_SESSION['imap_root'] : $this->config->get('imap_root'),
391      'debug_mode' => (bool) $this->config->get('imap_debug', 0),
392    );
393
394    $this->imap->set_options($options);
395 
396    // set global object for backward compatibility
397    $GLOBALS['IMAP'] = $this->imap;
398   
399    if ($connect)
400      $this->imap_connect();
401  }
402
403
404  /**
405   * Connect to IMAP server with stored session data
406   *
407   * @return bool True on success, false on error
408   */
409  public function imap_connect()
410  {
411    $conn = false;
412   
413    if ($_SESSION['imap_host'] && !$this->imap->conn) {
414      if (!($conn = $this->imap->connect($_SESSION['imap_host'], $_SESSION['username'], $this->decrypt($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']))) {
415        if ($this->output)
416          $this->output->show_message($this->imap->error_code == -1 ? 'imaperror' : 'sessionerror', 'error');
417      }
418
419      $this->set_imap_prop();
420    }
421
422    return $conn;
423  }
424
425
426  /**
427   * Perfom login to the IMAP server and to the webmail service.
428   * This will also create a new user entry if auto_create_user is configured.
429   *
430   * @param string IMAP user name
431   * @param string IMAP password
432   * @param string IMAP host
433   * @return boolean True on success, False on failure
434   */
435  function login($username, $pass, $host=NULL)
436  {
437    $user = NULL;
438    $config = $this->config->all();
439
440    if (!$host)
441      $host = $config['default_host'];
442
443    // Validate that selected host is in the list of configured hosts
444    if (is_array($config['default_host'])) {
445      $allowed = false;
446      foreach ($config['default_host'] as $key => $host_allowed) {
447        if (!is_numeric($key))
448          $host_allowed = $key;
449        if ($host == $host_allowed) {
450          $allowed = true;
451          break;
452        }
453      }
454      if (!$allowed)
455        return false;
456      }
457    else if (!empty($config['default_host']) && $host != $config['default_host'])
458      return false;
459
460    // parse $host URL
461    $a_host = parse_url($host);
462    if ($a_host['host']) {
463      $host = $a_host['host'];
464      $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
465      if(!empty($a_host['port']))
466        $imap_port = $a_host['port'];
467      else if ($imap_ssl && $imap_ssl != 'tls')
468        $imap_port = 993;
469    }
470   
471    $imap_port = $imap_port ? $imap_port : $config['default_port'];
472
473    /* Modify username with domain if required 
474       Inspired by Marco <P0L0_notspam_binware.org>
475    */
476    // Check if we need to add domain
477    if (!empty($config['username_domain']) && !strpos($username, '@')) {
478      if (is_array($config['username_domain']) && isset($config['username_domain'][$host]))
479        $username .= '@'.$config['username_domain'][$host];
480      else if (is_string($config['username_domain']))
481        $username .= '@'.$config['username_domain'];
482    }
483
484    // try to resolve email address from virtuser table
485    if (strpos($username, '@'))
486      if ($virtuser = rcube_user::email2user($username))
487        $username = $virtuser;
488
489    // lowercase username if it's an e-mail address (#1484473)
490    if (strpos($username, '@'))
491      $username = mb_strtolower($username);
492
493    // user already registered -> overwrite username
494    if ($user = rcube_user::query($username, $host))
495      $username = $user->data['username'];
496
497    // exit if IMAP login failed
498    if (!($imap_login  = $this->imap->connect($host, $username, $pass, $imap_port, $imap_ssl)))
499      return false;
500
501    $this->set_imap_prop();
502
503    // user already registered -> update user's record
504    if (is_object($user)) {
505      // create default folders on first login
506      if (!$user->data['last_login'] && $config['create_default_folders'])
507        $this->imap->create_default_folders();
508      $user->touch();
509    }
510    // create new system user
511    else if ($config['auto_create_user']) {
512      if ($created = rcube_user::create($username, $host)) {
513        $user = $created;
514        // create default folders on first login
515        if ($config['create_default_folders'])
516          $this->imap->create_default_folders();
517      }
518      else {
519        raise_error(array(
520          'code' => 600, 'type' => 'php',
521          'file' => __FILE__, 'line' => __LINE__,
522          'message' => "Failed to create a user record. Maybe aborted by a plugin?"
523          ), true, false);
524      }
525    }
526    else {
527      raise_error(array(
528        'code' => 600, 'type' => 'php',
529        'file' => __FILE__, 'line' => __LINE__,
530        'message' => "Acces denied for new user $username. 'auto_create_user' is disabled"
531        ), true, false);
532    }
533
534    // login succeeded
535    if (is_object($user) && $user->ID) {
536      $this->set_user($user);
537
538      // set session vars
539      $_SESSION['user_id']   = $user->ID;
540      $_SESSION['username']  = $user->data['username'];
541      $_SESSION['imap_host'] = $host;
542      $_SESSION['imap_port'] = $imap_port;
543      $_SESSION['imap_ssl']  = $imap_ssl;
544      $_SESSION['password']  = $this->encrypt($pass);
545      $_SESSION['login_time'] = mktime();
546     
547      if ($_REQUEST['_timezone'] != '_default_')
548        $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
549
550      // force reloading complete list of subscribed mailboxes
551      $this->imap->clear_cache('mailboxes');
552
553      return true;
554    }
555
556    return false;
557  }
558
559
560  /**
561   * Set root dir and last stored mailbox
562   * This must be done AFTER connecting to the server!
563   */
564  public function set_imap_prop()
565  {
566    $this->imap->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
567
568    if ($default_folders = $this->config->get('default_imap_folders')) {
569      $this->imap->set_default_mailboxes($default_folders);
570    }
571    if (!empty($_SESSION['mbox'])) {
572      $this->imap->set_mailbox($_SESSION['mbox']);
573    }
574    if (isset($_SESSION['page'])) {
575      $this->imap->set_page($_SESSION['page']);
576    }
577   
578    // cache IMAP root and delimiter in session for performance reasons
579    $_SESSION['imap_root'] = $this->imap->root_dir;
580    $_SESSION['imap_delimiter'] = $this->imap->delimiter;
581  }
582
583
584  /**
585   * Auto-select IMAP host based on the posted login information
586   *
587   * @return string Selected IMAP host
588   */
589  public function autoselect_host()
590  {
591    $default_host = $this->config->get('default_host');
592    $host = null;
593   
594    if (is_array($default_host)) {
595      $post_host = get_input_value('_host', RCUBE_INPUT_POST);
596     
597      // direct match in default_host array
598      if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) {
599        $host = $post_host;
600      }
601     
602      // try to select host by mail domain
603      list($user, $domain) = explode('@', get_input_value('_user', RCUBE_INPUT_POST));
604      if (!empty($domain)) {
605        foreach ($default_host as $imap_host => $mail_domains) {
606          if (is_array($mail_domains) && in_array($domain, $mail_domains)) {
607            $host = $imap_host;
608            break;
609          }
610        }
611      }
612
613      // take the first entry if $host is still an array
614      if (empty($host)) {
615        $host = array_shift($default_host);
616      }
617    }
618    else if (empty($default_host)) {
619      $host = get_input_value('_host', RCUBE_INPUT_POST);
620    }
621    else
622      $host = $default_host;
623
624    return $host;
625  }
626
627
628  /**
629   * Get localized text in the desired language
630   *
631   * @param mixed Named parameters array or label name
632   * @return string Localized text
633   */
634  public function gettext($attrib, $domain=null)
635  {
636    // load localization files if not done yet
637    if (empty($this->texts))
638      $this->load_language();
639   
640    // extract attributes
641    if (is_string($attrib))
642      $attrib = array('name' => $attrib);
643
644    $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
645    $name = $attrib['name'] ? $attrib['name'] : '';
646
647    // check for text with domain
648    if ($domain && ($text_item = $this->texts[$domain.'.'.$name]))
649      ;
650    // text does not exist
651    else if (!($text_item = $this->texts[$name])) {
652      return "[$name]";
653    }
654
655    // make text item array
656    $a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
657
658    // decide which text to use
659    if ($nr == 1) {
660      $text = $a_text_item['single'];
661    }
662    else if ($nr > 0) {
663      $text = $a_text_item['multiple'];
664    }
665    else if ($nr == 0) {
666      if ($a_text_item['none'])
667        $text = $a_text_item['none'];
668      else if ($a_text_item['single'])
669        $text = $a_text_item['single'];
670      else if ($a_text_item['multiple'])
671        $text = $a_text_item['multiple'];
672    }
673
674    // default text is single
675    if ($text == '') {
676      $text = $a_text_item['single'];
677    }
678
679    // replace vars in text
680    if (is_array($attrib['vars'])) {
681      foreach ($attrib['vars'] as $var_key => $var_value)
682        $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text);
683    }
684
685    // format output
686    if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
687      return ucfirst($text);
688    else if ($attrib['uppercase'])
689      return strtoupper($text);
690    else if ($attrib['lowercase'])
691      return strtolower($text);
692
693    return $text;
694  }
695
696
697  /**
698   * Load a localization package
699   *
700   * @param string Language ID
701   */
702  public function load_language($lang = null, $add = array())
703  {
704    $lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));
705   
706    // load localized texts
707    if (empty($this->texts) || $lang != $_SESSION['language']) {
708      $this->texts = array();
709
710      // get english labels (these should be complete)
711      @include(INSTALL_PATH . 'program/localization/en_US/labels.inc');
712      @include(INSTALL_PATH . 'program/localization/en_US/messages.inc');
713
714      if (is_array($labels))
715        $this->texts = $labels;
716      if (is_array($messages))
717        $this->texts = array_merge($this->texts, $messages);
718
719      // include user language files
720      if ($lang != 'en' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
721        include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc');
722        include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc');
723
724        if (is_array($labels))
725          $this->texts = array_merge($this->texts, $labels);
726        if (is_array($messages))
727          $this->texts = array_merge($this->texts, $messages);
728      }
729     
730      $_SESSION['language'] = $lang;
731    }
732
733    // append additional texts (from plugin)
734    if (is_array($add) && !empty($add))
735      $this->texts += $add;
736  }
737
738
739  /**
740   * Read directory program/localization and return a list of available languages
741   *
742   * @return array List of available localizations
743   */
744  public function list_languages()
745  {
746    static $sa_languages = array();
747
748    if (!sizeof($sa_languages)) {
749      @include(INSTALL_PATH . 'program/localization/index.inc');
750
751      if ($dh = @opendir(INSTALL_PATH . 'program/localization')) {
752        while (($name = readdir($dh)) !== false) {
753          if ($name{0}=='.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name))
754            continue;
755
756          if ($label = $rcube_languages[$name])
757            $sa_languages[$name] = $label;
758        }
759        closedir($dh);
760      }
761    }
762
763    return $sa_languages;
764  }
765
766
767  /**
768   * Check the auth hash sent by the client against the local session credentials
769   *
770   * @return boolean True if valid, False if not
771   */
772  function authenticate_session()
773  {
774    global $SESS_CLIENT_IP, $SESS_CHANGED;
775
776    // advanced session authentication
777    if ($this->config->get('double_auth')) {
778      $now = time();
779      $valid = ($_COOKIE['sessauth'] == $this->get_auth_hash(session_id(), $_SESSION['auth_time']) ||
780                $_COOKIE['sessauth'] == $this->get_auth_hash(session_id(), $_SESSION['last_auth']));
781
782      // renew auth cookie every 5 minutes (only for GET requests)
783      if (!$valid || ($_SERVER['REQUEST_METHOD']!='POST' && $now - $_SESSION['auth_time'] > 300)) {
784        $_SESSION['last_auth'] = $_SESSION['auth_time'];
785        $_SESSION['auth_time'] = $now;
786        rcmail::setcookie('sessauth', $this->get_auth_hash(session_id(), $now), 0);
787      }
788    }
789    else {
790      $valid = $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] == $SESS_CLIENT_IP : true;
791    }
792
793    // check session filetime
794    $lifetime = $this->config->get('session_lifetime');
795    if (!empty($lifetime) && isset($SESS_CHANGED) && $SESS_CHANGED + $lifetime*60 < time()) {
796      $valid = false;
797    }
798
799    return $valid;
800  }
801
802
803  /**
804   * Destroy session data and remove cookie
805   */
806  public function kill_session()
807  {
808    $this->plugins->exec_hook('kill_session');
809   
810    rcube_sess_unset();
811    $_SESSION = array('language' => $this->user->language, 'auth_time' => time(), 'temp' => true);
812    rcmail::setcookie('sessauth', '-del-', time() - 60);
813    $this->user->reset();
814  }
815
816
817  /**
818   * Do server side actions on logout
819   */
820  public function logout_actions()
821  {
822    $config = $this->config->all();
823   
824    // on logout action we're not connected to imap server 
825    if (($config['logout_purge'] && !empty($config['trash_mbox'])) || $config['logout_expunge']) {
826      if (!$this->authenticate_session())
827        return;
828
829      $this->imap_init(true);
830    }
831
832    if ($config['logout_purge'] && !empty($config['trash_mbox'])) {
833      $this->imap->clear_mailbox($config['trash_mbox']);
834    }
835
836    if ($config['logout_expunge']) {
837      $this->imap->expunge('INBOX');
838    }
839  }
840
841
842  /**
843   * Function to be executed in script shutdown
844   * Registered with register_shutdown_function()
845   */
846  public function shutdown()
847  {
848    if (is_object($this->imap)) {
849      $this->imap->close();
850      $this->imap->write_cache();
851    }
852
853    if (is_object($this->smtp))
854      $this->smtp->disconnect();
855
856    if (is_object($this->contacts))
857      $this->contacts->close();
858
859    // before closing the database connection, write session data
860    if ($_SERVER['REMOTE_ADDR'])
861      session_write_close();
862
863    // write performance stats to logs/console
864    if ($this->config->get('devel_mode')) {
865      if (function_exists('memory_get_usage'))
866        $mem = show_bytes(memory_get_usage());
867      if (function_exists('memory_get_peak_usage'))
868        $mem .= '/'.show_bytes(memory_get_peak_usage());
869
870      $log = $this->task . ($this->action ? '/'.$this->action : '') . ($mem ? " [$mem]" : '');
871      rcube_print_time(RCMAIL_START, $log);
872    }
873  }
874 
875 
876  /**
877   * Generate a unique token to be used in a form request
878   *
879   * @return string The request token
880   */
881  public function get_request_token()
882  {
883    $key = $this->task;
884   
885    if (!$_SESSION['request_tokens'][$key])
886      $_SESSION['request_tokens'][$key] = md5(uniqid($key . mt_rand(), true));
887   
888    return $_SESSION['request_tokens'][$key];
889  }
890 
891 
892  /**
893   * Check if the current request contains a valid token
894   *
895   * @param int Request method
896   * @return boolean True if request token is valid false if not
897   */
898  public function check_request($mode = RCUBE_INPUT_POST)
899  {
900    $token = get_input_value('_token', $mode);
901    return !empty($token) && $_SESSION['request_tokens'][$this->task] == $token;
902  }
903 
904 
905  /**
906   * Create unique authorization hash
907   *
908   * @param string Session ID
909   * @param int Timestamp
910   * @return string The generated auth hash
911   */
912  private function get_auth_hash($sess_id, $ts)
913  {
914    $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s',
915      $sess_id,
916      $ts,
917      $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***',
918      $_SERVER['HTTP_USER_AGENT']);
919
920    if (function_exists('sha1'))
921      return sha1($auth_string);
922    else
923      return md5($auth_string);
924  }
925
926
927  /**
928   * Encrypt using 3DES
929   *
930   * @param string $clear clear text input
931   * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
932   * @param boolean $base64 whether or not to base64_encode() the result before returning
933   *
934   * @return string encrypted text
935   */
936  public function encrypt($clear, $key = 'des_key', $base64 = true)
937  {
938    if (!$clear)
939      return '';
940    /*-
941     * Add a single canary byte to the end of the clear text, which
942     * will help find out how much of padding will need to be removed
943     * upon decryption; see http://php.net/mcrypt_generic#68082
944     */
945    $clear = pack("a*H2", $clear, "80");
946 
947    if (function_exists('mcrypt_module_open') &&
948        ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")))
949    {
950      $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
951      mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
952      $cipher = $iv . mcrypt_generic($td, $clear);
953      mcrypt_generic_deinit($td);
954      mcrypt_module_close($td);
955    }
956    else if (function_exists('des'))
957    {
958      define('DES_IV_SIZE', 8);
959      $iv = '';
960      for ($i = 0; $i < constant('DES_IV_SIZE'); $i++)
961        $iv .= sprintf("%c", mt_rand(0, 255));
962      $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv);
963    }
964    else
965    {
966      raise_error(array(
967        'code' => 500, 'type' => 'php',
968        'file' => __FILE__, 'line' => __LINE__,
969        'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available"
970      ), true, true);
971    }
972 
973    return $base64 ? base64_encode($cipher) : $cipher;
974  }
975
976  /**
977   * Decrypt 3DES-encrypted string
978   *
979   * @param string $cipher encrypted text
980   * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
981   * @param boolean $base64 whether or not input is base64-encoded
982   *
983   * @return string decrypted text
984   */
985  public function decrypt($cipher, $key = 'des_key', $base64 = true)
986  {
987    if (!$cipher)
988      return '';
989 
990    $cipher = $base64 ? base64_decode($cipher) : $cipher;
991
992    if (function_exists('mcrypt_module_open') &&
993        ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")))
994    {
995      $iv = substr($cipher, 0, mcrypt_enc_get_iv_size($td));
996      $cipher = substr($cipher, mcrypt_enc_get_iv_size($td));
997      mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
998      $clear = mdecrypt_generic($td, $cipher);
999      mcrypt_generic_deinit($td);
1000      mcrypt_module_close($td);
1001    }
1002    else if (function_exists('des'))
1003    {
1004      define('DES_IV_SIZE', 8);
1005      $iv = substr($cipher, 0, constant('DES_IV_SIZE'));
1006      $cipher = substr($cipher, constant('DES_IV_SIZE'));
1007      $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv);
1008    }
1009    else
1010    {
1011      raise_error(array(
1012        'code' => 500, 'type' => 'php',
1013        'file' => __FILE__, 'line' => __LINE__,
1014        'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available"
1015      ), true, true);
1016    }
1017 
1018    /*-
1019     * Trim PHP's padding and the canary byte; see note in
1020     * rcmail::encrypt() and http://php.net/mcrypt_generic#68082
1021     */
1022    $clear = substr(rtrim($clear, "\0"), 0, -1);
1023 
1024    return $clear;
1025  }
1026
1027  /**
1028   * Build a valid URL to this instance of RoundCube
1029   *
1030   * @param mixed Either a string with the action or url parameters as key-value pairs
1031   * @return string Valid application URL
1032   */
1033  public function url($p)
1034  {
1035    if (!is_array($p))
1036      $p = array('_action' => @func_get_arg(0));
1037   
1038    $task = $p['_task'] ? $p['_task'] : ($p['task'] ? $p['task'] : $this->task);
1039    $p['_task'] = $task;
1040    unset($p['task']);
1041
1042    $url = './';
1043    $delm = '?';
1044    foreach (array_reverse($p) as $key => $val)
1045    {
1046      if (!empty($val)) {
1047        $par = $key[0] == '_' ? $key : '_'.$key;
1048        $url .= $delm.urlencode($par).'='.urlencode($val);
1049        $delm = '&';
1050      }
1051    }
1052    return $url;
1053  }
1054
1055
1056  /**
1057   * Helper method to set a cookie with the current path and host settings
1058   *
1059   * @param string Cookie name
1060   * @param string Cookie value
1061   * @param string Expiration time
1062   */
1063  public static function setcookie($name, $value, $exp = 0)
1064  {
1065    if (headers_sent())
1066      return;
1067
1068    $cookie = session_get_cookie_params();
1069
1070    setcookie($name, $value, $exp, $cookie['path'], $cookie['domain'],
1071      rcube_https_check(), true);
1072  }
1073}
1074
1075
Note: See TracBrowser for help on using the repository browser.