thomascube
2007-12-10 fba1f5ab813f2eb4bedc5d9c4a75e77bbaa90131
New class rcube_user + send message disposition notification

2 files added
20 files modified
1349 ■■■■■ changed files
.htaccess 2 ●●●●● patch | view | raw | blame | history
CHANGELOG 5 ●●●●● patch | view | raw | blame | history
index.php 13 ●●●●● patch | view | raw | blame | history
program/include/main.inc 254 ●●●● patch | view | raw | blame | history
program/include/rcmail_template.inc 18 ●●●● patch | view | raw | blame | history
program/include/rcube_user.inc 470 ●●●●● patch | view | raw | blame | history
program/js/app.js 10 ●●●●● patch | view | raw | blame | history
program/lib/imap.inc 82 ●●●●● patch | view | raw | blame | history
program/lib/rc_mail_mime.inc 28 ●●●●● patch | view | raw | blame | history
program/localization/en_US/labels.inc 5 ●●●●● patch | view | raw | blame | history
program/localization/en_US/messages.inc 2 ●●●●● patch | view | raw | blame | history
program/steps/mail/compose.inc 11 ●●●● patch | view | raw | blame | history
program/steps/mail/func.inc 66 ●●●●● patch | view | raw | blame | history
program/steps/mail/sendmail.inc 111 ●●●● patch | view | raw | blame | history
program/steps/mail/sendmdn.inc 97 ●●●●● patch | view | raw | blame | history
program/steps/mail/show.inc 3 ●●●●● patch | view | raw | blame | history
program/steps/settings/delete_identity.inc 9 ●●●● patch | view | raw | blame | history
program/steps/settings/edit_identity.inc 9 ●●●● patch | view | raw | blame | history
program/steps/settings/func.inc 24 ●●●● patch | view | raw | blame | history
program/steps/settings/save_identity.inc 107 ●●●● patch | view | raw | blame | history
program/steps/settings/save_prefs.inc 11 ●●●●● patch | view | raw | blame | history
skins/default/mail.css 12 ●●●●● patch | view | raw | blame | history
.htaccess
@@ -7,6 +7,7 @@
  php_value    error_log    logs/errors
  php_value    upload_max_filesize    5M
  php_value    post_max_size    6M
  php_value memory_limit    64M
</IfModule>
<IfModule mod_php5.c>
@@ -15,6 +16,7 @@
  php_value    error_log    logs/errors
  php_value    upload_max_filesize    5M
  php_value    post_max_size    6M
  php_value memory_limit    64M
