Thomas Bruederli
2013-10-21 f06aa8058b7e32ba32d4551074b6e0b8a300f751
program/include/rcmail.php
@@ -7,7 +7,10 @@
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2008-2011, The Roundcube Dev Team                       |
 | Copyright (C) 2011, Kolab Systems AG                                  |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 | Licensed under the GNU General Public License version 3 or            |
 | any later version with exceptions for skins & plugins.                |
 | See the README file for a full license statement.                     |
 |                                                                       |
 | PURPOSE:                                                              |
 |   Application class providing core functions and holding              |
@@ -126,6 +129,12 @@
  private $caches = array();
  private $action_map = array();
  private $shutdown_functions = array();
  private $expunge_cache = false;
  const ERROR_STORAGE          = -2;
  const ERROR_INVALID_REQUEST  = 1;
  const ERROR_INVALID_HOST     = 2;
  const ERROR_COOKIES_DISABLED = 3;
  /**
@@ -243,14 +252,16 @@
      $this->config->set_user_prefs((array)$this->user->get_prefs());
    }
    $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language']));
    $lang = $this->language_prop($this->config->get('language', $_SESSION['language']));
    $_SESSION['language'] = $this->user->language = $lang;
    // set localization
    setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8');
    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($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ')))
      setlocale(LC_CTYPE, 'en_US' . '.utf8');
    if (in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) {
      setlocale(LC_CTYPE, 'en_US.utf8', 'en_US.UTF-8');
    }
  }
@@ -335,15 +346,20 @@
      $this->memcache = new Memcache;
      $this->mc_available = 0;
      // add alll configured hosts to pool
      // add all configured hosts to pool
      $pconnect = $this->config->get('memcache_pconnect', true);
      foreach ($this->config->get('memcache_hosts', array()) as $host) {
        list($host, $port) = explode(':', $host);
        if (!$port) $port = 11211;
        if (substr($host, 0, 7) != 'unix://') {
          list($host, $port) = explode(':', $host);
          if (!$port) $port = 11211;
        }
        else {
          $port = 0;
        }
        $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));
      }
      // test connection and failover (will result in $this->mc_available == 0 on complete failure)
      $this->memcache->increment('__CONNECTIONTEST__', 1);  // NOP if key doesn't exist
@@ -353,14 +369,14 @@
    return $this->memcache;
  }
  /**
   * Callback for memcache failure
   */
  public function memcache_failure($host, $port)
  {
    static $seen = array();
    // only report once
    if (!$seen["$host:$port"]++) {
      $this->mc_available--;
@@ -449,6 +465,10 @@
        true, true);
    }
    // set configured sort order
    if ($sort_col = $this->config->get('addressbook_sort_col'))
        $contacts->set_sort_order($sort_col);
    // add to the 'books' array for shutdown function
    $this->address_books[$id] = $contacts;
@@ -486,7 +506,11 @@
    if ($ldap_config) {
      $ldap_config = (array) $ldap_config;
      foreach ($ldap_config as $id => $prop)
      foreach ($ldap_config as $id => $prop) {
        // handle misconfiguration
        if (empty($prop) || !is_array($prop)) {
          continue;
        }
        $list[$id] = array(
          'id'       => $id,
          'name'     => $prop['name'],
@@ -495,6 +519,7 @@
          'hidden'   => $prop['hidden'],
          'autocomplete' => in_array($id, $autocomplete)
        );
      }
    }
    $plugin = $this->plugins->exec_hook('addressbooks_list', array('sources' => $list));
@@ -543,7 +568,7 @@
    $this->output->set_charset(RCMAIL_CHARSET);
    // add some basic labels to client
    $this->output->add_label('loading', 'servererror');
    $this->output->add_label('loading', 'servererror', 'requesttimedout');
    return $this->output;
  }
@@ -715,11 +740,10 @@
      }
      else {
        $this->set_storage_prop();
        return $storage->is_connected();
      }
    }
    return false;
    return $storage->is_connected();
  }
@@ -755,8 +779,7 @@
    $this->session = new rcube_session($this->get_dbh(), $this->config);
    $this->session->register_gc_handler('rcmail_temp_gc');
    if ($this->config->get('enable_caching'))
      $this->session->register_gc_handler('rcmail_cache_gc');
    $this->session->register_gc_handler(array($this, 'cache_gc'));
    // start PHP session (if not in CLI mode)
    if ($_SERVER['REMOTE_ADDR'])
