Index: config/main.inc.php.dist
===================================================================
--- config/main.inc.php.dist	(revision 2350)
+++ config/main.inc.php.dist	(working copy)
@@ -57,6 +57,10 @@
 // TCP port used for IMAP connections
 $rcmail_config['default_port'] = 143;
 
+// Captcha security image when logging in
+// possible values true or false
+$rcmail_config['use_captcha'] = true;
+
 // IMAP auth type. Can be "auth" (CRAM-MD5), "plain" (PLAIN) or "check" to auto detect.
 // Optional, defaults to "check"
 $rcmail_config['imap_auth_type'] = null;
Index: index.php
===================================================================
--- index.php	(revision 2350)
+++ index.php	(working copy)
@@ -42,13 +42,22 @@
   if (function_exists('ob_gzhandler')
       && !ini_get('zlib.output_compression')
       && ini_get('output_handler') != 'ob_gzhandler') {
-    ob_start('ob_gzhandler');
+        // ob_start('ob_gzhandler');
   }
   else {
-    ob_start();
+        // ob_start();
   }
 }
 
+// include task specific files
+if (($RCMAIL->task == 'login') && ($RCMAIL->action == 'sendcaptcha')) {
+    // include('program/include/rcube_captcha.inc');
+    $oCaptcha = new rcube_captcha(90, 30, 6);
+    rcube_captcha::SendImageToBrowser($oCaptcha);
+    exit();
+}
+
+
 // check if config files had errors
 if ($err_str = $RCMAIL->config->get_error()) {
   raise_error(array(
@@ -80,7 +89,7 @@
   }
   else if ($_SESSION['temp'] && !empty($_POST['_user']) && !empty($_POST['_pass']) &&
            $RCMAIL->login(trim(get_input_value('_user', RCUBE_INPUT_POST), ' '),
-              get_input_value('_pass', RCUBE_INPUT_POST, true, 'ISO-8859-1'), $host)) {
+              get_input_value('_pass', RCUBE_INPUT_POST, true, 'ISO-8859-1'), get_input_value('_captcha', RCUBE_INPUT_POST), $host)) {
     // create new session ID
     unset($_SESSION['temp']);
     rcube_sess_regenerate_id();
Index: program/font/VeraMono.ttf
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: program\font\VeraMono.ttf
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: program/include/rcmail.php
===================================================================
--- program/include/rcmail.php	(revision 2350)
+++ program/include/rcmail.php	(working copy)
@@ -1,955 +1,962 @@
-<?php
-
-/*
- +-----------------------------------------------------------------------+
- | program/include/rcmail.php                                            |
- |                                                                       |
- | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2008, RoundCube Dev. - Switzerland                      |
- | Licensed under the GNU GPL                                            |
- |                                                                       |
- | PURPOSE:                                                              |
- |   Application class providing core functions and holding              |
- |   instances of all 'global' objects like db- and imap-connections     |
- +-----------------------------------------------------------------------+
- | Author: Thomas Bruederli <roundcube@gmail.com>                        |
- +-----------------------------------------------------------------------+
-
- $Id: rcmail.php 328 2006-08-30 17:41:21Z thomasb $
-
-*/
-
-
-/**
- * Application class of RoundCube Webmail
- * implemented as singleton
- *
- * @package Core
- */
-class rcmail
-{
-  static public $main_tasks = array('mail','settings','addressbook','login','logout');
-  
-  static private $instance;
-  
-  public $config;
-  public $user;
-  public $db;
-  public $imap;
-  public $output;
-  public $task = 'mail';
-  public $action = '';
-  public $comm_path = './';
-  
-  private $texts;
-  
-  
-  /**
-   * This implements the 'singleton' design pattern
-   *
-   * @return object qvert The one and only instance
-   */
-  static function get_instance()
-  {
-    if (!self::$instance) {
-      self::$instance = new rcmail();
-      self::$instance->startup();  // init AFTER object was linked with self::$instance
-    }
-
-    return self::$instance;
-  }
-  
-  
-  /**
-   * Private constructor
-   */
-  private function __construct()
-  {
-    // load configuration
-    $this->config = new rcube_config();
-    
-    register_shutdown_function(array($this, 'shutdown'));
-  }
-  
-  
-  /**
-   * Initial startup function
-   * to register session, create database and imap connections
-   *
-   * @todo Remove global vars $DB, $USER
-   */
-  private function startup()
-  {
-    $config_all = $this->config->all();
-
-    // initialize syslog
-    if ($this->config->get('log_driver') == 'syslog') {
-      $syslog_id = $this->config->get('syslog_id', 'roundcube');
-      $syslog_facility = $this->config->get('syslog_facility', LOG_USER);
-      openlog($syslog_id, LOG_ODELAY, $syslog_facility);
-    }
-    				
-    // set task and action properties
-    $this->set_task(strip_quotes(get_input_value('_task', RCUBE_INPUT_GPC)));
-    $this->action = asciiwords(get_input_value('_action', RCUBE_INPUT_GPC));
-
-    // connect to database
-    $GLOBALS['DB'] = $this->get_dbh();
-
-    // use database for storing session data
-    include_once('include/session.inc');
-
-    // set session domain
-    if (!empty($config_all['session_domain'])) {
-      ini_set('session.cookie_domain', $config_all['session_domain']);
-    }
-    // set session garbage collecting time according to session_lifetime
-    if (!empty($config_all['session_lifetime'])) {
-      ini_set('session.gc_maxlifetime', ($config_all['session_lifetime']) * 120);
-    }
-
-    // start PHP session (if not in CLI mode)
-    if ($_SERVER['REMOTE_ADDR'])
-      session_start();
-
-    // set initial session vars
-    if (!isset($_SESSION['auth_time'])) {
-      $_SESSION['auth_time'] = time();
-      $_SESSION['temp'] = true;
-    }
-
-    // create user object
-    $this->set_user(new rcube_user($_SESSION['user_id']));
-
-    // reset some session parameters when changing task
-    if ($_SESSION['task'] != $this->task)
-      unset($_SESSION['page']);
-
-    // set current task to session
-    $_SESSION['task'] = $this->task;
-
-    // create IMAP object
-    if ($this->task == 'mail')
-      $this->imap_init();
-  }
-  
-  
-  /**
-   * Setter for application task
-   *
-   * @param string Task to set
-   */
-  public function set_task($task)
-  {
-    if (!in_array($task, self::$main_tasks))
-      $task = 'mail';
-    
-    $this->task = $task;
-    $this->comm_path = $this->url(array('task' => $task));
-    
-    if ($this->output)
-      $this->output->set_env('task', $task);
-  }
-  
-  
-  /**
-   * Setter for system user object
-   *
-   * @param object rcube_user Current user instance
-   */
-  public function set_user($user)
-  {
-    if (is_object($user)) {
-      $this->user = $user;
-      $GLOBALS['USER'] = $this->user;
-      
-      // overwrite config with user preferences
-      $this->config->merge((array)$this->user->get_prefs());
-    }
-    
-    $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language']));
-
-    // set localization
-    setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8');
-
-    // 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'); 
-  }
-  
-  
-  /**
-   * Check the given string and return a valid language code
-   *
-   * @param string Language code
-   * @return string Valid language code
-   */
-  private function language_prop($lang)
-  {
-    static $rcube_languages, $rcube_language_aliases;
-    
-    // user HTTP_ACCEPT_LANGUAGE if no language is specified
-    if (empty($lang) || $lang == 'auto') {
-       $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
-       $lang = str_replace('-', '_', $accept_langs[0]);
-     }
-     
-    if (empty($rcube_languages)) {
-      @include(INSTALL_PATH . 'program/localization/index.inc');
-    }
-    
-    // check if we have an alias for that language
-    if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
-      $lang = $rcube_language_aliases[$lang];
-    }
-    // try the first two chars
-    else if (!isset($rcube_languages[$lang])) {
-      $short = substr($lang, 0, 2);
-     
-      // check if we have an alias for the short language code
-      if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {
-        $lang = $rcube_language_aliases[$short];
-      }
-      // expand 'nn' to 'nn_NN'
-      else if (!isset($rcube_languages[$short])) {
-        $lang = $short.'_'.strtoupper($short);
-      }
-    }
-
-    if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
-      $lang = 'en_US';
-    }
-
-    return $lang;
-  }
-  
-  
-  /**
-   * Get the current database connection
-   *
-   * @return object rcube_mdb2  Database connection object
-   */
-  public function get_dbh()
-  {
-    if (!$this->db) {
-      $config_all = $this->config->all();
-
-      $this->db = new rcube_mdb2($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']);
-      $this->db->sqlite_initials = INSTALL_PATH . 'SQL/sqlite.initial.sql';
-      $this->db->set_debug((bool)$config_all['sql_debug']);
-      $this->db->db_connect('w');
-    }
-
-    return $this->db;
-  }
-  
-  
-  /**
-   * Return instance of the internal address book class
-   *
-   * @param boolean True if the address book needs to be writeable
-   * @return object rcube_contacts Address book object
-   */
-  public function get_address_book($id, $writeable = false)
-  {
-    $contacts = null;
-    $ldap_config = (array)$this->config->get('ldap_public');
-    $abook_type = strtolower($this->config->get('address_book_type'));
-    
-    if ($id && $ldap_config[$id]) {
-      $contacts = new rcube_ldap($ldap_config[$id]);
-    }
-    else if ($abook_type == 'ldap') {
-      // Use the first writable LDAP address book.
-      foreach ($ldap_config as $id => $prop) {
-        if (!$writeable || $prop['writable']) {
-          $contacts = new rcube_ldap($prop);
-          break;
-        }
-      }
-    }
-    else {
-      $contacts = new rcube_contacts($this->db, $this->user->ID);
-    }
-    
-    return $contacts;
-  }
-  
-  
-  /**
-   * Init output object for GUI and add common scripts.
-   * This will instantiate a rcmail_template object and set
-   * environment vars according to the current session and configuration
-   *
-   * @param boolean True if this request is loaded in a (i)frame
-   * @return object rcube_template Reference to HTML output object
-   */
-  public function load_gui($framed = false)
-  {
-    // init output page
-    if (!($this->output instanceof rcube_template))
-      $this->output = new rcube_template($this->task, $framed);
-
-    foreach (array('flag_for_deletion','read_when_deleted') as $js_config_var) {
-      $this->output->set_env($js_config_var, $this->config->get($js_config_var));
-    }
-    
-    // set keep-alive/check-recent interval
-    if ($keep_alive = $this->config->get('keep_alive')) {
-      // be sure that it's less than session lifetime
-      if ($session_lifetime = $this->config->get('session_lifetime'))
-        $keep_alive = min($keep_alive, $session_lifetime * 60 - 30);
-      $this->output->set_env('keep_alive', max(60, $keep_alive));
-    }
-
-    if ($framed) {
-      $this->comm_path .= '&_framed=1';
-      $this->output->set_env('framed', true);
-    }
-
-    $this->output->set_env('task', $this->task);
-    $this->output->set_env('action', $this->action);
-    $this->output->set_env('comm_path', $this->comm_path);
-    $this->output->set_charset($this->config->get('charset', RCMAIL_CHARSET));
-
-    // add some basic label to client
-    $this->output->add_label('loading');
-    
-    return $this->output;
-  }
-  
-  
-  /**
-   * Create an output object for JSON responses
-   *
-   * @return object rcube_json_output Reference to JSON output object
-   */
-  public function init_json()
-  {
-    if (!($this->output instanceof rcube_json_output))
-      $this->output = new rcube_json_output($this->task);
-    
-    return $this->output;
-  }
-  
-  
-  /**
-   * Create global IMAP object and connect to server
-   *
-   * @param boolean True if connection should be established
-   * @todo Remove global $IMAP
-   */
-  public function imap_init($connect = false)
-  {
-    $this->imap = new rcube_imap($this->db);
-    $this->imap->debug_level = $this->config->get('debug_level');
-    $this->imap->skip_deleted = $this->config->get('skip_deleted');
-
-    // enable caching of imap data
-    if ($this->config->get('enable_caching')) {
-      $this->imap->set_caching(true);
-    }
-
-    // set pagesize from config
-    $this->imap->set_pagesize($this->config->get('pagesize', 50));
-    
-    // Setting root and delimiter before iil_Connect can save time detecting them
-    // using NAMESPACE and LIST 
-    $options = array(
-      'imap' => $this->config->get('imap_auth_type', 'check'),
-      'delimiter' => isset($_SESSION['imap_delimiter']) ? $_SESSION['imap_delimiter'] : $this->config->get('imap_delimiter'),
-    );
-    
-    if (isset($_SESSION['imap_root']))
-      $options['rootdir'] = $_SESSION['imap_root'];
-    else if ($imap_root = $this->config->get('imap_root'))
-      $options['rootdir'] = $imap_root;
-    
-    $this->imap->set_options($options);
-  
-    // set global object for backward compatibility
-    $GLOBALS['IMAP'] = $this->imap;
-    
-    if ($connect)
-      $this->imap_connect();
-  }
-
-
-  /**
-   * Connect to IMAP server with stored session data
-   *
-   * @return bool True on success, false on error
-   */
-  public function imap_connect()
-  {
-    $conn = false;
-    
-    if ($_SESSION['imap_host'] && !$this->imap->conn) {
-      if (!($conn = $this->imap->connect($_SESSION['imap_host'], $_SESSION['username'], $this->decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']))) {
-        if ($this->output)
-          $this->output->show_message($this->imap->error_code == -1 ? 'imaperror' : 'sessionerror', 'error');
-      }
-
-      $this->set_imap_prop();
-    }
-
-    return $conn;
-  }
-
-
-  /**
-   * Perfom login to the IMAP server and to the webmail service.
-   * This will also create a new user entry if auto_create_user is configured.
-   *
-   * @param string IMAP user name
-   * @param string IMAP password
-   * @param string IMAP host
-   * @return boolean True on success, False on failure
-   */
-  function login($username, $pass, $host=NULL)
-  {
-    $user = NULL;
-    $config = $this->config->all();
-
-    if (!$host)
-      $host = $config['default_host'];
-
-    // Validate that selected host is in the list of configured hosts
-    if (is_array($config['default_host'])) {
-      $allowed = false;
-      foreach ($config['default_host'] as $key => $host_allowed) {
-        if (!is_numeric($key))
-          $host_allowed = $key;
-        if ($host == $host_allowed) {
-          $allowed = true;
-          break;
-        }
-      }
-      if (!$allowed)
-        return false;
-      }
-    else if (!empty($config['default_host']) && $host != $config['default_host'])
-      return false;
-
-    // parse $host URL
-    $a_host = parse_url($host);
-    if ($a_host['host']) {
-      $host = $a_host['host'];
-      $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
-      $imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : $config['default_port']);
-    }
-    else
-      $imap_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, '@')) {
-      if (is_array($config['username_domain']) && isset($config['username_domain'][$host]))
-        $username .= '@'.$config['username_domain'][$host];
-      else if (is_string($config['username_domain']))
-        $username .= '@'.$config['username_domain'];
-    }
-
-    // try to resolve email address from virtuser table    
-    if (!empty($config['virtuser_file']) && strpos($username, '@'))
-      $username = rcube_user::email2user($username);
-
-    // lowercase username if it's an e-mail address (#1484473)
-    if (strpos($username, '@'))
-      $username = rc_strtolower($username);
-
-    // user already registered -> overwrite username
-    if ($user = rcube_user::query($username, $host))
-      $username = $user->data['username'];
-
-    // exit if IMAP login failed
-    if (!($imap_login  = $this->imap->connect($host, $username, $pass, $imap_port, $imap_ssl)))
-      return false;
-
-    // user already registered -> update user's record
-    if (is_object($user)) {
-      $user->touch();
-    }
-    // create new system user
-    else if ($config['auto_create_user']) {
-      if ($created = rcube_user::create($username, $host)) {
-        $user = $created;
-
-        // get existing mailboxes (but why?)
-        // $a_mailboxes = $this->imap->list_mailboxes();
-      }
-    }
-    else {
-      raise_error(array(
-        'code' => 600,
-        'type' => 'php',
-        'file' => RCMAIL_CONFIG_DIR."/main.inc.php",
-        'message' => "Acces denied for new user $username. 'auto_create_user' is disabled"
-        ), true, false);
-    }
-
-    // login succeeded
-    if (is_object($user) && $user->ID) {
-      $this->set_user($user);
-
-      // set session vars
-      $_SESSION['user_id']   = $user->ID;
-      $_SESSION['username']  = $user->data['username'];
-      $_SESSION['imap_host'] = $host;
-      $_SESSION['imap_port'] = $imap_port;
-      $_SESSION['imap_ssl']  = $imap_ssl;
-      $_SESSION['password']  = $this->encrypt_passwd($pass);
-      $_SESSION['login_time'] = mktime();
-      
-      if ($_REQUEST['_timezone'] != '_default_')
-        $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
-
-      // force reloading complete list of subscribed mailboxes
-      $this->set_imap_prop();
-      $this->imap->clear_cache('mailboxes');
-
-      if ($config['create_default_folders'])
-          $this->imap->create_default_folders();
-
-      return true;
-    }
-
-    return false;
-  }
-
-
-  /**
-   * Set root dir and last stored mailbox
-   * This must be done AFTER connecting to the server!
-   */
-  public function set_imap_prop()
-  {
-    $this->imap->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
-
-    if ($default_folders = $this->config->get('default_imap_folders')) {
-      $this->imap->set_default_mailboxes($default_folders);
-    }
-    if (!empty($_SESSION['mbox'])) {
-      $this->imap->set_mailbox($_SESSION['mbox']);
-    }
-    if (isset($_SESSION['page'])) {
-      $this->imap->set_page($_SESSION['page']);
-    }
-    
-    // cache IMAP root and delimiter in session for performance reasons
-    $_SESSION['imap_root'] = $this->imap->root_dir;
-    $_SESSION['imap_delimiter'] = $this->imap->delimiter;
-  }
-
-
-  /**
-   * Auto-select IMAP host based on the posted login information
-   *
-   * @return string Selected IMAP host
-   */
-  public function autoselect_host()
-  {
-    $default_host = $this->config->get('default_host');
-    $host = null;
-    
-    if (is_array($default_host)) {
-      $post_host = get_input_value('_host', RCUBE_INPUT_POST);
-      
-      // direct match in default_host array
-      if ($default_host[$post_host] || in_array($post_host, array_values($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)) {
-        foreach ($default_host as $imap_host => $mail_domains) {
-          if (is_array($mail_domains) && in_array($domain, $mail_domains)) {
-            $host = $imap_host;
-            break;
-          }
-        }
-      }
-
-      // take the first entry if $host is still an array
-      if (empty($host)) {
-        $host = array_shift($default_host);
-      }
-    }
-    else if (empty($default_host)) {
-      $host = get_input_value('_host', RCUBE_INPUT_POST);
-    }
-    else
-      $host = $default_host;
-
-    return $host;
-  }
-
-
-  /**
-   * Get localized text in the desired language
-   *
-   * @param mixed Named parameters array or label name
-   * @return string Localized text
-   */
-  public function gettext($attrib)
-  {
-    // load localization files if not done yet
-    if (empty($this->texts))
-      $this->load_language();
-    
-    // extract attributes
-    if (is_string($attrib))
-      $attrib = array('name' => $attrib);
-
-    $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
-    $vars = isset($attrib['vars']) ? $attrib['vars'] : '';
-
-    $command_name = !empty($attrib['command']) ? $attrib['command'] : NULL;
-    $alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : '');
-
-    // text does not exist
-    if (!($text_item = $this->texts[$alias])) {
-      /*
-      raise_error(array(
-        'code' => 500,
-        'type' => 'php',
-        'line' => __LINE__,
-        'file' => __FILE__,
-        'message' => "Missing localized text for '$alias' in '$sess_user_lang'"), TRUE, FALSE);
-      */
-      return "[$alias]";
-    }
-
-    // make text item array 
-    $a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
-
-    // decide which text to use
-    if ($nr == 1) {
-      $text = $a_text_item['single'];
-    }
-    else if ($nr > 0) {
-      $text = $a_text_item['multiple'];
-    }
-    else if ($nr == 0) {
-      if ($a_text_item['none'])
-        $text = $a_text_item['none'];
-      else if ($a_text_item['single'])
-        $text = $a_text_item['single'];
-      else if ($a_text_item['multiple'])
-        $text = $a_text_item['multiple'];
-    }
-
-    // default text is single
-    if ($text == '') {
-      $text = $a_text_item['single'];
-    }
-
-    // replace vars in text
-    if (is_array($attrib['vars'])) {
-      foreach ($attrib['vars'] as $var_key => $var_value)
-        $a_replace_vars[$var_key{0}=='$' ? substr($var_key, 1) : $var_key] = $var_value;
-    }
-
-    if ($a_replace_vars)
-      $text = preg_replace('/\$\{?([_a-z]{1}[_a-z0-9]*)\}?/ei', '$a_replace_vars["\1"]', $text);
-
-    // format output
-    if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
-      return ucfirst($text);
-    else if ($attrib['uppercase'])
-      return strtoupper($text);
-    else if ($attrib['lowercase'])
-      return strtolower($text);
-
-    return $text;
-  }
-
-
-  /**
-   * Load a localization package
-   *
-   * @param string Language ID
-   */
-  public function load_language($lang = null)
-  {
-    $lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));
-    
-    // load localized texts
-    if (empty($this->texts) || $lang != $_SESSION['language']) {
-      $this->texts = array();
-
-      // get english labels (these should be complete)
-      @include(INSTALL_PATH . 'program/localization/en_US/labels.inc');
-      @include(INSTALL_PATH . 'program/localization/en_US/messages.inc');
-
-      if (is_array($labels))
-        $this->texts = $labels;
-      if (is_array($messages))
-        $this->texts = array_merge($this->texts, $messages);
-
-      // include user language files
-      if ($lang != 'en' && 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');
-
-        if (is_array($labels))
-          $this->texts = array_merge($this->texts, $labels);
-        if (is_array($messages))
-          $this->texts = array_merge($this->texts, $messages);
-      }
-      
-      $_SESSION['language'] = $lang;
-    }
-  }
-
-
-  /**
-   * Read directory program/localization and return a list of available languages
-   *
-   * @return array List of available localizations
-   */
-  public function list_languages()
-  {
-    static $sa_languages = array();
-
-    if (!sizeof($sa_languages)) {
-      @include(INSTALL_PATH . 'program/localization/index.inc');
-
-      if ($dh = @opendir(INSTALL_PATH . 'program/localization')) {
-        while (($name = readdir($dh)) !== false) {
-          if ($name{0}=='.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name))
-            continue;
-
-          if ($label = $rcube_languages[$name])
-            $sa_languages[$name] = $label ? $label : $name;
-        }
-        closedir($dh);
-      }
-    }
-
-    return $sa_languages;
-  }
-
-
-  /**
-   * Check the auth hash sent by the client against the local session credentials
-   *
-   * @return boolean True if valid, False if not
-   */
-  function authenticate_session()
-  {
-    global $SESS_CLIENT_IP, $SESS_CHANGED;
-
-    // advanced session authentication
-    if ($this->config->get('double_auth')) {
-      $now = time();
-      $valid = ($_COOKIE['sessauth'] == $this->get_auth_hash(session_id(), $_SESSION['auth_time']) ||
-                $_COOKIE['sessauth'] == $this->get_auth_hash(session_id(), $_SESSION['last_auth']));
-
-      // renew auth cookie every 5 minutes (only for GET requests)
-      if (!$valid || ($_SERVER['REQUEST_METHOD']!='POST' && $now - $_SESSION['auth_time'] > 300)) {
-        $_SESSION['last_auth'] = $_SESSION['auth_time'];
-        $_SESSION['auth_time'] = $now;
-        rcmail::setcookie('sessauth', $this->get_auth_hash(session_id(), $now), 0);
-      }
-    }
-    else {
-      $valid = $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] == $SESS_CLIENT_IP : true;
-    }
-
-    // check session filetime
-    $lifetime = $this->config->get('session_lifetime');
-    if (!empty($lifetime) && isset($SESS_CHANGED) && $SESS_CHANGED + $lifetime*60 < time()) {
-      $valid = false;
-    }
-
-    return $valid;
-  }
-
-
-  /**
-   * Destroy session data and remove cookie
-   */
-  public function kill_session()
-  {
-    $_SESSION = array('language' => $this->user->language, 'auth_time' => time(), 'temp' => true);
-    rcmail::setcookie('sessauth', '-del-', time() - 60);
-    $this->user->reset();
-  }
-
-
-  /**
-   * Do server side actions on logout
-   */
-  public function logout_actions()
-  {
-    $config = $this->config->all();
-    
-    // on logout action we're not connected to imap server  
-    if (($config['logout_purge'] && !empty($config['trash_mbox'])) || $config['logout_expunge']) {
-      if (!$this->authenticate_session())
-        return;
-
-      $this->imap_init(true);
-    }
-
-    if ($config['logout_purge'] && !empty($config['trash_mbox'])) {
-      $this->imap->clear_mailbox($config['trash_mbox']);
-    }
-
-    if ($config['logout_expunge']) {
-      $this->imap->expunge('INBOX');
-    }
-  }
-
-
-  /**
-   * Function to be executed in script shutdown
-   * Registered with register_shutdown_function()
-   */
-  public function shutdown()
-  {
-    if (is_object($this->imap)) {
-      $this->imap->close();
-      $this->imap->write_cache();
-    }
-
-    if (is_object($this->contacts))
-      $this->contacts->close();
-
-    // before closing the database connection, write session data
-    if ($_SERVER['REMOTE_ADDR'])
-      session_write_close();
-  }
-  
-  
-  /**
-   * Create unique authorization hash
-   *
-   * @param string Session ID
-   * @param int Timestamp
-   * @return string The generated auth hash
-   */
-  private function get_auth_hash($sess_id, $ts)
-  {
-    $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s',
-      $sess_id,
-      $ts,
-      $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***',
-      $_SERVER['HTTP_USER_AGENT']);
-
-    if (function_exists('sha1'))
-      return sha1($auth_string);
-    else
-      return md5($auth_string);
-  }
-
-  /**
-   * Encrypt IMAP password using DES encryption
-   *
-   * @param string Password to encrypt
-   * @return string Encryprted string
-   */
-  public function encrypt_passwd($pass)
-  {
-    if (function_exists('mcrypt_module_open') && ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, ""))) {
-      $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
-      mcrypt_generic_init($td, $this->config->get_des_key(), $iv);
-      $cypher = mcrypt_generic($td, $pass);
-      mcrypt_generic_deinit($td);
-      mcrypt_module_close($td);
-    }
-    else if (function_exists('des')) {
-      $cypher = des($this->config->get_des_key(), $pass, 1, 0, NULL);
-    }
-    else {
-      $cypher = $pass;
-
-      raise_error(array(
-        'code' => 500,
-        'type' => 'php',
-        'file' => __FILE__,
-        'message' => "Could not convert encrypt password. Make sure Mcrypt is installed or lib/des.inc is available"
-        ), true, false);
-    }
-
-    return base64_encode($cypher);
-  }
-
-
-  /**
-   * Decrypt IMAP password using DES encryption
-   *
-   * @param string Encrypted password
-   * @return string Plain password
-   */
-  public function decrypt_passwd($cypher)
-  {
-    if (function_exists('mcrypt_module_open') && ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, ""))) {
-      $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
-      mcrypt_generic_init($td, $this->config->get_des_key(), $iv);
-      $pass = mdecrypt_generic($td, base64_decode($cypher));
-      mcrypt_generic_deinit($td);
-      mcrypt_module_close($td);
-    }
-    else if (function_exists('des')) {
-      $pass = des($this->config->get_des_key(), base64_decode($cypher), 0, 0, NULL);
-    }
-    else {
-      $pass = base64_decode($cypher);
-    }
-
-    return preg_replace('/\x00/', '', $pass);
-  }
-
-
-  /**
-   * Build a valid URL to this instance of RoundCube
-   *
-   * @param mixed Either a string with the action or url parameters as key-value pairs
-   * @return string Valid application URL
-   */
-  public function url($p)
-  {
-    if (!is_array($p))
-      $p = array('_action' => @func_get_arg(0));
-
-    if (!$p['task'] || !in_array($p['task'], rcmail::$main_tasks))
-      $p['task'] = $this->task;
-
-    $p['_task'] = $p['task'];
-    unset($p['task']);
-
-    $url = './';
-    $delm = '?';
-    foreach (array_reverse($p) as $par => $val)
-    {
-      if (!empty($val)) {
-        $url .= $delm.urlencode($par).'='.urlencode($val);
-        $delm = '&';
-      }
-    }
-    return $url;
-  }
-
-
-  /**
-   * Helper method to set a cookie with the current path and host settings
-   *
-   * @param string Cookie name
-   * @param string Cookie value
-   * @param string Expiration time
-   */
-  public static function setcookie($name, $value, $exp = 0)
-  {
-    $cookie = session_get_cookie_params();
-    setcookie($name, $value, $exp, $cookie['path'], $cookie['domain'],
-      ($_SERVER['HTTPS'] && ($_SERVER['HTTPS'] != 'off')));
-  }
-}
-
-
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcmail.php                                            |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2008, RoundCube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Application class providing core functions and holding              |
+ |   instances of all 'global' objects like db- and imap-connections     |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcmail.php 328 2006-08-30 17:41:21Z thomasb $
+
+*/
+
+
+/**
+ * Application class of RoundCube Webmail
+ * implemented as singleton
+ *
+ * @package Core
+ */
+class rcmail
+{
+  static public $main_tasks = array('mail','settings','addressbook','login','logout');
+  
+  static private $instance;
+  
+  public $config;
+  public $user;
+  public $db;
+  public $imap;
+  public $output;
+  public $task = 'mail';
+  public $action = '';
+  public $comm_path = './';
+  
+  private $texts;
+  
+  
+  /**
+   * This implements the 'singleton' design pattern
+   *
+   * @return object qvert The one and only instance
+   */
+  static function get_instance()
+  {
+    if (!self::$instance) {
+      self::$instance = new rcmail();
+      self::$instance->startup();  // init AFTER object was linked with self::$instance
+    }
+
+    return self::$instance;
+  }
+  
+  
+  /**
+   * Private constructor
+   */
+  private function __construct()
+  {
+    // load configuration
+    $this->config = new rcube_config();
+    
+    register_shutdown_function(array($this, 'shutdown'));
+  }
+  
+  
+  /**
+   * Initial startup function
+   * to register session, create database and imap connections
+   *
+   * @todo Remove global vars $DB, $USER
+   */
+  private function startup()
+  {
+    $config_all = $this->config->all();
+
+    // initialize syslog
+    if ($this->config->get('log_driver') == 'syslog') {
+      $syslog_id = $this->config->get('syslog_id', 'roundcube');
+      $syslog_facility = $this->config->get('syslog_facility', LOG_USER);
+      openlog($syslog_id, LOG_ODELAY, $syslog_facility);
+    }
+    				
+    // set task and action properties
+    $this->set_task(strip_quotes(get_input_value('_task', RCUBE_INPUT_GPC)));
+    $this->action = asciiwords(get_input_value('_action', RCUBE_INPUT_GPC));
+
+    // connect to database
+    $GLOBALS['DB'] = $this->get_dbh();
+
+    // use database for storing session data
+    include_once('include/session.inc');
+
+    // set session domain
+    if (!empty($config_all['session_domain'])) {
+      ini_set('session.cookie_domain', $config_all['session_domain']);
+    }
+    // set session garbage collecting time according to session_lifetime
+    if (!empty($config_all['session_lifetime'])) {
+      ini_set('session.gc_maxlifetime', ($config_all['session_lifetime']) * 120);
+    }
+
+    // start PHP session (if not in CLI mode)
+    if ($_SERVER['REMOTE_ADDR'])
+      session_start();
+
+    // set initial session vars
+    if (!isset($_SESSION['auth_time'])) {
+      $_SESSION['auth_time'] = time();
+      $_SESSION['temp'] = true;
+    }
+
+    // create user object
+    $this->set_user(new rcube_user($_SESSION['user_id']));
+
+    // reset some session parameters when changing task
+    if ($_SESSION['task'] != $this->task)
+      unset($_SESSION['page']);
+
+    // set current task to session
+    $_SESSION['task'] = $this->task;
+
+    // create IMAP object
+    if ($this->task == 'mail')
+      $this->imap_init();
+  }
+  
+  
+  /**
+   * Setter for application task
+   *
+   * @param string Task to set
+   */
+  public function set_task($task)
+  {
+    if (!in_array($task, self::$main_tasks))
+      $task = 'mail';
+    
+    $this->task = $task;
+    $this->comm_path = $this->url(array('task' => $task));
+    
+    if ($this->output)
+      $this->output->set_env('task', $task);
+  }
+  
+  
+  /**
+   * Setter for system user object
+   *
+   * @param object rcube_user Current user instance
+   */
+  public function set_user($user)
+  {
+    if (is_object($user)) {
+      $this->user = $user;
+      $GLOBALS['USER'] = $this->user;
+      
+      // overwrite config with user preferences
+      $this->config->merge((array)$this->user->get_prefs());
+    }
+    
+    $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language']));
+
+    // set localization
+    setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8');
+
+    // 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'); 
+  }
+  
+  
+  /**
+   * Check the given string and return a valid language code
+   *
+   * @param string Language code
+   * @return string Valid language code
+   */
+  private function language_prop($lang)
+  {
+    static $rcube_languages, $rcube_language_aliases;
+    
+    // user HTTP_ACCEPT_LANGUAGE if no language is specified
+    if (empty($lang) || $lang == 'auto') {
+       $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
+       $lang = str_replace('-', '_', $accept_langs[0]);
+     }
+     
+    if (empty($rcube_languages)) {
+      @include(INSTALL_PATH . 'program/localization/index.inc');
+    }
+    
+    // check if we have an alias for that language
+    if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
+      $lang = $rcube_language_aliases[$lang];
+    }
+    // try the first two chars
+    else if (!isset($rcube_languages[$lang])) {
+      $short = substr($lang, 0, 2);
+     
+      // check if we have an alias for the short language code
+      if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {
+        $lang = $rcube_language_aliases[$short];
+      }
+      // expand 'nn' to 'nn_NN'
+      else if (!isset($rcube_languages[$short])) {
+        $lang = $short.'_'.strtoupper($short);
+      }
+    }
+
+    if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
+      $lang = 'en_US';
+    }
+
+    return $lang;
+  }
+  
+  
+  /**
+   * Get the current database connection
+   *
+   * @return object rcube_mdb2  Database connection object
+   */
+  public function get_dbh()
+  {
+    if (!$this->db) {
+      $config_all = $this->config->all();
+
+      $this->db = new rcube_mdb2($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']);
+      $this->db->sqlite_initials = INSTALL_PATH . 'SQL/sqlite.initial.sql';
+      $this->db->set_debug((bool)$config_all['sql_debug']);
+      $this->db->db_connect('w');
+    }
+
+    return $this->db;
+  }
+  
+  
+  /**
+   * Return instance of the internal address book class
+   *
+   * @param boolean True if the address book needs to be writeable
+   * @return object rcube_contacts Address book object
+   */
+  public function get_address_book($id, $writeable = false)
+  {
+    $contacts = null;
+    $ldap_config = (array)$this->config->get('ldap_public');
+    $abook_type = strtolower($this->config->get('address_book_type'));
+    
+    if ($id && $ldap_config[$id]) {
+      $contacts = new rcube_ldap($ldap_config[$id]);
+    }
+    else if ($abook_type == 'ldap') {
+      // Use the first writable LDAP address book.
+      foreach ($ldap_config as $id => $prop) {
+        if (!$writeable || $prop['writable']) {
+          $contacts = new rcube_ldap($prop);
+          break;
+        }
+      }
+    }
+    else {
+      $contacts = new rcube_contacts($this->db, $this->user->ID);
+    }
+    
+    return $contacts;
+  }
+  
+  
+  /**
+   * Init output object for GUI and add common scripts.
+   * This will instantiate a rcmail_template object and set
+   * environment vars according to the current session and configuration
+   *
+   * @param boolean True if this request is loaded in a (i)frame
+   * @return object rcube_template Reference to HTML output object
+   */
+  public function load_gui($framed = false)
+  {
+    // init output page
+    if (!($this->output instanceof rcube_template))
+      $this->output = new rcube_template($this->task, $framed);
+
+    foreach (array('flag_for_deletion','read_when_deleted') as $js_config_var) {
+      $this->output->set_env($js_config_var, $this->config->get($js_config_var));
+    }
+    
+    // set keep-alive/check-recent interval
+    if ($keep_alive = $this->config->get('keep_alive')) {
+      // be sure that it's less than session lifetime
+      if ($session_lifetime = $this->config->get('session_lifetime'))
+        $keep_alive = min($keep_alive, $session_lifetime * 60 - 30);
+      $this->output->set_env('keep_alive', max(60, $keep_alive));
+    }
+
+    if ($framed) {
+      $this->comm_path .= '&_framed=1';
+      $this->output->set_env('framed', true);
+    }
+
+    $this->output->set_env('task', $this->task);
+    $this->output->set_env('action', $this->action);
+    $this->output->set_env('comm_path', $this->comm_path);
+    $this->output->set_charset($this->config->get('charset', RCMAIL_CHARSET));
+
+    // add some basic label to client
+    $this->output->add_label('loading');
+    
+    return $this->output;
+  }
+  
+  
+  /**
+   * Create an output object for JSON responses
+   *
+   * @return object rcube_json_output Reference to JSON output object
+   */
+  public function init_json()
+  {
+    if (!($this->output instanceof rcube_json_output))
+      $this->output = new rcube_json_output($this->task);
+    
+    return $this->output;
+  }
+  
+  
+  /**
+   * Create global IMAP object and connect to server
+   *
+   * @param boolean True if connection should be established
+   * @todo Remove global $IMAP
+   */
+  public function imap_init($connect = false)
+  {
+    $this->imap = new rcube_imap($this->db);
+    $this->imap->debug_level = $this->config->get('debug_level');
+    $this->imap->skip_deleted = $this->config->get('skip_deleted');
+
+    // enable caching of imap data
+    if ($this->config->get('enable_caching')) {
+      $this->imap->set_caching(true);
+    }
+
+    // set pagesize from config
+    $this->imap->set_pagesize($this->config->get('pagesize', 50));
+    
+    // Setting root and delimiter before iil_Connect can save time detecting them
+    // using NAMESPACE and LIST 
+    $options = array(
+      'imap' => $this->config->get('imap_auth_type', 'check'),
+      'delimiter' => isset($_SESSION['imap_delimiter']) ? $_SESSION['imap_delimiter'] : $this->config->get('imap_delimiter'),
+    );
+    
+    if (isset($_SESSION['imap_root']))
+      $options['rootdir'] = $_SESSION['imap_root'];
+    else if ($imap_root = $this->config->get('imap_root'))
+      $options['rootdir'] = $imap_root;
+    
+    $this->imap->set_options($options);
+  
+    // set global object for backward compatibility
+    $GLOBALS['IMAP'] = $this->imap;
+    
+    if ($connect)
+      $this->imap_connect();
+  }
+
+
+  /**
+   * Connect to IMAP server with stored session data
+   *
+   * @return bool True on success, false on error
+   */
+  public function imap_connect()
+  {
+    $conn = false;
+    
+    if ($_SESSION['imap_host'] && !$this->imap->conn) {
+      if (!($conn = $this->imap->connect($_SESSION['imap_host'], $_SESSION['username'], $this->decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']))) {
+        if ($this->output)
+          $this->output->show_message($this->imap->error_code == -1 ? 'imaperror' : 'sessionerror', 'error');
+      }
+
+      $this->set_imap_prop();
+    }
+
+    return $conn;
+  }
+
+
+  /**
+   * Perfom login to the IMAP server and to the webmail service.
+   * This will also create a new user entry if auto_create_user is configured.
+   *
+   * @param string IMAP user name
+   * @param string IMAP password
+   * @param string captcha code
+   * @param string IMAP host
+   * @return boolean True on success, False on failure
+   */
+  function login($username, $pass, $captcha, $host=NULL)
+  {
+    $user = NULL;
+    $config = $this->config->all();
+
+    if ($config['use_captcha'] === true) {
+      if (rcube_captcha::validate($captcha) === false) {
+          return false;
+        }
+    }
+    
+    if (!$host)
+      $host = $config['default_host'];
+
+    // Validate that selected host is in the list of configured hosts
+    if (is_array($config['default_host'])) {
+      $allowed = false;
+      foreach ($config['default_host'] as $key => $host_allowed) {
+        if (!is_numeric($key))
+          $host_allowed = $key;
+        if ($host == $host_allowed) {
+          $allowed = true;
+          break;
+        }
+      }
+      if (!$allowed)
+        return false;
+      }
+    else if (!empty($config['default_host']) && $host != $config['default_host'])
+      return false;
+
+    // parse $host URL
+    $a_host = parse_url($host);
+    if ($a_host['host']) {
+      $host = $a_host['host'];
+      $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
+      $imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : $config['default_port']);
+    }
+    else
+      $imap_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, '@')) {
+      if (is_array($config['username_domain']) && isset($config['username_domain'][$host]))
+        $username .= '@'.$config['username_domain'][$host];
+      else if (is_string($config['username_domain']))
+        $username .= '@'.$config['username_domain'];
+    }
+
+    // try to resolve email address from virtuser table    
+    if (!empty($config['virtuser_file']) && strpos($username, '@'))
+      $username = rcube_user::email2user($username);
+
+    // lowercase username if it's an e-mail address (#1484473)
+    if (strpos($username, '@'))
+      $username = rc_strtolower($username);
+
+    // user already registered -> overwrite username
+    if ($user = rcube_user::query($username, $host))
+      $username = $user->data['username'];
+
+    // exit if IMAP login failed
+    if (!($imap_login  = $this->imap->connect($host, $username, $pass, $imap_port, $imap_ssl)))
+      return false;
+
+    // user already registered -> update user's record
+    if (is_object($user)) {
+      $user->touch();
+    }
+    // create new system user
+    else if ($config['auto_create_user']) {
+      if ($created = rcube_user::create($username, $host)) {
+        $user = $created;
+
+        // get existing mailboxes (but why?)
+        // $a_mailboxes = $this->imap->list_mailboxes();
+      }
+    }
+    else {
+      raise_error(array(
+        'code' => 600,
+        'type' => 'php',
+        'file' => RCMAIL_CONFIG_DIR."/main.inc.php",
+        'message' => "Acces denied for new user $username. 'auto_create_user' is disabled"
+        ), true, false);
+    }
+
+    // login succeeded
+    if (is_object($user) && $user->ID) {
+      $this->set_user($user);
+
+      // set session vars
+      $_SESSION['user_id']   = $user->ID;
+      $_SESSION['username']  = $user->data['username'];
+      $_SESSION['imap_host'] = $host;
+      $_SESSION['imap_port'] = $imap_port;
+      $_SESSION['imap_ssl']  = $imap_ssl;
+      $_SESSION['password']  = $this->encrypt_passwd($pass);
+      $_SESSION['login_time'] = mktime();
+      
+      if ($_REQUEST['_timezone'] != '_default_')
+        $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
+
+      // force reloading complete list of subscribed mailboxes
+      $this->set_imap_prop();
+      $this->imap->clear_cache('mailboxes');
+
+      if ($config['create_default_folders'])
+          $this->imap->create_default_folders();
+
+      return true;
+    }
+
+    return false;
+  }
+
+
+  /**
+   * Set root dir and last stored mailbox
+   * This must be done AFTER connecting to the server!
+   */
+  public function set_imap_prop()
+  {
+    $this->imap->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
+
+    if ($default_folders = $this->config->get('default_imap_folders')) {
+      $this->imap->set_default_mailboxes($default_folders);
+    }
+    if (!empty($_SESSION['mbox'])) {
+      $this->imap->set_mailbox($_SESSION['mbox']);
+    }
+    if (isset($_SESSION['page'])) {
+      $this->imap->set_page($_SESSION['page']);
+    }
+    
+    // cache IMAP root and delimiter in session for performance reasons
+    $_SESSION['imap_root'] = $this->imap->root_dir;
+    $_SESSION['imap_delimiter'] = $this->imap->delimiter;
+  }
+
+
+  /**
+   * Auto-select IMAP host based on the posted login information
+   *
+   * @return string Selected IMAP host
+   */
+  public function autoselect_host()
+  {
+    $default_host = $this->config->get('default_host');
+    $host = null;
+    
+    if (is_array($default_host)) {
+      $post_host = get_input_value('_host', RCUBE_INPUT_POST);
+      
+      // direct match in default_host array
+      if ($default_host[$post_host] || in_array($post_host, array_values($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)) {
+        foreach ($default_host as $imap_host => $mail_domains) {
+          if (is_array($mail_domains) && in_array($domain, $mail_domains)) {
+            $host = $imap_host;
+            break;
+          }
+        }
+      }
+
+      // take the first entry if $host is still an array
+      if (empty($host)) {
+        $host = array_shift($default_host);
+      }
+    }
+    else if (empty($default_host)) {
+      $host = get_input_value('_host', RCUBE_INPUT_POST);
+    }
+    else
+      $host = $default_host;
+
+    return $host;
+  }
+
+
+  /**
+   * Get localized text in the desired language
+   *
+   * @param mixed Named parameters array or label name
+   * @return string Localized text
+   */
+  public function gettext($attrib)
+  {
+    // load localization files if not done yet
+    if (empty($this->texts))
+      $this->load_language();
+    
+    // extract attributes
+    if (is_string($attrib))
+      $attrib = array('name' => $attrib);
+
+    $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
+    $vars = isset($attrib['vars']) ? $attrib['vars'] : '';
+
+    $command_name = !empty($attrib['command']) ? $attrib['command'] : NULL;
+    $alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : '');
+
+    // text does not exist
+    if (!($text_item = $this->texts[$alias])) {
+      /*
+      raise_error(array(
+        'code' => 500,
+        'type' => 'php',
+        'line' => __LINE__,
+        'file' => __FILE__,
+        'message' => "Missing localized text for '$alias' in '$sess_user_lang'"), TRUE, FALSE);
+      */
+      return "[$alias]";
+    }
+
+    // make text item array 
+    $a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
+
+    // decide which text to use
+    if ($nr == 1) {
+      $text = $a_text_item['single'];
+    }
+    else if ($nr > 0) {
+      $text = $a_text_item['multiple'];
+    }
+    else if ($nr == 0) {
+      if ($a_text_item['none'])
+        $text = $a_text_item['none'];
+      else if ($a_text_item['single'])
+        $text = $a_text_item['single'];
+      else if ($a_text_item['multiple'])
+        $text = $a_text_item['multiple'];
+    }
+
+    // default text is single
+    if ($text == '') {
+      $text = $a_text_item['single'];
+    }
+
+    // replace vars in text
+    if (is_array($attrib['vars'])) {
+      foreach ($attrib['vars'] as $var_key => $var_value)
+        $a_replace_vars[$var_key{0}=='$' ? substr($var_key, 1) : $var_key] = $var_value;
+    }
+
+    if ($a_replace_vars)
+      $text = preg_replace('/\$\{?([_a-z]{1}[_a-z0-9]*)\}?/ei', '$a_replace_vars["\1"]', $text);
+
+    // format output
+    if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
+      return ucfirst($text);
+    else if ($attrib['uppercase'])
+      return strtoupper($text);
+    else if ($attrib['lowercase'])
+      return strtolower($text);
+
+    return $text;
+  }
+
+
+  /**
+   * Load a localization package
+   *
+   * @param string Language ID
+   */
+  public function load_language($lang = null)
+  {
+    $lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));
+    
+    // load localized texts
+    if (empty($this->texts) || $lang != $_SESSION['language']) {
+      $this->texts = array();
+
+      // get english labels (these should be complete)
+      @include(INSTALL_PATH . 'program/localization/en_US/labels.inc');
+      @include(INSTALL_PATH . 'program/localization/en_US/messages.inc');
+
+      if (is_array($labels))
+        $this->texts = $labels;
+      if (is_array($messages))
+        $this->texts = array_merge($this->texts, $messages);
+
+      // include user language files
+      if ($lang != 'en' && 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');
+
+        if (is_array($labels))
+          $this->texts = array_merge($this->texts, $labels);
+        if (is_array($messages))
+          $this->texts = array_merge($this->texts, $messages);
+      }
+      
+      $_SESSION['language'] = $lang;
+    }
+  }
+
+
+  /**
+   * Read directory program/localization and return a list of available languages
+   *
+   * @return array List of available localizations
+   */
+  public function list_languages()
+  {
+    static $sa_languages = array();
+
+    if (!sizeof($sa_languages)) {
+      @include(INSTALL_PATH . 'program/localization/index.inc');
+
+      if ($dh = @opendir(INSTALL_PATH . 'program/localization')) {
+        while (($name = readdir($dh)) !== false) {
+          if ($name{0}=='.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name))
+            continue;
+
+          if ($label = $rcube_languages[$name])
+            $sa_languages[$name] = $label ? $label : $name;
+        }
+        closedir($dh);
+      }
+    }
+
+    return $sa_languages;
+  }
+
+
+  /**
+   * Check the auth hash sent by the client against the local session credentials
+   *
+   * @return boolean True if valid, False if not
+   */
+  function authenticate_session()
+  {
+    global $SESS_CLIENT_IP, $SESS_CHANGED;
+
+    // advanced session authentication
+    if ($this->config->get('double_auth')) {
+      $now = time();
+      $valid = ($_COOKIE['sessauth'] == $this->get_auth_hash(session_id(), $_SESSION['auth_time']) ||
+                $_COOKIE['sessauth'] == $this->get_auth_hash(session_id(), $_SESSION['last_auth']));
+
+      // renew auth cookie every 5 minutes (only for GET requests)
+      if (!$valid || ($_SERVER['REQUEST_METHOD']!='POST' && $now - $_SESSION['auth_time'] > 300)) {
+        $_SESSION['last_auth'] = $_SESSION['auth_time'];
+        $_SESSION['auth_time'] = $now;
+        rcmail::setcookie('sessauth', $this->get_auth_hash(session_id(), $now), 0);
+      }
+    }
+    else {
+      $valid = $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] == $SESS_CLIENT_IP : true;
+    }
+
+    // check session filetime
+    $lifetime = $this->config->get('session_lifetime');
+    if (!empty($lifetime) && isset($SESS_CHANGED) && $SESS_CHANGED + $lifetime*60 < time()) {
+      $valid = false;
+    }
+
+    return $valid;
+  }
+
+
+  /**
+   * Destroy session data and remove cookie
+   */
+  public function kill_session()
+  {
+    $_SESSION = array('language' => $this->user->language, 'auth_time' => time(), 'temp' => true);
+    rcmail::setcookie('sessauth', '-del-', time() - 60);
+    $this->user->reset();
+  }
+
+
+  /**
+   * Do server side actions on logout
+   */
+  public function logout_actions()
+  {
+    $config = $this->config->all();
+    
+    // on logout action we're not connected to imap server  
+    if (($config['logout_purge'] && !empty($config['trash_mbox'])) || $config['logout_expunge']) {
+      if (!$this->authenticate_session())
+        return;
+
+      $this->imap_init(true);
+    }
+
+    if ($config['logout_purge'] && !empty($config['trash_mbox'])) {
+      $this->imap->clear_mailbox($config['trash_mbox']);
+    }
+
+    if ($config['logout_expunge']) {
+      $this->imap->expunge('INBOX');
+    }
+  }
+
+
+  /**
+   * Function to be executed in script shutdown
+   * Registered with register_shutdown_function()
+   */
+  public function shutdown()
+  {
+    if (is_object($this->imap)) {
+      $this->imap->close();
+      $this->imap->write_cache();
+    }
+
+    if (is_object($this->contacts))
+      $this->contacts->close();
+
+    // before closing the database connection, write session data
+    if ($_SERVER['REMOTE_ADDR'])
+      session_write_close();
+  }
+  
+  
+  /**
+   * Create unique authorization hash
+   *
+   * @param string Session ID
+   * @param int Timestamp
+   * @return string The generated auth hash
+   */
+  private function get_auth_hash($sess_id, $ts)
+  {
+    $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s',
+      $sess_id,
+      $ts,
+      $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***',
+      $_SERVER['HTTP_USER_AGENT']);
+
+    if (function_exists('sha1'))
+      return sha1($auth_string);
+    else
+      return md5($auth_string);
+  }
+
+  /**
+   * Encrypt IMAP password using DES encryption
+   *
+   * @param string Password to encrypt
+   * @return string Encryprted string
+   */
+  public function encrypt_passwd($pass)
+  {
+    if (function_exists('mcrypt_module_open') && ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, ""))) {
+      $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
+      mcrypt_generic_init($td, $this->config->get_des_key(), $iv);
+      $cypher = mcrypt_generic($td, $pass);
+      mcrypt_generic_deinit($td);
+      mcrypt_module_close($td);
+    }
+    else if (function_exists('des')) {
+      $cypher = des($this->config->get_des_key(), $pass, 1, 0, NULL);
+    }
+    else {
+      $cypher = $pass;
+
+      raise_error(array(
+        'code' => 500,
+        'type' => 'php',
+        'file' => __FILE__,
+        'message' => "Could not convert encrypt password. Make sure Mcrypt is installed or lib/des.inc is available"
+        ), true, false);
+    }
+
+    return base64_encode($cypher);
+  }
+
+
+  /**
+   * Decrypt IMAP password using DES encryption
+   *
+   * @param string Encrypted password
+   * @return string Plain password
+   */
+  public function decrypt_passwd($cypher)
+  {
+    if (function_exists('mcrypt_module_open') && ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, ""))) {
+      $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
+      mcrypt_generic_init($td, $this->config->get_des_key(), $iv);
+      $pass = mdecrypt_generic($td, base64_decode($cypher));
+      mcrypt_generic_deinit($td);
+      mcrypt_module_close($td);
+    }
+    else if (function_exists('des')) {
+      $pass = des($this->config->get_des_key(), base64_decode($cypher), 0, 0, NULL);
+    }
+    else {
+      $pass = base64_decode($cypher);
+    }
+
+    return preg_replace('/\x00/', '', $pass);
+  }
+
+
+  /**
+   * Build a valid URL to this instance of RoundCube
+   *
+   * @param mixed Either a string with the action or url parameters as key-value pairs
+   * @return string Valid application URL
+   */
+  public function url($p)
+  {
+    if (!is_array($p))
+      $p = array('_action' => @func_get_arg(0));
+
+    if (!$p['task'] || !in_array($p['task'], rcmail::$main_tasks))
+      $p['task'] = $this->task;
+
+    $p['_task'] = $p['task'];
+    unset($p['task']);
+
+    $url = './';
+    $delm = '?';
+    foreach (array_reverse($p) as $par => $val)
+    {
+      if (!empty($val)) {
+        $url .= $delm.urlencode($par).'='.urlencode($val);
+        $delm = '&';
+      }
+    }
+    return $url;
+  }
+
+
+  /**
+   * Helper method to set a cookie with the current path and host settings
+   *
+   * @param string Cookie name
+   * @param string Cookie value
+   * @param string Expiration time
+   */
+  public static function setcookie($name, $value, $exp = 0)
+  {
+    $cookie = session_get_cookie_params();
+    setcookie($name, $value, $exp, $cookie['path'], $cookie['domain'],
+      ($_SERVER['HTTPS'] && ($_SERVER['HTTPS'] != 'off')));
+  }
+}
+
+
Index: program/include/rcube_captcha.php
===================================================================
--- program/include/rcube_captcha.php	(revision 0)
+++ program/include/rcube_captcha.php	(revision 0)
@@ -0,0 +1,176 @@
+<?php
+/**
+ * CaptchaSecurityImage for PHP5
+ * 
+ * Provides captha image security for authentication.
+ * The script is based on Simon Jarvis article PHP Captcha Security Images  
+ * 
+ * @author Simon Jarvis
+ * @author Erik Franzn <erik@franzens.nu>
+ * @copyright GNU General Public License
+ * @see http://www.white-hat-web-design.co.uk/articles/php-captcha.php
+ **/
+
+class rcube_captcha
+{
+
+    protected $_FontFile = 'program/font/VeraMono.ttf';
+    protected $_captchaImage;
+    protected $_Key = null;
+
+    /**
+     * Create a securty captcha image object
+     *
+     * @param int $a_Width Image width in pixels
+     * @param int $a_Height Image height in pixels
+     * @param int $a_Characters number of characters in security code
+     */
+    public function __construct($a_Width = 120, $a_Height = 40, $a_Characters = 6) {
+        
+        if (!extension_loaded('gd')) {
+            throw new Exception('GD extention not loaded in php configuration');
+        }
+        
+        $this->_createKey($a_Characters);
+        $this->_createImage($this->getKey(), $a_Width, $a_Height);
+    }
+    
+    /**
+     * validate security code
+     *
+     * @param string $a_stCode
+     * @return bool result
+     */
+    public static function validate($a_Key)
+    {
+        if (!isset($_SESSION['captcha_key'])) {
+            return false;
+        }
+
+        if ((!$a_Key) || ($a_Key != $_SESSION['captcha_key'])) {
+            unset($_SESSION['captcha_key']);
+            return false;
+        } else {
+            unset($_SESSION['captcha_key']);
+            return true;
+        }
+    }
+
+    /**
+     * validate security code
+     *
+     * @param int number of characters in key
+     * @return void
+     */
+    protected function _createKey($a_Characters = 6)
+    {
+		 $this->Key = $this->_generateKey($a_Characters);
+    }
+ 
+    /**
+     * Create security Key string
+     *
+     * @param int $a_iCharacters
+     */
+    private function _generateKey($a_Characters = 6)
+    {
+        $Key = '';
+        /* list all possible characters, similar looking characters and vowels have been removed */
+        $Chars = '23456789bcdfghjkmnpqrstvwxyz';
+
+        do {
+            $Key .= substr($Chars, mt_rand(0, strlen($Chars)-1), 1);
+        } while (--$a_Characters > 0);
+        
+        return $Key;
+    }    
+   
+    /**
+    * Return security Key
+    *
+    * @return string key
+    */
+    public function getKey()
+    {
+        if (!isset($this->Key)) {
+           $this->_createKey();
+        }
+        return $this->Key;
+    }    
+    
+    
+	/**
+     * Create security image
+     *
+     * @param string $a_Key 
+     * @param int $a_iWidth
+     * @param int $a_iHeight
+     */
+    protected function _createImage($a_Key, $a_Width = 120, $a_Height = 40) {
+              
+        // font size will be 75% of the image height
+        $font_size = $a_Height * 0.5;
+        $this->_captchaImage = imagecreate($a_Width, $a_Height);
+
+        // set the colours
+        $iBackgroundColor = imagecolorallocate($this->_captchaImage, 255, 255, 255);
+        $iColor = imagecolorallocate($this->_captchaImage, 20, 40, 100);
+        $iNoiseColor = imagecolorallocate($this->_captchaImage, 100, 120, 180);
+
+        // generate random dots
+        for( $i=0; $i<($a_Width*$a_Height)/3; $i++ ) {
+            imagefilledellipse($this->_captchaImage, mt_rand(0, $a_Width), mt_rand(0, $a_Height), 1, 1, $iNoiseColor);
+        }
+
+        // generate random lines
+        for( $i=0; $i<($a_Width*$a_Height)/150; $i++ ) {
+            imageline($this->_captchaImage, mt_rand(0, $a_Width), mt_rand(0, $a_Height), mt_rand(0, $a_Width), mt_rand(0,$a_Height), $iNoiseColor);
+        }
+
+        // create textbox and add text
+        $textbox = imagettfbbox($font_size, 0, $this->_FontFile, $a_Key);
+        $iX = ($a_Width - $textbox[4])/2;
+        $iY = ($a_Height - $textbox[5])/2;
+        if (!is_array(imagettftext($this->_captchaImage, $font_size, 0, $iX, $iY, $iColor, $this->_FontFile , $a_Key))) {
+	        throw new Exception('imagettftext failed');
+        }
+    }
+
+
+    /**
+    * Return securityimage as resource
+    *
+    * @return resource image
+    */
+    public function getImage()
+    {
+        if (!is_resource($this->_captchaImage)) {
+            $this->_createImage($this->getKey());
+        }
+        return $this->_captchaImage;
+    }
+
+    /**
+    * Display Image in browser
+    *
+    * @param CaptchaSecurityImage $a_oCaptchaSecurityImage
+    */
+    public static function SendImageToBrowser(rcube_captcha $a_CaptchaSecurityImage)
+    {
+        if (!is_resource(($Image = $a_CaptchaSecurityImage->getImage()))) {
+            throw new Exception(sprintf("%s::%s Image object is not a resource", __CLASS__, __FUNCTION__));
+        }
+
+        /* output captcha image to browser */
+	    header("Expires: Sun, 1 Jan 2000 12:00:00 GMT");
+	    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
+	    header("Cache-Control: no-store, no-cache, must-revalidate");
+	    header("Cache-Control: post-check=0, pre-check=0", false);
+	    header("Pragma: no-cache");        
+        header('Content-Type: image/jpeg');        
+        imagejpeg($Image);
+        imagedestroy($Image);
+        $_SESSION['captcha_key'] = $a_CaptchaSecurityImage->getKey();
+    }
+}
+?>
\ No newline at end of file
Index: program/include/rcube_template.php
===================================================================
--- program/include/rcube_template.php	(revision 2350)
+++ program/include/rcube_template.php	(working copy)
@@ -1,1098 +1,1117 @@
-<?php
-
-/*
- +-----------------------------------------------------------------------+
- | program/include/rcube_template.php                                    |
- |                                                                       |
- | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2006-2008, RoundCube Dev. - Switzerland                 |
- | Licensed under the GNU GPL                                            |
- |                                                                       |
- | PURPOSE:                                                              |
- |   Class to handle HTML page output using a skin template.             |
- |   Extends rcube_html_page class from rcube_shared.inc                 |
- |                                                                       |
- +-----------------------------------------------------------------------+
- | Author: Thomas Bruederli <roundcube@gmail.com>                        |
- +-----------------------------------------------------------------------+
-
- $Id$
-
- */
-
-
-/**
- * Class to create HTML page output using a skin template
- *
- * @package View
- * @todo Documentation
- * @uses rcube_html_page
- */
-class rcube_template extends rcube_html_page
-{
-    var $app;
-    var $config;
-    var $framed = false;
-    var $pagetitle = '';
-    var $env = array();
-    var $js_env = array();
-    var $js_commands = array();
-    var $object_handlers = array();
-
-    public $type = 'html';
-    public $ajax_call = false;
-
-    /**
-     * Constructor
-     *
-     * @todo   Use jQuery's $(document).ready() here.
-     * @todo   Replace $this->config with the real rcube_config object
-     */
-    public function __construct($task, $framed = false)
-    {
-        parent::__construct();
-
-        $this->app = rcmail::get_instance();
-        $this->config = $this->app->config->all();
-        $this->browser = new rcube_browser();
-        
-        //$this->framed = $framed;
-        $this->set_env('task', $task);
-
-        // load the correct skin (in case user-defined)
-        $this->set_skin($this->config['skin']);
-
-        // add common javascripts
-        $javascript = 'var '.JS_OBJECT_NAME.' = new rcube_webmail();';
-
-        // don't wait for page onload. Call init at the bottom of the page (delayed)
-        $javascript_foot = "if (window.call_init)\n call_init('".JS_OBJECT_NAME."');";
-
-        $this->add_script($javascript, 'head_top');
-        $this->add_script($javascript_foot, 'foot');
-        $this->scripts_path = 'program/js/';
-        $this->include_script('common.js');
-        $this->include_script('app.js');
-
-        // register common UI objects
-        $this->add_handlers(array(
-            'loginform'       => array($this, 'login_form'),
-            'username'        => array($this, 'current_username'),
-            'message'         => array($this, 'message_container'),
-            'charsetselector' => array($this, 'charset_selector'),
-        ));
-    }
-
-    /**
-     * Set environment variable
-     *
-     * @param string Property name
-     * @param mixed Property value
-     * @param boolean True if this property should be added to client environment
-     */
-    public function set_env($name, $value, $addtojs = true)
-    {
-        $this->env[$name] = $value;
-        if ($addtojs || isset($this->js_env[$name])) {
-            $this->js_env[$name] = $value;
-        }
-    }
-
-
-    /**
-     * Set page title variable
-     */
-    public function set_pagetitle($title)
-    {
-        $this->pagetitle = $title;
-    }
-
-
-    /**
-     * Getter for the current page title
-     *
-     * @return string The page title
-     */
-    public function get_pagetitle()
-    {
-        if (!empty($this->pagetitle)) {
-            $title = $this->pagetitle;
-        }
-        else if ($this->env['task'] == 'login') {
-            $title = rcube_label(array('name' => 'welcome', 'vars' => array('product' => $this->config['product_name'])));
-        }
-        else {
-            $title = ucfirst($this->env['task']);
-        }
-        
-        return $title;
-    }
-
-
-    /**
-     * Set skin
-     */
-    public function set_skin($skin)
-    {
-        if (!empty($skin) && is_dir('skins/'.$skin) && is_readable('skins/'.$skin))
-            $skin_path = 'skins/'.$skin;
-        else
-            $skin_path = $this->config['skin_path'] ? $this->config['skin_path'] : 'skins/default';
-
-        $this->app->config->set('skin_path', $skin_path);
-        $this->config['skin_path'] = $skin_path;
-    }
-
-    /**
-     * Check if a specific template exists
-     *
-     * @param string Template name
-     * @return boolean True if template exists
-     */
-    public function template_exists($name)
-    {
-        $filename = $this->config['skin_path'] . '/templates/' . $name . '.html';
-
-        return (is_file($filename) && is_readable($filename));
-    }
-
-    /**
-     * Register a template object handler
-     *
-     * @param  string Object name
-     * @param  string Function name to call
-     * @return void
-     */
-    public function add_handler($obj, $func)
-    {
-        $this->object_handlers[$obj] = $func;
-    }
-
-    /**
-     * Register a list of template object handlers
-     *
-     * @param  array Hash array with object=>handler pairs
-     * @return void
-     */
-    public function add_handlers($arr)
-    {
-        $this->object_handlers = array_merge($this->object_handlers, $arr);
-    }
-
-    /**
-     * Register a GUI object to the client script
-     *
-     * @param  string Object name
-     * @param  string Object ID
-     * @return void
-     */
-    public function add_gui_object($obj, $id)
-    {
-        $this->add_script(JS_OBJECT_NAME.".gui_object('$obj', '$id');");
-    }
-
-    /**
-     * Call a client method
-     *
-     * @param string Method to call
-     * @param ... Additional arguments
-     */
-    public function command()
-    {
-        $this->js_commands[] = func_get_args();
-    }
-
-
-    /**
-     * Add a localized label to the client environment
-     */
-    public function add_label()
-    {
-        $arg_list = func_get_args();
-        foreach ($arg_list as $i => $name) {
-            $this->command('add_label', $name, rcube_label($name));
-        }
-    }
-
-
-    /**
-     * Invoke display_message command
-     *
-     * @param string Message to display
-     * @param string Message type [notice|confirm|error]
-     * @param array Key-value pairs to be replaced in localized text
-     * @uses self::command()
-     */
-    public function show_message($message, $type='notice', $vars=NULL)
-    {
-        $this->command(
-            'display_message',
-            rcube_label(array('name' => $message, 'vars' => $vars)),
-            $type);
-    }
-
-
-    /**
-     * Delete all stored env variables and commands
-     *
-     * @return void
-     * @uses   rcube_html::reset()
-     * @uses   self::$env
-     * @uses   self::$js_env
-     * @uses   self::$js_commands
-     * @uses   self::$object_handlers
-     */
-    public function reset()
-    {
-        $this->env = array();
-        $this->js_env = array();
-        $this->js_commands = array();
-        $this->object_handlers = array();
-        parent::reset();
-    }
-
-
-    /**
-     * Redirect to a certain url
-     *
-     * @param mixed Either a string with the action or url parameters as key-value pairs
-     * @see rcmail::url()
-     */
-    public function redirect($p = array())
-    {
-        $location = $this->app->url($p);
-        header('Location: ' . $location);
-        exit;
-    }
-
-
-    /**
-     * Send the request output to the client.
-     * This will either parse a skin tempalte or send an AJAX response
-     *
-     * @param string  Template name
-     * @param boolean True if script should terminate (default)
-     */
-    public function send($templ = null, $exit = true)
-    {
-        if ($templ != 'iframe') {
-            $this->parse($templ, false);
-        }
-        else {
-            $this->framed = $templ == 'iframe' ? true : $this->framed;
-            $this->write();
-        }
-
-        if ($exit) {
-            exit;
-        }
-    }
-
-    /**
-     * Process template and write to stdOut
-     *
-     * @param string HTML template
-     * @see rcube_html_page::write()
-     * @override
-     */
-    public function write($template = '')
-    {
-        // unlock interface after iframe load
-        if ($this->framed) {
-            array_unshift($this->js_commands, array('set_busy', false));
-        }
-        // write all env variables to client
-        $js = $this->framed ? "if(window.parent) {\n" : '';
-        $js .= $this->get_js_commands() . ($this->framed ? ' }' : '');
-        $this->add_script($js, 'head_top');
-
-        // call super method
-        parent::write($template, $this->config['skin_path']);
-    }
-
-    /**
-     * Parse a specific skin template and deliver to stdout
-     *
-     * Either returns nothing, or exists hard (exit();)
-     *
-     * @param  string  Template name
-     * @param  boolean Exit script
-     * @return void
-     * @link   http://php.net/manual/en/function.exit.php
-     */
-    private function parse($name = 'main', $exit = true)
-    {
-        $skin_path = $this->config['skin_path'];
-        $path = "$skin_path/templates/$name.html";
-
-        // read template file
-        if (($templ = @file_get_contents($path)) === false) {
-            raise_error(array(
-                'code' => 501,
-                'type' => 'php',
-                'line' => __LINE__,
-                'file' => __FILE__,
-                'message' => 'Error loading template for '.$name
-                ), true, true);
-            return false;
-        }
-
-        // parse for specialtags
-        $output = $this->parse_conditions($templ);
-        $output = $this->parse_xml($output);
-
-        // add debug console
-        if ($this->config['debug_level'] & 8) {
-            $this->add_footer('<div style="position:absolute;top:5px;left:5px;width:405px;padding:2px;background:white;opacity:0.8;filter:alpha(opacity=80);z-index:9000">
-                <a href="#toggle" onclick="con=document.getElementById(\'dbgconsole\');con.style.display=(con.style.display==\'none\'?\'block\':\'none\');return false">console</a>
-                <form action="/" name="debugform" style="display:inline"><textarea name="console" id="dbgconsole" rows="20" cols="40" wrap="off" style="display:none;width:400px;border:none;font-size:x-small" spellcheck="false"></textarea></form></div>'
-            );
-        }
-        $output = $this->parse_with_globals($output);
-        $this->write(trim($output), $skin_path);
-        if ($exit) {
-            exit;
-        }
-    }
-
-
-    /**
-     * Return executable javascript code for all registered commands
-     *
-     * @return string $out
-     */
-    private function get_js_commands()
-    {
-        $out = '';
-        if (!$this->framed && !empty($this->js_env)) {
-            $out .= JS_OBJECT_NAME . '.set_env('.json_serialize($this->js_env).");\n";
-        }
-        foreach ($this->js_commands as $i => $args) {
-            $method = array_shift($args);
-            foreach ($args as $i => $arg) {
-                $args[$i] = json_serialize($arg);
-            }
-            $parent = $this->framed || preg_match('/^parent\./', $method);
-            $out .= sprintf(
-                "%s.%s(%s);\n",
-            ($parent ? 'parent.' : '') . JS_OBJECT_NAME,
-            preg_replace('/^parent\./', '', $method),
-            implode(',', $args)
-            );
-        }
-        
-        return $out;
-    }
-
-    /**
-     * Make URLs starting with a slash point to skin directory
-     *
-     * @param  string Input string
-     * @return string
-     */
-    public function abs_url($str)
-    {
-        return preg_replace('/^\//', $this->config['skin_path'].'/', $str);
-    }
-
-
-    /*****  Template parsing methods  *****/
-
-    /**
-     * Replace all strings ($varname)
-     * with the content of the according global variable.
-     */
-    private function parse_with_globals($input)
-    {
-        $GLOBALS['__comm_path'] = Q($this->app->comm_path);
-        return preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input);
-    }
-
-    /**
-     * Public wrapper to dipp into template parsing.
-     *
-     * @param  string $input
-     * @return string
-     * @uses   rcube_template::parse_xml()
-     * @since  0.1-rc1
-     */
-    public function just_parse($input)
-    {
-        return $this->parse_xml($input);
-    }
-
-    /**
-     * Parse for conditional tags
-     *
-     * @param  string $input
-     * @return string
-     */
-    private function parse_conditions($input)
-    {
-        $matches = preg_split('/<roundcube:(if|elseif|else|endif)\s+([^>]+)>/is', $input, 2, PREG_SPLIT_DELIM_CAPTURE);
-        if ($matches && count($matches) == 4) {
-            if (preg_match('/^(else|endif)$/i', $matches[1])) {
-                return $matches[0] . $this->parse_conditions($matches[3]);
-            }
-            $attrib = parse_attrib_string($matches[2]);
-            if (isset($attrib['condition'])) {
-                $condmet = $this->check_condition($attrib['condition']);
-                $submatches = preg_split('/<roundcube:(elseif|else|endif)\s+([^>]+)>/is', $matches[3], 2, PREG_SPLIT_DELIM_CAPTURE);
-                if ($condmet) {
-                    $result = $submatches[0];
-                    $result.= ($submatches[1] != 'endif' ? preg_replace('/.*<roundcube:endif\s+[^>]+>/Uis', '', $submatches[3], 1) : $submatches[3]);
-                }
-                else {
-                    $result = "<roundcube:$submatches[1] $submatches[2]>" . $submatches[3];
-                }
-                return $matches[0] . $this->parse_conditions($result);
-            }
-            raise_error(array(
-                'code' => 500,
-                'type' => 'php',
-                'line' => __LINE__,
-                'file' => __FILE__,
-                'message' => "Unable to parse conditional tag " . $matches[2]
-            ), true, false);
-        }
-        return $input;
-    }
-
-
-    /**
-     * Determines if a given condition is met
-     *
-     * @todo   Get rid off eval() once I understand what this does.
-     * @todo   Extend this to allow real conditions, not just "set"
-     * @param  string Condition statement
-     * @return boolean True if condition is met, False if not
-     */
-    private function check_condition($condition)
-    {
-            return eval("return (".$this->parse_expression($condition).");");
-    }
-
-
-    /**
-     * Parses expression and replaces variables
-     *
-     * @param  string Expression statement
-     * @return string Expression statement
-     */
-    private function parse_expression($expression)
-    {
-        return preg_replace(
-            array(
-                '/session:([a-z0-9_]+)/i',
-                '/config:([a-z0-9_]+)(:([a-z0-9_]+))?/i',
-                '/env:([a-z0-9_]+)/i',
-                '/request:([a-z0-9_]+)/i',
-                '/cookie:([a-z0-9_]+)/i'
-            ),
-            array(
-                "\$_SESSION['\\1']",
-                "\$this->app->config->get('\\1',get_boolean('\\3'))",
-                "\$this->env['\\1']",
-                "get_input_value('\\1', RCUBE_INPUT_GPC)",
-                "\$_COOKIE['\\1']"
-            ),
-            $expression);
-    }
-
-
-    /**
-     * Search for special tags in input and replace them
-     * with the appropriate content
-     *
-     * @param  string Input string to parse
-     * @return string Altered input string
-     * @todo   Use DOM-parser to traverse template HTML
-     * @todo   Maybe a cache.
-     */
-    private function parse_xml($input)
-    {
-        return preg_replace_callback('/<roundcube:([-_a-z]+)\s+([^>]+)>/Ui', array($this, 'xml_command_callback'), $input);
-    }
-
-
-    /**
-     * This is a callback function for preg_replace_callback (see #1485286)
-     * It's only purpose is to reconfigure parameters for xml_command, so that the signature isn't disturbed
-     */
-    private function xml_command_callback($matches)
-    {
-        $str_attrib = isset($matches[2]) ? $matches[2] : '';
-        $add_attrib = isset($matches[3]) ? $matches[3] : array();
-
-        $command = $matches[1];
-        //matches[0] is the entire matched portion of the string
-
-        return $this->xml_command($command, $str_attrib, $add_attrib);
-    }
-
-
-    /**
-     * Convert a xml command tag into real content
-     *
-     * @param  string Tag command: object,button,label, etc.
-     * @param  string Attribute string
-     * @return string Tag/Object content
-     */
-    private function xml_command($command, $str_attrib, $add_attrib = array())
-    {
-        $command = strtolower($command);
-        $attrib  = parse_attrib_string($str_attrib) + $add_attrib;
-
-        // empty output if required condition is not met
-        if (!empty($attrib['condition']) && !$this->check_condition($attrib['condition'])) {
-            return '';
-        }
-
-        // execute command
-        switch ($command) {
-            // return a button
-            case 'button':
-                if ($attrib['name'] || $attrib['command']) {
-                    return $this->button($attrib);
-                }
-                break;
-
-            // show a label
-            case 'label':
-                if ($attrib['name'] || $attrib['command']) {
-                    return Q(rcube_label($attrib + array('vars' => array('product' => $this->config['product_name']))));
-                }
-                break;
-
-            // include a file
-            case 'include':
-                $path = realpath($this->config['skin_path'].$attrib['file']);
-                if (is_readable($path)) {
-                    if ($this->config['skin_include_php']) {
-                        $incl = $this->include_php($path);
-                    }
-                    else {
-		        $incl = file_get_contents($path);
-		    }
-                    return $this->parse_xml($incl);
-                }
-                break;
-
-            case 'plugin.include':
-                //rcube::tfk_debug(var_export($this->config['skin_path'], true));
-                $path = realpath($this->config['skin_path'].$attrib['file']);
-                if (!$path) {
-                    //rcube::tfk_debug("Does not exist:");
-                    //rcube::tfk_debug($this->config['skin_path']);
-                    //rcube::tfk_debug($attrib['file']);
-                    //rcube::tfk_debug($path);
-                }
-                $incl = file_get_contents($path);
-                if ($incl) {
-                    return $this->parse_xml($incl);
-                }
-                break;
-
-            // return code for a specific application object
-            case 'object':
-                $object = strtolower($attrib['name']);
-
-                // we are calling a class/method
-                if (($handler = $this->object_handlers[$object]) && is_array($handler)) {
-                    if ((is_object($handler[0]) && method_exists($handler[0], $handler[1])) ||
-                    (is_string($handler[0]) && class_exists($handler[0])))
-                    return call_user_func($handler, $attrib);
-                }
-                else if (function_exists($handler)) {
-                    // execute object handler function
-                    return call_user_func($handler, $attrib);
-                }
-
-                if ($object=='productname') {
-                    $name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'RoundCube Webmail';
-                    return Q($name);
-                }
-                if ($object=='version') {
-                    $ver = (string)RCMAIL_VERSION;
-                    if (is_file(INSTALL_PATH . '.svn/entries')) {
-                        if (preg_match('/Revision:\s(\d+)/', @shell_exec('svn info'), $regs))
-                          $ver .= ' [SVN r'.$regs[1].']';
-                    }
-                    return $ver;
-                }
-                if ($object=='steptitle') {
-                  return Q($this->get_pagetitle());
-                }
-                if ($object=='pagetitle') {
-                    $title = !empty($this->config['product_name']) ? $this->config['product_name'].' :: ' : '';
-                    $title .= $this->get_pagetitle();
-                    return Q($title);
-                }
-                break;
-
-            // return code for a specified eval expression
-            case 'exp':
-        	$value = $this->parse_expression($attrib['expression']);
-                return eval("return Q($value);");
-            
-            // return variable
-            case 'var':
-                $var = explode(':', $attrib['name']);
-                $name = $var[1];
-                $value = '';
-
-                switch ($var[0]) {
-                    case 'env':
-                        $value = $this->env[$name];
-                        break;
-                    case 'config':
-                        $value = $this->config[$name];
-                        if (is_array($value) && $value[$_SESSION['imap_host']]) {
-                            $value = $value[$_SESSION['imap_host']];
-                        }
-                        break;
-                    case 'request':
-                        $value = get_input_value($name, RCUBE_INPUT_GPC);
-                        break;
-                    case 'session':
-                        $value = $_SESSION[$name];
-                        break;
-                    case 'cookie':
-                        $value = htmlspecialchars($_COOKIE[$name]);
-                        break;
-                }
-
-                if (is_array($value)) {
-                    $value = implode(', ', $value);
-                }
-
-                return Q($value);
-                break;
-        }
-        return '';
-    }
-
-    /**
-     * Include a specific file and return it's contents
-     *
-     * @param string File path
-     * @return string Contents of the processed file
-     */
-    private function include_php($file)
-    {
-        ob_start();
-        include $file;
-        $out = ob_get_contents();
-        ob_end_clean();
-
-        return $out;
-    }
-
-    /**
-     * Create and register a button
-     *
-     * @param  array Named button attributes
-     * @return string HTML button
-     * @todo   Remove all inline JS calls and use jQuery instead.
-     * @todo   Remove all sprintf()'s - they are pretty, but also slow.
-     */
-    public function button($attrib)
-    {
-        static $sa_buttons = array();
-        static $s_button_count = 100;
-
-        // these commands can be called directly via url
-        $a_static_commands = array('compose', 'list');
-
-        if (!($attrib['command'] || $attrib['name'])) {
-            return '';
-        }
-
-        // try to find out the button type
-        if ($attrib['type']) {
-            $attrib['type'] = strtolower($attrib['type']);
-        }
-        else {
-            $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link';
-        }
-        $command = $attrib['command'];
-
-        // take the button from the stack
-        if ($attrib['name'] && $sa_buttons[$attrib['name']]) {
-            $attrib = $sa_buttons[$attrib['name']];
-        }
-        else if($attrib['image'] || $attrib['imageact'] || $attrib['imagepas'] || $attrib['class']) {
-            // add button to button stack
-            if (!$attrib['name']) {
-                $attrib['name'] = $command;
-            }
-            if (!$attrib['image']) {
-                $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact'];
-            }
-            $sa_buttons[$attrib['name']] = $attrib;
-        }
-        else if ($command && $sa_buttons[$command]) {
-            // get saved button for this command/name
-            $attrib = $sa_buttons[$command];
-        }
-
-        if (!$attrib['id']) {
-            $attrib['id'] =  sprintf('rcmbtn%d', $s_button_count++);
-        }
-        // get localized text for labels and titles
-        if ($attrib['title']) {
-            $attrib['title'] = Q(rcube_label($attrib['title']));
-        }
-        if ($attrib['label']) {
-            $attrib['label'] = Q(rcube_label($attrib['label']));
-        }
-        if ($attrib['alt']) {
-            $attrib['alt'] = Q(rcube_label($attrib['alt']));
-        }
-        // set title to alt attribute for IE browsers
-        if ($this->browser->ie && $attrib['title'] && !$attrib['alt']) {
-            $attrib['alt'] = $attrib['title'];
-            unset($attrib['title']);
-        }
-
-        // add empty alt attribute for XHTML compatibility
-        if (!isset($attrib['alt'])) {
-            $attrib['alt'] = '';
-        }
-
-        // register button in the system
-        if ($attrib['command']) {
-            $this->add_script(sprintf(
-                "%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id'],
-                $attrib['type'],
-                $attrib['imageact'] ? $this->abs_url($attrib['imageact']) : $attrib['classact'],
-                $attrib['imagesel'] ? $this->abs_url($attrib['imagesel']) : $attrib['classsel'],
-                $attrib['imageover'] ? $this->abs_url($attrib['imageover']) : ''
-            ));
-
-            // make valid href to specific buttons
-            if (in_array($attrib['command'], rcmail::$main_tasks)) {
-                $attrib['href'] = rcmail_url(null, null, $attrib['command']);
-            }
-            else if (in_array($attrib['command'], $a_static_commands)) {
-                $attrib['href'] = rcmail_url($attrib['command']);
-            }
-            else if ($attrib['command'] == 'permaurl' && !empty($this->env['permaurl'])) {
-                $attrib['href'] = $this->env['permaurl'];
-            }
-        }
-
-        // overwrite attributes
-        if (!$attrib['href']) {
-            $attrib['href'] = '#';
-        }
-        if ($command) {
-            $attrib['onclick'] = sprintf(
-                "return %s.command('%s','%s',this)",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['prop']
-            );
-        }
-        if ($command && $attrib['imageover']) {
-            $attrib['onmouseover'] = sprintf(
-                "return %s.button_over('%s','%s')",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id']
-            );
-            $attrib['onmouseout'] = sprintf(
-                "return %s.button_out('%s','%s')",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id']
-            );
-        }
-
-        if ($command && $attrib['imagesel']) {
-            $attrib['onmousedown'] = sprintf(
-                "return %s.button_sel('%s','%s')",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id']
-            );
-            $attrib['onmouseup'] = sprintf(
-                "return %s.button_out('%s','%s')",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id']
-            );
-        }
-
-        $out = '';
-
-        // generate image tag
-        if ($attrib['type']=='image') {
-            $attrib_str = html::attrib_string(
-                $attrib,
-                array(
-                    'style', 'class', 'id', 'width',
-                    'height', 'border', 'hspace',
-                    'vspace', 'align', 'alt', 'tabindex'
-                )
-            );
-            $btn_content = sprintf('<img src="%s"%s />', $this->abs_url($attrib['image']), $attrib_str);
-            if ($attrib['label']) {
-                $btn_content .= ' '.$attrib['label'];
-            }
-            $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'title', 'target');
-        }
-        else if ($attrib['type']=='link') {
-            $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command'];
-            $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style', 'tabindex', 'target');
-        }
-        else if ($attrib['type']=='input') {
-            $attrib['type'] = 'button';
-
-            if ($attrib['label']) {
-                $attrib['value'] = $attrib['label'];
-            }
-
-            $attrib_str = html::attrib_string(
-                $attrib,
-                array(
-                    'type', 'value', 'onclick',
-                    'id', 'class', 'style', 'tabindex'
-                )
-            );
-            $out = sprintf('<input%s disabled="disabled" />', $attrib_str);
-        }
-
-        // generate html code for button
-        if ($btn_content) {
-            $attrib_str = html::attrib_string($attrib, $link_attrib);
-            $out = sprintf('<a%s>%s</a>', $attrib_str, $btn_content);
-        }
-
-        return $out;
-    }
-
-
-    /*  ************* common functions delivering gui objects **************  */
-
-
-    /**
-     * Create a form tag with the necessary hidden fields
-     *
-     * @param array Named tag parameters
-     * @return string HTML code for the form
-     */
-    public function form_tag($attrib, $content = null)
-    {
-      if ($this->framed) {
-        $hiddenfield = new html_hiddenfield(array('name' => '_framed', 'value' => '1'));
-        $hidden = $hiddenfield->show();
-      }
-      
-      if (!$content)
-        $attrib['noclose'] = true;
-      
-      return html::tag('form',
-        $attrib + array('action' => "./", 'method' => "get"),
-        $hidden . $content);
-    }
-
-
-    /**
-     * GUI object 'username'
-     * Showing IMAP username of the current session
-     *
-     * @param array Named tag parameters (currently not used)
-     * @return string HTML code for the gui object
-     */
-    public function current_username($attrib)
-    {
-        static $username;
-
-        // alread fetched
-        if (!empty($username)) {
-            return $username;
-        }
-
-        // get e-mail address form default identity
-        if ($sql_arr = $this->app->user->get_identity()) {
-            $username = $sql_arr['email'];
-        }
-        else {
-            $username = $this->app->user->get_username();
-        }
-
-        return $username;
-    }
-
-
-    /**
-     * GUI object 'loginform'
-     * Returns code for the webmail login form
-     *
-     * @param array Named parameters
-     * @return string HTML code for the gui object
-     */
-    private function login_form($attrib)
-    {
-        $default_host = $this->config['default_host'];
-
-        $_SESSION['temp'] = true;
-
-        $input_user   = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser', 'size' => 30) + $attrib);
-        $input_pass   = new html_passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd', 'size' => 30) + $attrib);
-        $input_action = new html_hiddenfield(array('name' => '_action', 'value' => 'login'));
-        $input_tzone  = new html_hiddenfield(array('name' => '_timezone', 'id' => 'rcmlogintz', 'value' => '_default_'));
-        $input_host   = null;
-
-        if (is_array($default_host)) {
-            $input_host = new html_select(array('name' => '_host', 'id' => 'rcmloginhost'));
-
-            foreach ($default_host as $key => $value) {
-                if (!is_array($value)) {
-                    $input_host->add($value, (is_numeric($key) ? $value : $key));
-                }
-                else {
-                    $input_host = null;
-                    break;
-                }
-            }
-        }
-        else if (empty($default_host)) {
-            $input_host = new html_inputfield(array('name' => '_host', 'id' => 'rcmloginhost', 'size' => 30));
-        }
-
-        $form_name  = !empty($attrib['form']) ? $attrib['form'] : 'form';
-        $this->add_gui_object('loginform', $form_name);
-
-        // create HTML table with two cols
-        $table = new html_table(array('cols' => 2));
-
-        $table->add('title', html::label('rcmloginuser', Q(rcube_label('username'))));
-        $table->add(null, $input_user->show(get_input_value('_user', RCUBE_INPUT_POST)));
-
-        $table->add('title', html::label('rcmloginpwd', Q(rcube_label('password'))));
-        $table->add(null, $input_pass->show());
-
-        // add host selection row
-        if (is_object($input_host)) {
-            $table->add('title', html::label('rcmloginhost', Q(rcube_label('server'))));
-            $table->add(null, $input_host->show(get_input_value('_host', RCUBE_INPUT_POST)));
-        }
-
-        $out = $input_action->show();
-        $out .= $input_tzone->show();
-        $out .= $table->show();
-
-        // surround html output with a form tag
-        if (empty($attrib['form'])) {
-            $out = $this->form_tag(array('name' => $form_name, 'method' => "post"), $out);
-        }
-
-        return $out;
-    }
-
-
-    /**
-     * GUI object 'searchform'
-     * Returns code for search function
-     *
-     * @param array Named parameters
-     * @return string HTML code for the gui object
-     */
-    private function search_form($attrib)
-    {
-        // add some labels to client
-        $this->add_label('searching');
-
-        $attrib['name'] = '_q';
-
-        if (empty($attrib['id'])) {
-            $attrib['id'] = 'rcmqsearchbox';
-        }
-        if ($attrib['type'] == 'search' && !$this->browser->khtml) {
-          unset($attrib['type'], $attrib['results']);
-        }
-        
-        $input_q = new html_inputfield($attrib);
-        $out = $input_q->show();
-
-        $this->add_gui_object('qsearchbox', $attrib['id']);
-
-        // add form tag around text field
-        if (empty($attrib['form'])) {
-            $out = $this->form_tag(array(
-                'name' => "rcmqsearchform",
-                'onsubmit' => JS_OBJECT_NAME . ".command('search');return false;",
-                'style' => "display:inline"),
-              $out);
-        }
-
-        return $out;
-    }
-
-
-    /**
-     * Builder for GUI object 'message'
-     *
-     * @param array Named tag parameters
-     * @return string HTML code for the gui object
-     */
-    private function message_container($attrib)
-    {
-        if (isset($attrib['id']) === false) {
-            $attrib['id'] = 'rcmMessageContainer';
-        }
-
-        $this->add_gui_object('message', $attrib['id']);
-        return html::div($attrib, "");
-    }
-
-
-    /**
-     * GUI object 'charsetselector'
-     *
-     * @param array Named parameters for the select tag
-     * @return string HTML code for the gui object
-     */
-    static function charset_selector($attrib)
-    {
-        // pass the following attributes to the form class
-        $field_attrib = array('name' => '_charset');
-        foreach ($attrib as $attr => $value) {
-            if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex'))) {
-                $field_attrib[$attr] = $value;
-            }
-        }
-        $charsets = array(
-            'US-ASCII'     => 'ASCII (English)',
-            'EUC-JP'       => 'EUC-JP (Japanese)',
-            'EUC-KR'       => 'EUC-KR (Korean)',
-            'BIG5'         => 'BIG5 (Chinese)',
-            'GB2312'       => 'GB2312 (Chinese)',
-            'ISO-2022-JP'  => 'ISO-2022-JP (Japanese)',
-            'ISO-8859-1'   => 'ISO-8859-1 (Latin-1)',
-            'ISO-8859-2'   => 'ISO-8895-2 (Central European)',
-            'ISO-8859-7'   => 'ISO-8859-7 (Greek)',
-            'ISO-8859-9'   => 'ISO-8859-9 (Turkish)',
-            'Windows-1251' => 'Windows-1251 (Cyrillic)',
-            'Windows-1252' => 'Windows-1252 (Western)',
-            'Windows-1255' => 'Windows-1255 (Hebrew)',
-            'Windows-1256' => 'Windows-1256 (Arabic)',
-            'Windows-1257' => 'Windows-1257 (Baltic)',
-            'UTF-8'        => 'UTF-8'
-            );
-
-            $select = new html_select($field_attrib);
-            $select->add(array_values($charsets), array_keys($charsets));
-
-            $set = $_POST['_charset'] ? $_POST['_charset'] : $this->get_charset();
-            return $select->show($set);
-    }
-
-}  // end class rcube_template
-
-
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_template.php                                    |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2006-2008, RoundCube Dev. - Switzerland                 |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Class to handle HTML page output using a skin template.             |
+ |   Extends rcube_html_page class from rcube_shared.inc                 |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ +-----------------------------------------------------------------------+
+
+ $Id$
+
+ */
+
+
+/**
+ * Class to create HTML page output using a skin template
+ *
+ * @package View
+ * @todo Documentation
+ * @uses rcube_html_page
+ */
+class rcube_template extends rcube_html_page
+{
+    var $app;
+    var $config;
+    var $framed = false;
+    var $pagetitle = '';
+    var $env = array();
+    var $js_env = array();
+    var $js_commands = array();
+    var $object_handlers = array();
+
+    public $type = 'html';
+    public $ajax_call = false;
+
+    /**
+     * Constructor
+     *
+     * @todo   Use jQuery's $(document).ready() here.
+     * @todo   Replace $this->config with the real rcube_config object
+     */
+    public function __construct($task, $framed = false)
+    {
+        parent::__construct();
+
+        $this->app = rcmail::get_instance();
+        $this->config = $this->app->config->all();
+        $this->browser = new rcube_browser();
+        
+        //$this->framed = $framed;
+        $this->set_env('task', $task);
+
+        // load the correct skin (in case user-defined)
+        $this->set_skin($this->config['skin']);
+
+        // add common javascripts
+        $javascript = 'var '.JS_OBJECT_NAME.' = new rcube_webmail();';
+
+        // don't wait for page onload. Call init at the bottom of the page (delayed)
+        $javascript_foot = "if (window.call_init)\n call_init('".JS_OBJECT_NAME."');";
+
+        $this->add_script($javascript, 'head_top');
+        $this->add_script($javascript_foot, 'foot');
+        $this->scripts_path = 'program/js/';
+        $this->include_script('common.js');
+        $this->include_script('app.js');
+
+        // register common UI objects
+        $this->add_handlers(array(
+            'loginform'       => array($this, 'login_form'),
+            'username'        => array($this, 'current_username'),
+            'message'         => array($this, 'message_container'),
+            'charsetselector' => array($this, 'charset_selector'),
+        ));
+    }
+
+    /**
+     * Set environment variable
+     *
+     * @param string Property name
+     * @param mixed Property value
+     * @param boolean True if this property should be added to client environment
+     */
+    public function set_env($name, $value, $addtojs = true)
+    {
+        $this->env[$name] = $value;
+        if ($addtojs || isset($this->js_env[$name])) {
+            $this->js_env[$name] = $value;
+        }
+    }
+
+
+    /**
+     * Set page title variable
+     */
+    public function set_pagetitle($title)
+    {
+        $this->pagetitle = $title;
+    }
+
+
+    /**
+     * Getter for the current page title
+     *
+     * @return string The page title
+     */
+    public function get_pagetitle()
+    {
+        if (!empty($this->pagetitle)) {
+            $title = $this->pagetitle;
+        }
+        else if ($this->env['task'] == 'login') {
+            $title = rcube_label(array('name' => 'welcome', 'vars' => array('product' => $this->config['product_name'])));
+        }
+        else {
+            $title = ucfirst($this->env['task']);
+        }
+        
+        return $title;
+    }
+
+
+    /**
+     * Set skin
+     */
+    public function set_skin($skin)
+    {
+        if (!empty($skin) && is_dir('skins/'.$skin) && is_readable('skins/'.$skin))
+            $skin_path = 'skins/'.$skin;
+        else
+            $skin_path = $this->config['skin_path'] ? $this->config['skin_path'] : 'skins/default';
+
+        $this->app->config->set('skin_path', $skin_path);
+        $this->config['skin_path'] = $skin_path;
+    }
+
+    /**
+     * Check if a specific template exists
+     *
+     * @param string Template name
+     * @return boolean True if template exists
+     */
+    public function template_exists($name)
+    {
+        $filename = $this->config['skin_path'] . '/templates/' . $name . '.html';
+
+        return (is_file($filename) && is_readable($filename));
+    }
+
+    /**
+     * Register a template object handler
+     *
+     * @param  string Object name
+     * @param  string Function name to call
+     * @return void
+     */
+    public function add_handler($obj, $func)
+    {
+        $this->object_handlers[$obj] = $func;
+    }
+
+    /**
+     * Register a list of template object handlers
+     *
+     * @param  array Hash array with object=>handler pairs
+     * @return void
+     */
+    public function add_handlers($arr)
+    {
+        $this->object_handlers = array_merge($this->object_handlers, $arr);
+    }
+
+    /**
+     * Register a GUI object to the client script
+     *
+     * @param  string Object name
+     * @param  string Object ID
+     * @return void
+     */
+    public function add_gui_object($obj, $id)
+    {
+        $this->add_script(JS_OBJECT_NAME.".gui_object('$obj', '$id');");
+    }
+
+    /**
+     * Call a client method
+     *
+     * @param string Method to call
+     * @param ... Additional arguments
+     */
+    public function command()
+    {
+        $this->js_commands[] = func_get_args();
+    }
+
+
+    /**
+     * Add a localized label to the client environment
+     */
+    public function add_label()
+    {
+        $arg_list = func_get_args();
+        foreach ($arg_list as $i => $name) {
+            $this->command('add_label', $name, rcube_label($name));
+        }
+    }
+
+
+    /**
+     * Invoke display_message command
+     *
+     * @param string Message to display
+     * @param string Message type [notice|confirm|error]
+     * @param array Key-value pairs to be replaced in localized text
+     * @uses self::command()
+     */
+    public function show_message($message, $type='notice', $vars=NULL)
+    {
+        $this->command(
+            'display_message',
+            rcube_label(array('name' => $message, 'vars' => $vars)),
+            $type);
+    }
+
+
+    /**
+     * Delete all stored env variables and commands
+     *
+     * @return void
+     * @uses   rcube_html::reset()
+     * @uses   self::$env
+     * @uses   self::$js_env
+     * @uses   self::$js_commands
+     * @uses   self::$object_handlers
+     */
+    public function reset()
+    {
+        $this->env = array();
+        $this->js_env = array();
+        $this->js_commands = array();
+        $this->object_handlers = array();
+        parent::reset();
+    }
+
+
+    /**
+     * Redirect to a certain url
+     *
+     * @param mixed Either a string with the action or url parameters as key-value pairs
+     * @see rcmail::url()
+     */
+    public function redirect($p = array())
+    {
+        $location = $this->app->url($p);
+        header('Location: ' . $location);
+        exit;
+    }
+
+
+    /**
+     * Send the request output to the client.
+     * This will either parse a skin tempalte or send an AJAX response
+     *
+     * @param string  Template name
+     * @param boolean True if script should terminate (default)
+     */
+    public function send($templ = null, $exit = true)
+    {
+        if ($templ != 'iframe') {
+            $this->parse($templ, false);
+        }
+        else {
+            $this->framed = $templ == 'iframe' ? true : $this->framed;
+            $this->write();
+        }
+
+        if ($exit) {
+            exit;
+        }
+    }
+
+    /**
+     * Process template and write to stdOut
+     *
+     * @param string HTML template
+     * @see rcube_html_page::write()
+     * @override
+     */
+    public function write($template = '')
+    {
+        // unlock interface after iframe load
+        if ($this->framed) {
+            array_unshift($this->js_commands, array('set_busy', false));
+        }
+        // write all env variables to client
+        $js = $this->framed ? "if(window.parent) {\n" : '';
+        $js .= $this->get_js_commands() . ($this->framed ? ' }' : '');
+        $this->add_script($js, 'head_top');
+
+        // call super method
+        parent::write($template, $this->config['skin_path']);
+    }
+
+    /**
+     * Parse a specific skin template and deliver to stdout
+     *
+     * Either returns nothing, or exists hard (exit();)
+     *
+     * @param  string  Template name
+     * @param  boolean Exit script
+     * @return void
+     * @link   http://php.net/manual/en/function.exit.php
+     */
+    private function parse($name = 'main', $exit = true)
+    {
+        $skin_path = $this->config['skin_path'];
+        $path = "$skin_path/templates/$name.html";
+
+        // read template file
+        if (($templ = @file_get_contents($path)) === false) {
+            raise_error(array(
+                'code' => 501,
+                'type' => 'php',
+                'line' => __LINE__,
+                'file' => __FILE__,
+                'message' => 'Error loading template for '.$name
+                ), true, true);
+            return false;
+        }
+
+        // parse for specialtags
+        $output = $this->parse_conditions($templ);
+        $output = $this->parse_xml($output);
+
+        // add debug console
+        if ($this->config['debug_level'] & 8) {
+            $this->add_footer('<div style="position:absolute;top:5px;left:5px;width:405px;padding:2px;background:white;opacity:0.8;filter:alpha(opacity=80);z-index:9000">
+                <a href="#toggle" onclick="con=document.getElementById(\'dbgconsole\');con.style.display=(con.style.display==\'none\'?\'block\':\'none\');return false">console</a>
+                <form action="/" name="debugform" style="display:inline"><textarea name="console" id="dbgconsole" rows="20" cols="40" wrap="off" style="display:none;width:400px;border:none;font-size:x-small" spellcheck="false"></textarea></form></div>'
+            );
+        }
+        $output = $this->parse_with_globals($output);
+        $this->write(trim($output), $skin_path);
+        if ($exit) {
+            exit;
+        }
+    }
+
+
+    /**
+     * Return executable javascript code for all registered commands
+     *
+     * @return string $out
+     */
+    private function get_js_commands()
+    {
+        $out = '';
+        if (!$this->framed && !empty($this->js_env)) {
+            $out .= JS_OBJECT_NAME . '.set_env('.json_serialize($this->js_env).");\n";
+        }
+        foreach ($this->js_commands as $i => $args) {
+            $method = array_shift($args);
+            foreach ($args as $i => $arg) {
+                $args[$i] = json_serialize($arg);
+            }
+            $parent = $this->framed || preg_match('/^parent\./', $method);
+            $out .= sprintf(
+                "%s.%s(%s);\n",
+            ($parent ? 'parent.' : '') . JS_OBJECT_NAME,
+            preg_replace('/^parent\./', '', $method),
+            implode(',', $args)
+            );
+        }
+        
+        return $out;
+    }
+
+    /**
+     * Make URLs starting with a slash point to skin directory
+     *
+     * @param  string Input string
+     * @return string
+     */
+    public function abs_url($str)
+    {
+        return preg_replace('/^\//', $this->config['skin_path'].'/', $str);
+    }
+
+
+    /*****  Template parsing methods  *****/
+
+    /**
+     * Replace all strings ($varname)
+     * with the content of the according global variable.
+     */
+    private function parse_with_globals($input)
+    {
+        $GLOBALS['__comm_path'] = Q($this->app->comm_path);
+        return preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input);
+    }
+
+    /**
+     * Public wrapper to dipp into template parsing.
+     *
+     * @param  string $input
+     * @return string
+     * @uses   rcube_template::parse_xml()
+     * @since  0.1-rc1
+     */
+    public function just_parse($input)
+    {
+        return $this->parse_xml($input);
+    }
+
+    /**
+     * Parse for conditional tags
+     *
+     * @param  string $input
+     * @return string
+     */
+    private function parse_conditions($input)
+    {
+        $matches = preg_split('/<roundcube:(if|elseif|else|endif)\s+([^>]+)>/is', $input, 2, PREG_SPLIT_DELIM_CAPTURE);
+        if ($matches && count($matches) == 4) {
+            if (preg_match('/^(else|endif)$/i', $matches[1])) {
+                return $matches[0] . $this->parse_conditions($matches[3]);
+            }
+            $attrib = parse_attrib_string($matches[2]);
+            if (isset($attrib['condition'])) {
+                $condmet = $this->check_condition($attrib['condition']);
+                $submatches = preg_split('/<roundcube:(elseif|else|endif)\s+([^>]+)>/is', $matches[3], 2, PREG_SPLIT_DELIM_CAPTURE);
+                if ($condmet) {
+                    $result = $submatches[0];
+                    $result.= ($submatches[1] != 'endif' ? preg_replace('/.*<roundcube:endif\s+[^>]+>/Uis', '', $submatches[3], 1) : $submatches[3]);
+                }
+                else {
+                    $result = "<roundcube:$submatches[1] $submatches[2]>" . $submatches[3];
+                }
+                return $matches[0] . $this->parse_conditions($result);
+            }
+            raise_error(array(
+                'code' => 500,
+                'type' => 'php',
+                'line' => __LINE__,
+                'file' => __FILE__,
+                'message' => "Unable to parse conditional tag " . $matches[2]
+            ), true, false);
+        }
+        return $input;
+    }
+
+
+    /**
+     * Determines if a given condition is met
+     *
+     * @todo   Get rid off eval() once I understand what this does.
+     * @todo   Extend this to allow real conditions, not just "set"
+     * @param  string Condition statement
+     * @return boolean True if condition is met, False if not
+     */
+    private function check_condition($condition)
+    {
+            return eval("return (".$this->parse_expression($condition).");");
+    }
+
+
+    /**
+     * Parses expression and replaces variables
+     *
+     * @param  string Expression statement
+     * @return string Expression statement
+     */
+    private function parse_expression($expression)
+    {
+        return preg_replace(
+            array(
+                '/session:([a-z0-9_]+)/i',
+                '/config:([a-z0-9_]+)(:([a-z0-9_]+))?/i',
+                '/env:([a-z0-9_]+)/i',
+                '/request:([a-z0-9_]+)/i',
+                '/cookie:([a-z0-9_]+)/i'
+            ),
+            array(
+                "\$_SESSION['\\1']",
+                "\$this->app->config->get('\\1',get_boolean('\\3'))",
+                "\$this->env['\\1']",
+                "get_input_value('\\1', RCUBE_INPUT_GPC)",
+                "\$_COOKIE['\\1']"
+            ),
+            $expression);
+    }
+
+
+    /**
+     * Search for special tags in input and replace them
+     * with the appropriate content
+     *
+     * @param  string Input string to parse
+     * @return string Altered input string
+     * @todo   Use DOM-parser to traverse template HTML
+     * @todo   Maybe a cache.
+     */
+    private function parse_xml($input)
+    {
+        return preg_replace_callback('/<roundcube:([-_a-z]+)\s+([^>]+)>/Ui', array($this, 'xml_command_callback'), $input);
+    }
+
+
+    /**
+     * This is a callback function for preg_replace_callback (see #1485286)
+     * It's only purpose is to reconfigure parameters for xml_command, so that the signature isn't disturbed
+     */
+    private function xml_command_callback($matches)
+    {
+        $str_attrib = isset($matches[2]) ? $matches[2] : '';
+        $add_attrib = isset($matches[3]) ? $matches[3] : array();
+
+        $command = $matches[1];
+        //matches[0] is the entire matched portion of the string
+
+        return $this->xml_command($command, $str_attrib, $add_attrib);
+    }
+
+
+    /**
+     * Convert a xml command tag into real content
+     *
+     * @param  string Tag command: object,button,label, etc.
+     * @param  string Attribute string
+     * @return string Tag/Object content
+     */
+    private function xml_command($command, $str_attrib, $add_attrib = array())
+    {
+        $command = strtolower($command);
+        $attrib  = parse_attrib_string($str_attrib) + $add_attrib;
+
+        // empty output if required condition is not met
+        if (!empty($attrib['condition']) && !$this->check_condition($attrib['condition'])) {
+            return '';
+        }
+
+        // execute command
+        switch ($command) {
+            // return a button
+            case 'button':
+                if ($attrib['name'] || $attrib['command']) {
+                    return $this->button($attrib);
+                }
+                break;
+
+            // show a label
+            case 'label':
+                if ($attrib['name'] || $attrib['command']) {
+                    return Q(rcube_label($attrib + array('vars' => array('product' => $this->config['product_name']))));
+                }
+                break;
+
+            // include a file
+            case 'include':
+                $path = realpath($this->config['skin_path'].$attrib['file']);
+                if (is_readable($path)) {
+                    if ($this->config['skin_include_php']) {
+                        $incl = $this->include_php($path);
+                    }
+                    else {
+		        $incl = file_get_contents($path);
+		    }
+                    return $this->parse_xml($incl);
+                }
+                break;
+
+            case 'plugin.include':
+                //rcube::tfk_debug(var_export($this->config['skin_path'], true));
+                $path = realpath($this->config['skin_path'].$attrib['file']);
+                if (!$path) {
+                    //rcube::tfk_debug("Does not exist:");
+                    //rcube::tfk_debug($this->config['skin_path']);
+                    //rcube::tfk_debug($attrib['file']);
+                    //rcube::tfk_debug($path);
+                }
+                $incl = file_get_contents($path);
+                if ($incl) {
+                    return $this->parse_xml($incl);
+                }
+                break;
+
+            // return code for a specific application object
+            case 'object':
+                $object = strtolower($attrib['name']);
+
+                // we are calling a class/method
+                if (($handler = $this->object_handlers[$object]) && is_array($handler)) {
+                    if ((is_object($handler[0]) && method_exists($handler[0], $handler[1])) ||
+                    (is_string($handler[0]) && class_exists($handler[0])))
+                    return call_user_func($handler, $attrib);
+                }
+                else if (function_exists($handler)) {
+                    // execute object handler function
+                    return call_user_func($handler, $attrib);
+                }
+
+                if ($object=='productname') {
+                    $name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'RoundCube Webmail';
+                    return Q($name);
+                }
+                if ($object=='version') {
+                    $ver = (string)RCMAIL_VERSION;
+                    if (is_file(INSTALL_PATH . '.svn/entries')) {
+                        if (preg_match('/Revision:\s(\d+)/', @shell_exec('svn info'), $regs))
+                          $ver .= ' [SVN r'.$regs[1].']';
+                    }
+                    return $ver;
+                }
+                if ($object=='steptitle') {
+                  return Q($this->get_pagetitle());
+                }
+                if ($object=='pagetitle') {
+                    $title = !empty($this->config['product_name']) ? $this->config['product_name'].' :: ' : '';
+                    $title .= $this->get_pagetitle();
+                    return Q($title);
+                }
+                break;
+
+            // return code for a specified eval expression
+            case 'exp':
+        	$value = $this->parse_expression($attrib['expression']);
+                return eval("return Q($value);");
+            
+            // return variable
+            case 'var':
+                $var = explode(':', $attrib['name']);
+                $name = $var[1];
+                $value = '';
+
+                switch ($var[0]) {
+                    case 'env':
+                        $value = $this->env[$name];
+                        break;
+                    case 'config':
+                        $value = $this->config[$name];
+                        if (is_array($value) && $value[$_SESSION['imap_host']]) {
+                            $value = $value[$_SESSION['imap_host']];
+                        }
+                        break;
+                    case 'request':
+                        $value = get_input_value($name, RCUBE_INPUT_GPC);
+                        break;
+                    case 'session':
+                        $value = $_SESSION[$name];
+                        break;
+                    case 'cookie':
+                        $value = htmlspecialchars($_COOKIE[$name]);
+                        break;
+                }
+
+                if (is_array($value)) {
+                    $value = implode(', ', $value);
+                }
+
+                return Q($value);
+                break;
+        }
+        return '';
+    }
+
+    /**
+     * Include a specific file and return it's contents
+     *
+     * @param string File path
+     * @return string Contents of the processed file
+     */
+    private function include_php($file)
+    {
+        ob_start();
+        include $file;
+        $out = ob_get_contents();
+        ob_end_clean();
+
+        return $out;
+    }
+
+    /**
+     * Create and register a button
+     *
+     * @param  array Named button attributes
+     * @return string HTML button
+     * @todo   Remove all inline JS calls and use jQuery instead.
+     * @todo   Remove all sprintf()'s - they are pretty, but also slow.
+     */
+    public function button($attrib)
+    {
+        static $sa_buttons = array();
+        static $s_button_count = 100;
+
+        // these commands can be called directly via url
+        $a_static_commands = array('compose', 'list');
+
+        if (!($attrib['command'] || $attrib['name'])) {
+            return '';
+        }
+
+        // try to find out the button type
+        if ($attrib['type']) {
+            $attrib['type'] = strtolower($attrib['type']);
+        }
+        else {
+            $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link';
+        }
+        $command = $attrib['command'];
+
+        // take the button from the stack
+        if ($attrib['name'] && $sa_buttons[$attrib['name']]) {
+            $attrib = $sa_buttons[$attrib['name']];
+        }
+        else if($attrib['image'] || $attrib['imageact'] || $attrib['imagepas'] || $attrib['class']) {
+            // add button to button stack
+            if (!$attrib['name']) {
+                $attrib['name'] = $command;
+            }
+            if (!$attrib['image']) {
+                $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact'];
+            }
+            $sa_buttons[$attrib['name']] = $attrib;
+        }
+        else if ($command && $sa_buttons[$command]) {
+            // get saved button for this command/name
+            $attrib = $sa_buttons[$command];
+        }
+
+        if (!$attrib['id']) {
+            $attrib['id'] =  sprintf('rcmbtn%d', $s_button_count++);
+        }
+        // get localized text for labels and titles
+        if ($attrib['title']) {
+            $attrib['title'] = Q(rcube_label($attrib['title']));
+        }
+        if ($attrib['label']) {
+            $attrib['label'] = Q(rcube_label($attrib['label']));
+        }
+        if ($attrib['alt']) {
+            $attrib['alt'] = Q(rcube_label($attrib['alt']));
+        }
+        // set title to alt attribute for IE browsers
+        if ($this->browser->ie && $attrib['title'] && !$attrib['alt']) {
+            $attrib['alt'] = $attrib['title'];
+            unset($attrib['title']);
+        }
+
+        // add empty alt attribute for XHTML compatibility
+        if (!isset($attrib['alt'])) {
+            $attrib['alt'] = '';
+        }
+
+        // register button in the system
+        if ($attrib['command']) {
+            $this->add_script(sprintf(
+                "%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');",
+                JS_OBJECT_NAME,
+                $command,
+                $attrib['id'],
+                $attrib['type'],
+                $attrib['imageact'] ? $this->abs_url($attrib['imageact']) : $attrib['classact'],
+                $attrib['imagesel'] ? $this->abs_url($attrib['imagesel']) : $attrib['classsel'],
+                $attrib['imageover'] ? $this->abs_url($attrib['imageover']) : ''
+            ));
+
+            // make valid href to specific buttons
+            if (in_array($attrib['command'], rcmail::$main_tasks)) {
+                $attrib['href'] = rcmail_url(null, null, $attrib['command']);
+            }
+            else if (in_array($attrib['command'], $a_static_commands)) {
+                $attrib['href'] = rcmail_url($attrib['command']);
+            }
+            else if ($attrib['command'] == 'permaurl' && !empty($this->env['permaurl'])) {
+                $attrib['href'] = $this->env['permaurl'];
+            }
+        }
+
+        // overwrite attributes
+        if (!$attrib['href']) {
+            $attrib['href'] = '#';
+        }
+        if ($command) {
+            $attrib['onclick'] = sprintf(
+                "return %s.command('%s','%s',this)",
+                JS_OBJECT_NAME,
+                $command,
+                $attrib['prop']
+            );
+        }
+        if ($command && $attrib['imageover']) {
+            $attrib['onmouseover'] = sprintf(
+                "return %s.button_over('%s','%s')",
+                JS_OBJECT_NAME,
+                $command,
+                $attrib['id']
+            );
+            $attrib['onmouseout'] = sprintf(
+                "return %s.button_out('%s','%s')",
+                JS_OBJECT_NAME,
+                $command,
+                $attrib['id']
+            );
+        }
+
+        if ($command && $attrib['imagesel']) {
+            $attrib['onmousedown'] = sprintf(
+                "return %s.button_sel('%s','%s')",
+                JS_OBJECT_NAME,
+                $command,
+                $attrib['id']
+            );
+            $attrib['onmouseup'] = sprintf(
+                "return %s.button_out('%s','%s')",
+                JS_OBJECT_NAME,
+                $command,
+                $attrib['id']
+            );
+        }
+
+        $out = '';
+
+        // generate image tag
+        if ($attrib['type']=='image') {
+            $attrib_str = html::attrib_string(
+                $attrib,
+                array(
+                    'style', 'class', 'id', 'width',
+                    'height', 'border', 'hspace',
+                    'vspace', 'align', 'alt', 'tabindex'
+                )
+            );
+            $btn_content = sprintf('<img src="%s"%s />', $this->abs_url($attrib['image']), $attrib_str);
+            if ($attrib['label']) {
+                $btn_content .= ' '.$attrib['label'];
+            }
+            $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'title', 'target');
+        }
+        else if ($attrib['type']=='link') {
+            $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command'];
+            $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style', 'tabindex', 'target');
+        }
+        else if ($attrib['type']=='input') {
+            $attrib['type'] = 'button';
+
+            if ($attrib['label']) {
+                $attrib['value'] = $attrib['label'];
+            }
+
+            $attrib_str = html::attrib_string(
+                $attrib,
+                array(
+                    'type', 'value', 'onclick',
+                    'id', 'class', 'style', 'tabindex'
+                )
+            );
+            $out = sprintf('<input%s disabled="disabled" />', $attrib_str);
+        }
+
+        // generate html code for button
+        if ($btn_content) {
+            $attrib_str = html::attrib_string($attrib, $link_attrib);
+            $out = sprintf('<a%s>%s</a>', $attrib_str, $btn_content);
+        }
+
+        return $out;
+    }
+
+
+    /*  ************* common functions delivering gui objects **************  */
+
+
+    /**
+     * Create a form tag with the necessary hidden fields
+     *
+     * @param array Named tag parameters
+     * @return string HTML code for the form
+     */
+    public function form_tag($attrib, $content = null)
+    {
+      if ($this->framed) {
+        $hiddenfield = new html_hiddenfield(array('name' => '_framed', 'value' => '1'));
+        $hidden = $hiddenfield->show();
+      }
+      
+      if (!$content)
+        $attrib['noclose'] = true;
+      
+      return html::tag('form',
+        $attrib + array('action' => "./", 'method' => "get"),
+        $hidden . $content);
+    }
+
+
+    /**
+     * GUI object 'username'
+     * Showing IMAP username of the current session
+     *
+     * @param array Named tag parameters (currently not used)
+     * @return string HTML code for the gui object
+     */
+    public function current_username($attrib)
+    {
+        static $username;
+
+        // alread fetched
+        if (!empty($username)) {
+            return $username;
+        }
+
+        // get e-mail address form default identity
+        if ($sql_arr = $this->app->user->get_identity()) {
+            $username = $sql_arr['email'];
+        }
+        else {
+            $username = $this->app->user->get_username();
+        }
+
+        return $username;
+    }
+
+
+    /**
+     * GUI object 'loginform'
+     * Returns code for the webmail login form
+     *
+     * @param array Named parameters
+     * @return string HTML code for the gui object
+     */
+    private function login_form($attrib)
+    {
+        $default_host = $this->config['default_host'];
+
+        $_SESSION['temp'] = true;
+
+        $input_user   = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser', 'size' => 30) + $attrib);
+        $input_pass   = new html_passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd', 'size' => 30, 'autocomplete' => 'off') + $attrib);
+        $input_action = new html_hiddenfield(array('name' => '_action', 'value' => 'login'));
+        $input_tzone  = new html_hiddenfield(array('name' => '_timezone', 'id' => 'rcmlogintz', 'value' => '_default_'));
+        $input_host   = null;
+        
+        if (is_array($default_host)) {
+            $input_host = new html_select(array('name' => '_host', 'id' => 'rcmloginhost'));
+
+            foreach ($default_host as $key => $value) {
+                if (!is_array($value)) {
+                    $input_host->add($value, (is_numeric($key) ? $value : $key));
+                }
+                else {
+                    $input_host = null;
+                    break;
+                }
+            }
+        }
+        else if (empty($default_host)) {
+            $input_host = new html_inputfield(array('name' => '_host', 'id' => 'rcmloginhost', 'size' => 30));
+        }
+
+        $input_captcha = null;
+        if ($this->app->config->get('use_captcha') === true) {
+            // create HTML table with two cols
+            $CaptchaTable = new html_table(array('cols' => 2));
+            $input_captcha = new html_inputfield(array('name' => '_captcha', 'id' => 'rcmcaptcha', 'size' => 6, 'autocomplete' => 'off', 'maxlength' => 6));          
+            $CaptchaTable->add(null, $input_captcha->show());
+
+            $reload = $this->abs_url("/images/refresh.gif");
+            
+            $CaptchaTable->add(null, "<img id=\"captcha_img\" src=\"./?_task=login&_action=sendcaptcha\"><a href=\"#\" title=\"Refresh Image\" onclick=\"document.getElementById('captcha_img').src = './?_task=login&_action=sendcaptcha&sid=' + Math.random(); return false\"><img src=\"$reload\" alt=\"Reload Image\" style=\"border-style: none; vertical-align:bottom;\" onclick=\"this.blur()\" /></a>");            
+            
+            $input_captcha = $CaptchaTable->show();
+        }        
+        
+        $form_name  = !empty($attrib['form']) ? $attrib['form'] : 'form';
+        $this->add_gui_object('loginform', $form_name);
+
+        // create HTML table with two cols
+        $table = new html_table(array('cols' => 2));
+
+        $table->add('title', html::label('rcmloginuser', Q(rcube_label('username'))));
+        $table->add(null, $input_user->show(get_input_value('_user', RCUBE_INPUT_POST)));
+
+        $table->add('title', html::label('rcmloginpwd', Q(rcube_label('password'))));
+        $table->add(null, $input_pass->show());
+        
+        if ($this->app->config->get('use_captcha') === true) {
+        $table->add('title', html::label('rcmcaptcha', Q(rcube_label('capctha'))));
+        $table->add(null, $input_captcha);
+        }      
+
+        // add host selection row
+        if (is_object($input_host)) {
+            $table->add('title', html::label('rcmloginhost', Q(rcube_label('server'))));
+            $table->add(null, $input_host->show(get_input_value('_host', RCUBE_INPUT_POST)));
+        }
+
+        $out = $input_action->show();
+        $out .= $input_tzone->show();
+        $out .= $table->show();
+
+        // surround html output with a form tag
+        if (empty($attrib['form'])) {
+            $out = $this->form_tag(array('name' => $form_name, 'method' => "post"), $out);
+        }
+
+        return $out;
+    }
+
+
+    /**
+     * GUI object 'searchform'
+     * Returns code for search function
+     *
+     * @param array Named parameters
+     * @return string HTML code for the gui object
+     */
+    private function search_form($attrib)
+    {
+        // add some labels to client
+        $this->add_label('searching');
+
+        $attrib['name'] = '_q';
+
+        if (empty($attrib['id'])) {
+            $attrib['id'] = 'rcmqsearchbox';
+        }
+        if ($attrib['type'] == 'search' && !$this->browser->khtml) {
+          unset($attrib['type'], $attrib['results']);
+        }
+        
+        $input_q = new html_inputfield($attrib);
+        $out = $input_q->show();
+
+        $this->add_gui_object('qsearchbox', $attrib['id']);
+
+        // add form tag around text field
+        if (empty($attrib['form'])) {
+            $out = $this->form_tag(array(
+                'name' => "rcmqsearchform",
+                'onsubmit' => JS_OBJECT_NAME . ".command('search');return false;",
+                'style' => "display:inline"),
+              $out);
+        }
+
+        return $out;
+    }
+
+
+    /**
+     * Builder for GUI object 'message'
+     *
+     * @param array Named tag parameters
+     * @return string HTML code for the gui object
+     */
+    private function message_container($attrib)
+    {
+        if (isset($attrib['id']) === false) {
+            $attrib['id'] = 'rcmMessageContainer';
+        }
+
+        $this->add_gui_object('message', $attrib['id']);
+        return html::div($attrib, "");
+    }
+
+
+    /**
+     * GUI object 'charsetselector'
+     *
+     * @param array Named parameters for the select tag
+     * @return string HTML code for the gui object
+     */
+    static function charset_selector($attrib)
+    {
+        // pass the following attributes to the form class
+        $field_attrib = array('name' => '_charset');
+        foreach ($attrib as $attr => $value) {
+            if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex'))) {
+                $field_attrib[$attr] = $value;
+            }
+        }
+        $charsets = array(
+            'US-ASCII'     => 'ASCII (English)',
+            'EUC-JP'       => 'EUC-JP (Japanese)',
+            'EUC-KR'       => 'EUC-KR (Korean)',
+            'BIG5'         => 'BIG5 (Chinese)',
+            'GB2312'       => 'GB2312 (Chinese)',
+            'ISO-2022-JP'  => 'ISO-2022-JP (Japanese)',
+            'ISO-8859-1'   => 'ISO-8859-1 (Latin-1)',
+            'ISO-8859-2'   => 'ISO-8895-2 (Central European)',
+            'ISO-8859-7'   => 'ISO-8859-7 (Greek)',
+            'ISO-8859-9'   => 'ISO-8859-9 (Turkish)',
+            'Windows-1251' => 'Windows-1251 (Cyrillic)',
+            'Windows-1252' => 'Windows-1252 (Western)',
+            'Windows-1255' => 'Windows-1255 (Hebrew)',
+            'Windows-1256' => 'Windows-1256 (Arabic)',
+            'Windows-1257' => 'Windows-1257 (Baltic)',
+            'UTF-8'        => 'UTF-8'
+            );
+
+            $select = new html_select($field_attrib);
+            $select->add(array_values($charsets), array_keys($charsets));
+
+            $set = $_POST['_charset'] ? $_POST['_charset'] : $this->get_charset();
+            return $select->show($set);
+    }
+
+}  // end class rcube_template
+
+
Index: program/localization/ar_SA/labels.inc
===================================================================
--- program/localization/ar_SA/labels.inc	(revision 2350)
+++ program/localization/ar_SA/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'مرحباً بكم في $product';
 $labels['username'] = 'اسم المستخدم';
 $labels['password'] = 'كلمة المرور';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'الخادم';
 $labels['login'] = 'تسجيل الدخول';
 $labels['logout'] = 'تسجيل الخروج';
