From 8fa58e72a333d753ec406d0725ac9c1b40ab6d9a Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Sat, 17 May 2008 13:46:43 -0400
Subject: [PATCH] New class rcube_message representing a mail message; changed global $MESSAGE from array to object

---
 program/include/html.php          |    2 
 program/include/session.inc       |    2 
 program/include/rcube_imap.php    |   30 -
 program/steps/mail/compose.inc    |  274 +++++--------
 program/steps/mail/func.inc       |  228 +++--------
 program/steps/mail/show.inc       |  120 ++---
 program/steps/mail/get.inc        |   72 +--
 program/include/rcube_message.php |  399 +++++++++++++++++++++
 8 files changed, 644 insertions(+), 483 deletions(-)

diff --git a/program/include/html.php b/program/include/html.php
index 6f60245..d0ab976 100644
--- a/program/include/html.php
+++ b/program/include/html.php
@@ -32,7 +32,7 @@
     protected $allowed;
     protected $content;
 
-    protected static $common_attrib = array('id','class','style','title','align');
+    public static $common_attrib = array('id','class','style','title','align');
     public static $containers = array('div','span','p','h1','h2','h3','form','textarea');
     public static $lc_tags = true;
 
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 1bf59b8..fa2845d 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -1180,36 +1180,6 @@
     
   
   /**
-   * Return a flat array with references to all parts, indexed by part numbers
-   *
-   * @param object rcube_message_part Message body structure
-   * @return Array with part number -> object pairs
-   */
-  function get_mime_numbers(&$structure)
-    {
-    $a_parts = array();
-    $this->_get_part_numbers($structure, $a_parts);
-    return $a_parts;
-    }
-  
-  
-  /**
-   * Helper method for recursive calls
-   *
-   * @access private
-   */
-  function _get_part_numbers(&$part, &$a_parts)
-    {
-    if ($part->mime_id)
-      $a_parts[$part->mime_id] = &$part;
-      
-    if (is_array($part->parts))
-      for ($i=0; $i<count($part->parts); $i++)
-        $this->_get_part_numbers($part->parts[$i], $a_parts);
-    }
-  
-
-  /**
    * Fetch message body of a specific message from the server
    *
    * @param  int    Message UID
diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php
new file mode 100644
index 0000000..174b1f3
--- /dev/null
+++ b/program/include/rcube_message.php
@@ -0,0 +1,399 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_message.php                                     |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2008, RoundCube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Logical representation of a mail message with all its data          |
+ |   and related functions                                               |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_imap.php 1344 2008-04-30 08:21:42Z thomasb $
+
+*/
+
+
+/**
+ * Interface class for accessing an IMAP server
+ *
+ * This is a wrapper that implements the Iloha IMAP Library (IIL)
+ *
+ * @package    Mail
+ * @author     Thomas Bruederli <roundcube@gmail.com>
+ */
+class rcube_message
+{
+  private $app;
+  private $imap;
+  private $opt = array();
+  private $inline_parts = array();
+  private $parse_alternative = false;
+  
+  public $uid = null;
+  public $headers;
+  public $structure;
+  public $parts = array();
+  public $mime_parts = array();
+  public $attachments = array();
+  public $subject = '';
+  public $is_safe = false;
+  
+  
+  function __construct($uid)
+  {
+    $this->app = rcmail::get_instance();
+    $this->imap = $this->app->imap;
+    
+    $this->uid = $uid;
+    $this->headers = $this->imap->get_headers($uid);
+    $this->subject = rcube_imap::decode_mime_string($this->headers->subject, $this->headers->charset);
+    
+    $this->is_safe = (intval($_GET['_safe']) || $_SESSION['safe_messages'][$uid]) ? true : false;
+    $_SESSION['safe_messages'][$uid] = $this->is_safe;
+    
+    $this->opt = array(
+      'safe' => $this->is_safe,
+      'prefer_html' => $this->app->config->get('prefer_html'),
+      'get_url' => rcmail_url('get', array('_mbox' => $this->imap->get_mailbox_name(), '_uid' => $uid))
+    );
+    
+    if ($this->structure = $this->imap->get_structure($uid)) {
+      $this->parse_structure($this->structure);
+      $this->get_mime_numbers($this->structure);
+    }
+    else {
+      $this->body = $this->imap->get_body($uid);
+    }
+  }
+  
+  
+  /**
+   * Return a (decoded) message header
+   *
+   * @param string Header name
+   * @param bool   Don't mime-decode the value
+   * @return string Header value
+   */
+  public function get_header($name, $raw = false)
+  {
+    $value = $this->header->$name;
+    return $raw ? $value : $this->imap->decode_header($value);
+  }
+  
+  
+  /**
+   * Compose a valid URL for getting a message part
+   *
+   * @param string Part MIME-ID
+   * @return string URL or false if part does not exist
+   */
+  public function get_part_url($mime_id)
+  {
+    if ($this->mime_parts[$mime_id])
+      return $this->opt['get_url'] . "&_part=" . $mime_id;
+    else
+      return false;
+  }
+  
+  
+  /**
+   * Get content of a specific part of this message
+   *
+   * @param string Part MIME-ID
+   * @return string Part content
+   */
+  public function get_part_content($mime_id)
+  {
+    if ($part = $this->mime_parts[$mime_id])
+      return $this->imap->get_message_part($this->uid, $mime_id, $part);
+    else
+      return null;
+  }
+  
+  
+  /**
+   * Determine if the message contains a HTML part
+   *
+   * @return bool True if a HTML is available, False if not
+   */
+  function has_html_part()
+  {
+     // check all message parts
+     foreach ($this->parts as $pid => $part) {
+        $mimetype = strtolower($part->ctype_primary . '/' . $part->ctype_secondary);
+        if ($mimetype == 'text/html')
+           return true;
+     }
+
+     return false;
+  }
+
+  /**
+   * Return the first HTML part of this message
+   *
+   * @return string HTML message part content
+   */
+  function first_html_part()
+    {
+    $html_part = null;
+
+    // check all message parts
+    foreach ($this->mime_parts as $mime_id => $part) {
+      $mimetype = strtolower($part->ctype_primary . '/' . $part->ctype_secondary);
+      if ($mimetype == 'text/html') {
+        $html_part = $this->imap->get_message_part($this->uid, $mime_id, $part);
+      }
+    }
+
+    return $html_part;
+  }
+
+
+  /**
+   * Return the first text part of this message
+   *
+   * @return string Plain text message/part content
+   */
+  function first_text_part()
+    {
+    // no message structure, return complete body
+    if (empty($this->parts))
+      return $this->body;
+      
+    $out = null;
+
+    // check all message parts
+    foreach ($this->mime_parts as $mime_id => $part) {
+      $mimetype = strtolower($part->ctype_primary . '/' . $part->ctype_secondary);
+
+      if ($mimetype == 'text/plain') {
+        $out = $this->imap->get_message_part($this->uid, $mime_id, $part);
+        break;
+      }
+      else if ($mimetype == 'text/html') {
+        $html_part = $this->imap->get_message_part($this->uid, $mime_id, $part);
+
+        // remove special chars encoding
+        $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
+        $html_part = strtr($html_part, $trans);
+
+        // create instance of html2text class
+        $txt = new html2text($html_part);
+        $out = $txt->get_text();
+        break;
+      }
+    }
+
+    return $out;
+  }
+
+
+  /**
+   * Raad the message structure returend by the IMAP server
+   * and build flat lists of content parts and attachments
+   *
+   * @param object rcube_message_part Message structure node
+   * @param bool  True when called recursively
+   */
+  private function parse_structure($structure, $recursive = false)
+  {
+    $message_ctype_primary = strtolower($structure->ctype_primary);
+    $message_ctype_secondary = strtolower($structure->ctype_secondary);
+
+    // show message headers
+    if ($recursive && is_array($structure->headers) && isset($structure->headers['subject'])) {
+      $c = new stdClass;
+      $c->type = 'headers';
+      $c->headers = &$structure->headers;
+      $this->parts[] = $c;
+    }
+
+    // print body if message doesn't have multiple parts
+    if ($message_ctype_primary == 'text' && !$recursive) {
+      $structure->type = 'content';
+      $this->parts[] = &$structure;
+    }
+    // message contains alternative parts
+    else if ($message_ctype_primary == 'multipart' && ($message_ctype_secondary == 'alternative') && is_array($structure->parts)) {
+      // get html/plaintext parts
+      $plain_part = $html_part = $print_part = $related_part = null;
+
+      foreach ($structure->parts as $p => $sub_part) {
+        $rel_parts = $attachmnts = null;
+        $sub_ctype_primary = strtolower($sub_part->ctype_primary);
+        $sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
+        
+        // check if sub part is 
+        if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
+          $plain_part = $p;
+        else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
+          $html_part = $p;
+        else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
+          $enriched_part = $p;
+        else if ($sub_ctype_primary=='multipart' && ($sub_ctype_secondary=='related' || $sub_ctype_secondary=='mixed'))
+          $related_part = $p;
+      }
+
+      // parse related part (alternative part could be in here)
+      if ($related_part !== null && !$this->parse_alternative) {
+        $this->parse_alternative = true;
+        $this->parse_structure($structure->parts[$related_part], true);
+        $this->parse_alternative = false;
+        
+        // if plain part was found, we should unset it if html is preferred
+        if ($this->opt['prefer_html'] && count($this->parts))
+          $plain_part = null;
+      }
+
+      // choose html/plain part to print
+      if ($html_part !== null && $this->opt['prefer_html']) {
+        $print_part = &$structure->parts[$html_part];
+      }
+      else if ($enriched_part !== null) {
+        $print_part = &$structure->parts[$enriched_part];
+      }
+      else if ($plain_part !== null) {
+        $print_part = &$structure->parts[$plain_part];
+      }
+
+      // add the right message body
+      if (is_object($print_part)) {
+        $print_part->type = 'content';
+        $this->parts[] = $print_part;
+      }
+      // show plaintext warning
+      else if ($html_part !== nullL && empty($this->parts)) {
+        $c = new stdClass;
+        $c->type = 'content';
+        $c->body = rcube_label('htmlmessage');
+        $c->ctype_primary = 'text';
+        $c->ctype_secondary = 'plain';
+
+        $this->parts[] = $c;
+      }
+
+      // add html part as attachment
+      if ($html_part !== null && $structure->parts[$html_part] !== $print_part) {
+        $html_part = &$structure->parts[$html_part];
+        $html_part->filename = rcube_label('htmlmessage');
+        $html_part->mimetype = 'text/html';
+
+        $this->attachments[] = $html_part;
+      }
+    }
+    // this is an ecrypted message -> create a plaintext body with the according message
+    else if ($message_ctype_primary == 'multipart' && $message_ctype_secondary == 'encrypted') {
+      $p = new stdClass;
+      $p->type = 'content';
+      $p->ctype_primary = 'text';
+      $p->ctype_secondary = 'plain';
+      $p->body = rcube_label('encryptedmessage');
+      
+      $this->parts[] = $p;
+    }
+    // message contains multiple parts
+    else if (is_array($structure->parts) && !empty($structure->parts)) {
+      // iterate over parts
+      for ($i=0; $i < count($structure->parts); $i++) {
+        $mail_part = &$structure->parts[$i];
+        $primary_type = strtolower($mail_part->ctype_primary);
+        $secondary_type = strtolower($mail_part->ctype_secondary);
+
+        // multipart/alternative
+        if ($primary_type=='multipart') {
+          $this->parse_structure($mail_part, true);
+        }
+        // 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' || $secondary_type == 'disposition-notification'))) {
+          
+          // add text part if we're not in alternative mode or if it matches the prefs
+          if (!$this->parse_alternative ||
+              ($secondary_type == 'html' && $this->opt['prefer_html']) ||
+              ($secondary_type == 'plain' && !$this->opt['prefer_html'])) {
+            $mail_part->type = 'content';
+            $this->parts[] = $mail_part;
+          }
+          
+          // list as attachment as well
+          if (!empty($mail_part->filename))
+            $this->attachments[] = $mail_part;
+        }
+        // part message/*
+        else if ($primary_type=='message') {
+          $this->parse_structure($mail_part, true);
+        }
+        // ignore "virtual" protocol parts
+        else if ($primary_type == 'protocol')
+          continue;
+
+        // part is file/attachment
+        else if ($mail_part->disposition == 'attachment' || $mail_part->disposition == 'inline' ||
+                 $mail_part->headers['content-id'] || (empty($mail_part->disposition) && $mail_part->filename)) {
+          // skip apple resource forks
+          if ($message_ctype_secondary == 'appledouble' && $secondary_type == 'applefile')
+            continue;
+
+          // part belongs to a related message
+          if ($message_ctype_secondary == 'related' && $mail_part->headers['content-id']) {
+            $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
+            $this->inline_parts[] = $mail_part;
+          }
+          // is regular attachment
+          else {
+            if (!$mail_part->filename)
+              $mail_part->filename = 'Part '.$mail_part->mime_id;
+            $this->attachments[] = $mail_part;
+          }
+        }
+      }
+
+      // if this was a related part try to resolve references
+      if ($message_ctype_secondary == 'related' && sizeof($this->inline_objects)) {
+        $a_replaces = array();
+
+        foreach ($this->inline_parts as $inline_object) {
+          $a_replaces['cid:'.$inline_object->content_id] = htmlspecialchars(sprintf($this->opt['get_url'], $inline_object->mime_id));
+        }
+
+        // add replace array to each content part
+        // (will be applied later when part body is available)
+        for ($i=0; $i<count($a_return_parts); $i++) {
+          if ($a_return_parts[$i]->type=='content')
+            $a_return_parts[$i]->replaces = $a_replaces;
+        }
+      }
+    }
+
+    // message is single part non-text
+    else if ($structure->filename) {
+      $this->attachments[] = $structure;
+    }
+  }
+
+
+  /**
+   * Fill aflat array with references to all parts, indexed by part numbers
+   *
+   * @param object rcube_message_part Message body structure
+   */
+  private function get_mime_numbers(&$part)
+  {
+    if (strlen($part->mime_id))
+      $this->mime_parts[$part->mime_id] = &$part;
+      
+    if (is_array($part->parts))
+      for ($i=0; $i<count($part->parts); $i++)
+        $this->get_mime_numbers($part->parts[$i]);
+  }
+
+
+}
+
diff --git a/program/include/session.inc b/program/include/session.inc
index a789fd2..ef8eb27 100644
--- a/program/include/session.inc
+++ b/program/include/session.inc
@@ -88,7 +88,7 @@
                 VALUES (?, ?, ?, ".$DB->now().", ".$DB->now().")",
                 $key,
                 $vars,