@@ -765,6 +788,10 @@
    // set initial session vars
    if (!$_SESSION['user_id'])
      $_SESSION['temp'] = true;
    // restore skin selection after logout
    if ($_SESSION['temp'] && !empty($_SESSION['skin']))
      $this->config->set('skin', $_SESSION['skin']);
  }
@@ -787,7 +814,7 @@
      $this->session->set_keep_alive($keep_alive);
    }
    $this->session->set_secret($this->config->get('des_key') . $_SERVER['HTTP_USER_AGENT']);
    $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME']));
    $this->session->set_ip_check($this->config->get('ip_check'));
  }
@@ -799,12 +826,20 @@
   * @param string Mail storage (IMAP) user name
   * @param string Mail storage (IMAP) password
   * @param string Mail storage (IMAP) host
   * @param bool   Enables cookie check
   *
   * @return boolean True on success, False on failure
   */
  function login($username, $pass, $host=NULL)
  function login($username, $pass, $host = null, $cookiecheck = false)
  {
    $this->login_error = null;
    if (empty($username)) {
      return false;
    }
    if ($cookiecheck && empty($_COOKIE)) {
      $this->login_error = self::ERROR_COOKIES_DISABLED;
      return false;
    }
@@ -824,11 +859,18 @@
          break;
        }
      }
      if (!$allowed)
        return false;
      if (!$allowed) {
        $host = null;
      }
    else if (!empty($config['default_host']) && $host != rcube_parse_host($config['default_host']))
    }
    else if (!empty($config['default_host']) && $host != rcube_parse_host($config['default_host'])) {
      $host = null;
    }
    if (!$host) {
      $this->login_error = self::ERROR_INVALID_HOST;
      return false;
    }
    // parse $host URL
    $a_host = parse_url($host);
@@ -859,7 +901,14 @@
    // Convert username to lowercase. If storage backend
    // is case-insensitive we need to store always the same username (#1487113)
    if ($config['login_lc']) {
      $username = mb_strtolower($username);
      if ($config['login_lc'] == 2 || $config['login_lc'] === true) {
        $username = mb_strtolower($username);
      }
      else if (strpos($username, '@')) {
        // lowercase domain name
        list($local, $domain) = explode('@', $username);
        $username = $local . '@' . mb_strtolower($domain);
      }
    }
    // try to resolve email address from virtuser table
@@ -869,17 +918,13 @@
    // Here we need IDNA ASCII
    // Only rcube_contacts class is using domain names in Unicode
    $host = rcube_idn_to_ascii($host);
    if (strpos($username, '@')) {
      // lowercase domain name
      list($local, $domain) = explode('@', $username);
      $username = $local . '@' . mb_strtolower($domain);
      $username = rcube_idn_to_ascii($username);
    }
    $host     = rcube_idn_to_ascii($host);
    $username = rcube_idn_to_ascii($username);
    // user already registered -> overwrite username
    if ($user = rcube_user::query($username, $host))
    if ($user = rcube_user::query($username, $host)) {
      $username = $user->data['username'];
    }
    if (!$this->storage)
      $this->storage_init();
@@ -951,7 +996,7 @@
      $_SESSION['storage_port'] = $port;
      $_SESSION['storage_ssl']  = $ssl;
      $_SESSION['password']     = $this->encrypt($pass);
      $_SESSION['login_time']   = mktime();
      $_SESSION['login_time']   = time();
      if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_')
        $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