Index: program/localization/az_AZ/labels.inc
===================================================================
--- program/localization/az_AZ/labels.inc	(revision 2350)
+++ program/localization/az_AZ/labels.inc	(working copy)
@@ -19,6 +19,7 @@
 $labels['welcome'] = '$product \-a xoş gəldiniz';
 $labels['username'] = 'İstifadəçi Adı';
 $labels['password'] = 'Şifrə';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Daxil ol';
 $labels['logout'] = 'Çıxış';
Index: program/localization/bg_BG/labels.inc
===================================================================
--- program/localization/bg_BG/labels.inc	(revision 2350)
+++ program/localization/bg_BG/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Добре дошли в $product';
 $labels['username'] = 'Потребител';
 $labels['password'] = 'Парола';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Сървър';
 $labels['login'] = 'Вход';
 $labels['logout'] = 'Изход';
Index: program/localization/bs_BA/labels.inc
===================================================================
--- program/localization/bs_BA/labels.inc	(revision 2350)
+++ program/localization/bs_BA/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome']   = 'Dobrodošli na $product';
 $labels['username']  = 'Korisničko ime';
 $labels['password']  = 'Lozinka';
+$labels['captcha'] = 'Security Code';
 $labels['server']    = 'Server';
 $labels['login']     = 'Prijava';
 