-                $_SERVER['REMOTE_ADDR']);
+                (string)$_SERVER['REMOTE_ADDR']);
     }
 
   return TRUE;
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 676b6b4..d2a48ab 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -5,7 +5,7 @@
  | program/steps/mail/compose.inc                                        |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2008, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -85,24 +85,19 @@
 if (!empty($msg_uid))
 {
   // similar as in program/steps/mail/show.inc
-  $MESSAGE = array('UID' => $msg_uid);
-  $MESSAGE['headers'] = &$IMAP->get_headers($msg_uid);
-  $MESSAGE['structure'] = &$IMAP->get_structure($msg_uid);
+  $MESSAGE = new rcube_message($msg_uid);
   
-  if (!empty($MESSAGE['headers']->charset))
-    $IMAP->set_charset($MESSAGE['headers']->charset);
+  if (!empty($MESSAGE->headers->charset))
+    $IMAP->set_charset($MESSAGE->headers->charset);
     
-  $MESSAGE['subject'] = $IMAP->decode_header($MESSAGE['headers']->subject);
-  $MESSAGE['parts'] = $IMAP->get_mime_numbers($MESSAGE['structure']);
-  
   if ($compose_mode == RCUBE_COMPOSE_REPLY)
   {
     $_SESSION['compose']['reply_uid'] = $msg_uid;
-    $_SESSION['compose']['reply_msgid'] = $MESSAGE['headers']->messageID;
-    $_SESSION['compose']['references']  = trim($MESSAGE['headers']->references . " " . $MESSAGE['headers']->messageID);
+    $_SESSION['compose']['reply_msgid'] = $MESSAGE->headers->messageID;
+    $_SESSION['compose']['references']  = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
 
     if (!empty($_GET['_all']))
-      $MESSAGE['reply_all'] = 1;
+      $MESSAGE->reply_all = 1;
   }
   else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
   {
@@ -173,19 +168,19 @@
   else if ($header && $compose_mode == RCUBE_COMPOSE_REPLY)
   {
     // get recipent address(es) out of the message headers
-    if ($header=='to' && !empty($MESSAGE['headers']->replyto))
-      $fvalue = $MESSAGE['headers']->replyto;
+    if ($header=='to' && !empty($MESSAGE->headers->replyto))
+      $fvalue = $MESSAGE->headers->replyto;
 
-    else if ($header=='to' && !empty($MESSAGE['headers']->from))
-      $fvalue = $MESSAGE['headers']->from;
+    else if ($header=='to' && !empty($MESSAGE->headers->from))
+      $fvalue = $MESSAGE->headers->from;
 
     // add recipent of original message if reply to all
-    else if ($header=='cc' && !empty($MESSAGE['reply_all']))
+    else if ($header=='cc' && !empty($MESSAGE->reply_all))
     {
-      if ($v = $MESSAGE['headers']->to)
+      if ($v = $MESSAGE->headers->to)
         $fvalue .= $v;
 
-      if ($v = $MESSAGE['headers']->cc)
+      if ($v = $MESSAGE->headers->cc)
         $fvalue .= (!empty($fvalue) ? ', ' : '') . $v;
     }
 
@@ -196,7 +191,7 @@
       $fvalue = '';
       foreach ($to_addresses as $addr_part)
       {
-        if (!empty($addr_part['mailto']) && !in_array($addr_part['mailto'], $sa_recipients) && (!$MESSAGE['FROM'] || !in_array($addr_part['mailto'], $MESSAGE['FROM'])))
+        if (!empty($addr_part['mailto']) && !in_array($addr_part['mailto'], $sa_recipients) && (!$MESSAGE->compose_from || !in_array($addr_part['mailto'], $MESSAGE->compose_from)))
         {
           $fvalue .= (strlen($fvalue) ? ', ':'').$addr_part['string'];
           $sa_recipients[] = $addr_part['mailto'];
@@ -207,14 +202,14 @@
   else if ($header && $compose_mode == RCUBE_COMPOSE_DRAFT)
   {
     // get drafted headers
-    if ($header=='to' && !empty($MESSAGE['headers']->to))
-      $fvalue = $IMAP->decode_header($MESSAGE['headers']->to);
+    if ($header=='to' && !empty($MESSAGE->headers->to))
+      $fvalue = $MESSAGE->get_header('to');
 
-    if ($header=='cc' && !empty($MESSAGE['headers']->cc))
-      $fvalue = $IMAP->decode_header($MESSAGE['headers']->cc);
+    if ($header=='cc' && !empty($MESSAGE->headers->cc))
+      $fvalue = $MESSAGE->get_header('cc');
 
-    if ($header=='bcc' && !empty($MESSAGE['headers']->bcc))
-      $fvalue = $IMAP->decode_header($MESSAGE['headers']->bcc);
+    if ($header=='bcc' && !empty($MESSAGE->headers->bcc))
+      $fvalue = $MESSAGE->get_header('bcc');
   }
 
         
@@ -241,7 +236,7 @@
 
 function rcmail_compose_header_from($attrib)
 {
-  global $IMAP, $MESSAGE, $DB, $USER, $OUTPUT, $CONFIG, $compose_mode;
+  global $IMAP, $MESSAGE, $DB, $USER, $OUTPUT, $compose_mode;
     
   // pass the following attributes to the form class
   $field_attrib = array('name' => '_from');
@@ -251,20 +246,20 @@
 
   // extract all recipients of the reply-message
   $a_recipients = array();
-  if ($compose_mode == RCUBE_COMPOSE_REPLY && is_object($MESSAGE['headers']))
+  if ($compose_mode == RCUBE_COMPOSE_REPLY && is_object($MESSAGE->headers))
   {
-    $MESSAGE['FROM'] = array();
+    $MESSAGE->compose_from = array();
 
-    $a_to = $IMAP->decode_address_list($MESSAGE['headers']->to);
+    $a_to = $IMAP->decode_address_list($MESSAGE->headers->to);
     foreach ($a_to as $addr)
     {
       if (!empty($addr['mailto']))
         $a_recipients[] = $addr['mailto'];
     }
 
-    if (!empty($MESSAGE['headers']->cc))
+    if (!empty($MESSAGE->headers->cc))
     {
-      $a_cc = $IMAP->decode_address_list($MESSAGE['headers']->cc);
+      $a_cc = $IMAP->decode_address_list($MESSAGE->headers->cc);
       foreach ($a_cc as $addr)
       {
         if (!empty($addr['mailto']))
@@ -306,10 +301,10 @@
       if (in_array($sql_arr['email'], $a_recipients))
         $from_id = $sql_arr['identity_id'];
 
-      if ($compose_mode == RCUBE_COMPOSE_REPLY && is_array($MESSAGE['FROM']))
-        $MESSAGE['FROM'][] = $sql_arr['email'];
+      if ($compose_mode == RCUBE_COMPOSE_REPLY && is_array($MESSAGE->compose_from))
+        $MESSAGE->compose_from[] = $sql_arr['email'];
 
-      if ($compose_mode == RCUBE_COMPOSE_DRAFT && strstr($MESSAGE['headers']->from, $sql_arr['email']))
+      if ($compose_mode == RCUBE_COMPOSE_DRAFT && strstr($MESSAGE->headers->from, $sql_arr['email']))
         $from_id = $sql_arr['identity_id'];
     }
 
@@ -356,65 +351,36 @@
 
   // use posted message body
   if (!empty($_POST['_message']))
+  {
+    $body = get_input_value('_message', RCUBE_INPUT_POST, true);
+  }
+  else if ($compose_mode)
+  {
+    if ($isHtml && $MESSAGE->has_html_part())
     {
-    $body = get_input_value('_message', RCUBE_INPUT_POST, TRUE);
-    }
-  // compose reply-body
-  else if ($compose_mode == RCUBE_COMPOSE_REPLY)
-    {
-    $hasHtml = rcmail_has_html_part($MESSAGE['parts']); 
-    if ($hasHtml && $CONFIG['htmleditor'])
-      {
-      $body = rcmail_first_html_part($MESSAGE);
+      $body = $MESSAGE->first_html_part();
       $isHtml = true;
-      }
-    else
-      {
-      $body = rcmail_first_text_part($MESSAGE);
-      $isHtml = false;
-      }
-
-    $body = rcmail_create_reply_body($body, $isHtml);
     }
-  // forward message body inline
-  else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
+    else
     {
-    $hasHtml = rcmail_has_html_part($MESSAGE['parts']);
-    if ($hasHtml && $CONFIG['htmleditor'])
-      {
-      $body = rcmail_first_html_part($MESSAGE);
-      $isHtml = true;
-      }
-    else
-      {
-      $body = rcmail_first_text_part($MESSAGE);
+      $body = $MESSAGE->first_text_part();
       $isHtml = false;
-      }
-
-    $body = rcmail_create_forward_body($body, $isHtml);
     }
-  else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
-    {
-    $hasHtml = rcmail_has_html_part($MESSAGE['parts']);
-    if ($hasHtml && $CONFIG['htmleditor'])
-      {
-      $body = rcmail_first_html_part($MESSAGE);
-      $isHtml = true;
-      }
-    else
-      {
-      $body = rcmail_first_text_part($MESSAGE);
-      $isHtml = false;
-      }
-
-    $body = rcmail_create_draft_body($body, $isHtml);
-    }
+    
+    // compose reply-body
+    if ($compose_mode == RCUBE_COMPOSE_REPLY)
+      $body = rcmail_create_reply_body($body, $isHtml);
+    // forward message body inline
+    else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
+      $body = rcmail_create_forward_body($body, $isHtml);
+    // load draft message body
+    else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
+      $body = rcmail_create_draft_body($body, $isHtml);
+  }
 
   $tinylang = substr($_SESSION['language'], 0, 2);
   if (!file_exists('program/js/tiny_mce/langs/'.$tinylang.'.js'))
-    { 
     $tinylang = 'en'; 
-    } 
 
   $OUTPUT->include_script('tiny_mce/tiny_mce.js');
   $OUTPUT->include_script("editor.js");
@@ -422,7 +388,7 @@
 
   $out = $form_start ? "$form_start\n" : '';
 
-  $saveid = new html_hiddenfield(array('name' => '_draft_saveid', 'value' => $compose_mode==RCUBE_COMPOSE_DRAFT ? str_replace(array('<','>'), "", $MESSAGE['headers']->messageID) : ''));
+  $saveid = new html_hiddenfield(array('name' => '_draft_saveid', 'value' => $compose_mode==RCUBE_COMPOSE_DRAFT ? str_replace(array('<','>'), "", $MESSAGE->headers->messageID) : ''));
   $out .= $saveid->show();
 
   $drafttoggle = new html_hiddenfield(array('name' => '_draft', 'value' => 'yes'));
@@ -500,8 +466,8 @@
 
     // add title line
     $prefix = sprintf("\n\n\nOn %s, %s wrote:\n",
-             $MESSAGE['headers']->date,
-             $IMAP->decode_header($MESSAGE['headers']->from));
+      $MESSAGE->headers->date,
+      $MESSAGE->get_header('from'));
 
     // try to remove the signature
     if ($sp = strrpos($body, '-- '))
@@ -513,12 +479,10 @@
   }
   else
   {
-    $prefix = sprintf("<br><br>On %s, %s wrote:<br><blockquote type=\"cite\" " .
-                      "style=\"padding-left: 5px; border-left: #1010ff 2px solid; " .
-                      "margin-left: 5px; width: 100%%\">",
-                      $MESSAGE['headers']->date,
-                      $IMAP->decode_header($MESSAGE['headers']->from));
-
+    $prefix = sprintf("<br /><br />On %s, %s wrote:<br />\n",
+      $MESSAGE->headers->date,
+      Q($MESSAGE->get_header('from')));
+    $prefix .= '<blockquote type="cite" style="padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%">';
     $suffix = "</blockquote>";
   }
 
@@ -530,35 +494,35 @@
 {
   global $IMAP, $MESSAGE;
 
-  if (! $bodyIsHtml)
+  if (!$bodyIsHtml)
   {
     // soft-wrap message first
     $body = wordwrap($body, 80);
   
     $prefix = sprintf("\n\n\n-------- Original Message --------\nSubject: %s\nDate: %s\nFrom: %s\nTo: %s\n\n",
-                     $MESSAGE['subject'],
-                     $MESSAGE['headers']->date,
-                     $IMAP->decode_header($MESSAGE['headers']->from),
-                     $IMAP->decode_header($MESSAGE['headers']->to));
+      $MESSAGE->subject,
+      $MESSAGE->headers->date,
+      $MESSAGE->get_header('from'),
+      $MESSAGE->get_header('to'));
   }
   else
   {
     $prefix = sprintf(
-        "<br><br>-------- Original Message --------" .
+      "<br><br>-------- Original Message --------" .
         "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
         "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Subject: </th><td>%s</td></tr>" .
         "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Date: </th><td>%s</td></tr>" .
         "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">From: </th><td>%s</td></tr>" .
         "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">To: </th><td>%s</td></tr>" .
         "</tbody></table><br>",
-                     Q($MESSAGE['subject']),
-                     Q($MESSAGE['headers']->date),
-                     Q($IMAP->decode_header($MESSAGE['headers']->from)),
-                     Q($IMAP->decode_header($MESSAGE['headers']->to)));
+      Q($MESSAGE->subject),
+      Q($MESSAGE->headers->date),
+      Q($MESSAGE->get_header('from')),
+      Q($MESSAGE->get_header('to')));
   }
 
   // add attachments
-  if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE['parts']))
+  if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE->mime_parts))
     rcmail_write_compose_attachments($MESSAGE);
     
   return $prefix.$body;
@@ -567,15 +531,15 @@
 
 function rcmail_create_draft_body($body, $bodyIsHtml)
 {
-  global $IMAP, $MESSAGE;
+  global $MESSAGE;
   
   /**
    * add attachments
-   * sizeof($MESSAGE['parts'] can be 1 - e.g. attachment, but no text!
+   * sizeof($MESSAGE->mime_parts can be 1 - e.g. attachment, but no text!
    */
   if (!isset($_SESSION['compose']['forward_attachments'])
-      && is_array($MESSAGE['parts'])
-      && count($MESSAGE['parts']) > 0)
+      && is_array($MESSAGE->mime_parts)
+      && count($MESSAGE->mime_parts) > 0)
     rcmail_write_compose_attachments($MESSAGE);
 
   return $body;
@@ -584,14 +548,14 @@
   
 function rcmail_write_compose_attachments(&$message)
 {
-  global $IMAP, $CONFIG;
+  global $RCMAIL, $IMAP;
 
-  $temp_dir = unslashify($CONFIG['temp_dir']);
+  $temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
 
   if (!is_array($_SESSION['compose']['attachments']))
     $_SESSION['compose']['attachments'] = array();
   
-  foreach ($message['parts'] as $pid => $part)
+  foreach ((array)$message->mime_parts as $pid => $part)
   {
     if ($part->ctype_primary != 'message' &&
         ($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id'] ||
@@ -600,7 +564,7 @@
       $tmp_path = tempnam($temp_dir, 'rcmAttmnt');
       if ($fp = fopen($tmp_path, 'w'))
       {
-        fwrite($fp, $IMAP->get_message_part($message['UID'], $pid, $part->encoding));
+        fwrite($fp, $message->get_part_content($pid));
         fclose($fp);
         
         $_SESSION['compose']['attachments'][] = array(
@@ -612,13 +576,13 @@
     }
   }
 	
-  $_SESSION['compose']['forward_attachments'] = TRUE;
+  $_SESSION['compose']['forward_attachments'] = true;
 }
 
 
 function rcmail_compose_subject($attrib)
 {
-  global $CONFIG, $MESSAGE, $compose_mode;
+  global $MESSAGE, $compose_mode;
   
   list($form_start, $form_end) = get_form_tags($attrib);
   unset($attrib['form']);
@@ -635,24 +599,24 @@
   // create a reply-subject
   else if ($compose_mode == RCUBE_COMPOSE_REPLY)
   {
-    if (eregi('^re:', $MESSAGE['subject']))
-      $subject = $MESSAGE['subject'];
+    if (eregi('^re:', $MESSAGE->subject))
+      $subject = $MESSAGE->subject;
     else
-      $subject = 'Re: '.$MESSAGE['subject'];
+      $subject = 'Re: '.$MESSAGE->subject;
   }
 
   // create a forward-subject
   else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
   {
-    if (eregi('^fwd:', $MESSAGE['subject']))
-      $subject = $MESSAGE['subject'];
+    if (eregi('^fwd:', $MESSAGE->subject))
+      $subject = $MESSAGE->subject;
     else
-      $subject = 'Fwd: '.$MESSAGE['subject'];
+      $subject = 'Fwd: '.$MESSAGE->subject;
   }
 
   // creeate a draft-subject
   else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
-    $subject = $MESSAGE['subject'];
+    $subject = $MESSAGE->subject;
   
   $out = $form_start ? "$form_start\n" : '';
   $out .= $textfield->show($subject);
@@ -670,35 +634,30 @@
   if (!$attrib['id'])
     $attrib['id'] = 'rcmAttachmentList';
   
-  // allow the following attributes to be added to the <ul> tag
-  $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style'));
- 
-  $out = '<ul'. $attrib_str . ">\n";
+  $out = "\n";
   
   if (is_array($_SESSION['compose']['attachments']))
   {
     if ($attrib['deleteicon'])
-      $button = sprintf('<img src="%s%s" alt="%s" border="0" style="padding-right:2px;vertical-align:middle" />',
-                        $CONFIG['skin_path'],
-                        $attrib['deleteicon'],
-                        rcube_label('delete'));
+      $button = html::img(array(
+        'src' => $CONFIG['skin_path'] . $attrib['deleteicon'],
+        'alt' => rcube_label('delete'),
+        'style' => "border:0;padding-right:2px;vertical-align:middle"));
     else
-      $button = rcube_label('delete');
+      $button = Q(rcube_label('delete'));
 
     foreach ($_SESSION['compose']['attachments'] as $id => $a_prop)
-      $out .= sprintf('<li id="rcmfile%d"><a href="#delete" onclick="return %s.command(\'remove-attachment\',\'rcmfile%d\', this)" title="%s">%s</a>%s</li>',
-                      $id,
-                      JS_OBJECT_NAME,
-                      $id,
-                      Q(rcube_label('delete')),
-                      $button,
-                      Q($a_prop['name']));
+      $out .= html::tag('li', array('id' => "rcmfile".$id),
+        html::a(array(
+            'href' => "#delete",
+            'title' => rcube_label('delete'),
+              'onclick' => sprintf("return %s.command(\'remove-attachment\',\'rcmfile%d\', this)", JS_OBJECT_NAME, $id)),
+          $button) . Q($a_prop['name']));
   }
 
   $OUTPUT->add_gui_object('attachmentlist', $attrib['id']);
     
-  $out .= '</ul>';
-  return $out;
+  return html::tag('ul', $attrib, $out, html::$common_attrib);
 }
 
 
@@ -752,7 +711,7 @@
                        rcube_label('highest')),
                  array(5, 4, 0, 2, 1));
                  
-  $sel = isset($_POST['_priority']) ? $_POST['_priority'] : intval($MESSAGE['headers']->priority);
+  $sel = isset($_POST['_priority']) ? $_POST['_priority'] : intval($MESSAGE->headers->priority);
 
   $out = $form_start ? "$form_start\n" : '';
   $out .= $selector->show($sel);
@@ -777,7 +736,7 @@
   $checkbox = new html_checkbox($attrib);
 
   $out = $form_start ? "$form_start\n" : '';
-  $out .= $checkbox->show($MESSAGE['headers']->mdn_to ? 1 : 0);
+  $out .= $checkbox->show($MESSAGE->headers->mdn_to ? 1 : 0);
   $out .= $form_end ? "\n$form_end" : '';
 
   return $out;
@@ -793,39 +752,20 @@
     'plain' => 'plaintoggle'
   );
 
-  // determine whether HTML or plain text should be checked 
-  if ($CONFIG['htmleditor'])
-    {
-    $useHtml = true;
-    }
-  else
-    {
-    $useHtml = false;
-    }
+  // determine whether HTML or plain text should be checked
+  $useHtml = $CONFIG['htmleditor'] ? true : false;
 
-  if ($compose_mode == RCUBE_COMPOSE_REPLY ||
-      $compose_mode == RCUBE_COMPOSE_FORWARD ||
-      $compose_mode == RCUBE_COMPOSE_DRAFT)
-  {
-    $hasHtml = rcmail_has_html_part($MESSAGE['parts']);
-    $useHtml = ($hasHtml && $CONFIG['htmleditor']);
-  }
-
-  $chosenvalue = $useHtml ? 'html' : 'plain';
+  if ($compose_mode)
+    $useHtml = ($useHtml && $MESSAGE->has_html_part());
 
   $selector = '';
-  
-  $attrib['name'] = '_editorSelect';
-  $attrib['onclick'] = 'return rcmail_toggle_editor(this)';
+  $chosenvalue = $useHtml ? 'html' : 'plain';
+  $radio = new html_radiobutton(array('name' => '_editorSelect', 'onclick' => 'return rcmail_toggle_editor(this)'));
   foreach ($choices as $value => $text)
   {
     $attrib['id'] = '_' . $value;
     $attrib['value'] = $value;
-    $rb = new html_radiobutton($attrib);
-    $selector .= sprintf("%s<label for=\"%s\">%s</label>",
-                         $rb->show($chosenvalue),
-                         $attrib['id'],
-                         rcube_label($text));
+    $selector .= $radio->show($chosenvalue, $attrib) . html::label($attrib['id'], Q(rcube_label($text)));
   }
 
   return $selector;
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index ec594bc..cbad987 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -5,7 +5,7 @@
  | program/steps/mail/func.inc                                           |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2008, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -52,11 +52,6 @@
   $OUTPUT->set_env('search_request', $_REQUEST['_search']);
   $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
   }
