From fba1f5ab813f2eb4bedc5d9c4a75e77bbaa90131 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Mon, 10 Dec 2007 10:27:19 -0500
Subject: [PATCH] New class rcube_user + send message disposition notification

---
 CHANGELOG                                  |    5 
 program/include/rcmail_template.inc        |   18 
 program/include/main.inc                   |  254 ++--------
 program/steps/mail/compose.inc             |   11 
 skins/default/mail.css                     |   12 
 program/steps/settings/save_identity.inc   |  107 +---
 program/steps/mail/show.inc                |    3 
 program/lib/imap.inc                       |   82 ++-
 program/steps/mail/sendmail.inc            |  111 ----
 program/steps/mail/sendmdn.inc             |   97 ++++
 program/steps/settings/save_prefs.inc      |   11 
 program/steps/settings/edit_identity.inc   |    9 
 program/include/rcube_user.inc             |  470 ++++++++++++++++++++
 index.php                                  |   13 
 program/lib/rc_mail_mime.inc               |   28 +
 program/steps/settings/delete_identity.inc |    9 
 program/localization/en_US/messages.inc    |    2 
 program/steps/mail/func.inc                |   66 ++
 program/steps/settings/func.inc            |   24 
 program/localization/en_US/labels.inc      |    5 
 .htaccess                                  |    2 
 program/js/app.js                          |   10 
 22 files changed, 871 insertions(+), 478 deletions(-)

diff --git a/.htaccess b/.htaccess
index b40b7ea..4d5bb3d 100644
--- a/.htaccess
+++ b/.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|\~)$">
diff --git a/CHANGELOG b/CHANGELOG
index cac1f93..3fa6b87 100644
--- a/CHANGELOG
+++ b/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
diff --git a/index.php b/index.php
index ce66d2c..9f2d336 100644
--- a/index.php
+++ b/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');
 
diff --git a/program/include/main.inc b/program/include/main.inc
index 55cd8ef..7892d09 100644
--- a/program/include/main.inc
+++ b/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
diff --git a/program/include/rcmail_template.inc b/program/include/rcmail_template.inc
index 558bd4b..cc5a58a 100644
--- a/program/include/rcmail_template.inc
+++ b/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
diff --git a/program/include/rcube_user.inc b/program/include/rcube_user.inc
new file mode 100644
index 0000000..841c843
--- /dev/null
+++ b/program/include/rcube_user.inc
@@ -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;
+  }
+
+}
+
+
+?>
diff --git a/program/js/app.js b/program/js/app.js
index 7162795..f0065ac 100644
--- a/program/js/app.js
+++ b/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;
 
diff --git a/program/lib/imap.inc b/program/lib/imap.inc
index fef0b8b..c594077 100644
--- a/program/lib/imap.inc
+++ b/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);
diff --git a/program/lib/rc_mail_mime.inc b/program/lib/rc_mail_mime.inc
index 12d80bb..ca4d0bf 100644
--- a/program/lib/rc_mail_mime.inc
+++ b/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);
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index e4f04f9..0b82137 100644
--- a/program/localization/en_US/labels.inc
+++ b/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';
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index 6010757..119e595 100644
--- a/program/localization/en_US/messages.inc
+++ b/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';
 
 ?>
\ No newline at end of file
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 5131010..36a3d70 100644
--- a/program/steps/mail/compose.inc
+++ b/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())
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index dd38016..de89872 100644
--- a/program/steps/mail/func.inc
+++ b/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
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index d118af5..6f4744d 100644
--- a/program/steps/mail/sendmail.inc
+++ b/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)
diff --git a/program/steps/mail/sendmdn.inc b/program/steps/mail/sendmdn.inc
new file mode 100644
index 0000000..0944e76
--- /dev/null
+++ b/program/steps/mail/sendmdn.inc
@@ -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();
+
+?>
\ No newline at end of file
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index 022edb0..45d64b8 100644
--- a/program/steps/mail/show.inc
+++ b/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
diff --git a/program/steps/settings/delete_identity.inc b/program/steps/settings/delete_identity.inc
index 3614a3e..06fdd94 100644
--- a/program/steps/settings/delete_identity.inc
+++ b/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
diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc
index 51a0cd0..4d89f7c 100644
--- a/program/steps/settings/edit_identity.inc
+++ b/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']);
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index ecdbcbd..e63f016 100644
--- a/program/steps/settings/func.inc
+++ b/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']);
diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc
index 8079b58..2c2a55e 100644
--- a/program/steps/settings/save_identity.inc
+++ b/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');
diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc
index 0565260..caa4a49 100644
--- a/program/steps/settings/save_prefs.inc
+++ b/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');
 
 
diff --git a/skins/default/mail.css b/skins/default/mail.css
index fb130ce..6e44c17 100644
--- a/skins/default/mail.css
+++ b/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

--
Gitblit v1.9.1