@@ -231,4 +232,4 @@
 $labels['sortasc']  = 'Sortiraj rastućim nizom';
 $labels['sortdesc'] = 'Sortiraj opadajućim nizom';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/ca_ES/labels.inc
===================================================================
--- program/localization/ca_ES/labels.inc	(revision 2350)
+++ program/localization/ca_ES/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Benvingut a $product';
 $labels['username'] = 'Nom d\'usuari';
 $labels['password'] = 'Contrasenya';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Servidor';
 $labels['login'] = 'Entrar';
 $labels['logout'] = 'Tancar sessió';
@@ -265,4 +266,4 @@
 $labels['MB'] = 'MB';
 $labels['GB'] = 'GB';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/cs_CZ/labels.inc
===================================================================
--- program/localization/cs_CZ/labels.inc	(revision 2350)
+++ program/localization/cs_CZ/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome'] = 'Vítejte v $product';
 $labels['username'] = 'Uživatel';
 $labels['password'] = 'Heslo';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Přihlásit';
 $labels['logout'] = 'Odhlásit';
@@ -246,4 +247,4 @@
 $labels['sortasc'] = 'Seřadit vzestupně';
 $labels['sortdesc'] = 'Seřadit sestupně';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/cy_GB/labels.inc
===================================================================
--- program/localization/cy_GB/labels.inc	(revision 2350)
+++ program/localization/cy_GB/labels.inc	(working copy)
@@ -18,6 +18,7 @@
 $labels['welcome'] = 'Croeso i $product';
 $labels['username'] = 'Enw defnyddiwr';
 $labels['password'] = 'Cyfrinair';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Gweinydd';
 $labels['login'] = 'Mewngofnodi';
 $labels['logout'] = 'Allgofnodi';