-
-
-// define url for getting message parts
-if (strlen($_GET['_uid']))
-  $GET_URL = rcmail_url('get', array('_mbox'=>$IMAP->get_mailbox_name(), '_uid'=>get_input_value('_uid', RCUBE_INPUT_GET)));
 
 
 // set current mailbox in client environment
@@ -475,10 +470,10 @@
   {
   global $IMAP, $MESSAGE;
   
-  if (isset($MESSAGE['index']))
+  if (isset($MESSAGE->index))
     {
     return rcube_label(array('name' => 'messagenrof',
-                             'vars' => array('nr'  => $MESSAGE['index']+1,
+                             'vars' => array('nr'  => $MESSAGE->index+1,
                                              'count' => $count!==NULL ? $count : $IMAP->messagecount())));
     }
 
@@ -959,7 +954,7 @@
 
   // get associative array of headers object
   if (!$headers)
-    $headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
+    $headers = is_object($MESSAGE->headers) ? get_object_vars($MESSAGE->headers) : $MESSAGE->headers;
   
   $header_count = 0;
   
@@ -997,15 +992,15 @@
 
 function rcmail_message_body($attrib)
   {
-  global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $GET_URL, $REMOTE_OBJECTS;
+  global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $REMOTE_OBJECTS;
   
-  if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
+  if (!is_array($MESSAGE->parts) && empty($MESSAGE->body))
     return '';
     
   if (!$attrib['id'])
     $attrib['id'] = 'rcmailMsgBody';
 
-  $safe_mode = $MESSAGE['is_safe'] || intval($_GET['_safe']);
+  $safe_mode = $MESSAGE->is_safe || intval($_GET['_safe']);
   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
   $out = '<div '. $attrib_str . ">\n";
   
@@ -1014,33 +1009,20 @@
     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
       $header_attrib[$regs[1]] = $value;
 
-
-  // this is an ecrypted message
-  // -> create a plaintext body with the according message
-  if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
+  if (!empty($MESSAGE->parts))
     {
-    $p = new stdClass;
-    $p->type = 'content';
-    $p->ctype_primary = 'text';
-    $p->ctype_secondary = 'plain';
-    $p->body = rcube_label('encryptedmessage');
-    $MESSAGE['parts'][0] = $p;
-    }
-  
-  if ($MESSAGE['parts'])
-    {
-    foreach ($MESSAGE['parts'] as $i => $part)
+    foreach ($MESSAGE->parts as $i => $part)
       {
-      if ($part->type=='headers')
+      if ($part->type == 'headers')
         $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
-      else if ($part->type=='content')
+      else if ($part->type == 'content')
         {
         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
-          $part->ctype_parameters['charset'] = $MESSAGE['headers']->charset;
+          $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
 
         // fetch part if not available
         if (!isset($part->body))
-          $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part);
+          $part->body = $MESSAGE->get_part_content($part->mime_id);
 
         $body = rcmail_print_body($part, $safe_mode, !$CONFIG['prefer_html']);
         $out .= '<div class="message-part">';
@@ -1055,25 +1037,26 @@
       }
     }
   else
-    $out .= $MESSAGE['body'];
+    $out .= $MESSAGE->body;
 
 
-  $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
-  $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
+  $ctype_primary = strtolower($MESSAGE->structure->ctype_primary);
+  $ctype_secondary = strtolower($MESSAGE->structure->ctype_secondary);
   
   // list images after mail body
-  if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' &&
-      !empty($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
-    {
-    foreach ($MESSAGE['attachments'] as $attach_prop)
-      {
-      if (strpos($attach_prop->mimetype, 'image/')===0)
-        $out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&amp;_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
-                        htmlspecialchars($GET_URL), $attach_prop->mime_id,
-                        $attach_prop->filename,
-                        $attach_prop->filename);
-      }
+  if (get_boolean($attrib['showimages']) && $ctype_primary == 'multipart' &&
+      !empty($MESSAGE->attachments) && !strstr($message_body, '<html')) {
+    foreach ($MESSAGE->attachments as $attach_prop) {
+      if (strpos($attach_prop->mimetype, 'image/') === 0) {
+        $out .= html::tag('hr') . html::p(array('align' => "center"),
+          html::img(array(
+            'src' => $MESSAGE->get_part_url($attach_prop->mime_id),
+            'title' => $attach_prop->filename,
+            'alt' => $attach_prop->filename,
+          )));
+        }
     }
+  }
   
   // tell client that there are blocked remote objects
   if ($REMOTE_OBJECTS && !$safe_mode)
@@ -1193,91 +1176,6 @@
   }
 
 
-function rcmail_has_html_part($message_parts)
-{
-   if (!is_array($message_parts))
-      return FALSE;
-
-   // check all message parts
-   foreach ($message_parts as $pid => $part)
-   {
-      $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
-      if ($mimetype=='text/html')
-      {
-         return TRUE;
-      }
-   }
-    
-   return FALSE;
-}
-
-// return first HTML part of a message
-function rcmail_first_html_part($message_struct)
-  {
-  global $IMAP;
-
-  if (!is_array($message_struct['parts']))
-    return FALSE;
-    
-  $html_part = NULL;
-
-  // check all message parts
-  foreach ($message_struct['parts'] as $pid => $part)
-    {
-    $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
-    if ($mimetype=='text/html')
-      {
-      $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
-      }
-    }
-
-  if ($html_part)
-    {
-    // remove special chars encoding
-    //$trans = array_flip(get_html_translation_table(HTML_ENTITIES));
-    //$html_part = strtr($html_part, $trans);
-
-    return $html_part;
-    }
-
-  return FALSE;
-}
-
-
-// return first text part of a message
-function rcmail_first_text_part($message_struct)
-  {
-  global $IMAP;
-
-  if (empty($message_struct['parts']))
-    return $message_struct['UID'] ? $IMAP->get_body($message_struct['UID']) : false;
-
-  // check all message parts
-  foreach ($message_struct['parts'] as $pid => $part)
-    {
-    $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
-
-    if ($mimetype=='text/plain')
-      return $IMAP->get_message_part($message_struct['UID'], $pid, $part);
-
-    else if ($mimetype=='text/html')
-      {
-      $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
-      
-      // remove special chars encoding
-      $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
-      $html_part = strtr($html_part, $trans);
-
-      // create instance of html2text class
-      $txt = new html2text($html_part);
-      return $txt->get_text();
-      }
-    }
-
-  return FALSE;
-  }
-
-
 // decode address string and re-format it as HTML links
 function rcmail_address_string($input, $max=NULL, $addicon=NULL)
   {
@@ -1338,33 +1236,27 @@
 
 function rcmail_message_part_controls()
   {
-  global $CONFIG, $IMAP, $MESSAGE;
+  global $MESSAGE;
   
   $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
-  if (!is_array($MESSAGE) || !is_array($MESSAGE['parts']) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE['parts'][$part])
+  if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part])
     return '';
     
-  $part = $MESSAGE['parts'][$part];
-  $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary'));
-  $out = '<table '. $attrib_str . ">\n";
+  $part = $MESSAGE->mime_parts[$part];
+  $table = new html_table(array('cols' => 3));
   
-  if ($part->filename)
-    {
-    $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n",
-                    Q(rcube_label('filename')),
-                    Q($part->filename),
-                    str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
-                    Q(rcube_label('download')));
-    }
-    
-  if ($part->size)
-    $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
-                    Q(rcube_label('filesize')),
-                    show_bytes($part->size));
+  if (!empty($part->filename)) {
+    $table->add('title', Q(rcube_label('filename')));
+    $table->add(null, Q($part->filename));
+    $table->add(null, '[' . html::a(str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))) . ']');
+  }
   
