More readable spacing
[squirrelmail.git] / functions / mailbox_display.php
index d892cdca21f000a1185e439bd038c51713404a07..29a3b340e178cf25fdcf279eb6956b2e2d02eb55 100644 (file)
@@ -1,26 +1,16 @@
 <?php
 
 /**
-* mailbox_display.php
-*
-* Copyright (c) 1999-2005 The SquirrelMail Project Team
-* Licensed under the GNU GPL. For full terms see the file COPYING.
-*
-* This contains functions that display mailbox information, such as the
-* table row that has sender, date, subject, etc...
-*
-* @version $Id$
-* @package squirrelmail
-*/
-
-/** The standard includes.. */
-require_once(SM_PATH . 'functions/strings.php');
-require_once(SM_PATH . 'functions/html.php');
-require_once(SM_PATH . 'functions/imap_mailbox.php');
-require_once(SM_PATH . 'functions/imap_messages.php');
-require_once(SM_PATH . 'functions/imap_asearch.php');
-require_once(SM_PATH . 'functions/mime.php');
-require_once(SM_PATH . 'functions/forms.php');
+ * mailbox_display.php
+ *
+ * This contains functions that display mailbox information, such as the
+ * table row that has sender, date, subject, etc...
+ *
+ * @copyright &copy; 1999-2007 The SquirrelMail Project Team
+ * @license http://opensource.org/licenses/gpl-license.php GNU Public License
+ * @version $Id$
+ * @package squirrelmail
+ */
 
 
 /**
@@ -31,7 +21,10 @@ require_once(SM_PATH . 'functions/forms.php');
  * @param string   $mailbox mailbox to select and retrieve message headers from
  * @param array    $aConfig array with system config settings and incoming vars
  * @param array    $aProps mailbox specific properties
+ *
  * @return array   $aMailbox mailbox array with all relevant information
+ *
+ * @since 1.5.1
  * @author Marc Groot Koerkamp
  */
 function sqm_api_mailbox_select($imapConnection,$account,$mailbox,$aConfig,$aProps) {
@@ -106,6 +99,7 @@ function sqm_api_mailbox_select($imapConnection,$account,$mailbox,$aConfig,$aPro
     $aMailbox['UIDSET'][$iSetIndx] = false;
     $aMailbox['ID'] = false;
     $aMailbox['SETINDEX'] = $iSetIndx;
+    $aMailbox['MSG_HEADERS'] = false;
 
     if ($aCachedMailbox) {
         /**
@@ -205,6 +199,11 @@ function sqm_api_mailbox_select($imapConnection,$account,$mailbox,$aConfig,$aPro
             array(SQM_COL_FLAGS,SQM_COL_FROM, SQM_COL_SUBJ, SQM_COL_FLAGS);
     }
 
+    /**
+     * Restore the headers we fetch the last time. Saves intitialisation stuff in read_body.
+     */
+    $aMailbox['FETCHHEADERS'] = (isset($aCachedMailbox['FETCHHEADERS'])) ? $aCachedMailbox['FETCHHEADERS'] : null;
+
     if (!isset($aProps[MBX_PREF_AUTO_EXPUNGE]) && isset($aCachedMailbox['AUTO_EXPUNGE'])) {
         $aMailbox['AUTO_EXPUNGE'] = $aCachedMailbox['AUTO_EXPUNGE'];
     } else {
@@ -212,6 +211,12 @@ function sqm_api_mailbox_select($imapConnection,$account,$mailbox,$aConfig,$aPro
     }
     if (!isset($aConfig['search']) && isset($aCachedMailbox['SEARCH'][$iSetIndx])) {
         $aMailbox['SEARCH'][$iSetIndx] = $aCachedMailbox['SEARCH'][$iSetIndx];
+    } else if (isset($aConfig['search']) && isset($aCachedMailbox['SEARCH'][$iSetIndx]) &&
+        $aConfig['search'] != $aCachedMailbox['SEARCH'][$iSetIndx]) {
+        // reset the pageindex
+        $aMailbox['SEARCH'][$iSetIndx] = $aConfig['search'];
+        $aMailbox['OFFSET'] = 0;
+        $aMailbox['PAGEOFFSET'] = 1;
     } else {
         $aMailbox['SEARCH'][$iSetIndx] =  (isset($aConfig['search'])) ? $aConfig['search'] : 'ALL';
     }
@@ -248,14 +253,16 @@ function sqm_api_mailbox_select($imapConnection,$account,$mailbox,$aConfig,$aPro
 
 /**
  * Fetch the message headers for a mailbox. Settings are part of the aMailbox
- * array.
+ * array. Dependent of the mailbox settings it deals with sort, thread and search
+ * If server sort is supported then SORT is also used for retrieving sorted search results
  *
  * @param resource $imapConnection imap socket handle
  * @param array    $aMailbox (reference) mailbox retrieved from sqm_api_mailbox_select
  * @return error   $error error number
+ * @since 1.5.1
  * @author Marc Groot Koerkamp
  */
-function fetchMessageHeaders($imapConnection, &$aMailbox, $aFetchHeaders) {
+function fetchMessageHeaders($imapConnection, &$aMailbox) {
 
     /* FIX ME, this function is kind of big, maybe we can split it up in
        a couple of functions. Make sure the functions are private and starts with _
@@ -280,16 +287,18 @@ function fetchMessageHeaders($imapConnection, &$aMailbox, $aFetchHeaders) {
             $start_msg = 1;
         }
     }
-    //sm_print_r($aMailbox);
+
     if (is_array($aMailbox['UIDSET'])) {
         $aUid =& $aMailbox['UIDSET'][$iSetIndx];
     } else {
         $aUid = false;
     }
+    $aFetchHeaders = $aMailbox['FETCHHEADERS'];
 
     $iError = 0;
     $aFetchItems = $aHeaderItems = array();
     // initialize the fields we want to retrieve:
+    $aHeaderFields = array();
     foreach ($aFetchHeaders as $v) {
       switch ($v) {
         case SQM_COL_DATE:       $aHeaderFields[] = 'Date';         break;
@@ -310,7 +319,6 @@ function fetchMessageHeaders($imapConnection, &$aMailbox, $aFetchHeaders) {
      * A uidset with sorted uid's is available. We can use the cache
      */
     if (isset($aUid) && $aUid ) {
-
         // limit the cache to SQM_MAX_PAGES_IN_CACHE
         if (!$aMailbox['SHOWALL'][$iSetIndx] && isset($aMailbox['MSG_HEADERS'])) {
             $iMaxMsgs = $iLimit * SQM_MAX_PAGES_IN_CACHE;
@@ -330,7 +338,13 @@ function fetchMessageHeaders($imapConnection, &$aMailbox, $aFetchHeaders) {
 
         $id_slice = array_slice($aUid,$start_msg-1,$iLimit);
         /* do some funky cache checks */
-        if (isset($aMailbox['MSG_HEADERS'])) {
+        if (isset($aMailbox['MSG_HEADERS']) && is_array($aMailbox['MSG_HEADERS'])) {
+            // temp code, read_body del / next links fo not update fields.
+            foreach ($aMailbox['MSG_HEADERS'] as $iUid => $aValue) {
+                if (!isset($aValue['UID'])) {
+                    unset($aMailbox['MSG_HEADERS'][$iUid]);
+                }
+            }
             $aUidCached = array_keys($aMailbox['MSG_HEADERS']);
         } else {
             $aMailbox['MSG_HEADERS'] = array();
@@ -400,7 +414,21 @@ function fetchMessageHeaders($imapConnection, &$aMailbox, $aFetchHeaders) {
     return $iError;
 }
 
+/**
+ * Prepares the message headers for display inside a template. The links are calculated,
+ * color for row highlighting is calculated and optionally the strings are truncated.
+ *
+ * @param array    $aMailbox (reference) mailbox retrieved from sqm_api_mailbox_select
+ * @param array    $aProps properties
+ * @return array   $aFormattedMessages array with message headers and format info
+ * @since 1.5.1
+ * @author Marc Groot Koerkamp
+ */
 function prepareMessageList(&$aMailbox, $aProps) {
+
+    /* Globalize link attributes so plugins can share in modifying them */
+    global $link, $title, $target, $onclick, $link_extra, $preselected;
+
     /* retrieve the properties */
     $my_email_address = (isset($aProps['email'])) ? $aProps['email'] : false;
     $highlight_list   = (isset($aProps['config']['highlight_list'])) ? $aProps['config']['highlight_list'] : false;
@@ -436,6 +464,17 @@ function prepareMessageList(&$aMailbox, $aProps) {
           default: break;
         }
     }
+    $aExtraHighLightColumns = array();
+    foreach ($aExtraColumns as $v) {
+        switch ($v) {
+          case SQM_COL_FROM:       $aExtraHighLightColumns[] = 'from';         break;
+          case SQM_COL_SUBJ:       $aExtraHighLightColumns[] = 'subject';      break;
+          case SQM_COL_TO:         $aExtraHighLightColumns[] = 'to';           break;
+          case SQM_COL_CC:         $aExtraHighLightColumns[] = 'cc';           break;
+          case SQM_COL_BCC:        $aExtraHighLightColumns[] = 'bcc';          break;
+          default: break;
+        }
+    }
     $aFormattedMessages = array();
 
 
@@ -478,7 +517,7 @@ function prepareMessageList(&$aMailbox, $aProps) {
         if (isset($aId[$i])) {
 
             $bHighLight = false;
-            $value = $title = $link = $target = '';
+            $value = $title = $link = $target = $onclick = $link_extra = '';
             $aQuery = ($aInitQuery !== false) ? $aInitQuery : false;
             $aMsg = $aHeaders[$aId[$i]];
             if (isset($aSearch) && count($aSearch) > 1 && $aQuery) {
@@ -492,7 +531,7 @@ function prepareMessageList(&$aMailbox, $aProps) {
             }
 
             foreach ($aCol as $k => $v) {
-                $link = $target = $title = '';
+                $title = $link = $target = $onclick = $link_extra = '';
                 $aColumns[$k] = array();
                 $value = (isset($aMsg[$v]))  ? $aMsg[$v]  : '';
                 $sUnknown = _("Unknown recipient");
@@ -507,35 +546,72 @@ function prepareMessageList(&$aMailbox, $aProps) {
                         if ($highlight_list && !$bHighLight) {
                             $bHighLight = highlightMessage($aCol[$k], $value, $highlight_list,$aFormattedMessages[$iUid]);
                         }
-                        $sTmp = getAddressString(parseRFC822Address($value),array('best' => true));
+                        $aAddressList = parseRFC822Address($value);
+                        $sTmp = getAddressString($aAddressList,array('best' => true));
+                        $title = $title_maybe = '';
+                        foreach ($aAddressList as $aAddr) {
+                            $sPersonal = (isset($aAddr[SQM_ADDR_PERSONAL])) ? $aAddr[SQM_ADDR_PERSONAL] : '';
+                            $sMailbox  = (isset($aAddr[SQM_ADDR_MAILBOX]))  ? $aAddr[SQM_ADDR_MAILBOX]  : '';
+                            $sHost     = (isset($aAddr[SQM_ADDR_HOST]))     ? $aAddr[SQM_ADDR_HOST]     : '';
+                            if ($sPersonal) {
+                                $title .= htmlspecialchars($sMailbox.'@'.$sHost).', ';
+                            } else {
+                                // if $value gets truncated we need to add the addresses with no
+                                // personal name as well
+                                $title_maybe .= htmlspecialchars($sMailbox.'@'.$sHost).', ';
+                            }
+                        }
+                        if ($title) {
+                            $title = substr($title,0,-2); // strip ', ';
+                        }
+                        $sTmp = decodeHeader($sTmp);
                         if (isset($aColumnDesc[$k]['truncate']) && $aColumnDesc[$k]['truncate']) {
                             $sTrunc = truncateWithEntities($sTmp, $aColumnDesc[$k]['truncate']);
-                            $title = ($sTrunc != $sTmp) ? htmlspecialchars($sTmp) : '';
+                            if ($sTrunc != $sTmp) {
+                                if (!$title) {
+                                    $title = $sTmp;
+                                } else if ($title_maybe) {
+                                    $title = $title .', '.$title_maybe;
+                                    $title = substr($title,0,-2); // strip ', ';
+                                }
+                            }
                             $sTmp = $sTrunc;
                         }
                     }
-                    $value = ($sTmp) ? htmlspecialchars($sTmp) : $sUnknown;
+                    $value = ($sTmp) ? $sTmp : $sUnknown;
                     break;
                 case SQM_COL_SUBJ:
+                    // subject is mime encoded, decode it.
+                    // value is sanitized in decoding function.
+                    // TODO, verify if it should be done before or after the highlighting
+                    $value=decodeHeader($value);
                     if ($highlight_list && !$bHighLight) {
                         $bHighLight = highlightMessage('SUBJECT', $value, $highlight_list, $aFormattedMessages[$iUid]);
                     }
                     $iIndent = (isset($aIndentArray[$aId[$i]])) ? $aIndentArray[$aId[$i]] : 0;
+                    // FIXME: don't break 8bit symbols and html entities during truncation
                     if (isset($aColumnDesc[$k]['truncate']) && $aColumnDesc[$k]['truncate']) {
                         $sTmp = truncateWithEntities($value, $aColumnDesc[$k]['truncate']-$iIndent);
-                        $title = ($sTmp != $value) ? htmlspecialchars($value) : '';
+                        // drop any double spaces since these will be displayed in the title
+                        $title = ($sTmp != $value) ? preg_replace('/\s{2,}/', ' ', $value) : '';
                         $value = $sTmp;
                     }
                     /* generate the link to the message */
                     if ($aQuery) {
                         // TODO, $sTargetModule should be a query parameter so that we can use a single entrypoint
                         $link = $sTargetModule.'.php?' . implode('&amp;',$aQuery);
-                    }
 
+                        // see top of this function for which attributes are available
+                        // in the global scope for plugin use (like $link, $target,
+                        // $onclick, $link_extra, $title, and so forth)
+                        // plugins are responsible for sharing nicely (such as for
+                        // setting the target, etc)
+                        $temp = array(&$iPageOffset, &$sSearch, &$aSearch, $aMsg);
+                        do_hook('subject_link', $temp);
+                    }
                     $value = (trim($value)) ? $value : _("(no subject)");
                     /* add thread indentation */
                     $aColumns[$k]['indent']  = $iIndent;
-                    $value = htmlspecialchars($value);
                     break;
                 case SQM_COL_SIZE:
                     $value = show_readable_size($value);
@@ -550,7 +626,9 @@ function prepareMessageList(&$aMailbox, $aProps) {
                                          'answered'=>false,
                                          'flagged' => false,
                                          'draft' => false);
-                    foreach ($value as $sFlag => $value) {
+
+                    if(!is_array($value)) $value = array();
+                    foreach ($value as $sFlag => $v) {
                         switch ($sFlag) {
                           case '\\seen'    : $aFlagColumn['seen']     = true; break;
                           case '\\deleted' : $aFlagColumn['deleted']  = true; break;
@@ -569,20 +647,22 @@ function prepareMessageList(&$aMailbox, $aProps) {
                     $value = (is_array($value) && $value[0] == 'multipart' && $value[1] == 'mixed') ? true : false;
                     break;
                 case SQM_COL_CHECK:
-                    $value = $checkall;
+                    $value = ($checkall || in_array($iUid, $preselected));
                     break;
                 default : break;
                 }
-                if ($title)  { $aColumns[$k]['title']  = $title;  }
-                if ($link)   { $aColumns[$k]['link']   = $link;   }
-                if ($target) { $aColumns[$k]['target'] = $target; }
+                if ($title)      { $aColumns[$k]['title']      = $title;      }
+                if ($link)       { $aColumns[$k]['link']       = $link;       }
+                if ($link_extra) { $aColumns[$k]['link_extra'] = $link_extra; }
+                if ($onclick)    { $aColumns[$k]['onclick']    = $onclick;    }
+                if ($target)     { $aColumns[$k]['target']     = $target;     }
                 $aColumns[$k]['value']  = $value;
             }
             /* columns which will not be displayed but should be inspected
                because the highlight list contains rules with those columns */
-            foreach ($aExtraColumns as $k) {
-                if ($highlight_list && !$bHighLight) {
-                     $bHighLight = highlightMessage($k, $value, $highlight_list,$aFormattedMessages[$iUid]);
+            foreach ($aExtraHighLightColumns as $v) {
+                if ($highlight_list && !$bHighLight && isset($aMsg[$v])) {
+                    $bHighLight = highlightMessage($v, $aMsg[$v], $highlight_list,$aFormattedMessages[$iUid]);
                 }
             }
             $aFormattedMessages[$iUid]['columns'] = $aColumns;
@@ -595,16 +675,24 @@ function prepareMessageList(&$aMailbox, $aProps) {
 }
 
 
-
+/**
+ * Sets the row color if the provided column value pair  matches a hightlight rule
+ *
+ * @param string   $sCol column name
+ * @param string   $sVal column value
+ * @param array    $highlight_list highlight rules
+ * @param array    $aFormat (reference) array where row color info is stored
+ * @return bool     match found
+ * @since 1.5.1
+ * @author Marc Groot Koerkamp
+ */
 function highlightMessage($sCol, $sVal, $highlight_list, &$aFormat) {
-
-
     if (!is_array($highlight_list) && count($highlight_list) == 0) {
         return false;
     }
     $hlt_color = false;
     $sCol = strtoupper($sCol);
-    //echo "$sCol $sVal<br>";
+
     foreach ($highlight_list as $highlight_list_part) {
         if (trim($highlight_list_part['value'])) {
             $high_val   = strtolower($highlight_list_part['value']);
@@ -627,7 +715,7 @@ function highlightMessage($sCol, $sVal, $highlight_list, &$aFormat) {
         }
     }
     if ($hlt_color) {
-        // Bug in highlight color??? 
+        // Bug in highlight color???
         if ($hlt_color{0} != '#') {
             $hlt_color = '#'. $hlt_color;
         }
@@ -644,17 +732,18 @@ function setUserPref($username, $pref, $value) {
 }
 
 /**
-* Execute the sorting for a mailbox
-*
-* @param  resource $imapConnection Imap connection
-* @param  array    $aMailbox (reference) Mailbox retrieved with sqm_api_mailbox_select
-* @return int      $error (reference) Error number
-* @private
-* @author Marc Groot Koerkamp
-*/
+ * Execute the sorting for a mailbox
+ *
+ * @param  resource $imapConnection Imap connection
+ * @param  array    $aMailbox (reference) Mailbox retrieved with sqm_api_mailbox_select
+ * @return int      $error (reference) Error number
+ * @private
+ * @since 1.5.1
+ * @author Marc Groot Koerkamp
+ */
 function _get_sorted_msgs_list($imapConnection,&$aMailbox) {
     $iSetIndx = (isset($aMailbox['SETINDEX'])) ? $aMailbox['SETINDEX'] : 0;
-    $bDirection = ($aMailbox['SORT'] % 2);
+    $bDirection = !($aMailbox['SORT'] % 2);
     $error = 0;
     if (!$aMailbox['SEARCH'][$iSetIndx]) {
         $aMailbox['SEARCH'][$iSetIndx] = 'ALL';
@@ -698,13 +787,14 @@ function _get_sorted_msgs_list($imapConnection,&$aMailbox) {
 }
 
 /**
-* Does the $srt $_GET var to field mapping
-*
-* @param int $srt Field to sort on
-* @param bool $bServerSort Server sorting is true
-* @return string $sSortField Field to sort on
-* @private
-*/
+ * Does the $srt $_GET var to field mapping
+ *
+ * @param int $srt Field to sort on
+ * @param bool $bServerSort Server sorting is true
+ * @return string $sSortField Field to sort on
+ * @since 1.5.1
+ * @private
+ */
 function _getSortField($sort,$bServerSort) {
     switch($sort) {
         case SQSORT_NONE:
@@ -747,55 +837,24 @@ function _getSortField($sort,$bServerSort) {
     return $sSortField;
 }
 
-
-
-
 /**
-* This function loops through a group of messages in the mailbox
-* and shows them to the user.
-*
-* @param resource $imapConnection
-* @param array    $aMailbox associative array with mailbox related vars
-* @param array    $aProps
-* @param int      $iError error code, 0 is no error
-*/
-function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
-    global $PHP_SELF;
-    global $boxes;
+ * This function is a utility function for setting which headers should be
+ * fetched. It takes into account the highlight list which requires extra
+ * headers to be fetch in order to make those rules work. It's called before
+ * the headers are fetched which happens in showMessagesForMailbox and when
+ * the next and prev links in read_body.php are used.
+ *
+ * @param array    $aMailbox associative array with mailbox related vars
+ * @param array    $aProps
+ * @return void
+ * @since 1.5.1
+ */
+
+function calcFetchColumns(&$aMailbox, &$aProps) {
 
     $highlight_list    = (isset($aProps['config']['highlight_list'])) ? $aProps['config']['highlight_list'] : false;
-    $fancy_index_highlite = (isset($aProps['config']['fancy_index_highlite'])) ? $aProps['config']['fancy_index_highlite'] : true;
     $aColumnsDesc      = (isset($aProps['columns'])) ? $aProps['columns'] : false;
-    $iAccount          = (isset($aProps['account'])) ? (int) $aProps['account'] : 0;
-    $sMailbox          = (isset($aProps['mailbox'])) ? $aProps['mailbox'] : false;
-    $sTargetModule     = (isset($aProps['module'])) ? $aProps['module'] : 'read_body';
-    $show_flag_buttons = (isset($aProps['config']['show_flag_buttons'])) ? $aProps['config']['show_flag_buttons'] : true;
-    $lastTargetMailbox = (isset($aProps['config']['lastTargetMailbox'])) ? $aProps['config']['lastTargetMailbox'] : '';
-    $aOrder = array_keys($aProps['columns']);
-    $trash_folder      = (isset($aProps['config']['trash_folder']) && $aProps['config']['trash_folder'])
-                          ? $aProps['config']['trash_folder'] : false;
-    $sent_folder       = (isset($aProps['config']['sent_folder']) && $aProps['config']['sent_folder'])
-                          ? $aProps['config']['sent_folder'] : false;
-    $draft_folder      = (isset($aProps['config']['draft_folder']) && $aProps['config']['draft_folder'])
-                          ? $aProps['config']['draft_folder'] : false;
-    $page_selector     = (isset($aProps['config']['page_selector'])) ? $aProps['config']['page_selector'] : false;
-    $page_selector_max = (isset($aProps['config']['page_selector_max'])) ? $aProps['config']['page_selector_max'] : 10;
-    $color             = $aProps['config']['color'];
 
-
-    /*
-     * Form ID
-     */
-    static $iFormId;
-
-    if (!isset($iFormId)) {
-        $iFormId=1;
-    } else {
-        ++$iFormId;
-    }
-    /*
-     * Remove the checkbox column because we cannot fetch it from the imap server
-     */
     $aFetchColumns = $aColumnsDesc;
     if (isset($aFetchColumns[SQM_COL_CHECK])) {
         unset($aFetchColumns[SQM_COL_CHECK]);
@@ -810,15 +869,14 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
         foreach ($highlight_list as $highlight_list_part) {
             if (trim($highlight_list_part['value'])) {
                 $match_type = strtoupper($highlight_list_part['match_type']);
-                $aHighlightColumns = array();
                 switch ($match_type) {
                     case 'TO_CC':
                         $aHighlightColumns[SQM_COL_TO] = true;
                         $aHighlightColumns[SQM_COL_CC] = true;
                         break;
-                    case 'TO': $aHighlightColumns[SQM_COL_TO] = true; break;
-                    case 'CC': $aHighlightColumns[SQM_COL_CC] = true; break;
-                    case 'FROM': $aHighlightColumns[SQM_COL_FROM] = true; break;
+                    case 'TO':     $aHighlightColumns[SQM_COL_TO] = true; break;
+                    case 'CC':     $aHighlightColumns[SQM_COL_CC] = true; break;
+                    case 'FROM':   $aHighlightColumns[SQM_COL_FROM] = true; break;
                     case 'SUBJECT':$aHighlightColumns[SQM_COL_SUBJ] = true; break;
                 }
             }
@@ -834,12 +892,63 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
             $aProps['extra_columns'] = $aExtraColumns;
         }
     }
-    $aFetchColumns = array_keys($aFetchColumns);
-    // store the columns to fetch to the session so we can pick them up in read_body
+    $aMailbox['FETCHHEADERS'] =  array_keys($aFetchColumns);
+}
+
+
+/**
+ * This function loops through a group of messages in the mailbox
+ * and shows them to the user.
+ *
+ * @param resource $imapConnection
+ * @param array    $aMailbox associative array with mailbox related vars
+ * @param array    $aProps
+ * @param int      $iError error code, 0 is no error
+ */
+function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
+    global $PHP_SELF;
+    global $boxes, $show_copy_buttons;
+
+    $highlight_list    = (isset($aProps['config']['highlight_list'])) ? $aProps['config']['highlight_list'] : false;
+    $fancy_index_highlite = (isset($aProps['config']['fancy_index_highlite'])) ? $aProps['config']['fancy_index_highlite'] : true;
+    $aColumnsDesc      = (isset($aProps['columns'])) ? $aProps['columns'] : false;
+    $iAccount          = (isset($aProps['account'])) ? (int) $aProps['account'] : 0;
+    $sMailbox          = (isset($aProps['mailbox'])) ? $aProps['mailbox'] : false;
+    $sTargetModule     = (isset($aProps['module'])) ? $aProps['module'] : 'read_body';
+    $show_flag_buttons = (isset($aProps['config']['show_flag_buttons'])) ? $aProps['config']['show_flag_buttons'] : true;
+
+    /* allows to control copy button in function call. If array key is not set, code follows user preferences */
+    if (isset($aProps['config']['show_copy_buttons']))
+        $show_copy_buttons = $aProps['config']['show_copy_buttons'];
+
+    $lastTargetMailbox = (isset($aProps['config']['lastTargetMailbox'])) ? $aProps['config']['lastTargetMailbox'] : '';
+    $aOrder = array_keys($aProps['columns']);
+    $trash_folder      = (isset($aProps['config']['trash_folder']) && $aProps['config']['trash_folder'])
+                          ? $aProps['config']['trash_folder'] : false;
+    $sent_folder       = (isset($aProps['config']['sent_folder']) && $aProps['config']['sent_folder'])
+                          ? $aProps['config']['sent_folder'] : false;
+    $draft_folder      = (isset($aProps['config']['draft_folder']) && $aProps['config']['draft_folder'])
+                          ? $aProps['config']['draft_folder'] : false;
+    $page_selector     = (isset($aProps['config']['page_selector'])) ? $aProps['config']['page_selector'] : false;
+    $page_selector_max = (isset($aProps['config']['page_selector_max'])) ? $aProps['config']['page_selector_max'] : 10;
+    $color             = $aProps['config']['color'];
+
+
+    /*
+     * Form ID
+     */
+    static $iFormId;
+
+    if (!isset($iFormId)) {
+        $iFormId=1;
+    } else {
+        ++$iFormId;
+    }
+    // store the columns to fetch so we can pick them up in read_body
     // where we validate the cache.
-    /////// sqsession_register($aFetchColumns,'aFetchColumns');
+    calcFetchColumns($aMailbox  ,$aProps);
 
-    $iError = fetchMessageHeaders($imapConnection, $aMailbox, $aFetchColumns);
+    $iError = fetchMessageHeaders($imapConnection, $aMailbox);
     if ($iError) {
         return array();
     } else {
@@ -852,6 +961,7 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
              $aMailbox['PAGEOFFSET'] + $iLimit - 1 : $aMailbox['EXISTS'];
 
     $iNumberOfMessages = $aMailbox['TOTAL'][$iSetIndx];
+    $iEnd = min ( $iEnd, $iNumberOfMessages );
 
     $php_self = $PHP_SELF;
 
@@ -878,11 +988,11 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
             $thread_name = _("Thread View");
             $newsort = $aMailbox['SORT'] + SQSORT_THREAD;
         }
-        $thread_link_str = '<small>[<a href="' . $baseurl . '&amp;srt='
-            . $newsort . '&amp;startMessage=1">' . $thread_name
-            . '</a>]</small>';
+        $thread_link_uri = $baseurl . '&amp;srt=' . $newsort 
+                         . '&amp;startMessage=1';
     } else {
-        $thread_link_str ='';
+        $thread_link_uri ='';
+        $thread_name = '';
     }
     $sort = $aMailbox['SORT'];
 
@@ -891,67 +1001,79 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
 
 
     /* future admin control over displayable buttons */
-
     $aAdminControl = array(
                            'markUnflagged' => 1,
                            'markFlagged'   => 1,
                            'markRead'      => 1,
                            'markUnread'    => 1,
+                           'forward'       => 1,
                            'delete'        => 1,
                            'undeleteButton'=> 1,
                            'bypass_trash'  => 1,
                            'expungeButton' => 1,
                            'moveButton'    => 1,
-                           'forward'       => 1
+                           'copyButton'    => 1
                            );
+
     /* user prefs control */
     $aUserControl = array (
+
                            'markUnflagged' => $show_flag_buttons,
                            'markFlagged'   => $show_flag_buttons,
                            'markRead'      => 1,
                            'markUnread'    => 1,
+                           'forward'       => 1,
                            'delete'        => 1,
                            'undeleteButton'=> 1,
                            'bypass_trash'  => 1,
                            'expungeButton' => 1,
                            'moveButton'    => 1,
-                           'forward'       => 1
+                           'copyButton'    => $show_copy_buttons
+
                           );
 
     $showDelete = ($aMailbox['RIGHTS'] != 'READ-ONLY' &&
                    in_array('\\deleted',$aMailbox['PERMANENTFLAGS'], true)) ? true : false;
-    $showByPassTrash = (!$aMailbox['AUTO_EXPUNGE'] && $aMailbox['RIGHTS'] != 'READ-ONLY' &&
+    $showByPassTrash = (($aMailbox['AUTO_EXPUNGE'] && $aMailbox['RIGHTS'] != 'READ-ONLY' &&
                    in_array('\\deleted',$aMailbox['PERMANENTFLAGS'], true)) &&
-                   $trash_folder ? true : false; //
+                   $trash_folder) ? true : false; //
+
     $showUndelete = (!$aMailbox['AUTO_EXPUNGE'] && $aMailbox['RIGHTS'] != 'READ-ONLY' &&
                    in_array('\\deleted',$aMailbox['PERMANENTFLAGS'], true) && !$trash_folder) ? true : false;
     $showMove   = ($aMailbox['RIGHTS'] != 'READ-ONLY') ? true : false;
     $showExpunge = (!$aMailbox['AUTO_EXPUNGE'] && $aMailbox['RIGHTS'] != 'READ-ONLY' &&
                    in_array('\\deleted',$aMailbox['PERMANENTFLAGS'], true)) ? true : false;
+
+    /* Button options that depend on IMAP server and selected folder */
     $aImapControl = array (
                            'markUnflagged' => in_array('\\flagged',$aMailbox['PERMANENTFLAGS'], true),
                            'markFlagged'   => in_array('\\flagged',$aMailbox['PERMANENTFLAGS'], true),
                            'markRead'      => in_array('\\seen',$aMailbox['PERMANENTFLAGS'], true),
                            'markUnread'    => in_array('\\seen',$aMailbox['PERMANENTFLAGS'], true),
+                           'forward'       => 1,
                            'delete'        => $showDelete,
                            'undeleteButton'=> $showUndelete,
                            'bypass_trash'  => $showByPassTrash,
                            'expungeButton' => $showExpunge,
                            'moveButton'    => $showMove,
-                           'forward'       => 1
+                           'copyButton'    => 1
                           );
+    /* Button strings */
     $aButtonStrings = array(
                            'markUnflagged' => _("Unflag"),
                            'markFlagged'   => _("Flag"),
                            'markRead'      => _("Read"),
                            'markUnread'    => _("Unread"),
+                           'forward'       => _("Forward"),
                            'delete'    => _("Delete"),
                            'undeleteButton'  => _("Undelete"),
                            'bypass_trash'  => _("Bypass Trash"),
                            'expungeButton' => _("Expunge"),
                            'moveButton'          => _("Move"),
-                           'forward'       => _("Forward")
+                           'copyButton'          => _("Copy")
                            );
+
+
     /**
      * Register buttons in order to an array
      * The key is the "name", the first element of the value array is the "value", second argument is the type.
@@ -968,27 +1090,35 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
               case 'undeleteButton':
               case 'expungeButton':
               case 'forward':
-                $aFormElements[$k] = array($aButtonStrings[$k],'submit');
+                $aFormElements[$k] 
+                    = array('value' => $aButtonStrings[$k], 'type' => 'submit');
                 break;
               case 'bypass_trash':
-                $aFormElements[$k] = array($aButtonStrings[$k],'checkbox');
+                $aFormElements[$k] 
+                    = array('value' => $aButtonStrings[$k], 'type' => 'checkbox');
                 break;
               case 'moveButton':
-                $aFormElements['targetMailbox'] =
-                   array(sqimap_mailbox_option_list($imapConnection, array(strtolower($lastTargetMailbox)), 0, $boxes),'select');
-                $aFormElements['mailbox']       = array($aMailbox['NAME'],'hidden');
-                $aFormElements['startMessage']  = array($aMailbox['PAGEOFFSET'],'hidden');
-                $aFormElements[$k]              = array($aButtonStrings[$k],'submit');
+              case 'copyButton':
+                $aFormElements['targetMailbox']
+                    = array('options_list' => sqimap_mailbox_option_list($imapConnection, array(strtolower($lastTargetMailbox)), 0, $boxes),
+                            'type' => 'select');
+                $aFormElements['mailbox']       
+                    = array('value' => $aMailbox['NAME'], 'type' => 'hidden');
+                $aFormElements['startMessage']  
+                    = array('value' => $aMailbox['PAGEOFFSET'], 'type' => 'hidden');
+                $aFormElements[$k]              
+                    = array('value' => $aButtonStrings[$k], 'type' => 'submit');
                 break;
             }
         }
-        $aFormElements['account']  = array($iAccount,'hidden');
+        $aFormElements['account']  = array('value' => $iAccount,'type' => 'hidden');
     }
+    do_hook('message_list_controls', $aFormElements);
 
     /*
-    * This is the beginning of the message list table.
-    * It wraps around all messages
-    */
+     * This is the beginning of the message list table.
+     * It wraps around all messages
+     */
     $safe_name = preg_replace("/[^0-9A-Za-z_]/", '_', $aMailbox['NAME']);
     $form_name = "FormMsgs" . $safe_name;
 
@@ -999,8 +1129,8 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
 
     /* finally set the template vars */
 
-    // FIX ME, before we support multiple templates we must review the names of the vars
-
+// FIXME, before we support multiple templates we must review the names of the vars
+// BUMP!
 
 
     $aTemplate['color']     = $color;
@@ -1022,9 +1152,11 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
     $aTemplate['trash_folder'] = $trash_folder;
     $aTemplate['sent_folder'] = $sent_folder;
     $aTemplate['draft_folder'] = $draft_folder;
-    $aTemplate['thread_link_str'] = $thread_link_str;
+    $aTemplate['thread_link_uri'] = $thread_link_uri;
+    $aTemplate['thread_name'] = $thread_name;
     $aTemplate['php_self'] = str_replace('&','&amp;',$php_self);
     $aTemplate['mailbox'] = $sMailbox;
+//FIXME: javascript_on is always assigned to the template object in places like init.php; is there some reason to reassign it here?  is there some chance that it was changed?  if not, please remove this line!
     $aTemplate['javascript_on'] = (isset($aProps['config']['javascript_on'])) ? $aProps['config']['javascript_on'] : false;
     $aTemplate['enablesort'] = (isset($aProps['config']['enablesort'])) ? $aProps['config']['enablesort'] : false;
     $aTemplate['icon_theme'] = (isset($aProps['config']['icon_theme'])) ? $aProps['config']['icon_theme'] : false;
@@ -1032,57 +1164,123 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
     $aTemplate['alt_index_colors'] = (isset($aProps['config']['alt_index_colors'])) ? $aProps['config']['alt_index_colors'] : false;
     $aTemplate['fancy_index_highlite'] = $fancy_index_highlite;
 
+
+    /**
+      * Set up sort possibilities; one could argue that this is best
+      * placed in the template, but most template authors won't understand
+      * or need to understand it, so some advanced templates can override 
+      * it if they do something different.
+      */
+    if (!($aTemplate['sort'] & SQSORT_THREAD) && $aTemplate['enablesort']) {
+        $aTemplate['aSortSupported']
+            = array(SQM_COL_SUBJ =>     array(SQSORT_SUBJ_ASC     , SQSORT_SUBJ_DESC),
+                    SQM_COL_DATE =>     array(SQSORT_DATE_DESC    , SQSORT_DATE_ASC),
+                    SQM_COL_INT_DATE => array(SQSORT_INT_DATE_DESC, SQSORT_INT_DATE_ASC),
+                    SQM_COL_FROM =>     array(SQSORT_FROM_ASC     , SQSORT_FROM_DESC),
+                    SQM_COL_TO =>       array(SQSORT_TO_ASC       , SQSORT_TO_DESC),
+                    SQM_COL_CC =>       array(SQSORT_CC_ASC       , SQSORT_CC_DESC),
+                    SQM_COL_SIZE =>     array(SQSORT_SIZE_ASC     , SQSORT_SIZE_DESC));
+    } else {
+        $aTemplate['aSortSupported'] = array();
+    }
+
+
+    /**
+      * Figure out which columns should serve as labels for checkbox:
+      * we try to grab the two columns before and after the checkbox,
+      * except the subject column, since it is the link that opens
+      * the message view
+      *
+      * if $javascript_on is set, then the highlighting code takes
+      * care of this; just skip it
+      *
+      * This code also might be more appropriate in a template file, but
+      * we are moving this complex stuff out of the way of template 
+      * authors; advanced template sets are always free to override
+      * the resultant values.
+      *
+      */
+    $show_label_columns = array();
+    $index_order_part = array();
+    if (!($aTemplate['javascript_on'] && $aTemplate['fancy_index_highlite'])) {
+        $get_next_two = 0;
+        $last_order_part = 0;
+        $last_last_order_part = 0;
+        foreach ($aTemplate['aOrder'] as $index_order_part) {
+            if ($index_order_part == SQM_COL_CHECK) {
+                $get_next_two = 1;
+                if ($last_last_order_part != SQM_COL_SUBJ)
+                    $show_label_columns[] = $last_last_order_part;
+                if ($last_order_part != SQM_COL_SUBJ)
+                    $show_label_columns[] = $last_order_part;
+    
+            } else if ($get_next_two > 0 && $get_next_two < 3 && $index_order_part != SQM_COL_SUBJ) {
+                $show_label_columns[] = $index_order_part;
+                $get_next_two++;
+            }
+            $last_last_order_part = $last_order_part;
+            $last_order_part = $index_order_part;
+        }
+    }
+    $aTemplate['show_label_columns'] = $show_label_columns;
+
+
     return $aTemplate;
+
 }
 
 
 /**
-* FIXME: Undocumented function
-*/
-function truncateWithEntities($subject, $trim_at)
-{
-    $ent_strlen = strlen($subject);
-    if (($trim_at <= 0) || ($ent_strlen <= $trim_at))
-        return $subject;
-
+ * Truncates a string and take care of html encoded characters
+ *
+ * @param string  $s string to truncate
+ * @param int $iTrimAt Trim at nn characters
+ * @return string  Trimmed string
+ */
+function truncateWithEntities($s, $iTrimAt) {
     global $languages, $squirrelmail_language;
 
-    /*
-    * see if this is entities-encoded string
-    * If so, Iterate through the whole string, find out
-    * the real number of characters, and if more
-    * than $trim_at, substr with an updated trim value.
-    */
-    $trim_val = $trim_at;
-    $ent_offset = 0;
-    $ent_loc = 0;
-    while ( $ent_loc < $trim_val && (($ent_loc = strpos($subject, '&', $ent_offset)) !== false) &&
-            (($ent_loc_end = strpos($subject, ';', $ent_loc+3)) !== false) ) {
-        $trim_val += ($ent_loc_end-$ent_loc);
-        $ent_offset  = $ent_loc_end+1;
-    }
-    if (($trim_val > $trim_at) && ($ent_strlen > $trim_val) && (strpos($subject,';',$trim_val) < ($trim_val + 6))) {
-        $i = strpos($subject,';',$trim_val);
-        if ($i) {
-            $trim_val = strpos($subject,';',$trim_val);
-        }
-    }
-    // only print '...' when we're actually dropping part of the subject
-    if ($ent_strlen <= $trim_val)
-        return $subject;
+    $ent_strlen = strlen($s);
+    if (($iTrimAt <= 0) || ($ent_strlen <= $iTrimAt))
+        return $s;
 
     if (isset($languages[$squirrelmail_language]['XTRA_CODE']) &&
         function_exists($languages[$squirrelmail_language]['XTRA_CODE'] . '_strimwidth')) {
-        return call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_strimwidth', $subject, $trim_val);
-    }
+        return call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_strimwidth', $s, $iTrimAt);
+    } else {
+        /*
+         * see if this is entities-encoded string
+         * If so, Iterate through the whole string, find out
+         * the real number of characters, and if more
+         * than $iTrimAt, substr with an updated trim value.
+         */
+        $trim_val = $iTrimAt;
+        $ent_offset = 0;
+        $ent_loc = 0;
+        while ( $ent_loc < $trim_val && (($ent_loc = strpos($s, '&', $ent_offset)) !== false) &&
+                (($ent_loc_end = strpos($s, ';', $ent_loc+3)) !== false) ) {
+            $trim_val += ($ent_loc_end-$ent_loc);
+            $ent_offset  = $ent_loc_end+1;
+        }
 
-    return substr_replace($subject, '...', $trim_val);
+        if (($trim_val > $iTrimAt) && ($ent_strlen > $trim_val) && (strpos($s,';',$trim_val) < ($trim_val + 6))) {
+            $i = strpos($s,';',$trim_val);
+            if ($i !== false) {
+                $trim_val = strpos($s,';',$trim_val)+1;
+            }
+        }
+        // only print '...' when we're actually dropping part of the subject
+        if ($ent_strlen <= $trim_val)
+            return $s;
+    }
+    return substr_replace($s, '...', $trim_val);
 }
 
+
 /**
-* This should go in imap_mailbox.php
-* @param string $mailbox
-*/
+ * This should go in imap_mailbox.php
+ * @param string $mailbox
+ */
 function handleAsSent($mailbox) {
     global $handleAsSent_result;
 
@@ -1102,27 +1300,32 @@ function handleAsSent($mailbox) {
  * use it i.e. in read_body.php for del move next and update the cache
  *
  * @param  resource $imapConnection imap connection
- * @param  array $aMailbox (reference) cached mailbox
- * @param  string $sButton fake a submit button
- * @param  array  $aUid    fake the $msg array
+ * @param  array    $aMailbox       (reference) cached mailbox
+ * @param  string   $sButton        fake a submit button
+ * @param  array    $aUid           fake the $msg array
+ * @param  string   $targetMailbox  fake the target mailbox for move operations
+ * @param  boolean  $bypass_trash   fake the bypass trash checkbox for delete operations
  * @return string $sError error string in case of an error
+ * @since 1.5.1
  * @author Marc Groot Koerkamp
  */
-function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = array()) {
+function handleMessageListForm($imapConnection, &$aMailbox, $sButton='',
+                               $aUid = array(), $targetMailbox='', $bypass_trash=NULL) {
     /* incoming formdata */
-    $sButton = (sqgetGlobalVar('moveButton',      $sTmp, SQ_POST)) ? 'move'         : $sButton;
-    $sButton = (sqgetGlobalVar('expungeButton',   $sTmp, SQ_POST)) ? 'expunge'      : $sButton;
-    $sButton = (sqgetGlobalVar('forward',         $sTmp, SQ_POST)) ? 'forward'      : $sButton;
-    $sButton = (sqgetGlobalVar('delete',          $sTmp, SQ_POST)) ? 'setDeleted'   : $sButton;
-    $sButton = (sqgetGlobalVar('undeleteButton',  $sTmp, SQ_POST)) ? 'unsetDeleted'   : $sButton;
-    $sButton = (sqgetGlobalVar('markRead',        $sTmp, SQ_POST)) ? 'setSeen'      : $sButton;
-    $sButton = (sqgetGlobalVar('markUnread',      $sTmp, SQ_POST)) ? 'unsetSeen'    : $sButton;
-    $sButton = (sqgetGlobalVar('markFlagged',     $sTmp, SQ_POST)) ? 'setFlagged'   : $sButton;
-    $sButton = (sqgetGlobalVar('markUnflagged',   $sTmp, SQ_POST)) ? 'unsetFlagged' : $sButton;
-    sqgetGlobalVar('targetMailbox', $targetMailbox,   SQ_POST);
-    sqgetGlobalVar('bypass_trash',  $bypass_trash,    SQ_POST);
-    sqgetGlobalVar('msg',           $msg,             SQ_POST);
-    if (sqgetGlobalVar('account',       $iAccount,        SQ_POST) === false) {
+    $sButton = (sqgetGlobalVar('moveButton',      $sTmp, SQ_FORM)) ? 'move'         : $sButton;
+    $sButton = (sqgetGlobalVar('copyButton',      $sTmp, SQ_FORM)) ? 'copy'         : $sButton;
+    $sButton = (sqgetGlobalVar('expungeButton',   $sTmp, SQ_FORM)) ? 'expunge'      : $sButton;
+    $sButton = (sqgetGlobalVar('forward',         $sTmp, SQ_FORM)) ? 'forward'      : $sButton;
+    $sButton = (sqgetGlobalVar('delete',          $sTmp, SQ_FORM)) ? 'setDeleted'   : $sButton;
+    $sButton = (sqgetGlobalVar('undeleteButton',  $sTmp, SQ_FORM)) ? 'unsetDeleted'   : $sButton;
+    $sButton = (sqgetGlobalVar('markRead',        $sTmp, SQ_FORM)) ? 'setSeen'      : $sButton;
+    $sButton = (sqgetGlobalVar('markUnread',      $sTmp, SQ_FORM)) ? 'unsetSeen'    : $sButton;
+    $sButton = (sqgetGlobalVar('markFlagged',     $sTmp, SQ_FORM)) ? 'setFlagged'   : $sButton;
+    $sButton = (sqgetGlobalVar('markUnflagged',   $sTmp, SQ_FORM)) ? 'unsetFlagged' : $sButton;
+    if (empty($targetMailbox)) sqgetGlobalVar('targetMailbox', $targetMailbox,   SQ_FORM);
+    if (is_null($bypass_trash)) sqgetGlobalVar('bypass_trash',  $bypass_trash,    SQ_FORM);
+    sqgetGlobalVar('msg',           $msg,             SQ_FORM);
+    if (sqgetGlobalVar('account',       $iAccount,        SQ_FORM) === false) {
         $iAccount = 0;
     }
     $sError = '';
@@ -1155,10 +1358,16 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
             $aUpdatedMsgs = sqimap_toggle_flag($imapConnection, $aUid, $sFlag, $bSet, true);
             break;
           case 'move':
-            $aUpdatedMsgs = sqimap_msgs_list_move($imapConnection,$aUid,$targetMailbox);
+            $aUpdatedMsgs = sqimap_msgs_list_move($imapConnection,$aUid,$targetMailbox,true,$mailbox);
             sqsession_register($targetMailbox,'lastTargetMailbox');
             $bExpunge = true;
             break;
+          case 'copy':
+            // sqimap_msgs_list_copy returns true or false.
+            // If error happens - fourth argument handles it inside function.
+            sqimap_msgs_list_copy($imapConnection,$aUid,$targetMailbox,true);
+            sqsession_register($targetMailbox,'lastTargetMailbox');
+            break;
           case 'forward':
             $aMsgHeaders = array();
             foreach ($aUid as $iUid) {
@@ -1172,13 +1381,13 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
             break;
           default:
              // Hook for plugin buttons
-             do_hook_function('mailbox_display_button_action', $aUid);
+             do_hook('mailbox_display_button_action', $aUid);
              break;
         }
         /**
-         * Updates messages is an array containing the result of the untagged
+         * $aUpdatedMsgs is an array containing the result of the untagged
          * fetch responses send by the imap server due to a flag change. That
-         * response is parsed in a array with msg arrays by the parseFetch function
+         * response is parsed in an array with msg arrays by the parseFetch function
          */
         if ($aUpdatedMsgs) {
             // Update the message headers cache
@@ -1192,6 +1401,31 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
                     if (isset($aMailbox['MSG_HEADERS'][$iUid])) {
                         $aMailbox['MSG_HEADERS'][$iUid]['FLAGS'] = $aMsg['FLAGS'];
                     }
+                    /**
+                     * Also update flags in message object
+                     */
+//FIXME: WHY are we keeping flags in TWO places?!?  This is error-prone and some core code uses the is_xxxx message object values while other code uses the flags array above.  That's a mess.
+                    if (isset($aMailbox['MSG_HEADERS'][$iUid]['MESSAGE_OBJECT'])) {
+                        $message = $aMailbox['MSG_HEADERS'][$iUid]['MESSAGE_OBJECT'];
+                        $message->is_seen = false;
+                        $message->is_answered = false;
+                        $message->is_deleted = false;
+                        $message->is_flagged = false;
+                        $message->is_mdnsent = false;
+                        foreach ($aMsg['FLAGS'] as $flag => $value) {
+                            if (strtolower($flag) == '\\seen' && $value)
+                                $message->is_seen = true;
+                            else if (strtolower($flag) == '\\answered' && $value)
+                                $message->is_answered = true;
+                            else if (strtolower($flag) == '\\deleted' && $value)
+                                $message->is_deleted = true;
+                            else if (strtolower($flag) == '\\flagged' && $value)
+                                $message->is_flagged = true;
+                            else if (strtolower($flag) == '$mdnsent' && $value)
+                                $message->is_mdnsent = true;
+                        }
+                        $aMailbox['MSG_HEADERS'][$iUid]['MESSAGE_OBJECT'] = $message;
+                    }
                     /**
                      * Count the messages with the \Delete flag set so we can determine
                      * if the number of expunged messages equals the number of flagged
@@ -1247,7 +1481,7 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
             /**
              * on expunge we do not know which messages will be deleted
              * so it's useless to try to sync the cache
-
+             *
              * Close the mailbox so we do not need to parse the untagged expunge
              * responses which do not contain uid info.
              * NB: Closing a mailbox is faster then expunge because the imap
@@ -1264,10 +1498,15 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
     return $sError;
 }
 
+/**
+ * Attach messages to a compose session
+ *
+ * @param  resource $imapConnection imap connection
+ * @param  array $aMsgHeaders
+ * @return int $composesession unique compose_session_id where the attached messages belong to
+ * @author Marc Groot Koerkamp
+ */
 function attachSelectedMessages($imapConnection,$aMsgHeaders) {
-    global $username, $attachment_dir,
-           $data_dir;
-
 
     sqgetGlobalVar('composesession', $composesession, SQ_SESSION);
     sqgetGlobalVar('compose_messages', $compose_messages, SQ_SESSION);
@@ -1284,8 +1523,6 @@ function attachSelectedMessages($imapConnection,$aMsgHeaders) {
         sqsession_register($composesession,'composesession');
     }
 
-    $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
-
     $composeMessage = new Message();
     $rfc822_header = new Rfc822Header();
     $composeMessage->rfc822_header = $rfc822_header;
@@ -1305,14 +1542,14 @@ function attachSelectedMessages($imapConnection,$aMsgHeaders) {
             $body = implode('', $body_a);
             $body .= "\r\n";
 
-            $localfilename = GenerateRandomString(32, 'FILE', 7);
-            $full_localfilename = "$hashed_attachment_dir/$localfilename";
-
-            $fp = fopen( $full_localfilename, 'wb');
+            global $username, $attachment_dir;
+            $filename = sq_get_attach_tempfile();
+            $fullpath = getHashedDir($username, $attachment_dir) . '/' . $filename;
+            $fp = fopen($fullpath, 'wb');
             fwrite ($fp, $body);
             fclose($fp);
-            $composeMessage->initAttachment('message/rfc822',$subject.'.msg',
-                 $full_localfilename);
+
+            $composeMessage->initAttachment('message/rfc822', $subject . '.eml', $filename);
         }
     }
 
@@ -1321,4 +1558,3 @@ function attachSelectedMessages($imapConnection,$aMsgHeaders) {
     return $composesession;
 }
 
-?>