Index: program/localization/da_DK/labels.inc
===================================================================
--- program/localization/da_DK/labels.inc	(revision 2350)
+++ program/localization/da_DK/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome'] = 'Velkommen til $product';
 $labels['username'] = 'Brugernavn';
 $labels['password'] = 'Adgangskode';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Log på';
 $labels['logout'] = 'Log af';
Index: program/localization/de_CH/labels.inc
===================================================================
--- program/localization/de_CH/labels.inc	(revision 2350)
+++ program/localization/de_CH/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Willkommen bei $product';
 $labels['username'] = 'Benutzername';
 $labels['password'] = 'Passwort';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Login';
 $labels['logout'] = 'Logout';
Index: program/localization/de_DE/labels.inc
===================================================================
--- program/localization/de_DE/labels.inc	(revision 2350)
+++ program/localization/de_DE/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Willkommen bei $product';
 $labels['username'] = 'Benutzername';
 $labels['password'] = 'Passwort';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Anmelden';
 $labels['logout'] = 'Abmelden';
Index: program/localization/el_GR/labels.inc
===================================================================
--- program/localization/el_GR/labels.inc	(revision 2350)
+++ program/localization/el_GR/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Καλώς ήρθατε στο $product';
 $labels['username'] = 'Όνομα Χρήστη';
 $labels['password'] = 'Κωδικός Πρόσβασης';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Διακομιστής';
 $labels['login'] = 'Είσοδος';
 $labels['logout'] = 'Έξοδος';