-  $out .= "\n</table>";
+  if (!empty($part->size)) {
+    $table->add('title', Q(rcube_label('filesize')));
+    $table->add(null, Q(show_bytes($part->size)));
+  }
   
-  return $out;
+  return $table->show($attrib);
   }
 
 
@@ -1373,7 +1265,7 @@
   {
   global $MESSAGE;
   
-  $part = $MESSAGE['parts'][asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
+  $part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
   $ctype_primary = strtolower($part->ctype_primary);
 
   $attrib['src'] = Q('./?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']));
@@ -1407,8 +1299,8 @@
 {
   global $CONFIG;
 
-  $headers = $message->headers();
   $msg_body = $message->get();
+  $headers = $message->headers();
   
   // send thru SMTP server using custom SMTP library
   if ($CONFIG['smtp_server'])
@@ -1468,16 +1360,14 @@
 function rcmail_send_mdn($uid)
 {
   global $CONFIG, $USER, $IMAP;
+
+  $message = new rcube_message($uid);
   
-  $message = array('UID' => $uid);
-  $message['headers'] = $IMAP->get_headers($message['UID']);
-  $message['subject'] = $IMAP->decode_header($message['headers']->subject);
-  
-  if ($message['headers']->mdn_to && !$message['headers']->mdn_sent)
+  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));
+    $recipient = array_shift($IMAP->decode_address_list($message->headers->mdn_to));
     $mailto = $recipient['mailto'];
 
     $compose = new rcube_mail_mime(rcmail_header_delm());