@@ -966,6 +1011,23 @@
    return false;
  }
    /**
     * Returns error code of last login operation
     *
     * @return int Error code
     */
    public function login_error()
    {
        if ($this->login_error) {
            return $this->login_error;
        }
        if ($this->storage && $this->storage->get_error_code() < -1) {
            return self::ERROR_STORAGE;
        }
    }
  /**
@@ -1002,26 +1064,32 @@
    if (is_array($default_host)) {
      $post_host = get_input_value('_host', RCUBE_INPUT_POST);
      $post_user = get_input_value('_user', RCUBE_INPUT_POST);
      list($user, $domain) = explode('@', $post_user);
      // direct match in default_host array
      if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) {
      if ($default_host[$post_host] || in_array($post_host, $default_host)) {
        $host = $post_host;
      }
      // try to select host by mail domain
      list($user, $domain) = explode('@', get_input_value('_user', RCUBE_INPUT_POST));
      if (!empty($domain)) {
      else if (!empty($domain)) {
        foreach ($default_host as $storage_host => $mail_domains) {
          if (is_array($mail_domains) && in_array($domain, $mail_domains)) {
          if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) {
            $host = $storage_host;
            break;
          }
          else if (stripos($storage_host, $domain) !== false || stripos(strval($mail_domains), $domain) !== false) {
            $host = is_numeric($storage_host) ? $mail_domains : $storage_host;
            break;
          }
        }
      }
      // take the first entry if $host is still an array
      // take the first entry if $host is still not set
      if (empty($host)) {
        $host = array_shift($default_host);
        list($key, $val) = each($default_host);
        $host = is_numeric($key) ? $val : $key;
      }
    }
    else if (empty($default_host)) {
@@ -1147,7 +1215,7 @@
        $this->texts = array_merge($this->texts, $messages);
      // include user language files
      if ($lang != 'en' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
      if ($lang != 'en' && $lang != 'en_US' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
        include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc');
        include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc');
@@ -1204,7 +1272,7 @@
    $this->plugins->exec_hook('session_destroy');
    $this->session->kill();
    $_SESSION = array('language' => $this->user->language, 'temp' => true);
    $_SESSION = array('language' => $this->user->language, 'temp' => true, 'skin' => $this->config->get('skin'));
    $this->user->reset();
  }
@@ -1261,8 +1329,11 @@
            $cache->close();
    }
    if (is_object($this->storage))
    if (is_object($this->storage)) {
        if ($this->expunge_cache)
            $this->storage->expunge_cache();
      $this->storage->close();
  }
    // before closing the database connection, write session data
    if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
@@ -1295,6 +1366,18 @@
  public function add_shutdown_function($function)
  {
    $this->shutdown_functions[] = $function;
  }
  /**
   * Garbage collector for cache entries.
   * Set flag to expunge caches on shutdown
   */
  function cache_gc()
  {
    // because this gc function is called before storage is initialized,
    // we just set a flag to expunge storage cache on shutdown.
    $this->expunge_cache = true;
  }
@@ -1490,73 +1573,13 @@
    $url = './';
    $delm = '?';
    foreach (array_reverse($p) as $key => $val) {
      if ($val !== '') {
      if ($val !== '' && $val !== null) {
        $par = $key[0] == '_' ? $key : '_'.$key;
        $url .= $delm.urlencode($par).'='.urlencode($val);
        $delm = '&';
      }
    }
    return $url;
  }
  /**
   * Use imagemagick or GD lib to read image properties
   *
   * @param string Absolute file path
   * @return mixed Hash array with image props like type, width, height or False on error
   */
  public static function imageprops($filepath)
  {
    $rcmail = rcmail::get_instance();
    if ($cmd = $rcmail->config->get('im_identify_path', false)) {
      list(, $type, $size) = explode(' ', strtolower(rcmail::exec($cmd. ' 2>/dev/null {in}', array('in' => $filepath))));
      if ($size)
        list($width, $height) = explode('x', $size);
    }
    else if (function_exists('getimagesize')) {
      $imsize = @getimagesize($filepath);
      $width = $imsize[0];
      $height = $imsize[1];
      $type = preg_replace('!image/!', '', $imsize['mime']);
    }
    return $type ? array('type' => $type, 'width' => $width, 'height' => $height) : false;
  }
  /**
   * Convert an image to a given size and type using imagemagick (ensures input is an image)
   *
   * @param $p['in']  Input filename (mandatory)
   * @param $p['out'] Output filename (mandatory)
   * @param $p['size']  Width x height of resulting image, e.g. "160x60"
   * @param $p['type']  Output file type, e.g. "jpg"
   * @param $p['-opts'] Custom command line options to ImageMagick convert
   * @return Success of convert as true/false
   */
  public static function imageconvert($p)
  {
    $result = false;
    $rcmail = rcmail::get_instance();
    $convert  = $rcmail->config->get('im_convert_path', false);
    $identify = $rcmail->config->get('im_identify_path', false);
    // imagemagick is required for this
    if (!$convert)
        return false;
    if (!(($imagetype = @exif_imagetype($p['in'])) && ($type = image_type_to_extension($imagetype, false))))
      list(, $type) = explode(' ', strtolower(rcmail::exec($identify . ' 2>/dev/null {in}', $p))); # for things like eps
    $type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps"));
    $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75);
    $p['-opts'] = array('-resize' => $p['size'].'>') + (array)$p['-opts'];
    if (in_array($type, explode(',', $p['types']))) # Valid type?
      $result = rcmail::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {in} {type}:{out}', $p) === "";
    return $result;
  }