@@ -190,4 +191,4 @@
 $labels['sortasc'] = 'Αύξουσα ταξινόμηση';
 $labels['sortdesc'] = 'Φθίνουσα ταξινόμηση';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/en_GB/labels.inc
===================================================================
--- program/localization/en_GB/labels.inc	(revision 2350)
+++ program/localization/en_GB/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Welcome to $product';
 $labels['username'] = 'Username';
 $labels['password'] = 'Password';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Login';
 $labels['logout'] = 'Logout';
Index: program/localization/en_US/labels.inc
===================================================================
--- program/localization/en_US/labels.inc	(revision 2350)
+++ program/localization/en_US/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome']   = 'Welcome to $product';
 $labels['username']  = 'Username';
 $labels['password']  = 'Password';
+$labels['captcha'] = 'Security Code';
 $labels['server']    = 'Server';
 $labels['login']     = 'Login';
 
Index: program/localization/eo/labels.inc
===================================================================
--- program/localization/eo/labels.inc	(revision 2350)
+++ program/localization/eo/labels.inc	(working copy)
@@ -19,6 +19,7 @@
 $labels['welcome'] = 'Bonvenon al $product';
 $labels['username'] = 'Uzantnomo';
 $labels['password'] = 'Pasvorto';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Servilo';
 $labels['login'] = 'Ensalutu';
 $labels['logout'] = 'Elsalutu';