@@ -1494,8 +1384,8 @@
     $headers = array(
       'Date' => date('r'),
       'From' => $sender,
-      'To'   => $message['headers']->mdn_to,
-      'Subject' => rcube_label('receiptread') . ': ' . $message['subject'],
+      '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',
@@ -1505,30 +1395,30 @@
       $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($message['headers']->date, $CONFIG['date_long']) . "\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($message->headers->date, $CONFIG['date_long']) . "\r\n" .
       "\r\n" . rcube_label("receiptnote") . "\r\n";
     
     $ua = !empty($CONFIG['useragent']) ? $CONFIG['useragent'] : "RoundCube Webmail (Version ".RCMAIL_VERSION.")";
     $report = "Reporting-UA: $ua\r\n";
     
-    if ($message['headers']->to)
-        $report .= "Original-Recipient: {$message['headers']->to}\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" .
+               "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->headers($headers);
+    $compose->setTXTBody(wordwrap($body, 75, "\r\n"));
     $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');
+      $IMAP->set_flag($message->uid, 'MDNSENT');
       return true;
     }
   }
diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc
index 06c22ec..4d78952 100644
--- a/program/steps/mail/get.inc
+++ b/program/steps/mail/get.inc
@@ -5,7 +5,7 @@
  | program/steps/mail/get.inc                                            |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2008, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -23,8 +23,7 @@
 
 
 // show loading page