</IfModule>
<FilesMatch "(\.inc|\~)$">
CHANGELOG
@@ -1,6 +1,11 @@
CHANGELOG RoundCube Webmail
---------------------------
2007/12/10 (thomasb)
----------
- Delegate user-stuff to new class rcube_user
- Implement Message-Disposition-Notification (Receipts)
2007/12/05 (tomekp)
----------
- update bg, uk localization
index.php
@@ -2,7 +2,7 @@
/*
 +-----------------------------------------------------------------------+
 | RoundCube Webmail IMAP Client                                         |
 | Version 0.1-20071017                                                  |
 | Version 0.1-20071210                                                  |
 |                                                                       |
 | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
 | Licensed under the GNU GPL                                            |
@@ -41,7 +41,7 @@
*/
// application constants
define('RCMAIL_VERSION', '0.1-20071017');
define('RCMAIL_VERSION', '0.1-20071210');
define('RCMAIL_CHARSET', 'UTF-8');
define('JS_OBJECT_NAME', 'rcmail');
@@ -196,7 +196,7 @@
// log in to imap server
if (!empty($_SESSION['user_id']) && $_task=='mail')
if (!empty($USER->ID) && $_task=='mail')
{
  $conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']);
  if (!$conn)
@@ -210,7 +210,7 @@
// not logged in -> set task to 'login
if (empty($_SESSION['user_id']))
if (empty($USER->ID))
{
  if ($OUTPUT->ajax_call)
    $OUTPUT->remote_response("setTimeout(\"location.href='\"+this.env.comm_path+\"'\", 2000);");
@@ -238,7 +238,7 @@
// not logged in -> show login page
if (!$_SESSION['user_id'])
if (empty($USER->ID))
{
  $OUTPUT->task = 'login';
  $OUTPUT->send('login');
@@ -274,6 +274,9 @@
  if ($_action=='viewsource')
    include('program/steps/mail/viewsource.inc');
  if ($_action=='sendmdn')
    include('program/steps/mail/sendmdn.inc');
  if ($_action=='send')
    include('program/steps/mail/sendmail.inc');
program/include/main.inc
@@ -29,6 +29,7 @@
require_once('lib/des.inc');
require_once('lib/utf7.inc');
require_once('lib/utf8.class.php');
require_once('include/rcube_user.inc');
require_once('include/rcube_shared.inc');
require_once('include/rcmail_template.inc');
@@ -48,7 +49,7 @@
function rcmail_startup($task='mail')
  {
  global $sess_id, $sess_user_lang;
  global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB;
  global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB, $USER;
  // check client
  $BROWSER = rcube_browser();
@@ -87,6 +88,8 @@
  // set session vars global
  $sess_user_lang = rcube_language_prop($_SESSION['user_lang']);
  // create user object
  $USER = new rcube_user($_SESSION['user_id']);
  // overwrite config with user preferences
  if (is_array($_SESSION['user_prefs']))
@@ -321,21 +324,18 @@
 */
function rcmail_kill_session()
  {
  // save user preferences
  $a_user_prefs = $_SESSION['user_prefs'];
  if (!is_array($a_user_prefs))
    $a_user_prefs = array();
  global $USER;
  if ((isset($_SESSION['sort_col']) && $_SESSION['sort_col']!=$a_user_prefs['message_sort_col']) ||
      (isset($_SESSION['sort_order']) && $_SESSION['sort_order']!=$a_user_prefs['message_sort_order']))
    {
    $a_user_prefs['message_sort_col'] = $_SESSION['sort_col'];
    $a_user_prefs['message_sort_order'] = $_SESSION['sort_order'];
    rcmail_save_user_prefs($a_user_prefs);
    $a_user_prefs = array('message_sort_col' => $_SESSION['sort_col'], 'message_sort_order' => $_SESSION['sort_order']);
    $USER->save_prefs($a_user_prefs);
    }
  $_SESSION = array('user_lang' => $GLOBALS['sess_user_lang'], 'auth_time' => time(), 'temp' => true);
  setcookie('sessauth', '-del-', time()-60);
  $USER->reset();
  }
@@ -531,7 +531,7 @@
 */
function rcmail_login($user, $pass, $host=NULL)
  {
  global $CONFIG, $IMAP, $DB, $sess_user_lang;
  global $CONFIG, $IMAP, $DB, $USER, $sess_user_lang;
  $user_id = NULL;
  
  if (!$host)
@@ -583,58 +583,51 @@
  // try to resolve email address from virtuser table    
  if (!empty($CONFIG['virtuser_file']) && strpos($user, '@'))
    $user = rcmail_email2user($user);
    $user = rcube_user::email2user($user);
  // lowercase username if it's an e-mail address (#1484473)
  if (strpos($user, '@'))
    $user = strtolower($user);
  // query if user already registered
  $sql_result = $DB->query(
    "SELECT user_id, username, language, preferences
     FROM ".get_table_name('users')."
     WHERE  mail_host=? AND (username=? OR alias=?)",
    $host,
    $user,
    $user);
  if ($existing = rcube_user::query($user, $host))
  $USER = $existing;
  // user already registered -> overwrite username
  if ($sql_arr = $DB->fetch_assoc($sql_result))
  if ($USER->ID)
    {
    $user_id = $sql_arr['user_id'];
    $user = $sql_arr['username'];
    $user_id = $USER->ID;
    $user = $USER->data['username'];
    }
  // exit if IMAP login failed
  if (!($imap_login  = $IMAP->connect($host, $user, $pass, $imap_port, $imap_ssl)))
    return FALSE;
    return false;
  // user already registered
  if ($user_id && !empty($sql_arr))
  if ($USER->ID)
    {
    // get user prefs
    if (strlen($sql_arr['preferences']))
      {
      $user_prefs = unserialize($sql_arr['preferences']);
      $_SESSION['user_prefs'] = $user_prefs;
      array_merge($CONFIG, $user_prefs);
      }
    $_SESSION['user_prefs'] = $USER->get_prefs();
    array_merge($CONFIG, $_SESSION['user_prefs']);
    // set user specific language
    if (strlen($sql_arr['language']))
      $sess_user_lang = $_SESSION['user_lang'] = $sql_arr['language'];
    if (!empty($USER->data['language']))
      $sess_user_lang = $_SESSION['user_lang'] = $USER->data['language'];
      
    // update user's record
    $DB->query("UPDATE ".get_table_name('users')."
                SET    last_login=".$DB->now()."
                WHERE  user_id=?",
                $user_id);
    $USER->touch();
    }
  // create new system user
  else if ($CONFIG['auto_create_user'])
    {
    $user_id = rcmail_create_user($user, $host);
    if ($created = rcube_user::create($user, $host))
    {
      $USER = $created;
      // get existing mailboxes
      $a_mailboxes = $IMAP->list_mailboxes();
    }
    }
  else
    {
@@ -646,13 +639,13 @@
      ), true, false);
    }
  if ($user_id)
  if ($USER->ID)
    {
    $_SESSION['user_id']   = $user_id;
    $_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['username']  = $user;
    $_SESSION['user_lang'] = $sess_user_lang;
    $_SESSION['password']  = encrypt_passwd($pass);
    $_SESSION['login_time'] = mktime();
@@ -669,83 +662,6 @@
  return FALSE;
  }
/**
 * Create new entry in users and identities table
 *
 * @param string User name
 * @param string IMAP host
 * @return mixed New user ID or False on failure
 */
function rcmail_create_user($user, $host)
{
  global $DB, $CONFIG, $IMAP;
  $user_email = '';
  // try to resolve user in virtusertable
  if (!empty($CONFIG['virtuser_file']) && !strpos($user, '@'))
    $user_email = rcmail_user2email($user);
  $DB->query("INSERT INTO ".get_table_name('users')."
              (created, last_login, username, mail_host, alias, language)
              VALUES (".$DB->now().", ".$DB->now().", ?, ?, ?, ?)",
              strip_newlines($user),
              strip_newlines($host),
              strip_newlines($user_email),
              $_SESSION['user_lang']);
  if ($user_id = $DB->insert_id(get_sequence_name('users')))
  {
    $mail_domain = rcmail_mail_domain($host);
    if ($user_email=='')
      $user_email = strpos($user, '@') ? $user : sprintf('%s@%s', $user, $mail_domain);
    $user_name = $user!=$user_email ? $user : '';
    // try to resolve the e-mail address from the virtuser table
    if (!empty($CONFIG['virtuser_query']) &&
        ($sql_result = $DB->query(preg_replace('/%u/', $DB->escapeSimple($user), $CONFIG['virtuser_query']))) &&
        ($DB->num_rows()>0))
    {
      while ($sql_arr = $DB->fetch_array($sql_result))
      {
        $DB->query("INSERT INTO ".get_table_name('identities')."
                   (user_id, del, standard, name, email)
                   VALUES (?, 0, 1, ?, ?)",
                   $user_id,
                   strip_newlines($user_name),
                   preg_replace('/^@/', $user . '@', $sql_arr[0]));
      }
    }
    else
    {
      // also create new identity records
      $DB->query("INSERT INTO ".get_table_name('identities')."
                  (user_id, del, standard, name, email)
                  VALUES (?, 0, 1, ?, ?)",
                  $user_id,
                  strip_newlines($user_name),
                  strip_newlines($user_email));
    }
    // get existing mailboxes
    $a_mailboxes = $IMAP->list_mailboxes();
  }
  else
  {
    raise_error(array(
      'code' => 500,
      'type' => 'php',
      'line' => __LINE__,
      'file' => __FILE__,
      'message' => "Failed to create new user"), TRUE, FALSE);
  }
  return $user_id;
}
/**
@@ -790,90 +706,6 @@
    }
  return $result;
  }
/**
 * Resolve username using a virtuser table
 *
 * @param string E-mail address to resolve
 * @return string Resolved IMAP username
 */
function rcmail_email2user($email)
  {
  $user = $email;
  $r = rcmail_findinvirtual("^$email");
  for ($i=0; $i<count($r); $i++)
    {
    $data = $r[$i];
    $arr = preg_split('/\s+/', $data);
    if(count($arr)>0)
      {
      $user = trim($arr[count($arr)-1]);
      break;
      }
    }
  return $user;
  }
/**
 * Resolve e-mail address from virtuser table
 *
 * @param string User name
 * @return string Resolved e-mail address
 */
function rcmail_user2email($user)
  {
  $email = "";
  $r = rcmail_findinvirtual("$user$");
  for ($i=0; $i<count($r); $i++)
    {
    $data=$r[$i];
    $arr = preg_split('/\s+/', $data);
    if (count($arr)>0)
      {
      $email = trim($arr[0]);
      break;
      }
    }
  return $email;
  }
/**
 * Write the given user prefs to the user's record
 *
 * @param mixed User prefs to save
 * @return boolean True on success, False on failure
 */
function rcmail_save_user_prefs($a_user_prefs)
  {
  global $DB, $CONFIG, $sess_user_lang;
  // merge (partial) prefs array with existing settings
  $a_user_prefs += (array)$_SESSION['user_prefs'];
  $DB->query("UPDATE ".get_table_name('users')."
              SET    preferences=?,
                     language=?
              WHERE  user_id=?",
              serialize($a_user_prefs),
              $sess_user_lang,
              $_SESSION['user_id']);
  if ($DB->affected_rows())
    {
    $_SESSION['user_prefs'] = $a_user_prefs;
    $CONFIG = array_merge($CONFIG, $a_user_prefs);
    return TRUE;
    }
  return FALSE;
  }
@@ -1514,6 +1346,26 @@
  return $styles;
  }
/**
 * Try to autodetect operating system and find the correct line endings
 *
 * @return string The appropriate mail header delimiter
 */
function rcmail_header_delm()
{
  global $CONFIG;
  // use the configured delimiter for headers
  if (!empty($CONFIG['mail_header_delimiter']))
    return $CONFIG['mail_header_delimiter'];
  else if (strtolower(substr(PHP_OS, 0, 3)=='win'))
    return "\r\n";
  else if (strtolower(substr(PHP_OS, 0, 3)=='mac'))
    return "\r\n";
  else
    return "\n";
}
/**
 * Compose a valid attribute string for HTML tags
program/include/rcmail_template.inc
@@ -761,27 +761,15 @@
 */
function rcmail_current_username($attrib)
  {
  global $DB;
  global $USER;
  static $s_username;
  // alread fetched  
  if (!empty($s_username))
    return $s_username;
  // get e-mail address form default identity
  $sql_result = $DB->query(
    "SELECT email AS mailto
     FROM ".get_table_name('identities')."
     WHERE  user_id=?
     AND    standard=1
     AND    del<>1",
    $_SESSION['user_id']);
  if ($DB->num_rows($sql_result))
    {
    $sql_arr = $DB->fetch_assoc($sql_result);
    $s_username = $sql_arr['mailto'];
    }
  if ($sql_arr = $USER->get_identity())
    $s_username = $sql_arr['email'];
  else if (strstr($_SESSION['username'], '@'))
    $s_username = $_SESSION['username'];
  else
program/include/rcube_user.inc
New file
@@ -0,0 +1,470 @@
<?php
/*
 +-----------------------------------------------------------------------+
 | program/include/rcube_user.inc                                        |
 |                                                                       |
 | This file is part of the RoundCube Webmail client                     |
 | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 | PURPOSE:                                                              |
 |   This class represents a system user linked and provides access      |
 |   to the related database records.                                    |
 |                                                                       |
 +-----------------------------------------------------------------------+
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 +-----------------------------------------------------------------------+
 $Id: rcube_user.inc 933 2007-11-29 14:17:32Z thomasb $
*/
/**
 * Class representing a system user
 *
 * @package    core
 * @author     Thomas Bruederli <roundcube@gmail.com>
 */
class rcube_user
{
  var $ID = null;
  var $data = null;
  /**
   * Object constructor
   *
   * @param object DB Database connection
   */
  function __construct($id = null, $sql_arr = null)
  {
    global $DB;
    if ($id && !$sql_arr)
    {
      $sql_result = $DB->query("SELECT * FROM ".get_table_name('users')." WHERE  user_id=?", $id);
      $sql_arr = $DB->fetch_assoc($sql_result);
    }
    if (!empty($sql_arr))
    {
      $this->ID = $sql_arr['user_id'];
      $this->data = $sql_arr;
    }
  }
  /**
   * PHP 4 object constructor
   *
   * @see  rcube_user::__construct
   */
  function rcube_user($id = null, $sql_arr = null)
  {
    $this->__construct($id, $sql_arr);
  }
  /**
   * Build a user name string (as e-mail address)
   *
   * @return string Full user name
   */
  function get_username()
  {
    return $this->data['username'] ? $this->data['username'] . (!strpos($this->data['username'], '@') ? '@'.$this->data['mail_host'] : '') : false;
  }
  /**
   * Get the preferences saved for this user
   *
   * @return array Hash array with prefs
   */
  function get_prefs()
  {
    if ($this->ID && $this->data['preferences'])
      return unserialize($this->data['preferences']);
    else
      return array();
  }
  /**
   * Write the given user prefs to the user's record
   *
   * @param mixed User prefs to save
   * @return boolean True on success, False on failure
   */
  function save_prefs($a_user_prefs)
  {
    global $DB, $CONFIG, $sess_user_lang;
    if (!$this->ID)
      return false;
    // merge (partial) prefs array with existing settings
    $a_user_prefs += (array)$this->get_prefs();
    $DB->query(
      "UPDATE ".get_table_name('users')."
       SET    preferences=?,
              language=?
       WHERE  user_id=?",
      serialize($a_user_prefs),
      $sess_user_lang,
      $this->ID);
    if ($DB->affected_rows())
    {
      $CONFIG = array_merge($CONFIG, $a_user_prefs);
      return true;
    }
    return false;
  }
  /**
   * Get default identity of this user
   *
   * @param int  Identity ID. If empty, the default identity is returned
   * @return array Hash array with all cols of the
   */
  function get_identity($id = null)
  {
    global $DB;
    $sql_result = $this->list_identities($id ? sprintf('AND identity_id=%d', $id) : '');
    return $DB->fetch_assoc($sql_result);
  }
  /**
   * Return a list of all identities linked with this user
   *
   * @return array List of identities
   */
  function list_identities($sql_add = '')
  {
    global $DB;
    // get contacts from DB
    $sql_result = $DB->query(
      "SELECT * FROM ".get_table_name('identities')."
       WHERE  del<>1
       AND    user_id=?
       $sql_add
       ORDER BY ".$DB->quoteIdentifier('standard')." DESC, name ASC",
      $this->ID);
    return $sql_result;
  }
  /**
   * Update a specific identity record
   *
   * @param int    Identity ID
   * @param array  Hash array with col->value pairs to save
   * @return boolean True if saved successfully, false if nothing changed
   */
  function update_identity($iid, $data)
  {
    global $DB;
    if (!$this->ID)
      return false;
    $write_sql = array();
    foreach ((array)$data as $col => $value)
    {
      $write_sql[] = sprintf("%s=%s",
        $DB->quoteIdentifier($col),
        $DB->quote($value));
    }
    $DB->query(
      "UPDATE ".get_table_name('identities')."
       SET ".join(', ', $write_sql)."
       WHERE  identity_id=?
       AND    user_id=?
       AND    del<>1",
      $iid,
      $this->ID);
    return $DB->affected_rows();
  }
  /**
   * Create a new identity record linked with this user
   *
   * @param array  Hash array with col->value pairs to save
   * @return int  The inserted identity ID or false on error
   */
  function insert_identity($data)
  {
    global $DB;
    if (!$this->ID)
      return false;
    $insert_cols = $insert_values = array();
    foreach ((array)$data as $col => $value)
    {
      $insert_cols[] = $DB->quoteIdentifier($col);
      $insert_values[] = $DB->quote($value);
    }
    $DB->query(
      "INSERT INTO ".get_table_name('identities')."
        (user_id, ".join(', ', $insert_cols).")
       VALUES (?, ".join(', ', $insert_values).")",
      $this->ID);
    return $DB->insert_id(get_sequence_name('identities'));
  }
  /**
   * Mark the given identity as deleted
   *
   * @param int  Identity ID
   * @return boolean True if deleted successfully, false if nothing changed
   */
  function delete_identity($iid)
  {
    global $DB;
    if (!$this->ID)
      return false;
    $DB->query(
      "UPDATE ".get_table_name('identities')."
       SET    del=1
       WHERE  user_id=?
       AND    identity_id=?",
      $this->ID,
      $iid);
    return $DB->affected_rows();
  }
  /**
   * Make this identity the default one for this user
   *
   * @param int The identity ID
   */
  function set_default($iid)
  {
    global $DB;
    if ($this->ID && $iid)
    {
      $DB->query(
        "UPDATE ".get_table_name('identities')."
         SET ".$DB->quoteIdentifier('standard')."='0'
         WHERE  user_id=?
         AND    identity_id<>?
         AND    del<>1",
        $this->ID,
        $iid);
    }
  }
  /**
   * Update user's last_login timestamp
   */
  function touch()
  {
    global $DB;
    if ($this->ID)
    {
      $DB->query(
        "UPDATE ".get_table_name('users')."
         SET    last_login=".$DB->now()."
         WHERE  user_id=?",
        $this->ID);
    }
  }
  /**
   * Clear the saved object state
   */
  function reset()
  {
    $this->ID = null;
    $this->data = null;
  }
  /**
   * Find a user record matching the given name and host
   *
   * @param string IMAP user name
   * @param string IMAP host name
   * @return object rcube_user New user instance
   * @static
   */
  function query($user, $host)
  {
    global $DB;
    // query if user already registered
    $sql_result = $DB->query(
      "SELECT * FROM ".get_table_name('users')."
       WHERE  mail_host=? AND (username=? OR alias=?)",
      $host,
      $user,
      $user);
    // user already registered -> overwrite username
    if ($sql_arr = $DB->fetch_assoc($sql_result))
      return new rcube_user($sql_arr['user_id'], $sql_arr);
    else
      return false;
  }
  /**
   * Create a new user record and return a rcube_user instance
   *
   * @param string IMAP user name
   * @param string IMAP host
   * @return object rcube_user New user instance
   * @static
   */
  function create($user, $host)
  {
    global $DB, $CONFIG;
    $user_email = '';
    // try to resolve user in virtusertable
    if (!empty($CONFIG['virtuser_file']) && !strpos($user, '@'))
      $user_email = self::user2email($user);
    $DB->query(
      "INSERT INTO ".get_table_name('users')."
        (created, last_login, username, mail_host, alias, language)
       VALUES (".$DB->now().", ".$DB->now().", ?, ?, ?, ?)",
      strip_newlines($user),
      strip_newlines($host),
      strip_newlines($user_email),
      $_SESSION['user_lang']);
    if ($user_id = $DB->insert_id(get_sequence_name('users')))
    {
      $mail_domain = rcmail_mail_domain($host);
      if ($user_email=='')
        $user_email = strpos($user, '@') ? $user : sprintf('%s@%s', $user, $mail_domain);
      $user_name = $user != $user_email ? $user : '';
      // try to resolve the e-mail address from the virtuser table
      if (!empty($CONFIG['virtuser_query']) &&
          ($sql_result = $DB->query(preg_replace('/%u/', $DB->escapeSimple($user), $CONFIG['virtuser_query']))) &&
          ($DB->num_rows()>0))
      {
        while ($sql_arr = $DB->fetch_array($sql_result))
        {
          $DB->query(
            "INSERT INTO ".get_table_name('identities')."
              (user_id, del, standard, name, email)
             VALUES (?, 0, 1, ?, ?)",
            $user_id,
            strip_newlines($user_name),
            preg_replace('/^@/', $user . '@', $sql_arr[0]));
        }
      }
      else
      {
        // also create new identity records
        $DB->query(
          "INSERT INTO ".get_table_name('identities')."
            (user_id, del, standard, name, email)
           VALUES (?, 0, 1, ?, ?)",
          $user_id,
          strip_newlines($user_name),
          strip_newlines($user_email));
      }
    }
    else
    {
      raise_error(array(
        'code' => 500,
        'type' => 'php',
        'line' => __LINE__,
        'file' => __FILE__,
        'message' => "Failed to create new user"), true, false);
    }
    return $user_id ? new rcube_user($user_id) : false;
  }
  /**
   * Resolve username using a virtuser table
   *
   * @param string E-mail address to resolve
   * @return string Resolved IMAP username
   * @static
   */
  function email2user($email)
  {
    $user = $email;
    $r = rcmail_findinvirtual("^$email");
    for ($i=0; $i<count($r); $i++)
    {
      $data = $r[$i];
      $arr = preg_split('/\s+/', $data);
      if (count($arr) > 0)
      {
        $user = trim($arr[count($arr)-1]);
        break;
      }
    }
    return $user;
  }
  /**
   * Resolve e-mail address from virtuser table
   *
   * @param string User name
   * @return string Resolved e-mail address
   * @static
   */
  function user2email($user)
  {
    $email = "";
    $r = rcmail_findinvirtual("$user$");
    for ($i=0; $i<count($r); $i++)
    {
      $data = $r[$i];
      $arr = preg_split('/\s+/', $data);
      if (count($arr) > 0)
      {
        $email = trim($arr[0]);
        break;
      }
    }
    return $email;
  }
}
?>
program/js/app.js
@@ -223,6 +223,16 @@
          this.gui_objects.folderlist = this.gui_objects.mailboxlist;
          this.http_request('getunread', '');
        }
        // ask user to send MDN
        if (this.env.mdn_request && this.env.uid)
        {
          var mdnurl = '_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox);
          if (confirm(this.get_label('mdnrequest')))
            this.http_post('sendmdn', mdnurl);
          else
            this.http_post('mark', mdnurl+'&_flag=mdnsent');
        }
        break;
program/lib/imap.inc
@@ -1255,6 +1255,7 @@
            $result[$id]=new iilBasicHeader;
            $result[$id]->id = $id;
            $result[$id]->subject = "";
            $result[$id]->messageID = "mid:".$id;
            /*
                Start parsing headers.  The problem is, some header "lines" take up multiple lines.
                So, we'll read ahead, and if the one we're reading now is a valid header, we'll
@@ -1293,38 +1294,63 @@
            }
            //    create array with header field:data
            $headers = array();
            while ( list($lines_key, $str) = each($lines) ){
                list($field, $string) = iil_SplitHeaderLine($str);
                $field = strtolower($field);
                $headers[$field] = $string;
                switch ($field){
                    case 'date';
                        $result[$id]->date = $string;
                        $result[$id]->timestamp = iil_StrToTime($string);
                        break;
                    case 'from':
                        $result[$id]->from = $string;
                        break;
                    case 'to':
                        $result[$id]->to = str_replace("\n", " ", $string);
                        break;
                    case 'subject':
                        $result[$id]->subject = str_replace("\n", "", $string);
                        break;
                    case 'reply-to':
                        $result[$id]->replyto = str_replace("\n", " ", $string);
                        break;
                    case 'cc':
                        $result[$id]->cc = str_replace("\n", " ", $string);
                        break;
                    case 'bcc':
                        $result[$id]->bcc = str_replace("\n", " ", $string);
                        break;
                    case 'content-transfer-encoding':
                        $result[$id]->encoding = str_replace("\n", " ", $string);
                        break;
                    case 'content-type':
                         $ctype_parts = explode(";", $string);
                        $result[$id]->ctype = array_shift($ctype_parts);
                        foreach ($ctype_parts as $ctype_add)
                            if (preg_match('/charset="?([a-z0-9\-]+)"?/i', $ctype_add, $regs))
                                $result[$id]->charset = $regs[1];
                        break;
                    case 'in-reply-to':
                        $result[$id]->in_reply_to = ereg_replace("[\n<>]", '', $string);
                        break;
                    case 'references':
                        $result[$id]->references = $string;
                        break;
                        case 'return-receipt-to':
                        case 'disposition-notification-to':
                        case 'x-confirm-reading-to':
                            $result[$id]->mdn_to = str_replace("\n", " ", $string);
                            break;
                    case 'message-id':
                        $result[$id]->messageID = $string;
                        break;
                }
            }
            $result[$id]->date = $headers["date"];
            $result[$id]->timestamp = iil_StrToTime($headers["date"]);
            $result[$id]->from = $headers["from"];
            $result[$id]->to = str_replace("\n", " ", $headers["to"]);
            $result[$id]->subject = str_replace("\n", "", $headers["subject"]);
            $result[$id]->replyto = str_replace("\n", " ", $headers["reply-to"]);
            $result[$id]->cc = str_replace("\n", " ", $headers["cc"]);
            $result[$id]->bcc = str_replace("\n", " ", $headers["bcc"]);
            $result[$id]->encoding = str_replace("\n", " ", $headers["content-transfer-encoding"]);
            $result[$id]->ctype = str_replace("\n", " ", $headers["content-type"]);
            $result[$id]->in_reply_to = ereg_replace("[\n<>]",'', $headers['in-reply-to']);
            $result[$id]->references = $headers["references"];
            $result[$id]->mdn_to = $headers["disposition-notification-to"];
            list($result[$id]->ctype, $ctype_add) = explode(";", $headers["content-type"]);
            if (preg_match('/charset="?([a-z0-9\-]+)"?/i', $ctype_add, $regs))
                $result[$id]->charset = $regs[1];
            $messageID = $headers["message-id"];
            if (!$messageID) "mid:".$id;
            $result[$id]->messageID = $messageID;
            }
            else {
            $a=explode(" ", $line);
            }
        }
        else {
            $a=explode(" ", $line);
        }
            
        }
    }while(strcmp($a[0], $key)!=0);
program/lib/rc_mail_mime.inc
@@ -25,6 +25,14 @@
class rc_mail_mime extends Mail_mime
{
  /**
   * Set build parameters
   */
  function setParam($param)
  {
    if (is_array($param))
      $this->_build_params = array_merge($this->_build_params, $param);
  }
  
  /**
   * Adds an image to the list of embedded images.
@@ -63,7 +71,7 @@
    return true;
  }
  
  /**
  * returns the HTML body portion of the message
@@ -73,6 +81,22 @@
  function getHTMLBody()
  {
     return $this->_htmlbody;
  }
  /**
   * Creates a new mimePart object, using multipart/mixed as
   * the initial content-type and returns it during the
   * build process.
   *
   * @return object  The multipart/mixed mimePart object
   * @access private
   */
  function &_addMixedPart()
  {
    $params['content_type'] = $this->_headers['Content-Type'] ? $this->_headers['Content-Type'] : 'multipart/mixed';
    $ret = new Mail_mimePart('', $params);
    return $ret;
  }
  
  
@@ -138,7 +162,7 @@
        // add chunk to output string by regarding the header maxlen
        $len = strlen($value);
        if ($line_len + $len < $maxlen)
        if ($i == 0 || $line_len + $len < $maxlen)
        {
          $hdr_value .= ($i>0?', ':'') . $value;
          $line_len += $len + ($i>0?2:0);
program/localization/en_US/labels.inc
@@ -160,6 +160,11 @@
$labels['addbcc'] = 'Add Bcc';
$labels['addreplyto'] = 'Add Reply-To';
// mdn
$labels['mdnrequest'] = 'The sender of this message has asked to be notified when you read this message. Do you wish to notify the sender?';
$labels['receiptread'] = 'Return Receipt (read)';
$labels['yourmessage'] = 'This is a Return Receipt for your message';
$labels['receiptnote'] = 'Note: This receipt only acknowledges that the message was displayed on the recipient\'s computer. There is no guarantee that the recipient has read or understood the message contents.';
// address boook
$labels['name']      = 'Display name';
program/localization/en_US/messages.inc
@@ -74,5 +74,7 @@
$messages['sourceisreadonly'] = 'This address source is read only';
$messages['errorsavingcontact'] = 'Could not save the contact address';
$messages['movingmessage'] = 'Moving message...';
$messages['receiptsent'] = 'Successfully send the receipt message';
$messages['errorsendingreceipt'] = 'Could not send the receipt';
?>
program/steps/mail/compose.inc
@@ -230,7 +230,7 @@
function rcmail_compose_header_from($attrib)
  {
  global $IMAP, $MESSAGE, $DB, $OUTPUT, $compose_mode;
  global $IMAP, $MESSAGE, $DB, $USER, $OUTPUT, $compose_mode;
    
  // pass the following attributes to the form class
  $field_attrib = array('name' => '_from');
@@ -263,12 +263,7 @@
    }
  // get this user's identities
  $sql_result = $DB->query("SELECT identity_id, name, email, signature, html_signature
                            FROM   ".get_table_name('identities')."
                            WHERE user_id=?
                            AND    del<>1
                            ORDER BY ".$DB->quoteIdentifier('standard')." DESC, name ASC",
                           $_SESSION['user_id']);
  $sql_result = $USER->list_identities();
  if ($DB->num_rows($sql_result))
    {
@@ -869,7 +864,7 @@
require_once('include/rcube_contacts.inc');
$CONTACTS = new rcube_contacts($DB, $_SESSION['user_id']);
$CONTACTS = new rcube_contacts($DB, $USER->ID);
$CONTACTS->set_pagesize(1000);
                                   
if ($result = $CONTACTS->list_records())
program/steps/mail/func.inc
@@ -21,6 +21,7 @@
require_once('lib/html2text.inc');
require_once('lib/enriched.inc');
require_once('include/rcube_smtp.inc');
$EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
@@ -725,7 +726,7 @@
      // part text/[plain|html] OR message/delivery-status
      else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html') && $mail_part->disposition!='attachment') ||
               ($primary_type=='message' && $secondary_type=='delivery-status'))
               ($primary_type=='message' && ($secondary_type=='delivery-status' || $secondary_type=='disposition-notification')))
        {
        $mail_part->type = 'content';
        $a_return_parts[] = $mail_part;
@@ -758,7 +759,7 @@
        else
          {
          if (!$mail_part->filename)
            $mail_part->filename = 'file_'.$mail_part->mime_id;
            $mail_part->filename = 'Part '.$mail_part->mime_id;
          $a_attachments[] = $mail_part;
          }
        }
@@ -1250,6 +1251,67 @@
  
  unset($_SESSION['compose']);
  }
/**
 * Send the given message compose object using the configured method
 */
function rcmail_deliver_message(&$message, $from, $mailto)
{
  global $CONFIG;
  $headers = $message->headers();
  $msg_body = $message->get();
  // send thru SMTP server using custom SMTP library
  if ($CONFIG['smtp_server'])
    {
    // generate list of recipients
    $a_recipients = array($mailto);
    if (strlen($headers['Cc']))
      $a_recipients[] = $headers['Cc'];
    if (strlen($headers['Bcc']))
      $a_recipients[] = $headers['Bcc'];
    // clean Bcc from header for recipients
    $send_headers = $headers;
    unset($send_headers['Bcc']);
    // send message
    $smtp_response = array();
    $sent = smtp_mail($from, $a_recipients, ($foo = $message->txtHeaders($send_headers)), $msg_body, $smtp_response);
    // log error
    if (!$sent)
      raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__,
                        'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE);
    }
  // send mail using PHP's mail() function
  else
    {
    // unset some headers because they will be added by the mail() function
    $headers_enc = $message->headers($headers);
    $headers_php = $message->_headers;
    unset($headers_php['To'], $headers_php['Subject']);
    // reset stored headers and overwrite
    $message->_headers = array();
    $header_str = $message->txtHeaders($headers_php);
    if (ini_get('safe_mode'))
      $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
    else
      $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
    }
  $message->_headers = array();
  $message->headers($headers);
  return $sent;
}
// register UI objects
program/steps/mail/sendmail.inc
@@ -22,7 +22,6 @@
//require_once('lib/smtp.inc');
require_once('include/rcube_smtp.inc');
require_once('lib/html2text.inc');
require_once('lib/rc_mail_mime.inc');
@@ -37,26 +36,19 @@
/****** message sending functions ********/
// get identity record
function rcmail_get_identity($id)
  {
  global $DB, $OUTPUT;
  global $USER, $OUTPUT;
  
  // get identity record
  $sql_result = $DB->query("SELECT *, email AS mailto
                            FROM ".get_table_name('identities')."
                            WHERE  identity_id=?
                            AND    user_id=?
                            AND    del<>1",
                            $id,$_SESSION['user_id']);
  if ($DB->num_rows($sql_result))
  if ($sql_arr = $USER->get_identity($id))
    {
    $sql_arr = $DB->fetch_assoc($sql_result);
    $out = $sql_arr;
    $out['mailto'] = $sql_arr['email'];
    $name = strpos($sql_arr['name'], ",") ? '"'.$sql_arr['name'].'"' : $sql_arr['name'];
    $out['string'] = sprintf('%s <%s>',
                             rcube_charset_convert($name, RCMAIL_CHARSET, $OUTPUT->get_charset()),
                             $sql_arr['mailto']);
                             $sql_arr['email']);
    return $out;
    }
@@ -166,7 +158,7 @@
  $identity_arr['string'] = $from;
// compose headers array
$headers = array('Date' => date('D, j M Y H:i:s O'),
$headers = array('Date' => date('r'),
                 'From' => rcube_charset_convert($identity_arr['string'], RCMAIL_CHARSET, $message_charset),
                 'To'   => $mailto);
@@ -225,23 +217,11 @@
if (!$savedraft && !empty($CONFIG['generic_message_footer']) && ($footer = file_get_contents(realpath($CONFIG['generic_message_footer']))))
  $message_body .= "\r\n" . rcube_charset_convert($footer, 'UTF-8', $message_charset);
// try to autodetect operating system and use the correct line endings
// use the configured delimiter for headers
if (!empty($CONFIG['mail_header_delimiter']))
  $header_delm = $CONFIG['mail_header_delimiter'];
else if (strtolower(substr(PHP_OS, 0, 3)=='win'))
  $header_delm = "\r\n";
else if (strtolower(substr(PHP_OS, 0, 3)=='mac'))
  $header_delm = "\r\n";
else
  $header_delm = "\n";
$isHtmlVal = strtolower(get_input_value('_is_html', RCUBE_INPUT_POST));
$isHtml = ($isHtmlVal == "1");
// create extended PEAR::Mail_mime instance
$MAIL_MIME = new rc_mail_mime($header_delm);
$MAIL_MIME = new rc_mail_mime(rcmail_header_delm());
// For HTML-formatted messages, construct the MIME message with both
// the HTML part and the plain-text part
@@ -282,80 +262,30 @@
$transfer_encoding = in_array(strtoupper($message_charset), $charset_7bit) ? '7bit' : '8bit';
// encoding settings for mail composing
$message_param = array(
$MAIL_MIME->setParam(array(
  'text_encoding' => $transfer_encoding,
  'html_encoding' => 'quoted-printable',
  'head_encoding' => 'quoted-printable',
  'head_charset'  => $message_charset,
  'html_charset'  => $message_charset,
  'text_charset'  => $message_charset,
);
// compose message body and get headers
$msg_body = $MAIL_MIME->get($message_param);
// unset to save memory.
unset($MAIL_MIME->_parts);
));
// encoding subject header with mb_encode provides better results with asian characters
if ($MBSTRING && function_exists("mb_encode_mimeheader"))
{
  mb_internal_encoding($message_charset);
  $mb_subject = mb_encode_mimeheader($headers['Subject'], $message_charset, 'Q');
  $headers['Subject'] = mb_encode_mimeheader($headers['Subject'], $message_charset, 'Q');
  mb_internal_encoding(RCMAIL_CHARSET);
}
// Begin SMTP Delivery Block
if (!$savedraft) {
// pass headers to message object
$MAIL_MIME->headers($headers);
  // send thru SMTP server using custom SMTP library
  if ($CONFIG['smtp_server'])
    {
    // generate list of recipients
    $a_recipients = array($mailto);
    if (strlen($headers['Cc']))
      $a_recipients[] = $headers['Cc'];
    if (strlen($headers['Bcc']))
      $a_recipients[] = $headers['Bcc'];
    // clean Bcc from header for recipients
    $send_headers = $headers;
    unset($send_headers['Bcc']);
    if (!empty($mb_subject))
      $send_headers['Subject'] = $mb_subject;
    // send message
    $smtp_response = array();
    $sent = smtp_mail($from, $a_recipients, ($foo = $MAIL_MIME->txtHeaders($send_headers)), $msg_body, $smtp_response);
    // log error
    if (!$sent)
      raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__,
                        'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE);
    }
  // send mail using PHP's mail() function
  else
    {
    // unset some headers because they will be added by the mail() function
    $headers_enc = $MAIL_MIME->headers($headers);
    $headers_php = $MAIL_MIME->_headers;
    unset($headers_php['To'], $headers_php['Subject']);
    if (!empty($mb_subject))
      $headers_enc['Subject'] = $mb_subject;
    // reset stored headers and overwrite
    $MAIL_MIME->_headers = array();
    $header_str = $MAIL_MIME->txtHeaders($headers_php);
    if (ini_get('safe_mode'))
      $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
    else
      $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
    }
// Begin SMTP Delivery Block
if (!$savedraft)
{
  $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto);
  
  // return to compose page if sending failed
  if (!$sent)
@@ -364,7 +294,6 @@
    $OUTPUT->send('iframe');
    return;
    }
  
  // set repliead flag
  if ($_SESSION['compose']['reply_uid'])
@@ -382,21 +311,15 @@
if ($CONFIG[$store_target])
  {
  // create string of complete message headers
  $header_str = $MAIL_MIME->txtHeaders($headers);
  // check if mailbox exists
  if (!in_array_nocase($CONFIG[$store_target], $IMAP->list_mailboxes()))
    $store_folder = $IMAP->create_mailbox($CONFIG[$store_target], TRUE);
  else
    $store_folder = TRUE;
  
  // add headers to message body
  $msg_body = $header_str."\r\n".$msg_body;
  // append message to sent box
  if ($store_folder)
    $saved = $IMAP->save_message($CONFIG[$store_target], $msg_body);
    $saved = $IMAP->save_message($CONFIG[$store_target], $MAIL_MIME->getMessage());
  // raise error if saving failed
  if (!$saved)
program/steps/mail/sendmdn.inc
New file
@@ -0,0 +1,97 @@
<?php
/*
 +-----------------------------------------------------------------------+
 | program/steps/mail/sendmdn.inc                                        |
 |                                                                       |
 | This file is part of the RoundCube Webmail client                     |
 | Copyright (C) 2007, RoundCube Dev. - Switzerland                      |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 | PURPOSE:                                                              |
 |   Send a message disposition notification for a specific mail         |
 |                                                                       |
 +-----------------------------------------------------------------------+
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 +-----------------------------------------------------------------------+
 $Id: sendmail.inc 930 2007-11-25 17:34:19Z thomasb $
*/
require_once('lib/rc_mail_mime.inc');
if (!empty($_POST['_uid']))
 {
  $MESSAGE = array('UID' => get_input_value('_uid', RCUBE_INPUT_POST));
  $MESSAGE['headers'] = $IMAP->get_headers($MESSAGE['UID']);
  $MESSAGE['subject'] = rcube_imap::decode_mime_string($MESSAGE['headers']->subject, $MESSAGE['headers']->charset);
  if ($MESSAGE['headers']->mdn_to && !$MESSAGE['headers']->mdn_sent)
  {
    $identity = $USER->get_identity();
    $sender = format_email_recipient($identity['email'], $identity['name']);
    $recipient = array_shift($IMAP->decode_address_list($MESSAGE['headers']->mdn_to));
    $mailto = $recipient['mailto'];
    $COMPOSE = new rc_mail_mime(rcmail_header_delm());
    $COMPOSE->setParam(array(
      'text_encoding' => 'quoted-printable',
      'html_encoding' => 'quoted-printable',
      'head_encoding' => 'quoted-printable',
      'head_charset'  => RCMAIL_CHARSET,
      'html_charset'  => RCMAIL_CHARSET,
      'text_charset'  => RCMAIL_CHARSET,
    ));
    // compose headers array
    $headers = array(
      'Date' => date('r'),
      'From' => $sender,
      'To'   => $MESSAGE['headers']->mdn_to,
      'Subject' => rcube_label('receiptread') . ': ' . $MESSAGE['subject'],
      'Message-ID' => sprintf('<%s@%s>', md5(uniqid('rcmail'.rand(),true)), rcmail_mail_domain($_SESSION['imap_host'])),
      'X-Sender' => $identity['email'],
      'Content-Type' => 'multipart/report; report-type=disposition-notification',
    );
    if (!empty($CONFIG['useragent']))
      $headers['User-Agent'] = $CONFIG['useragent'];
    $body = rcube_label("yourmessage") . "\r\n\r\n" .
      "\t" . rcube_label("to") . ': ' . rcube_imap::decode_mime_string($MESSAGE['headers']->to, $MESSAGE['headers']->charset) . "\r\n" .
      "\t" . rcube_label("subject") . ': ' . $MESSAGE['subject'] . "\r\n" .
      "\t" . rcube_label("sent") . ': ' . format_date(strtotime($MESSAGE['headers']->date), $CONFIG['date_long']) . "\r\n" .
      "\r\n" . rcube_label("receiptnote") . "\r\n";
    $report = "Reporting-UA: RoundCube Webmail (Version ".RCMAIL_VERSION.")\r\n";
    if ($MESSAGE['headers']->to)
        $report .= "Original-Recipient: {$MESSAGE['headers']->to}\r\n";
    $report .= "Final-Recipient: rfc822; {$identity['email']}\r\n" .
               "Original-Message-ID: {$MESSAGE['headers']->messageID}\r\n" .
               "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
    $COMPOSE->headers($headers, true);
    $COMPOSE->setTXTBody($body);
    $COMPOSE->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
    $sent = rcmail_deliver_message($COMPOSE, $identity['email'], $mailto);
    if ($sent)
    {
      $IMAP->set_flag($MESSAGE['UID'], 'MDNSENT');
      $OUTPUT->set_env('mdn_request', false);
      $OUTPUT->show_message('receiptsent', 'confirmation');
      $OUTPUT->send();
    }
  }
}
// Error if arrive here
$OUTPUT->show_message('errorsendingreceipt', 'error');
$OUTPUT->send();
?>
program/steps/mail/show.inc
@@ -75,7 +75,10 @@
  
  // check for unset disposition notification
  if ($MESSAGE['headers']->mdn_to && !$MESSAGE['headers']->mdn_sent)
  {
    rcube_add_label('mdnrequest');
    $OUTPUT->set_env('mdn_request', true);
  }
  $next = $prev = -1;
  // get previous, first, next and last message UID
program/steps/settings/delete_identity.inc
@@ -21,15 +21,8 @@
if (($ids = get_input_value('_iid', RCUBE_INPUT_GET)) && preg_match('/^[0-9]+(,[0-9]+)*$/', $ids))
{
  $DB->query(
    "UPDATE ".get_table_name('identities')."
     SET    del=1
     WHERE  user_id=?
     AND    identity_id IN (".$ids.")",
    $_SESSION['user_id']);
  $count = $DB->affected_rows();
  if ($count)
  if ($USER->delete_identity($ids))
    $OUTPUT->show_message('deletedsuccessfully', 'confirmation');
  // send response
program/steps/settings/edit_identity.inc
@@ -21,14 +21,7 @@
if (($_GET['_iid'] || $_POST['_iid']) && $_action=='edit-identity')
  {
  $DB->query("SELECT * FROM ".get_table_name('identities')."
              WHERE  identity_id=?
              AND    user_id=?
              AND    del<>1",
              get_input_value('_iid', RCUBE_INPUT_GPC),
              $_SESSION['user_id']);
  $IDENTITY_RECORD = $DB->fetch_assoc();
  $IDENTITY_RECORD = $USER->get_identity(get_input_value('_iid', RCUBE_INPUT_GPC));
  
  if (is_array($IDENTITY_RECORD))
    $OUTPUT->set_env('iid', $IDENTITY_RECORD['identity_id']);
program/steps/settings/func.inc
@@ -20,15 +20,8 @@
*/
// get user record
$sql_result = $DB->query("SELECT username, mail_host FROM ".get_table_name('users')."
                          WHERE  user_id=?",
                          $_SESSION['user_id']);
if ($USER_DATA = $DB->fetch_assoc($sql_result))
{
  $OUTPUT->set_pagetitle(rcube_label('settingsfor') . " ". $USER_DATA['username'] . (!strpos($USER_DATA['username'], '@') ? '@'.$USER_DATA['mail_host'] : ''));
}
if ($USER->ID)
  $OUTPUT->set_pagetitle(rcube_label('settingsfor') . " ". $USER->get_username());
@@ -204,16 +197,7 @@
function rcmail_identities_list($attrib)
  {
  global $DB, $CONFIG, $OUTPUT;
  // get contacts from DB
  $sql_result = $DB->query("SELECT * FROM ".get_table_name('identities')."
                            WHERE  del<>1
                            AND    user_id=?
                            ORDER BY standard DESC, name ASC",
                            $_SESSION['user_id']);
  global $OUTPUT, $USER;
  // add id to message list table if not specified
  if (!strlen($attrib['id']))
@@ -223,7 +207,7 @@
  $a_show_cols = array('name', 'email', 'organization', 'reply-to');
  // create XHTML table  
  $out = rcube_table_output($attrib, $sql_result, $a_show_cols, 'identity_id');
  $out = rcube_table_output($attrib, $USER->list_identities(), $a_show_cols, 'identity_id');
  
  // set client env
  $OUTPUT->add_gui_object('identitieslist', $attrib['id']);
program/steps/settings/save_identity.inc
@@ -33,123 +33,78 @@
  }
$save_data = array();
foreach ($a_save_cols as $col)
{
  $fname = '_'.$col;
  if (isset($_POST[$fname]))
    $save_data[$col] = get_input_value($fname, RCUBE_INPUT_POST, in_array($col, $a_html_cols));
}
// set "off" values for checkboxes that were not checked, and therefore
// not included in the POST body.
foreach ($a_boolean_cols as $col)
{
  $fname = '_' . $col;
  if (!isset($_POST[$fname]))
    $save_data[$col] = 0;
}
// update an existing contact
if ($_POST['_iid'])
{
  if ($updated = $USER->update_identity(get_input_value('_iid', RCUBE_INPUT_POST), $save_data))
  {
  $a_write_sql = array();
  foreach ($a_save_cols as $col)
    {
    $fname = '_'.$col;
    if (isset($_POST[$fname]))
      $a_write_sql[] = sprintf("%s=%s",
        $DB->quoteIdentifier($col),
        $DB->quote(get_input_value($fname, RCUBE_INPUT_POST, in_array($col, $a_html_cols))));
    }
  // set "off" values for checkboxes that were not checked, and therefore
  // not included in the POST body.
  foreach ($a_boolean_cols as $col)
    {
    $fname = '_' . $col;
    if (!isset($_POST[$fname]))
      $a_write_sql[] = sprintf("%s=0", $DB->quoteIdentifier($col));
    }
  if (sizeof($a_write_sql))
    {
    $DB->query(
      "UPDATE ".get_table_name('identities')."
       SET ".join(', ', $a_write_sql)."
       WHERE  identity_id=?
       AND    user_id=?
       AND    del<>1",
      get_input_value('_iid', RCUBE_INPUT_POST),
      $_SESSION['user_id']);
    $updated = $DB->affected_rows();
    }
  if ($updated)
    {
    $OUTPUT->show_message('successfullysaved', 'confirmation');
    
    if (!empty($_POST['_standard']))
      $default_id = get_input_value('_iid', RCUBE_INPUT_POST);
    
    if ($_POST['_framed'])
      {
    {
      // update the changed col in list
      // ...      
      }
    }
  }
  else if ($DB->is_error())
    {
  {
    // show error message
    $OUTPUT->show_message('errorsaving', 'error');
    rcmail_overwrite_action('edit-identitiy');
    return;
    }
  }
}
// insert a new contact
else
{
  if ($insert_id = $USER->insert_identity($save_data))
  {
  $a_insert_cols = $a_insert_values = array();
  foreach ($a_save_cols as $col)
    {
    $fname = '_'.$col;
    if (!isset($_POST[$fname]))
      continue;
    $a_insert_cols[] = $DB->quoteIdentifier($col);
    $a_insert_values[] = $DB->quote(get_input_value($fname, RCUBE_INPUT_POST, in_array($col, $a_html_cols)));
    }
  if (sizeof($a_insert_cols))
    {
    $DB->query("INSERT INTO ".get_table_name('identities')."
                (user_id, ".join(', ', $a_insert_cols).")
                VALUES (?, ".join(', ', $a_insert_values).")",
                $_SESSION['user_id']);
    $insert_id = $DB->insert_id(get_sequence_name('identities'));
    }
  if ($insert_id)
    {
    $_GET['_iid'] = $insert_id;
    if (!empty($_POST['_standard']))
      $default_id = $insert_id;
    if ($_POST['_framed'])
      {
    {
      // add contact row or jump to the page where it should appear
      // ....
      }
    }
  }
  else
    {
  {
    // show error message
    $OUTPUT->show_message('errorsaving', 'error');
    rcmail_overwrite_action('edit-identity');
    return;
    }
  }
}
// mark all other identities as 'not-default'
if ($default_id)
  $DB->query(
    "UPDATE ".get_table_name('identities')."
     SET ".$DB->quoteIdentifier('standard')."='0'
     WHERE  user_id=?
     AND    identity_id<>?
     AND    del<>1",
    $_SESSION['user_id'],
    $default_id);
  $USER->set_default($default_id);
// go to next step
rcmail_overwrite_action($_framed ? 'edit-identity' : 'identities');
program/steps/settings/save_prefs.inc
@@ -44,15 +44,12 @@
// force min size
if ($a_user_prefs['pagesize'] < 1)
  {
  $a_user_prefs['pagesize'] = 10;
  }
if (isset($CONFIG['max_pagesize']) && ($a_user_prefs['pagesize'] > $CONFIG['max_pagesize']))
  {
  $a_user_prefs['pagesize'] = (int) $CONFIG['max_pagesize'];
  }
if (rcmail_save_user_prefs($a_user_prefs))
if (isset($CONFIG['max_pagesize']) && ($a_user_prefs['pagesize'] > $CONFIG['max_pagesize']))
  $a_user_prefs['pagesize'] = (int) $CONFIG['max_pagesize'];
if ($USER->save_prefs($a_user_prefs))
  $OUTPUT->show_message('successfullysaved', 'confirmation');
skins/default/mail.css
@@ -632,16 +632,20 @@
{
  position:relative;
  min-height: 300px;
  padding-top: 10px;
  padding-bottom: 10px;
  background-color: #FFFFFF;
}
div.message-part
{
  padding: 8px;
  padding-top: 10px;
  overflow: hidden;
  padding: 10px 8px;
  border-top: 1px solid #ccc;
/*  overflow: hidden; */
}
#messagebody div:first-child
{
  border-top: 0;
}
div.message-part a