Index: program/localization/es_ES/labels.inc
===================================================================
--- program/localization/es_ES/labels.inc	(revision 2350)
+++ program/localization/es_ES/labels.inc	(working copy)
@@ -24,6 +24,7 @@
 $labels['welcome'] = 'Bienvenido a $product';
 $labels['username'] = 'Nombre de usuario';
 $labels['password'] = 'Contraseña';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Servidor';
 $labels['login'] = 'Entrar';
 $labels['logout'] = 'Cerrar sesión';
Index: program/localization/et_EE/labels.inc
===================================================================
--- program/localization/et_EE/labels.inc	(revision 2350)
+++ program/localization/et_EE/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Teretulemast kasutama $product';
 $labels['username'] = 'Kasutajanimi';
 $labels['password'] = 'Parool';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Logi sisse';
 $labels['logout'] = 'Logi välja';
Index: program/localization/eu_ES/labels.inc
===================================================================
--- program/localization/eu_ES/labels.inc	(revision 2350)
+++ program/localization/eu_ES/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Ongietorri Roundcube|Mail-era';
 $labels['username'] = 'Erabiltzailea';
 $labels['password'] = 'Pasahitza';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Zerbitzaria';
 $labels['login'] = 'Saio hasiera';
 $labels['logout'] = 'Irten';