-if ($_GET['_preload'])
-  {
+if (!empty($_GET['_preload'])) {
   $url = str_replace('&_preload=1', '', $_SERVER['REQUEST_URI']);
   $message = rcube_label('loadingdata');
 
@@ -34,29 +33,24 @@
         $message .
         "\n</body>\n</html>";
   exit;
-  }
+}
 
 
 // similar code as in program/steps/mail/show.inc
-if ($_GET['_uid'])
-  {
-  $MESSAGE = array('UID' => get_input_value('_uid', RCUBE_INPUT_GET));
-  $MESSAGE['structure'] = $IMAP->get_structure($MESSAGE['UID']);
-  $MESSAGE['parts'] = $IMAP->get_mime_numbers($MESSAGE['structure']);
-  }
+if (!empty($_GET['_uid'])) {
+  $RCMAIL->config->set('prefer_html', true);
+  $MESSAGE = new rcube_message(get_input_value('_uid', RCUBE_INPUT_GET));
+}
 
 
 // show part page
-if ($_GET['_frame'])
-  {
+if (!empty($_GET['_frame'])) {
   $OUTPUT->send('messagepart');
   exit;
-  }
+}
 
-else if ($pid = get_input_value('_part', RCUBE_INPUT_GET))
-  {
-  if ($part = $MESSAGE['parts'][$pid])
-    {
+else if ($pid = get_input_value('_part', RCUBE_INPUT_GET)) {
+  if ($part = $MESSAGE->mime_parts[$pid]) {
     $ctype_primary = strtolower($part->ctype_primary);
     $ctype_secondary = strtolower($part->ctype_secondary);
     $mimetype = sprintf('%s/%s', $ctype_primary, $ctype_secondary);
@@ -67,25 +61,23 @@
     header("Content-Transfer-Encoding: binary");
 
     // send download headers
-    if ($_GET['_download'])
-      {
+    if ($_GET['_download']) {
       header("Cache-Control: private", false);
       header("Content-Type: application/octet-stream");
-      }
+    }
     else
       header("Content-Type: $mimetype");
 
     // We need to set the following headers to make downloads work using IE in HTTPS mode.
-    if (isset($_SERVER['HTTPS']))
-      {
+    if (isset($_SERVER['HTTPS'])) {
       header('Pragma: ');
       header('Cache-Control: ');
-      }
+    }
 
     // deliver part content
-    if ($ctype_primary=='text' && $ctype_secondary=='html')
-      {
+    if ($ctype_primary == 'text' && $ctype_secondary == 'html') {
       // we have to analyze the whole structure again to find inline objects
+      /* what was this good for again ?
       list($new_parts, $new_attachments) =
         rcmail_parse_message($MESSAGE['structure'],
                              array('safe' => intval($_GET['_safe']),
@@ -96,42 +88,32 @@
       for ($partix = 0; $partix < sizeof($all_parts); $partix++)
         if ($all_parts[$partix]->mime_id == $pid)
           $part = &$all_parts[$partix];
+      */
 
       // get part body if not available
       if (!$part->body)
-        $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part);
+        $part->body = $MESSAGE->get_part_content($part->mime_id);
 
       $OUTPUT = new rcube_html_page();
-      $OUTPUT->write(rcmail_print_body($part, intval($_GET['_safe'])));
-      }
-    else
-      {
+      $OUTPUT->write(rcmail_print_body($part, $MESSAGE->is_safe));
+    }
+    else {
       header(sprintf('Content-Disposition: %s; filename="%s";',
                      $_GET['_download'] ? 'attachment' : 'inline',
                      $part->filename ? abbreviate_string($part->filename, 55) : "roundcube.$ctype_secondary"));
 
       // turn off output buffering and print part content
-      $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part, true);
-      }
+      $IMAP->get_message_part($MESSAGE->uid, $part->mime_id, $part, true);
+    }
 
     exit;
-    }
   }
