Aleksander Machniak
2013-12-22 6b2b2eca5fa48720c4e5b31b9aae200a185dfc0e
program/include/rcmail.php
@@ -51,6 +51,7 @@
   */
  public $action = '';
  public $comm_path = './';
  public $filename = '';
  private $address_books = array();
  private $action_map = array();
@@ -65,12 +66,13 @@
  /**
   * This implements the 'singleton' design pattern
   *
   * @param string Environment name to run (e.g. live, dev, test)
   * @return rcmail The one and only instance
   */
  static function get_instance()
  static function get_instance($env = '')
  {
    if (!self::$instance || !is_a(self::$instance, 'rcmail')) {
      self::$instance = new rcmail();
      self::$instance = new rcmail($env);
      self::$instance->startup();  // init AFTER object was linked with self::$instance
    }
@@ -85,6 +87,10 @@
  protected function startup()
  {
    $this->init(self::INIT_WITH_DB | self::INIT_WITH_PLUGINS);
    // set filename if not index.php
    if (($basename = basename($_SERVER['SCRIPT_FILENAME'])) && $basename != 'index.php')
      $this->filename = $basename;
    // start session
    $this->session_init();
@@ -162,7 +168,7 @@
    setlocale(LC_ALL, $lang . '.utf8', $lang . '.UTF-8', 'en_US.utf8', 'en_US.UTF-8');
    // workaround for http://bugs.php.net/bug.php?id=18556
    if (in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) {
    if (version_compare(PHP_VERSION, '5.5.0', '<') && in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) {
      setlocale(LC_CTYPE, 'en_US.utf8', 'en_US.UTF-8');
    }
  }
@@ -228,6 +234,11 @@
    }
    if (!$contacts) {
      // there's no default, just return
      if ($default) {
        return null;
      }
      self::raise_error(array(
        'code' => 700, 'type' => 'php',
        'file' => __FILE__, 'line' => __LINE__,
@@ -248,6 +259,23 @@
    }
    return $contacts;
  }
  /**
   * Return identifier of the address book object
   *
   * @param rcube_addressbook Addressbook source object
   *
   * @return string Source identifier
   */
  public function get_address_book_id($object)
  {
    foreach ($this->address_books as $index => $book) {
      if ($book === $object) {
        return $index;
      }
    }
  }
@@ -289,7 +317,7 @@
        $list[$id] = array(
          'id'       => $id,
          'name'     => html::quote($prop['name']),
          'groups'   => is_array($prop['groups']),
          'groups'   => !empty($prop['groups']) || !empty($prop['group_filters']),
          'readonly' => !$prop['writable'],
          'hidden'   => $prop['hidden'],
          'autocomplete' => in_array($id, $autocomplete)
@@ -316,6 +344,44 @@
    }
    return $list;
  }
  /**
   * Getter for compose responses.
   * These are stored in local config and user preferences.
   *
   * @param boolean True to sort the list alphabetically
   * @param boolean True if only this user's responses shall be listed
   * @return array List of the current user's stored responses
   */
  public function get_compose_responses($sorted = false, $user_only = false)
  {
    $responses = array();
    if (!$user_only) {
      foreach ($this->config->get('compose_responses_static', array()) as $response) {
        if (empty($response['key']))
          $response['key'] = substr(md5($response['name']), 0, 16);
        $response['static'] = true;
        $response['class'] = 'readonly';
        $k = $sorted ? '0000-' . strtolower($response['name']) : $response['key'];
        $responses[$k] = $response;
      }
    }
    foreach ($this->config->get('compose_responses', array()) as $response) {
      if (empty($response['key']))
        $response['key'] = substr(md5($response['name']), 0, 16);
      $k = $sorted ? strtolower($response['name']) : $response['key'];
      $responses[$k] = $response;
    }
    // sort list by name
    if ($sorted) {
      ksort($responses, SORT_LOCALE_STRING);
    }
    return array_values($responses);
  }
@@ -346,6 +412,9 @@
    $this->output->set_env('action', $this->action);
    $this->output->set_env('comm_path', $this->comm_path);
    $this->output->set_charset(RCUBE_CHARSET);
    if ($this->user && $this->user->ID)
      $this->output->set_env('user_id', $this->user->get_hash());
    // add some basic labels to client
    $this->output->add_label('loading', 'servererror', 'requesttimedout', 'refreshing');
@@ -453,15 +522,22 @@
        $port = $config['default_port'];
    }
    /* Modify username with domain if required
       Inspired by Marco <P0L0_notspam_binware.org>
    */
    // Check if we need to add domain
    if (!empty($config['username_domain']) && strpos($username, '@') === false) {
      if (is_array($config['username_domain']) && isset($config['username_domain'][$host]))
        $username .= '@'.rcube_utils::parse_host($config['username_domain'][$host], $host);
      else if (is_string($config['username_domain']))
        $username .= '@'.rcube_utils::parse_host($config['username_domain'], $host);
    // Check if we need to add/force domain to username
    if (!empty($config['username_domain'])) {
      $domain = is_array($config['username_domain']) ? $config['username_domain'][$host] : $config['username_domain'];
      if ($domain = rcube_utils::parse_host((string)$domain, $host)) {
        $pos = strpos($username, '@');
        // force configured domains
        if (!empty($config['username_domain_forced']) && $pos !== false) {
          $username = substr($username, 0, $pos) . '@' . $domain;
        }
        // just add domain if not specified
        else if ($pos === false) {
          $username .= '@' . $domain;
        }
      }
    }
    if (!isset($config['login_lc'])) {
@@ -719,7 +795,7 @@
    $p['_task'] = $task;
    unset($p['task']);
    $url = './';
    $url = './' . $this->filename;
    $delm = '?';
    foreach (array_reverse($p) as $key => $val) {
      if ($val !== '' && $val !== null) {
@@ -742,11 +818,6 @@
    foreach ($this->address_books as $book) {
      if (is_object($book) && is_a($book, 'rcube_addressbook'))
        $book->close();
    }
    // before closing the database connection, write session data
    if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
      $this->session->write_close();
    }
    // write performance stats to logs/console
@@ -931,22 +1002,32 @@
    /**
     * Write login data (name, ID, IP address) to the 'userlogins' log file.
     */
    public function log_login()
    public function log_login($user = null, $failed_login = false, $error_code = 0)
    {
        if (!$this->config->get('log_logins')) {
            return;
        }
        $user_name = $this->get_user_name();
        $user_id   = $this->get_user_id();
        // failed login
        if ($failed_login) {
            $message = sprintf('Failed login for %s from %s in session %s (error: %d)',
                $user, rcube_utils::remote_ip(), session_id(), $error_code);
        }
        // successful login
        else {
            $user_name = $this->get_user_name();
            $user_id   = $this->get_user_id();
        if (!$user_id) {
            return;
            if (!$user_id) {
                return;
            }
            $message = sprintf('Successful login for %s (ID: %d) from %s in session %s',
                    $user_name, $user_id, rcube_utils::remote_ip(), session_id());
        }
        self::write_log('userlogins',
            sprintf('Successful login for %s (ID: %d) from %s in session %s',
                $user_name, $user_id, rcube_utils::remote_ip(), session_id()));
        // log login
        self::write_log('userlogins', $message);
    }
@@ -1201,13 +1282,22 @@
        }
        else {
            $js_mailboxlist = array();
            $out = html::tag('ul', $attrib, $rcmail->render_folder_tree_html($a_mailboxes, $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib);
            $tree = $rcmail->render_folder_tree_html($a_mailboxes, $mbox_name, $js_mailboxlist, $attrib);
            $rcmail->output->include_script('treelist.js');
            $rcmail->output->add_gui_object('mailboxlist', $attrib['id']);
            if ($type != 'js') {
                $out = html::tag('ul', $attrib, $tree, html::$common_attrib);
                $rcmail->output->include_script('treelist.js');
                $rcmail->output->add_gui_object('mailboxlist', $attrib['id']);
                $rcmail->output->set_env('unreadwrap', $attrib['unreadwrap']);
                $rcmail->output->set_env('collapsed_folders', (string)$rcmail->config->get('collapsed_folders'));
            }
            $rcmail->output->set_env('mailboxes', $js_mailboxlist);
            $rcmail->output->set_env('unreadwrap', $attrib['unreadwrap']);
            $rcmail->output->set_env('collapsed_folders', (string)$rcmail->config->get('collapsed_folders'));
            // we can't use object keys in javascript because they are unordered
            // we need sorted folders list for folder-selector widget
            $rcmail->output->set_env('mailboxes_list', array_keys($js_mailboxlist));
        }
        return $out;
@@ -1325,6 +1415,7 @@
        $realnames = (bool)$attrib['realnames'];
        $msgcounts = $this->storage->get_cache('messagecount');
        $collapsed = $this->config->get('collapsed_folders');
        $realnames = $this->config->get('show_real_foldernames');
        $out = '';
        foreach ($arrFolders as $folder) {
@@ -1391,8 +1482,12 @@
            $jslist[$folder['id']] = array(
                'id'      => $folder['id'],
                'name'    => $foldername,
                'virtual' => $folder['virtual']
                'virtual' => $folder['virtual'],
            );
            if (!empty($folder_class)) {
                $jslist[$folder['id']]['class'] = $folder_class;
            }
            if (!empty($folder['folders'])) {
                $out .= html::tag('ul', array('style' => ($is_collapsed ? "display:none;" : null)),
@@ -1481,8 +1576,10 @@
     */
    public function localize_foldername($name, $with_path = true)
    {
        $realnames = $this->config->get('show_real_foldernames');
        // try to localize path of the folder
        if ($with_path) {
        if ($with_path && !$realnames) {
            $storage   = $this->get_storage();
            $delimiter = $storage->get_hierarchy_delimiter();
            $path      = explode($delimiter, $name);
@@ -1499,12 +1596,11 @@
            }
        }
        if ($folder_class = $this->folder_classname($name)) {
        if (!$realnames && ($folder_class = $this->folder_classname($name))) {
            return $this->gettext($folder_class);
        }
        else {
            return rcube_charset::convert($name, 'UTF7-IMAP');
        }
        return rcube_charset::convert($name, 'UTF7-IMAP');
    }
@@ -1547,7 +1643,7 @@
        $rcmail->output->add_script('rcmail.set_quota('.rcube_output::json_serialize($quota).');', 'docready');
        return html::span($attrib, '');
        return html::span($attrib, '&nbsp;');
    }
@@ -1758,7 +1854,8 @@
    public function upload_init()
    {
        // Enable upload progress bar
        if (($seconds = $this->config->get('upload_progress')) && ini_get('apc.rfc1867')) {
        $rfc1867 = filter_var(ini_get('apc.rfc1867'), FILTER_VALIDATE_BOOLEAN);
        if ($rfc1867 && ($seconds = $this->config->get('upload_progress'))) {
            if ($field_name = ini_get('apc.rfc1867_name')) {
                $this->output->set_env('upload_progress_name', $field_name);
                $this->output->set_env('upload_progress_time', (int) $seconds);
@@ -1773,7 +1870,7 @@
        }
        $this->output->set_env('max_filesize', $max_filesize);
        $max_filesize = self::show_bytes($max_filesize);
        $max_filesize = $this->show_bytes($max_filesize);
        $this->output->set_env('filesizeerror', $this->gettext(array(
            'name' => 'filesizeerror', 'vars' => array('size' => $max_filesize))));