Index: program/localization/fa/labels.inc
===================================================================
--- program/localization/fa/labels.inc	(revision 2350)
+++ program/localization/fa/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'خوش آمدید $product به';
 $labels['username'] = 'نام کاربری';
 $labels['password'] = 'گذرواژه';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'سرور';
 $labels['login'] = 'ورود';
 $labels['logout'] = 'خروج';
Index: program/localization/fi_FI/labels.inc
===================================================================
--- program/localization/fi_FI/labels.inc	(revision 2350)
+++ program/localization/fi_FI/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Tervetuloa $product -käyttäjäksi';
 $labels['username'] = 'Käyttäjätunnus';
 $labels['password'] = 'Salasana';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Palvelin';
 $labels['login'] = 'Kirjaudu';
 $labels['logout'] = 'Kirjaudu ulos';
Index: program/localization/fr_FR/labels.inc
===================================================================
--- program/localization/fr_FR/labels.inc	(revision 2350)
+++ program/localization/fr_FR/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome'] = 'Bienvenue sur $product';
 $labels['username'] = 'Utilisateur';
 $labels['password'] = 'Mot de passe';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Serveur';
 $labels['login'] = 'Authentification';
 $labels['logout'] = 'Quitter';
Index: program/localization/ga_IE/labels.inc
===================================================================
--- program/localization/ga_IE/labels.inc	(revision 2350)
+++ program/localization/ga_IE/labels.inc	(working copy)
@@ -20,6 +20,7 @@
 $labels['welcome'] = 'Fáilte go $product';
 $labels['username'] = 'Úsáideoir';
 $labels['password'] = 'Pasfhocal';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Freastalaí';
 $labels['login'] = 'Logáil isteach';
 $labels['logout'] = 'Logáil amach';
