Implement ability to move to next message after moving the one you are viewing, per...
[squirrelmail.git] / src / read_body.php
index 44ae694205a7951c0b759903080e2494f0d8f9f0..dbc58c3593863b4fef9c32b35288111b31613a29 100644 (file)
@@ -6,12 +6,15 @@
  * This file is used for reading the msgs array and displaying
  * the resulting emails in the right frame.
  *
- * @copyright © 1999-2006 The SquirrelMail Project Team
+ * @copyright 1999-2020 The SquirrelMail Project Team
  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
  */
 
+/** This is the read_body page */
+define('PAGE_NAME', 'read_body');
+
 /**
  * Include the SquirrelMail initialization file.
  */
@@ -27,13 +30,14 @@ require_once(SM_PATH . 'functions/identity.php');
 require_once(SM_PATH . 'functions/mailbox_display.php');
 require_once(SM_PATH . 'functions/forms.php');
 require_once(SM_PATH . 'functions/attachment_common.php');
+require_once(SM_PATH . 'functions/compose.php');
 
 /**
  * Given an IMAP message id number, this will look it up in the cached
  * and sorted msgs array and return the index of the next message
  *
  * @param int $passed_id The current message UID
- * @return the index of the next valid message from the array
+ * @return the index of the next valid message from the array or -1 if there is no next message
  */
 function findNextMessage($uidset,$passed_id='backwards') {
     if (!is_array($uidset)) {
@@ -55,7 +59,7 @@ function findNextMessage($uidset,$passed_id='backwards') {
  * and sorted msgs array and return the index of the previous message
  *
  * @param int $passed_id The current message UID
- * @return the index of the next valid message from the array
+ * @return the index of the previous valid message from the array or -1 if there is no previous message
  */
 
 function findPreviousMessage($uidset, $passed_id) {
@@ -123,10 +127,10 @@ function ServerMDNSupport($aFlags) {
              in_array('\\*',$aFlags,true) ) ;
 }
 
-function SendMDN ( $mailbox, $passed_id, $sender, $message, $imapConnection) {
-    global $username, $attachment_dir, $popuser, $username, $color,
-           $version, $squirrelmail_language, $default_charset,
-           $languages, $useSendmail, $domain, $sent_folder;
+function SendMDN ( $mailbox, $passed_id, $message, $imapConnection) {
+    global $squirrelmail_language, $default_charset, $default_move_to_sent,
+           $languages, $useSendmail, $domain, $sent_folder, $username,
+           $data_dir;
 
     sqgetGlobalVar('SERVER_NAME', $SERVER_NAME, SQ_SERVER);
 
@@ -142,38 +146,29 @@ function SendMDN ( $mailbox, $passed_id, $sender, $message, $imapConnection) {
     }
     $rfc822_header->content_type = $content_type;
     $rfc822_header->to[] = $header->dnt;
-    $rfc822_header->subject = _("Read:") . ' ' . encodeHeader($header->subject);
-
-    // Patch #793504 Return Receipt Failing with <@> from Tim Craig (burny_md)
-    // This merely comes from compose.php and only happens when there is no
-    // email_addr specified in user's identity (which is the startup config)
-    if (ereg("^([^@%/]+)[@%/](.+)$", $username, $usernamedata)) {
-       $popuser = $usernamedata[1];
-       $domain  = $usernamedata[2];
-       unset($usernamedata);
-    } else {
-       $popuser = $username;
-    }
+    $rfc822_header->subject = _("Read:") . ' ' . decodeHeader($header->subject,true,false);
 
-    $reply_to = '';
-    $ident = get_identities();
-    if(!isset($identity)) $identity = 0;
-    $full_name = $ident[$identity]['full_name'];
-    $from_mail = $ident[$identity]['email_address'];
-    $from_addr = '"'.$full_name.'" <'.$from_mail.'>';
-    $reply_to  = $ident[$identity]['reply_to'];
-
-    if (!$from_mail) {
-       $from_mail = "$popuser@$domain";
-       $from_addr = $from_mail;
+    $idents = get_identities();
+    $needles = array();
+    if ($header->to) {
+        foreach ($header->to as $message_to) {
+             $needles[] = $message_to->mailbox.'@'.$message_to->host;
+        }
     }
+    $identity = find_identity($needles);
+    $from_addr = build_from_header($identity);
+    $reply_to = isset($idents[$identity]['reply_to']) ? $idents[$identity]['reply_to'] : '';
+    // FIXME: this must actually be the envelope address of the orginal message,
+    // but do we have that information? For now the first identity is our best guess.
+    $final_recipient = $idents[0]['email_address'];
+
     $rfc822_header->from = $rfc822_header->parseAddress($from_addr,true);
     if ($reply_to) {
        $rfc822_header->reply_to = $rfc822_header->parseAddress($reply_to,true);
     }
 
     // part 1 (RFC2298)
-    $senton = getLongDateString( $header->date );
+    $senton = getLongDateString( $header->date, $header->date_unparsed );
     $to_array = $header->to;
     $to = '';
     foreach ($to_array as $line) {
@@ -211,7 +206,7 @@ function SendMDN ( $mailbox, $passed_id, $sender, $message, $imapConnection) {
     if ($special_encoding) {
         $mime_header->encoding = $special_encoding;
     } else {
-        $mime_header->encoding = 'us-ascii';
+        $mime_header->encoding = '7bit';
     }
     if ($default_charset) {
         $mime_header->parameters['charset'] = $default_charset;
@@ -222,11 +217,10 @@ function SendMDN ( $mailbox, $passed_id, $sender, $message, $imapConnection) {
     $original_recipient  = $to;
     $original_message_id = $header->message_id;
 
-    $report = "Reporting-UA : $SERVER_NAME ; SquirrelMail (version $version) \r\n";
+    $report = "Reporting-UA : $SERVER_NAME ; SquirrelMail (version " . SM_VERSION . ") \r\n";
     if ($original_recipient != '') {
         $report .= "Original-Recipient : $original_recipient\r\n";
     }
-    $final_recipient = $sender;
     $report .= "Final-Recipient: rfc822; $final_recipient\r\n" .
               "Original-Message-ID : $original_message_id\r\n" .
               "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
@@ -236,7 +230,7 @@ function SendMDN ( $mailbox, $passed_id, $sender, $message, $imapConnection) {
     $mime_header = new MessageHeader;
     $mime_header->type0 = 'message';
     $mime_header->type1 = 'disposition-notification';
-    $mime_header->encoding = 'us-ascii';
+    $mime_header->encoding = '7bit';
     $part2->mime_header = $mime_header;
 
     $composeMessage = new Message();
@@ -261,33 +255,60 @@ function SendMDN ( $mailbox, $passed_id, $sender, $message, $imapConnection) {
     } else {
         require_once(SM_PATH . 'class/deliver/Deliver_SMTP.class.php');
         $deliver = new Deliver_SMTP();
-        global $smtpServerAddress, $smtpPort, $pop_before_smtp;
+        global $smtpServerAddress, $smtpPort, $pop_before_smtp, $pop_before_smtp_host;
         $authPop = (isset($pop_before_smtp) && $pop_before_smtp) ? true : false;
+        if (empty($pop_before_smtp_host)) $pop_before_smtp_host = $smtpServerAddress;
         get_smtp_user($user, $pass);
         $stream = $deliver->initStream($composeMessage,$domain,0,
-                                       $smtpServerAddress, $smtpPort, $user, $pass, $authPop);
+                                       $smtpServerAddress, $smtpPort, $user, $pass, $authPop, $pop_before_smtp_host);
     }
     $success = false;
     if ($stream) {
-        $length  = $deliver->mail($composeMessage, $stream);
+        $deliver->mail($composeMessage, $stream);
         $success = $deliver->finalizeStream($stream);
     }
     if (!$success) {
-        $msg = $deliver->dlv_msg;
+        $msg = _("Message not sent.") . "\n" .
+            $deliver->dlv_msg;
         if (! empty($deliver->dlv_server_msg)) {
-            $msg.= '<br />' .
+            $msg.= "\n" .
                 _("Server replied:") . ' ' . $deliver->dlv_ret_nr . ' ' .
-                nl2br(htmlspecialchars($deliver->dlv_server_msg));
+                nl2br(sm_encode_html_special_chars($deliver->dlv_server_msg));
         }
-        plain_error_message($msg, $color);
+        plain_error_message($msg);
     } else {
         unset ($deliver);
-        if (sqimap_mailbox_exists ($imapConnection, $sent_folder)) {
-            $sid = sqimap_append ($imapConnection, $sent_folder, $length);
+
+        // move to sent folder
+        //
+        $move_to_sent = getPref($data_dir,$username,'move_to_sent');
+        if (isset($default_move_to_sent) && ($default_move_to_sent != 0)) {
+            $svr_allow_sent = true;
+        } else {
+            $svr_allow_sent = false;
+        }
+
+        if (isset($sent_folder) && (($sent_folder != '') || ($sent_folder != 'none'))
+                && sqimap_mailbox_exists( $imapConnection, $sent_folder)) {
+            $fld_sent = true;
+        } else {
+            $fld_sent = false;
+        }
+
+        if ((isset($move_to_sent) && ($move_to_sent != 0)) || (!isset($move_to_sent))) {
+            $lcl_allow_sent = true;
+        } else {
+            $lcl_allow_sent = false;
+        }
+
+        if (($fld_sent && $svr_allow_sent && !$lcl_allow_sent) || ($fld_sent && $lcl_allow_sent)) {
+            $save_reply_with_orig=getPref($data_dir,$username,'save_reply_with_orig');
+            if ($save_reply_with_orig) {
+                $sent_folder = $mailbox;
+            }
             require_once(SM_PATH . 'class/deliver/Deliver_IMAP.class.php');
             $imap_deliver = new Deliver_IMAP();
-            $imap_deliver->mail($composeMessage, $imapConnection);
-            sqimap_append_done ($imapConnection, $sent_folder);
+            $imap_deliver->mail($composeMessage, $imapConnection, 0, 0, $imapConnection, $sent_folder);
             unset ($imap_deliver);
         }
     }
@@ -302,7 +323,7 @@ function ToggleMDNflag ($set ,$imapConnection, $mailbox, $passed_id) {
 }
 
 function formatRecipientString($recipients, $item ) {
-    global $show_more_cc, $show_more, $show_more_bcc,
+    global $show_more, $show_more_cc, $show_more_bcc,
            $PHP_SELF, $oTemplate;
 
     $string = '';
@@ -331,21 +352,22 @@ function formatRecipientString($recipients, $item ) {
                 $url = set_url_var($PHP_SELF, 'show_more_bcc',1);
             }
         }
-        
+
         $a = array();
         foreach ($recipients as $r) {
             $a[] = array(
-                            'Name'  => htmlspecialchars($r->getAddress(false)),
-                            'Email' => htmlspecialchars($r->getEmail()),
-                            'Full'  => htmlspecialchars($r->getAddress(true))
+                            // note: decodeHeader is htmlsafe by default
+                            'Name'  => decodeHeader($r->getAddress(false)),
+                            'Email' => sm_encode_html_special_chars($r->getEmail()),
+                            'Full'  => decodeHeader($r->getAddress(true))
                         );
         }
-        
+
         $oTemplate->assign('which_field', $item);
         $oTemplate->assign('recipients', $a);
         $oTemplate->assign('more_less_toggle_href', $url);
         $oTemplate->assign('show_more', $show);
-        
+
         $string = $oTemplate->fetch('read_recipient_list.tpl');
     }
     return $string;
@@ -370,7 +392,7 @@ function formatEnvheader($aMailbox, $passed_id, $passed_ent_id, $message,
         $env[_("From")] = _("Unknown sender");
     else
         $env[_("From")] = decodeHeader($from_name);
-    $env[_("Date")] = getLongDateString($header->date);
+    $env[_("Date")] = getLongDateString($header->date, $header->date_unparsed);
     $env[_("To")] = formatRecipientString($header->to, "to");
     $env[_("Cc")] = formatRecipientString($header->cc, "cc");
     $env[_("Bcc")] = formatRecipientString($header->bcc, "bcc");
@@ -382,58 +404,92 @@ function formatEnvheader($aMailbox, $passed_id, $passed_ent_id, $message,
         $oTemplate->assign('xmailer', decodeHeader($header->xmailer));
         $env[_("Mailer")] = $oTemplate->fetch('read_xmailer.tpl');
     }
+
+    // this is used for both mdn and also general use for plugins, etc
+    $oTemplate->assign('first_time_reading', $FirstTimeSee);
+
     if ($default_use_mdn) {
         if ($mdn_user_support) {
             if ($header->dnt) {
                 $mdn_url = $PHP_SELF;
-                $mdn_url = set_url_var($PHP_SELF, 'mailbox', urlencode($mailbox));
-                $mdn_url = set_url_var($PHP_SELF, 'passed_id', $passed_id);
-                $mdn_url = set_url_var($PHP_SELF, 'passed_ent_id', $passed_ent_id);
-                $mdn_url = set_url_var($PHP_SELF, 'sendreceipt', 1);
+                $mdn_url = set_url_var($mdn_url, 'mailbox', urlencode($mailbox));
+                $mdn_url = set_url_var($mdn_url, 'passed_id', $passed_id);
+                $mdn_url = set_url_var($mdn_url, 'passed_ent_id', $passed_ent_id);
+                $mdn_url = set_url_var($mdn_url, 'sendreceipt', 1);
 
                 $oTemplate->assign('read_receipt_sent', $message->is_mdnsent);
-                $oTemplate->assign('first_time_reading', $FirstTimeSee);
                 $oTemplate->assign('send_receipt_href', $mdn_url);
-                
+
                 $env[_("Read Receipt")] = $oTemplate->fetch('read_handle_receipt.tpl');
             }
         }
     }
+
+    $statuses = array();
+    if (isset($aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'])) {
+        if (isset($aMailbox['MSG_HEADERS'][$passed_id]['FLAGS']['\\deleted']) &&
+                  $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS']['\\deleted'] === true) {
+            $statuses[] = _("deleted");
+        }
+        if (isset($aMailbox['MSG_HEADERS'][$passed_id]['FLAGS']['\\answered']) &&
+                  $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS']['\\answered'] === true) {
+            $statuses[] = _("answered");
+        }
+        if (isset($aMailbox['MSG_HEADERS'][$passed_id]['FLAGS']['\\draft']) &&
+                  $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS']['\\draft'] === true) {
+            $statuses[] = _("draft");
+        }
+        if (isset($aMailbox['MSG_HEADERS'][$passed_id]['FLAGS']['\\flagged']) &&
+                  $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS']['\\flagged'] === true) {
+            $statuses[] = _("flagged");
+        }
+        if ( count($statuses) ) {
+            $env[_("Status")] = implode(', ', $statuses);
+        }
+    }
+
     $env[_("Options")] = formatToolbar($mailbox, $passed_id, $passed_ent_id, $message, $color);
 
+
     $oTemplate->assign('headers_to_display', $env);
-    
+
     $oTemplate->display('read_headers.tpl');
 }
 
 /**
  * Format message toolbar
  *
- * @param string $mailbox Name of current mailbox
- * @param int $passed_id UID of current message
- * @param int $passed_ent_id Id of entity within message
- * @param object $message Current message object
- * @param object $mbx_response
+ * @param array   $aMailbox      Current mailbox information array
+ * @param int     $passed_id     UID of current message
+ * @param int     $passed_ent_id Id of entity within message
+ * @param object  $message       Current message object
+ * @param void    $removedVar    This parameter is no longer used, but remains
+ *                               so as not to break this function's prototype
+ *                               (OPTIONAL)
+ * @param boolean $nav_on_top    When TRUE, the menubar is being constructed
+ *                               for use at the top of the page, otherwise it
+ *                               will be used for page bottom (OPTIONAL;
+ *                               default = TRUE)
  */
-function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removedVar, $nav_on_top = TRUE) {
+function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message,
+                       $removedVar=FALSE, $nav_on_top=TRUE) {
+
     global $base_uri, $draft_folder, $where, $what, $sort,
            $startMessage, $PHP_SELF, $save_as_draft,
            $enable_forward_as_attachment, $imapConnection, $lastTargetMailbox,
            $delete_prev_next_display, $show_copy_buttons,
            $compose_new_win, $compose_width, $compose_height,
-           $oTemplate;
+           $oTemplate, $return_to_message_list_after_move;
 
     //FIXME cleanup argument list, use $aMailbox where possible
     $mailbox = $aMailbox['NAME'];
 
     $urlMailbox = urlencode($mailbox);
 
-    $msgs_url = $base_uri . 'src/';
-
     // Create Prev & Next links
     // Handle nested entities first (i.e. Mime Attach parts)
     $prev_href = $next_href = $up_href = $del_href = $del_prev_href = $del_next_href = '';
-    $msg_list_href = $search_href = $view_msg_href = ''; 
+    $msg_list_href = $search_href = $view_msg_href = '';
     if (isset($passed_ent_id) && $passed_ent_id) {
         // code for navigating through attached message/rfc822 messages
         $url = set_url_var($PHP_SELF, 'passed_ent_id',0);
@@ -443,7 +499,7 @@ function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removed
 
         foreach($message->parent->entities as $ent) {
             if ($ent->type0 == 'message' && $ent->type1 == 'rfc822') {
-                
+
                 $c++;
                 $entity_count[$c] = $ent->entity_id;
                 $entities[$ent->entity_id] = $c;
@@ -498,7 +554,8 @@ function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removed
                        '&amp;mailbox='.$urlMailbox.'&amp;sort='.$sort.
                        '&amp;startMessage='.$startMessage.'&amp;show_more=0'.
                        "&amp;where=$where&amp;what=$what" .
-                       '&amp;delete_id='.$passed_id;
+                       '&amp;delete_id='.$passed_id .
+                       '&amp;smtoken='.sm_generate_security_token();
             }
 
             if ($next >= 0) {
@@ -506,16 +563,17 @@ function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removed
                        '&amp;mailbox='.$urlMailbox.'&amp;sort='.$sort.
                        '&amp;startMessage='.$startMessage.'&amp;show_more=0'.
                        "&amp;where=$where&amp;what=$what" .
-                       '&amp;delete_id='.$passed_id;
+                       '&amp;delete_id='.$passed_id .
+                       '&amp;smtoken='.sm_generate_security_token();
             }
         }
     }
 
-    // Start with Search Results or Message List link.
-    $list_xtra = "?where=read_body.php&amp;what=$what&amp;mailbox=" . $urlMailbox.
-                 "&amp;startMessage=$startMessage";
-    $msg_list_href = $base_uri .'src/right_main.php'. $list_xtra;
-    $search_href = $where=='search.php' ? $base_uri .'src/search.php?'.$list_xtra : '';
+    $msg_list_href = get_message_list_uri($aMailbox['NAME'], $startMessage, $what);
+    if ($where == 'search.php')
+        $search_href = str_replace('read_body.php', 'search.php', $msg_list_href);
+    else
+        $search_href = '';
 
     $comp_uri = $base_uri.'src/compose.php' .
                 '?passed_id=' . $passed_id .
@@ -526,7 +584,7 @@ function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removed
     // Start form for reply/reply all/forward..
     $target = '';
     $on_click='';
-    $method='method="post" ';
+    $method='post';
     $onsubmit='';
     if ($compose_new_win == '1') {
         if (!preg_match("/^[0-9]{3,4}$/", $compose_width)) {
@@ -536,12 +594,12 @@ function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removed
             $compose_height = '550';
         }
         if ( checkForJavascript() ) {
-          $on_click=' onclick="comp_in_new_form(\''.$comp_uri.'\', this, this.form,'. $compose_width .',' . $compose_height .')"';
+          $on_click='comp_in_new_form(\''.$comp_uri.'\', this, this.form,'. $compose_width .',' . $compose_height .')';
           $comp_uri = 'javascript:void(0)';
-          $method='method="get" ';
-          $onsubmit = 'onsubmit="return false" ';
+          $method='get';
+          $onsubmit = 'return false';
         } else {
-          $target = 'target="_blank"';
+          $target = '_blank';
         }
     }
 
@@ -557,38 +615,47 @@ function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removed
     $oTemplate->assign('message_list_href', $msg_list_href);
     $oTemplate->assign('search_href', $search_href);
 
-    $oTemplate->assign('form_extra', $method . $target . $onsubmit);
+    $oTemplate->assign('form_extra', '');
+    $oTemplate->assign('form_method', $method);
+    $oTemplate->assign('form_target', $target);
+    $oTemplate->assign('form_onsubmit', $onsubmit);
     $oTemplate->assign('compose_href', $comp_uri);
     $oTemplate->assign('button_onclick', $on_click);
     $oTemplate->assign('forward_as_attachment_enabled', $enable_forward_as_attachment==1);
-    
+
     //FIXME: I am surprised these aren't already given to the template; probably needs to be given at a higher level, so I have NO IDEA if this is the right place to do this...  adding them so template can construct its own API calls... we can build those herein too if preferrable
     $oTemplate->assign('mailbox', $aMailbox['NAME']);
     $oTemplate->assign('passed_id', $passed_id);
     $oTemplate->assign('what', $what);
 
     // If Draft folder - create Resume link
-    $resume_draft = $edit_as_new = false;    
-    if (($mailbox == $draft_folder) && ($save_as_draft)) {
-        $resume_draft = true; 'smaction_draft';
+    $resume_draft = $edit_as_new = false;
+    if (isDraftMailbox($mailbox) && ($save_as_draft)) {
+        $resume_draft = true;
     } else if (handleAsSent($mailbox)) {
         $edit_as_new = true;
     }
     $oTemplate->assign('can_resume_draft', $resume_draft);
-    $oTemplate->assign('can_edit_as_new', $edit_as_new);    
-    
+    $oTemplate->assign('can_edit_as_new', $edit_as_new);
+
     $oTemplate->assign('mailboxes', sqimap_mailbox_option_array($imapConnection));
     if (in_array('\\deleted', $aMailbox['PERMANENTFLAGS'],true)) {
-        $delete_url = $base_uri . "src/$where";
         $oTemplate->assign('can_be_deleted', true);
-        $oTemplate->assign('move_delete_form_action', $base_uri.'src/'.$where);
+        // force return-to-message-list if this is the only message in the folder
+        if ($return_to_message_list_after_move || ($next < 0 && $prev < 0))
+            $oTemplate->assign('move_delete_form_action', $base_uri.'src/'.$where);
+        else
+            $oTemplate->assign('move_delete_form_action', $base_uri.'src/read_body.php');
         $oTemplate->assign('delete_form_extra', addHidden('mailbox', $aMailbox['NAME'])."\n" .
                                                 addHidden('msg[0]', $passed_id)."\n" .
                                                 addHidden('startMessage', $startMessage)."\n" );
         if (!(isset($passed_ent_id) && $passed_ent_id)) {
             $oTemplate->assign('can_be_moved', true);
             $oTemplate->assign('move_form_extra', addHidden('mailbox', $aMailbox['NAME'])."\n" .
-                                                  addHidden('msg[0]', $passed_id)."\n" );
+                                                  addHidden('msg[0]', $passed_id)."\n" .
+                                                  // only need when $return_to_message_list_after_move is off
+                                                  addHidden('passed_id', ($next >= 0 ? $next : $prev))."\n" .
+                                                  addHidden('startMessage', $startMessage)."\n" );
             $oTemplate->assign('last_move_target', isset($lastTargetMailbox) && !empty($lastTargetMailbox) ? $lastTargetMailbox : '');
             $oTemplate->assign('can_be_copied', $show_copy_buttons==1);
         } else {
@@ -606,7 +673,37 @@ function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removed
         $oTemplate->assign('last_move_target', '');
         $oTemplate->assign('can_be_copied', false);
     }
-    
+
+    // access keys... only add to the top menubar, because adding
+    // them twice makes them less functional (press access key, *then*
+    // press <enter> to make it work)
+    //
+    if ($nav_on_top) {
+        global $accesskey_read_msg_reply, $accesskey_read_msg_reply_all,
+               $accesskey_read_msg_forward, $accesskey_read_msg_as_attach,
+               $accesskey_read_msg_delete, $accesskey_read_msg_bypass_trash,
+               $accesskey_read_msg_move, $accesskey_read_msg_move_to,
+               $accesskey_read_msg_copy;
+    } else {
+        $accesskey_read_msg_reply = $accesskey_read_msg_reply_all =
+        $accesskey_read_msg_forward = $accesskey_read_msg_as_attach =
+        $accesskey_read_msg_delete = $accesskey_read_msg_bypass_trash =
+        $accesskey_read_msg_move = $accesskey_read_msg_move_to =
+        $accesskey_read_msg_copy = 'NONE';
+    }
+    $oTemplate->assign('accesskey_read_msg_reply', $accesskey_read_msg_reply);
+    $oTemplate->assign('accesskey_read_msg_reply_all', $accesskey_read_msg_reply_all);
+    $oTemplate->assign('accesskey_read_msg_forward', $accesskey_read_msg_forward);
+    $oTemplate->assign('accesskey_read_msg_as_attach', $accesskey_read_msg_as_attach);
+    $oTemplate->assign('accesskey_read_msg_delete', $accesskey_read_msg_delete);
+    $oTemplate->assign('accesskey_read_msg_bypass_trash', $accesskey_read_msg_bypass_trash);
+    $oTemplate->assign('accesskey_read_msg_move_to', $accesskey_read_msg_move_to);
+    $oTemplate->assign('accesskey_read_msg_move', $accesskey_read_msg_move);
+    $oTemplate->assign('accesskey_read_msg_copy', $accesskey_read_msg_copy);
+
+    global $null;
+    do_hook('read_body_menu', $null);
+
     if ($nav_on_top) {
         $oTemplate->display('read_menubar_nav.tpl');
         $oTemplate->display('read_menubar_buttons.tpl');
@@ -614,14 +711,12 @@ function formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, $removed
         $oTemplate->display('read_menubar_buttons.tpl');
         $oTemplate->display('read_menubar_nav.tpl');
     }
-    
-    global $null;
-    do_hook('read_body_menu_bottom', $null);
+
 }
 
 function formatToolbar($mailbox, $passed_id, $passed_ent_id, $message, $color) {
     global $base_uri, $where, $what, $show_html_default,
-           $oTemplate, $download_href, 
+           $oTemplate, $download_href, $PHP_SELF,
            $unsafe_image_toggle_href, $unsafe_image_toggle_text;
 
     $urlMailbox = urlencode($mailbox);
@@ -637,28 +732,25 @@ function formatToolbar($mailbox, $passed_id, $passed_ent_id, $message, $color) {
     }
     $url = $base_uri.'src/view_header.php?'.$query_string;
 
-
-    // Build the printer friend link
-    /* hackydiehack */
-    if( !sqgetGlobalVar('view_unsafe_images', $view_unsafe_images, SQ_GET) ) {
-        $view_unsafe_images = false;
-    } else {
-        $view_unsafe_images = true;
-    }
-    $pf_params = '?passed_ent_id=' . $urlPassed_ent_id .
-                 '&mailbox=' . $urlMailbox .
-                 '&passed_id=' . $urlPassed_id .
-                 '&view_unsafe_images='. (bool) $view_unsafe_images .
-                 '&show_html_default=' . $show_html_default;    
     $links = array();
     $links[] = array (
                         'URL'   => $url,
                         'Text'  => _("View Full Header")
                      );
-    $links[] = array (
-                        'URL'   => $pf_params,
-                        'Text'  => _("View Printable Version")
+
+    if ( checkForJavaScript() ) { 
+        $links[] = array (
+                        'URL'   => 'javascript:printThis();',
+                        'Text'  => _("Print"),
+                     );
+    } else {
+        $links[] = array (
+                        'URL'   => set_url_var($PHP_SELF, 'print', '1'),
+                        'Text'  => _("Print"),
+                        'Target' => '_blank'
                      );
+    }
+
     $links[] = array (
                         'URL'   => $download_href,
                         'Text'  => _("Download this as a file")
@@ -680,7 +772,7 @@ function formatToolbar($mailbox, $passed_id, $passed_ent_id, $message, $color) {
     do_hook('read_body_header_right', $links);
 
     $oTemplate->assign('links', $links);
-    
+
     return $oTemplate->fetch('read_toolbar.tpl');
 }
 
@@ -732,12 +824,10 @@ if ( sqgetGlobalVar('account', $temp,  SQ_GET) ) {
 }
 
 /** GET/POST VARS */
+sqgetGlobalVar('passed_id',     $passed_id, SQ_INORDER, NULL, SQ_TYPE_BIGINT);
 sqgetGlobalVar('passed_ent_id', $passed_ent_id);
 sqgetGlobalVar('mailbox',       $mailbox);
 
-if ( sqgetGlobalVar('passed_id', $temp) ) {
-    $passed_id = (int) $temp;
-}
 if ( sqgetGlobalVar('sort', $temp) ) {
     $sort = (int) $temp;
 }
@@ -758,13 +848,13 @@ if(sqgetGlobalVar('view_unsafe_images', $temp)) {
 } else {
     $view_unsafe_images = 0;
 }
+
 /**
  * Retrieve mailbox cache
  */
 sqgetGlobalVar('mailbox_cache',$mailbox_cache,SQ_SESSION);
 
 /* end of get globals */
-global $sqimap_capabilities, $lastTargetMailbox;
 
 $imapConnection = sqimap_login($username, false, $imapServerAddress, $imapPort, 0);
 $aMailbox = sqm_api_mailbox_select($imapConnection, $iAccount, $mailbox,array('setindex' => $what, 'offset' => $startMessage),array());
@@ -845,6 +935,14 @@ if ( sqgetGlobalVar('delete_id', $delete_id, SQ_GET) ) {
     handleMessageListForm($imapConnection,$aMailbox,$sButton='setDeleted', array($delete_id));
 }
 
+/**
+ * or move button... why is handleMessageListForm (per above) conditional anway?
+ */
+if ( sqgetGlobalVar('moveButton', $ignore, SQ_POST) ) {
+    $sError = handleMessageListForm($imapConnection,$aMailbox);
+    sqgetGlobalVar('targetMailbox', $lastTargetMailbox, SQ_POST);
+}
+
 /**
  * $message contains all information about the message
  * including header and body
@@ -856,9 +954,14 @@ if (isset($aMailbox['MSG_HEADERS'][$passed_id]['MESSAGE_OBJECT'])) {
 } else {
     $message = sqimap_get_message($imapConnection, $passed_id, $mailbox);
     $FirstTimeSee = !$message->is_seen;
-    $message->is_seen = true;
-    $aMailbox['MSG_HEADERS'][$passed_id]['MESSAGE_OBJECT'] = $message;
 }
+
+/**
+ * update message seen status and put in cache
+ */
+$message->is_seen = true;
+$aMailbox['MSG_HEADERS'][$passed_id]['MESSAGE_OBJECT'] = $message;
+
 if (isset($passed_ent_id) && $passed_ent_id) {
     $message = $message->getEntity($passed_ent_id);
     if ($message->type0 != 'message'  && $message->type1 != 'rfc822') {
@@ -878,6 +981,14 @@ if (isset($passed_ent_id) && $passed_ent_id) {
 }
 $header = $message->header;
 
+// gmail does not mark messages as read when retrieving the message body
+// even though RFC 3501, section 6.4.5 (FETCH Command) says:
+// "The \Seen flag is implicitly set; if this causes the flags to change,
+// they SHOULD be included as part of the FETCH responses."
+//
+if ($imap_server_type == 'gmail') {
+    sqimap_toggle_flag($imapConnection, $passed_id, '\\Seen', true, true);
+}
 
 /****************************************/
 /* Block for handling incoming url vars */
@@ -885,13 +996,8 @@ $header = $message->header;
 
 if (isset($sendreceipt)) {
    if ( !$message->is_mdnsent ) {
-      $final_recipient = '';
-      if ((isset($identity)) && ($identity != 0)) //Main identity
-         $final_recipient = trim(getPref($data_dir, $username, 'email_address' . $identity, '' ));
-      if ($final_recipient == '' )
-         $final_recipient = trim(getPref($data_dir, $username, 'email_address', '' ));
       $supportMDN = ServerMDNSupport($aMailbox["PERMANENTFLAGS"]);
-      if ( SendMDN( $mailbox, $passed_id, $final_recipient, $message, $imapConnection ) > 0 && $supportMDN ) {
+      if ( SendMDN( $mailbox, $passed_id, $message, $imapConnection ) > 0 && $supportMDN ) {
          ToggleMDNflag( true, $imapConnection, $mailbox, $passed_id);
          $message->is_mdnsent = true;
          $aMailbox['MSG_HEADERS'][$passed_id]['MESSAGE_OBJECT'] = $message;
@@ -902,6 +1008,8 @@ if (isset($sendreceipt)) {
 /* End of block for handling incoming url vars */
 /***********************************************/
 
+$oTemplate->assign('aAttribs', array('class' => 'entity_sep'));
+$hr = $oTemplate->fetch('horizontal_rule.tpl');
 $messagebody = '';
 do_hook('read_body_top', $null);
 if ($show_html_default == 1) {
@@ -913,7 +1021,7 @@ $cnt = count($ent_ar);
 for ($i = 0; $i < $cnt; $i++) {
    $messagebody .= formatBody($imapConnection, $message, $color, $wrap_at, $ent_ar[$i], $passed_id, $mailbox);
    if ($i != $cnt-1) {
-       $messagebody .= '<hr />';
+       $messagebody .= $hr;
    }
 }
 
@@ -924,8 +1032,17 @@ $mailbox_cache[$iAccount.'_'.$aMailbox['NAME']] = $aMailbox;
 sqsession_register($mailbox_cache,'mailbox_cache');
 $_SESSION['mailbox_cache'] = $mailbox_cache;
 
+// message list URI is used in page header when on read_body
+$oTemplate->assign('message_list_href', get_message_list_uri($aMailbox['NAME'], $startMessage, $what));
+
 displayPageHeader($color, $mailbox,'','');
-formatMenuBar($aMailbox, $passed_id, $passed_ent_id, $message,false);
+
+/* this is the non-javascript version of printer friendly */
+if ( sqgetGlobalVar('print', $print, SQ_GET) ) {
+    $oTemplate->display('read_message_print.tpl');
+} else {
+    formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message,false);
+}
 formatEnvheader($aMailbox, $passed_id, $passed_ent_id, $message, $color, $FirstTimeSee);
 
 $oTemplate->assign('message_body', $messagebody);
@@ -949,14 +1066,13 @@ if ($attachment_common_show_images && is_array($attachment_common_show_images_li
         $a['DownloadURL'] = $img['download_href'];
         $images[] = $a;
     }
-    
+
     $oTemplate->assign('images', $images);
     $oTemplate->display('read_display_images_inline.tpl');
 }
 
-formatMenuBar($aMailbox, $passed_id, $passed_ent_id, $message, false, FALSE);
+formatMenubar($aMailbox, $passed_id, $passed_ent_id, $message, false, FALSE);
 
 do_hook('read_body_bottom', $null);
 sqimap_logout($imapConnection);
 $oTemplate->display('footer.tpl');
-?>