+}
 
 // print message
-else
-  {
-  $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
-  $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
-  $mimetype = sprintf('%s/%s', $ctype_primary, $ctype_secondary);
-
+else {
   // send correct headers for content type
   header("Content-Type: text/html");
-
-  $cont = ''; 
-  list($MESSAGE['parts']) = rcmail_parse_message($MESSAGE['structure'],
-                                                 array('safe' => intval($_GET['_safe']),
-                                                 'get_url' => $GET_URL.'&_part=%s'));
 
   $cont = "<html>\n<head><title></title>\n</head>\n<body>";
   $cont .= rcmail_message_body(array());
@@ -141,7 +123,7 @@
   $OUTPUT->write($cont);
 
   exit;
-  }
+}
 
 
 // if we arrive here, the requested part was not found
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index daf2e08..0984973 100644
--- a/program/steps/mail/show.inc
+++ b/program/steps/mail/show.inc
@@ -22,79 +22,59 @@
 $PRINT_MODE = $RCMAIL->action=='print' ? TRUE : FALSE;
 
 // similar code as in program/steps/mail/get.inc
-if ($_GET['_uid'])
-  {
-  $MESSAGE = array('UID' => get_input_value('_uid', RCUBE_INPUT_GET));
-  $MESSAGE['headers'] = $IMAP->get_headers($MESSAGE['UID']);
+if ($_GET['_uid']) {
+  $MESSAGE = new rcube_message(get_input_value('_uid', RCUBE_INPUT_GET));
   
   // set message charset as default
-  if (!empty($MESSAGE['headers']->charset))
-    $IMAP->set_charset($MESSAGE['headers']->charset);
+  if (!empty($MESSAGE->headers->charset))
+    $IMAP->set_charset($MESSAGE->headers->charset);
 
   // go back to list if message not found (wrong UID)
-  if (!$MESSAGE['headers'])
-    {
+  if (empty($MESSAGE->headers)) {
     $OUTPUT->show_message('messageopenerror', 'error');
     if ($RCMAIL->action=='preview' && template_exists('messagepreview'))
         $OUTPUT->send('messagepreview');
-    else
-      {
+    else {
       $RCMAIL->action = 'list';
       return;
-      }
     }
+  }
     
-  // check if safe flag is set
-  if ($MESSAGE['is_safe'] = intval($_GET['_safe']))
-    $_SESSION['safe_messages'][$MESSAGE['UID']] = true;
-  else if ($_SESSION['safe_messages'][$MESSAGE['UID']])
-    $MESSAGE['is_safe'] = 1;
-
   $mbox_name = $IMAP->get_mailbox_name();
   
   // calculate Etag for this request
-  $etag = md5($MESSAGE['UID'].$mbox_name.session_id().intval($MESSAGE['headers']->mdn_sent).intval($MESSAGE['is_safe']).intval($PRINT_MODE));
+  $etag = md5($MESSAGE->uid.$mbox_name.session_id().intval($MESSAGE->headers->mdn_sent).intval($MESSAGE->is_safe).intval($PRINT_MODE));
 
   // allow caching, unless remote images are present
-  if ((bool)$MESSAGE['is_safe'])
+  if ((bool)$MESSAGE->is_safe)
     send_nocacheing_headers();
   else if (empty($CONFIG['devel_mode']))
-    send_modified_header($_SESSION['login_time'], $etag, !$MESSAGE['headers']->seen);
+    send_modified_header($_SESSION['login_time'], $etag, !$MESSAGE->headers->seen);
 
-  $MESSAGE['subject'] = $IMAP->decode_header($MESSAGE['headers']->subject);
-  $OUTPUT->set_pagetitle($MESSAGE['subject']);
+  $OUTPUT->set_pagetitle($MESSAGE->subject);
   
-  if ($MESSAGE['structure'] = $IMAP->get_structure($MESSAGE['UID']))
-    list($MESSAGE['parts'], $MESSAGE['attachments']) = rcmail_parse_message(
-      $MESSAGE['structure'],
-      array('safe' => $MESSAGE['is_safe'],
-            'prefer_html' => $CONFIG['prefer_html'],
-            'get_url' => $GET_URL.'&_part=%s')
-      );
-  else
-    $MESSAGE['body'] = $IMAP->get_body($MESSAGE['UID']);
-
   // mark message as read
-  if (!$MESSAGE['headers']->seen)
+  if (!$MESSAGE->headers->seen)
   {
-    $marked = $IMAP->set_flag($MESSAGE['UID'], 'SEEN');
+    $marked = $IMAP->set_flag($MESSAGE->uid, 'SEEN');
     if($RCMAIL->action == 'preview' && $marked != -1)
     {
       $OUTPUT->command('set_unread_count_from_preview', $mbox_name, $IMAP->messagecount($mbox_name, 'UNSEEN'), ($mbox_name == 'INBOX'));
-      $OUTPUT->command('mark_as_read_from_preview', $MESSAGE['UID']);
+      $OUTPUT->command('mark_as_read_from_preview', $MESSAGE->uid);
     }
   }
 
   // give message uid to the client
-  $OUTPUT->set_env('uid', $MESSAGE['UID']);
-  $OUTPUT->set_env('safemode', $MESSAGE['is_safe']);
+  $OUTPUT->set_env('uid', $MESSAGE->uid);
+  $OUTPUT->set_env('safemode', $MESSAGE->is_safe);
   
   // check for unset disposition notification
-  if ($MESSAGE['headers']->mdn_to && !$MESSAGE['headers']->mdn_sent && $mbox_name != $CONFIG['drafts_mbox'])
+  if ($MESSAGE->headers->mdn_to && !$MESSAGE->headers->mdn_sent &&
+      $mbox_name != $CONFIG['drafts_mbox'] && $mbox_name != $CONFIG['sent_mbox'])
   {
     if (intval($CONFIG['mdn_requests']) === 1)
     {
-      if (rcmail_send_mdn($MESSAGE['UID']))
+      if (rcmail_send_mdn($MESSAGE->uid))
         $OUTPUT->show_message('receiptsent', 'confirmation');
     }
     else if (empty($CONFIG['mdn_requests']))
@@ -113,21 +93,21 @@
     // Only if we use custom sorting
     $a_msg_index = $IMAP->message_index(NULL, $_SESSION['sort_col'], $_SESSION['sort_order']);
  
-    $MESSAGE['index'] = array_search((string)$MESSAGE['UID'], $a_msg_index, TRUE);
-    $prev = isset($a_msg_index[$MESSAGE['index']-1]) ? $a_msg_index[$MESSAGE['index']-1] : -1 ;
+    $MESSAGE->index = array_search((string)$MESSAGE->uid, $a_msg_index, TRUE);
+    $prev = isset($a_msg_index[$MESSAGE->index-1]) ? $a_msg_index[$MESSAGE->index-1] : -1 ;
     $first = count($a_msg_index)>0 ? $a_msg_index[0] : -1;
-    $next = isset($a_msg_index[$MESSAGE['index']+1]) ? $a_msg_index[$MESSAGE['index']+1] : -1 ;
+    $next = isset($a_msg_index[$MESSAGE->index+1]) ? $a_msg_index[$MESSAGE->index+1] : -1 ;
     $last = count($a_msg_index)>0 ? $a_msg_index[count($a_msg_index)-1] : -1;
     }
   else
     {
     // this assumes that we are sorted by date_DESC
-    $seq = $IMAP->get_id($MESSAGE['UID']);
+    $seq = $IMAP->get_id($MESSAGE->uid);
     $prev = $IMAP->get_uid($seq + 1);
     $first = $IMAP->get_uid($IMAP->messagecount());
     $next = $IMAP->get_uid($seq - 1);
     $last = $IMAP->get_uid(1);
-    $MESSAGE['index'] = $IMAP->messagecount() - $seq;
+    $MESSAGE->index = $IMAP->messagecount() - $seq;
     }
   
   if ($prev > 0)
@@ -143,35 +123,35 @@
 
 
 function rcmail_message_attachments($attrib)
-  {
-  global $CONFIG, $OUTPUT, $PRINT_MODE, $MESSAGE, $GET_URL;
+{
+  global $PRINT_MODE, $MESSAGE;
+  
+  $out = $ol = '';
 
-  if (sizeof($MESSAGE['attachments']))
-    {
-    // allow the following attributes to be added to the <ul> tag
-    $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
-    $out = '<ul' . $attrib_str . ">\n";
-
-    foreach ($MESSAGE['attachments'] as $attach_prop)
-      {
-      if ($PRINT_MODE)
-        $out .= sprintf('<li>%s (%s)</li>'."\n",
-                        $attach_prop->filename,
-                        show_bytes($attach_prop->size));
-      else
-        $out .= sprintf('<li><a href="%s&amp;_part=%s" onclick="return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)">%s</a></li>'."\n",
-                        htmlspecialchars($GET_URL),
-                        $attach_prop->mime_id,
-                        JS_OBJECT_NAME,
-                        $attach_prop->mime_id,
-                        $attach_prop->mimetype,
-                        $attach_prop->filename);
+  if (sizeof($MESSAGE->attachments)) {
+    foreach ($MESSAGE->attachments as $attach_prop) {
+      if ($PRINT_MODE) {
+        $ol .= html::tag('li', null, sprintf("%s (%s)", Q($attach_prop->filename), Q(show_bytes($attach_prop->size))));
       }
+      else {
+        $ol .= html::tag('li', null,
+          html::a(array(
+            'href' => $MESSAGE->get_part_url($attach_prop->mime_id),
+            'onclick' => sprintf(
+              'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)',
+              JS_OBJECT_NAME,
+              $attach_prop->mime_id,
+              $attach_prop->mimetype),
+            ),
+            Q($attach_prop->filename)));
+      }
+    }
 
-    $out .= "</ul>";
-    return $out;
-    }  
-  }
+    $out = html::tag('ul', $attrib, $ol, html::$common_attrib);
+  } 
+  
+  return $out;
+}
 
 
 

--
Gitblit v1.9.1