@@ -176,4 +177,4 @@
 $labels['sortasc'] = 'Sórtáil in ord méadaitheach';
 $labels['sortdesc'] = 'Sórtáil in ord neartaitheach';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/gl_ES/labels.inc
===================================================================
--- program/localization/gl_ES/labels.inc	(revision 2350)
+++ program/localization/gl_ES/labels.inc	(working copy)
@@ -18,6 +18,7 @@
 $labels['welcome'] = 'Benvido a $product';
 $labels['username'] = 'Nome de usuario';
 $labels['password'] = 'Contrasinal';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Servidor';
 $labels['login'] = 'Acceder';
 $labels['logout'] = 'Saír';
Index: program/localization/he_IL/labels.inc
===================================================================
--- program/localization/he_IL/labels.inc	(revision 2350)
+++ program/localization/he_IL/labels.inc	(working copy)
@@ -19,6 +19,7 @@
 $labels['welcome'] = 'ברוך בואך אל $product';
 $labels['username'] = 'שם משתמש';
 $labels['password'] = 'סיסמה';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'שרת';
 $labels['login'] = 'כניסה';
 $labels['logout'] = 'יציאה';
Index: program/localization/hi_IN/labels.inc
===================================================================
--- program/localization/hi_IN/labels.inc	(revision 2350)
+++ program/localization/hi_IN/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome']   = '$product में स्वागत है';
 $labels['username']  = 'यूसरनाम';
 $labels['password']  = 'पासवर्ड';
+$labels['captcha'] = 'Security Code';
 $labels['server']    = 'सर्वर';
 $labels['login']     = 'लॉगिन';
 
@@ -236,4 +237,4 @@
 $labels['sortasc']		= 'पहले से आखीरी तय करें';
 $labels['sortdesc']		= 'आखीरी से पहले तय करें';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/hr_HR/labels.inc
===================================================================
--- program/localization/hr_HR/labels.inc	(revision 2350)
+++ program/localization/hr_HR/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Dobrodošli na $product';
 $labels['username'] = 'E-mail';
 $labels['password'] = 'Lozinka';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Prijava';
 $labels['logout'] = 'Odjava';
Index: program/localization/hu_HU/labels.inc
===================================================================
--- program/localization/hu_HU/labels.inc	(revision 2350)
+++ program/localization/hu_HU/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Üdvözli a $product';
 $labels['username'] = 'Felhasználónév';
 $labels['password'] = 'Jelszó';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Szerver';
 $labels['login'] = 'Belépés';
 $labels['logout'] = 'Kijelentkezés';
Index: program/localization/hy_AM/labels.inc
===================================================================
--- program/localization/hy_AM/labels.inc	(revision 2350)
+++ program/localization/hy_AM/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Բարի գալուստ $product';
 $labels['username'] = 'Օգտվող';
 $labels['password'] = 'Գաղտնաբառ';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Սերվեր';
 $labels['login'] = 'Մուտք';
 $labels['logout'] = 'Ելք';
@@ -189,4 +190,4 @@
 $labels['sortasc'] = 'ըստ աճման';
 $labels['sortdesc'] = 'ըստ նվազման';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/id_ID/labels.inc
===================================================================
--- program/localization/id_ID/labels.inc	(revision 2350)
+++ program/localization/id_ID/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Selamat datang di $product';
 $labels['username'] = 'Username';
 $labels['password'] = 'Password';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Masuk';
 $labels['logout'] = 'Keluar';
Index: program/localization/is_IS/labels.inc
===================================================================
--- program/localization/is_IS/labels.inc	(revision 2350)
+++ program/localization/is_IS/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome']   = 'Vefpóstur Vodafone';
 $labels['username']  = 'Netfang';
 $labels['password']  = 'Lykilorð';
+$labels['captcha'] = 'Security Code';
 $labels['server']    = 'Server';
 $labels['login']     = 'Innskrá';
 
Index: program/localization/it_IT/labels.inc
===================================================================
--- program/localization/it_IT/labels.inc	(revision 2350)
+++ program/localization/it_IT/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Benvenuto in $product';
 $labels['username'] = 'Utente';
 $labels['password'] = 'Password';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Entra';
 $labels['logout'] = 'Esci';
Index: program/localization/ja_JP/labels.inc
===================================================================
--- program/localization/ja_JP/labels.inc	(revision 2350)
+++ program/localization/ja_JP/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'ようこそ $product へ';
 $labels['username'] = 'ユーザー名';
 $labels['password'] = 'パスワード';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'サーバ';
 $labels['login'] = 'ログイン';
 $labels['logout'] = 'ログアウト';
Index: program/localization/ka_GE/labels.inc
===================================================================
--- program/localization/ka_GE/labels.inc	(revision 2350)
+++ program/localization/ka_GE/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'მოგესალმებათ $product';
 $labels['username'] = 'მომხმარებელი';
 $labels['password'] = 'პაროლი';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'სერვერი';
 $labels['login'] = 'შესვლა';
 $labels['logout'] = 'გამოსვლა';
Index: program/localization/ku/labels.inc
===================================================================
--- program/localization/ku/labels.inc	(revision 2350)
+++ program/localization/ku/labels.inc	(working copy)
@@ -18,6 +18,7 @@
 $labels['welcome'] = 'Bixêr hatî $product \\\'ê';
 $labels['username'] = 'Bikarhêner';
 $labels['password'] = 'Nasnav';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Pêşkêşkar';
 $labels['login'] = 'Têkevê';
 $labels['logout'] = 'Derkeve';
Index: program/localization/lt_LT/labels.inc
===================================================================
--- program/localization/lt_LT/labels.inc	(revision 2350)
+++ program/localization/lt_LT/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Sveiki atvykę į $product';
 $labels['username'] = 'Vartotojas';
 $labels['password'] = 'Slaptažodis';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Serveris';
 $labels['login'] = 'Prisijungti';
 $labels['logout'] = 'Atsijungti';
Index: program/localization/lv_LV/labels.inc
===================================================================
--- program/localization/lv_LV/labels.inc	(revision 2350)
+++ program/localization/lv_LV/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Esiet laipni lūgti $product';
 $labels['username'] = 'Lietotājvārds';
 $labels['password'] = 'Parole';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Serveris';
 $labels['login'] = 'Pieslēgties';
 $labels['logout'] = 'Atslēgties';
@@ -190,4 +191,4 @@
 $labels['sortasc'] = 'Kārtot augošā secībā';
 $labels['sortdesc'] = 'Kārtot dilstošā secībā';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/mk_MK/labels.inc
===================================================================
--- program/localization/mk_MK/labels.inc	(revision 2350)
+++ program/localization/mk_MK/labels.inc	(working copy)
@@ -20,6 +20,7 @@
 $labels['welcome'] = 'Добредојдовте во $product';
 $labels['username'] = 'Корисничко Име';
 $labels['password'] = 'Лозинка';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Сервер';
 $labels['login'] = 'Логирај Се';
 $labels['logout'] = 'Одлогирај Се';
Index: program/localization/ms_MY/labels.inc
===================================================================
--- program/localization/ms_MY/labels.inc	(revision 2350)
+++ program/localization/ms_MY/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Selamat datang ke $product';
 $labels['username'] = 'Nama Pengguna';
 $labels['password'] = 'Kata Laluan';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Pelayan';
 $labels['login'] = 'Log Masuk';
 $labels['logout'] = 'Log Keluar';
Index: program/localization/nb_NO/labels.inc
===================================================================
--- program/localization/nb_NO/labels.inc	(revision 2350)
+++ program/localization/nb_NO/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Velkommen til $product';
 $labels['username'] = 'Brukernavn:';
 $labels['password'] = 'Passord:';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Logg på';
 $labels['logout'] = 'Logg ut';
@@ -218,4 +219,4 @@
 $labels['sortasc'] = 'Eldste først';
 $labels['sortdesc'] = 'Nyeste først';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/ne_NP/labels.inc
===================================================================
--- program/localization/ne_NP/labels.inc	(revision 2350)
+++ program/localization/ne_NP/labels.inc	(working copy)
@@ -19,6 +19,7 @@
 $labels['welcome'] = '$product मा स्वागत छ';
 $labels['username'] = 'युसरनेम';
 $labels['password'] = 'पासवर्ड';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'सर्भर';
 $labels['login'] = 'लगईन';
 $labels['logout'] = 'लगआउट';
Index: program/localization/nl_BE/labels.inc
===================================================================
--- program/localization/nl_BE/labels.inc	(revision 2350)
+++ program/localization/nl_BE/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Welkom bij $product';
 $labels['username'] = 'Gebruikersnaam';
 $labels['password'] = 'Wachtwoord';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Aanmelden';
 $labels['logout'] = 'Afmelden';
Index: program/localization/nl_NL/labels.inc
===================================================================
--- program/localization/nl_NL/labels.inc	(revision 2350)
+++ program/localization/nl_NL/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome'] = 'Welkom bij $product';
 $labels['username'] = 'Gebruikersnaam';
 $labels['password'] = 'Wachtwoord';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Inloggen';
 $labels['logout'] = 'Uitloggen';
Index: program/localization/nn_NO/labels.inc
===================================================================
--- program/localization/nn_NO/labels.inc	(revision 2350)
+++ program/localization/nn_NO/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Velkomen til $product';
 $labels['username'] = 'Brukarnamn';
 $labels['password'] = 'Passord';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Tenar';
 $labels['login'] = 'Logg inn';
 $labels['logout'] = 'Logg ut';
Index: program/localization/pl_PL/labels.inc
===================================================================
--- program/localization/pl_PL/labels.inc	(revision 2350)
+++ program/localization/pl_PL/labels.inc	(working copy)
@@ -26,6 +26,7 @@
 $labels['welcome'] = 'Witamy w $product';
 $labels['username'] = 'Nazwa';
 $labels['password'] = 'Hasło';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Serwer';
 $labels['login'] = 'Zaloguj';
 $labels['logout'] = 'Wyloguj';
Index: program/localization/pt_BR/labels.inc
===================================================================
--- program/localization/pt_BR/labels.inc	(revision 2350)
+++ program/localization/pt_BR/labels.inc	(working copy)
@@ -25,6 +25,7 @@
 $labels['welcome'] = 'Bem Vindo ao $product';
 $labels['username'] = 'Usuário';
 $labels['password'] = 'Senha';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Servidor';
 $labels['login'] = 'Entrar';
 $labels['logout'] = 'Sair';
Index: program/localization/pt_PT/labels.inc
===================================================================
--- program/localization/pt_PT/labels.inc	(revision 2350)
+++ program/localization/pt_PT/labels.inc	(working copy)
@@ -24,6 +24,7 @@
 $labels['welcome'] = 'Bem-vindo ao $product';
 $labels['username'] = 'Utilizador';
 $labels['password'] = 'Senha';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Servidor';
 $labels['login'] = 'Entrar';
 $labels['logout'] = 'Sair';
Index: program/localization/ro_RO/labels.inc
===================================================================
--- program/localization/ro_RO/labels.inc	(revision 2350)
+++ program/localization/ro_RO/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Bine ati venit la $product';
 $labels['username'] = 'Utilizator';
 $labels['password'] = 'Parola';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Autentificare';
 $labels['logout'] = 'Deconectare';
@@ -190,4 +191,4 @@
 $labels['sortasc'] = 'Sorteaza ascendent';
 $labels['sortdesc'] = 'Sorteaza descendent';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/ru_RU/labels.inc
===================================================================
--- program/localization/ru_RU/labels.inc	(revision 2350)
+++ program/localization/ru_RU/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome'] = 'Добро пожаловать в $product!';
 $labels['username'] = 'Имя пользователя';
 $labels['password'] = 'Пароль';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Сервер';
 $labels['login'] = 'Войти';
 $labels['logout'] = 'Выход';
Index: program/localization/si_LK/labels.inc
===================================================================
--- program/localization/si_LK/labels.inc	(revision 2350)
+++ program/localization/si_LK/labels.inc	(working copy)
@@ -20,6 +20,7 @@
 $labels['welcome'] = '$product ආයුබොවන්';
 $labels['username'] = 'නම';
 $labels['password'] = 'මුරපදය';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'පද්ධතිය';
 $labels['login'] = 'අතුල්වීම';
 $labels['logout'] = 'පිටවීම';
@@ -188,4 +189,4 @@
 $labels['sortasc'] = 'ආරෝහණ වර්ග කිරීම';
 $labels['sortdesc'] = 'අවරෝහණ වර්ග කිරීම';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/sk_SK/labels.inc
===================================================================
--- program/localization/sk_SK/labels.inc	(revision 2350)
+++ program/localization/sk_SK/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome'] = 'Vitajte v $product';
 $labels['username'] = 'Prihlasovacie meno';
 $labels['password'] = 'Heslo';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
 $labels['login'] = 'Prihlásiť';
 $labels['logout'] = 'Odhlásiť';
Index: program/localization/sl_SI/labels.inc
===================================================================
--- program/localization/sl_SI/labels.inc	(revision 2350)
+++ program/localization/sl_SI/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = 'Dobrodošel v $product';
 $labels['username'] = 'Uporabniško ime';
 $labels['password'] = 'Geslo';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Strežnik';
 $labels['login'] = 'Prijava';
 $labels['logout'] = 'Odjava';
Index: program/localization/sq_AL/labels.inc
===================================================================
--- program/localization/sq_AL/labels.inc	(revision 2350)
+++ program/localization/sq_AL/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Mirëseerdhe te $product';
 $labels['username'] = 'Përdoruesi';
 $labels['password'] = 'Fjalëkalimi';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Serveri';
 $labels['login'] = 'Hyr';
 $labels['logout'] = 'Dil';
Index: program/localization/sr_CS/labels.inc
===================================================================
--- program/localization/sr_CS/labels.inc	(revision 2350)
+++ program/localization/sr_CS/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Добродошли на $product';
 $labels['username'] = 'Корисничко име';
 $labels['password'] = 'Лозинка';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Сервер';
 $labels['login'] = 'Пријава';
 $labels['logout'] = 'Одјава';
Index: program/localization/sv_SE/labels.inc
===================================================================
--- program/localization/sv_SE/labels.inc	(revision 2350)
+++ program/localization/sv_SE/labels.inc	(working copy)
@@ -23,7 +23,9 @@
 $labels['welcome'] = 'Välkommen till $product';
 $labels['username'] = 'Användarnamn';
 $labels['password'] = 'Lösenord';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Server';
+$labels['capctha'] = 'Säkerhetskod';
 $labels['login'] = 'Logga in';
 $labels['logout'] = 'Logga ut';
 $labels['mail'] = 'Meddelanden';
Index: program/localization/th_TH/labels.inc
===================================================================
--- program/localization/th_TH/labels.inc	(revision 2350)
+++ program/localization/th_TH/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'ยินดีต้อนรับสู่ $product';
 $labels['username'] = 'บัญชีผู้ใช้';
 $labels['password'] = 'รหัสผ่าน';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'เซิร์ฟเวอร์';
 $labels['login'] = 'เข้าสู่ระบบ';
 $labels['logout'] = 'ออกจากระบบ';
@@ -171,4 +172,4 @@
 $labels['sortasc'] = 'เรียงจากหน้าไปหลัง';
 $labels['sortdesc'] = 'เรียงจากหลังไปหน้า';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/tr_TR/labels.inc
===================================================================
--- program/localization/tr_TR/labels.inc	(revision 2350)
+++ program/localization/tr_TR/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = '$product, Hoş Geldiniz';
 $labels['username'] = 'Kullanıcı Adı';
 $labels['password'] = 'Şifre';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Sunucu';
 $labels['login'] = 'Oturum Aç';
 $labels['logout'] = 'Oturumu Kapat';
Index: program/localization/uk_UA/labels.inc
===================================================================
--- program/localization/uk_UA/labels.inc	(revision 2350)
+++ program/localization/uk_UA/labels.inc	(working copy)
@@ -21,6 +21,7 @@
 $labels['welcome'] = 'Ласкаво просимо в $product!';
 $labels['username'] = 'Ім`я користувача';
 $labels['password'] = 'Пароль';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = 'Сервер';
 $labels['login'] = 'Увійти';
 $labels['logout'] = 'Вийти';
@@ -181,4 +182,4 @@
 $labels['sortasc'] = 'Сортувати за зростанням';
 $labels['sortdesc'] = 'Сортувати за спаданням';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/vi_VN/labels.inc
===================================================================
--- program/localization/vi_VN/labels.inc	(revision 2350)
+++ program/localization/vi_VN/labels.inc	(working copy)
@@ -23,6 +23,7 @@
 $labels['welcome']   = 'Xin mời sử dụng $product';
 $labels['username']  = 'Tên người dùng';
 $labels['password']  = 'Mật khẩu';
+$labels['captcha'] = 'Security Code';
 $labels['server']    = 'Máy chủ?';
 $labels['login']     = 'Đăng nhập';
 
@@ -214,4 +215,4 @@
 $labels['sortasc']  = 'Xếp tăng dần';
 $labels['sortdesc'] = 'Xếp giảm dần';
 
-?>
\ No newline at end of file
+?>
Index: program/localization/zh_CN/labels.inc
===================================================================
--- program/localization/zh_CN/labels.inc	(revision 2350)
+++ program/localization/zh_CN/labels.inc	(working copy)
@@ -22,6 +22,7 @@
 $labels['welcome'] = '欢迎使用 $product';
 $labels['username'] = '用户名';
 $labels['password'] = '密码';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = '服务器';
 $labels['login'] = '登录';
 $labels['logout'] = '注销';
Index: program/localization/zh_TW/labels.inc
===================================================================
--- program/localization/zh_TW/labels.inc	(revision 2350)
+++ program/localization/zh_TW/labels.inc	(working copy)
@@ -25,6 +25,7 @@
 $labels['welcome'] = '歡迎使用 $product';
 $labels['username'] = '使用者名稱';
 $labels['password'] = '使用者密碼';
+$labels['captcha'] = 'Security Code';
 $labels['server'] = '伺服器';
 $labels['login'] = '登入';
 
@@ -303,4 +304,4 @@
 $labels['sortasc'] = '遞增排序';
 $labels['sortdesc'] = '遞減排序';
 
-?>
\ No newline at end of file
+?>
Index: skins/default/images/refresh.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\refresh.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

