Remove quotes around personal names in message list (#3292587)
[squirrelmail.git] / functions / mailbox_display.php
index 03c74d7222e1d8f16b00151b71f15f5d16f042d2..a31c1445547a6ff9343c3bc48876217ef08dfbcd 100644 (file)
@@ -6,7 +6,7 @@
  * This contains functions that display mailbox information, such as the
  * table row that has sender, date, subject, etc...
  *
- * @copyright © 1999-2006 The SquirrelMail Project Team
+ * @copyright 1999-2011 The SquirrelMail Project Team
  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
@@ -21,7 +21,9 @@
  * @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
  */
@@ -425,7 +427,7 @@ function fetchMessageHeaders($imapConnection, &$aMailbox) {
 function prepareMessageList(&$aMailbox, $aProps) {
 
     /* Globalize link attributes so plugins can share in modifying them */
-    global $link, $title, $target, $onclick, $link_extra;
+    global $link, $title, $target, $onclick, $link_extra, $preselected;
 
     /* retrieve the properties */
     $my_email_address = (isset($aProps['email'])) ? $aProps['email'] : false;
@@ -564,10 +566,10 @@ function prepareMessageList(&$aMailbox, $aProps) {
                         }
                         $sTmp = decodeHeader($sTmp);
                         if (isset($aColumnDesc[$k]['truncate']) && $aColumnDesc[$k]['truncate']) {
-                            $sTrunc = truncateWithEntities($sTmp, $aColumnDesc[$k]['truncate']);
+                            $sTrunc = sm_truncate_string($sTmp, $aColumnDesc[$k]['truncate'], '...', TRUE);
                             if ($sTrunc != $sTmp) {
                                 if (!$title) {
-                                    $title = htmlspecialchars($sTmp);
+                                    $title = $sTmp;
                                 } else if ($title_maybe) {
                                     $title = $title .', '.$title_maybe;
                                     $title = substr($title,0,-2); // strip ', ';
@@ -576,7 +578,7 @@ function prepareMessageList(&$aMailbox, $aProps) {
                             $sTmp = $sTrunc;
                         }
                     }
-                    $value = ($sTmp) ? $sTmp : $sUnknown;
+                    $value = ($sTmp) ? (substr($sTmp, 0, 6) == '"' && substr($sTmp, -6) == '"' ? substr(substr($sTmp, 0, -6), 6) : $sTmp) : $sUnknown;
                     break;
                 case SQM_COL_SUBJ:
                     // subject is mime encoded, decode it.
@@ -589,7 +591,7 @@ function prepareMessageList(&$aMailbox, $aProps) {
                     $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);
+                        $sTmp = sm_truncate_string($value, $aColumnDesc[$k]['truncate']-$iIndent, '...', TRUE);
                         // drop any double spaces since these will be displayed in the title
                         $title = ($sTmp != $value) ? preg_replace('/\s{2,}/', ' ', $value) : '';
                         $value = $sTmp;
@@ -604,7 +606,8 @@ function prepareMessageList(&$aMailbox, $aProps) {
                         // $onclick, $link_extra, $title, and so forth)
                         // plugins are responsible for sharing nicely (such as for
                         // setting the target, etc)
-                        do_hook('subject_link', array($iPageOffset, $sSearch, $aSearch));
+                        $temp = array(&$iPageOffset, &$sSearch, &$aSearch, $aMsg);
+                        do_hook('subject_link', $temp);
                     }
                     $value = (trim($value)) ? $value : _("(no subject)");
                     /* add thread indentation */
@@ -621,17 +624,19 @@ function prepareMessageList(&$aMailbox, $aProps) {
                     $aFlagColumn = array('seen' => false,
                                          'deleted'=>false,
                                          'answered'=>false,
+                                         'forwarded'=>false,
                                          'flagged' => false,
                                          'draft' => false);
 
                     if(!is_array($value)) $value = array();
-                    foreach ($value as $sFlag => $value) {
+                    foreach ($value as $sFlag => $v) {
                         switch ($sFlag) {
-                          case '\\seen'    : $aFlagColumn['seen']     = true; break;
-                          case '\\deleted' : $aFlagColumn['deleted']  = true; break;
-                          case '\\answered': $aFlagColumn['answered'] = true; break;
-                          case '\\flagged' : $aFlagColumn['flagged']  = true; break;
-                          case '\\draft'   : $aFlagColumn['draft']    = true; break;
+                          case '\\seen'    : $aFlagColumn['seen']      = true; break;
+                          case '\\deleted' : $aFlagColumn['deleted']   = true; break;
+                          case '\\answered': $aFlagColumn['answered']  = true; break;
+                          case '$forwarded': $aFlagColumn['forwarded'] = true; break;
+                          case '\\flagged' : $aFlagColumn['flagged']   = true; break;
+                          case '\\draft'   : $aFlagColumn['draft']     = true; break;
                           default:  break;
                         }
                     }
@@ -644,7 +649,7 @@ 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;
                 }
@@ -970,7 +975,7 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
         $source_url = $php_self;
     }
 
-    $baseurl = $source_url.'?mailbox=' . urlencode($aMailbox['NAME']) .'&account='.$aMailbox['ACCOUNT'];
+    $baseurl = $source_url.'?mailbox=' . urlencode($aMailbox['NAME']) .'&account='.$aMailbox['ACCOUNT'] . (strpos($source_url, 'src/search.php') ? '&smtoken=' . sm_generate_security_token() : '');
     $where = urlencode($aMailbox['SEARCH'][$iSetIndx][0]);
     $what = urlencode($aMailbox['SEARCH'][$iSetIndx][1]);
     $baseurl .= '&where=' . $where .  '&what=' .  $what;
@@ -985,11 +990,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'];
 
@@ -999,8 +1004,8 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
 
     /* future admin control over displayable buttons */
     $aAdminControl = array(
-                           'markUnflagged' => 1,
                            'markFlagged'   => 1,
+                           'markUnflagged' => 1,
                            'markRead'      => 1,
                            'markUnread'    => 1,
                            'forward'       => 1,
@@ -1015,8 +1020,8 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
     /* user prefs control */
     $aUserControl = array (
 
-                           'markUnflagged' => $show_flag_buttons,
                            'markFlagged'   => $show_flag_buttons,
+                           'markUnflagged' => $show_flag_buttons,
                            'markRead'      => 1,
                            'markUnread'    => 1,
                            'forward'       => 1,
@@ -1036,7 +1041,7 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
                    $trash_folder) ? true : false; //
 
     $showUndelete = (!$aMailbox['AUTO_EXPUNGE'] && $aMailbox['RIGHTS'] != 'READ-ONLY' &&
-                   in_array('\\deleted',$aMailbox['PERMANENTFLAGS'], true) && !$trash_folder) ? true : false;
+                   in_array('\\deleted',$aMailbox['PERMANENTFLAGS'], true) /* trash folder unrelated methinks: && !$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;
@@ -1057,17 +1062,37 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
                           );
     /* 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"),
-                           'copyButton'          => _("Copy")
+                           'markFlagged'    => _("Flag"),
+                           'markUnflagged'  => _("Unflag"),
+                           'markRead'       => _("Read"),
+                           'markUnread'     => _("Unread"),
+                           'forward'        => _("Forward"),
+                           'delete'         => _("Delete"),
+                           'undeleteButton' => _("Undelete"),
+                           'bypass_trash'   => _("Bypass Trash"),
+                           'expungeButton'  => _("Expunge"),
+                           'moveButton'     => _("Move"),
+                           'copyButton'     => _("Copy")
+                           );
+    /* Button access keys */
+    global $accesskey_mailbox_flag, $accesskey_mailbox_unflag,
+           $accesskey_mailbox_read, $accesskey_mailbox_unread,
+           $accesskey_mailbox_forward, $accesskey_mailbox_delete,
+           $accesskey_mailbox_undelete, $accesskey_mailbox_bypass_trash,
+           $accesskey_mailbox_expunge, $accesskey_mailbox_move,
+           $accesskey_mailbox_copy, $accesskey_mailbox_move_to;
+    $aButtonAccessKeys = array(
+                           'markFlagged'    => $accesskey_mailbox_flag,
+                           'markUnflagged'  => $accesskey_mailbox_unflag,
+                           'markRead'       => $accesskey_mailbox_read,
+                           'markUnread'     => $accesskey_mailbox_unread,
+                           'forward'        => $accesskey_mailbox_forward,
+                           'delete'         => $accesskey_mailbox_delete,
+                           'undeleteButton' => $accesskey_mailbox_undelete,
+                           'bypass_trash'   => $accesskey_mailbox_bypass_trash,
+                           'expungeButton'  => $accesskey_mailbox_expunge,
+                           'moveButton'     => $accesskey_mailbox_move,
+                           'copyButton'     => $accesskey_mailbox_copy,
                            );
 
 
@@ -1079,31 +1104,39 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
     foreach($aAdminControl as $k => $v) {
         if ($v & $aUserControl[$k] & $aImapControl[$k]) {
             switch ($k) {
-              case 'markUnflagged':
               case 'markFlagged':
+              case 'markUnflagged':
               case 'markRead':
               case 'markUnread':
               case 'delete':
               case 'undeleteButton':
               case 'expungeButton':
               case 'forward':
-                $aFormElements[$k] = array($aButtonStrings[$k],'submit');
+                $aFormElements[$k] 
+                    = array('value' => $aButtonStrings[$k], 'type' => 'submit', 'accesskey' => (isset($aButtonAccessKeys[$k]) ? $aButtonAccessKeys[$k] : 'NONE'));
                 break;
               case 'bypass_trash':
-                $aFormElements[$k] = array($aButtonStrings[$k],'checkbox');
+                $aFormElements[$k] 
+                    = array('value' => $aButtonStrings[$k], 'type' => 'checkbox', 'accesskey' => (isset($aButtonAccessKeys[$k]) ? $aButtonAccessKeys[$k] : 'NONE'));
                 break;
               case 'moveButton':
               case 'copyButton':
-                $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');
+                $aFormElements['targetMailbox']
+                    = array('options_list' => sqimap_mailbox_option_list($imapConnection, array(strtolower($lastTargetMailbox)), 0, $boxes),
+                            'type' => 'select',
+                            'accesskey' => $accesskey_mailbox_move_to);
+                $aFormElements['mailbox']       
+                    = array('value' => $aMailbox['NAME'], 'type' => 'hidden');
+                $aFormElements['startMessage']  
+                    = array('value' => $aMailbox['PAGEOFFSET'], 'type' => 'hidden');
+                $aFormElements[$k]              
+                    = array('value' => $aButtonStrings[$k], 'type' => 'submit', 'accesskey' => (isset($aButtonAccessKeys[$k]) ? $aButtonAccessKeys[$k] : 'NONE'));
                 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.
@@ -1119,7 +1152,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;
@@ -1141,9 +1175,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;
@@ -1152,103 +1188,103 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) {
     $aTemplate['fancy_index_highlite'] = $fancy_index_highlite;
 
 
-    return $aTemplate;
-}
-
-
-/**
- * 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;
-
-    $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', $s, $iTrimAt);
+    /**
+      * 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 {
-        /*
-         * 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;
-        }
+        $aTemplate['aSortSupported'] = array();
+    }
+
 
-        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;
+    /**
+      * 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;
         }
-        // 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
- */
-function handleAsSent($mailbox) {
-    global $handleAsSent_result;
+    $aTemplate['show_label_columns'] = $show_label_columns;
 
-    /* First check if this is the sent or draft folder. */
-    $handleAsSent_result = isSentMailbox($mailbox) || isDraftMailbox($mailbox);
 
-    /* Then check the result of the handleAsSent hook. */
-    do_hook('check_handleAsSent_result', $mailbox);
+    return $aTemplate;
 
-    /* And return the result. */
-    return $handleAsSent_result;
 }
 
+
 /**
  * Process messages list form and handle the cache gracefully. If $sButton and
  * $aUid are provided as argument then you can fake a message list submit and
  * 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('copyButton',      $sTmp, SQ_POST)) ? 'copy'         : $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 = '';
@@ -1257,6 +1293,16 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
     /* retrieve the check boxes */
     $aUid = (isset($msg) && is_array($msg)) ? array_values($msg) : $aUid;
     if (count($aUid) && $sButton != 'expunge') {
+
+        // don't do anything to any messages until we have done security check
+        // FIXME: not sure this code really belongs here, but there's nowhere else to put it with this architecture
+        sqgetGlobalVar('smtoken', $submitted_token, SQ_FORM, '');
+        sm_validate_security_token($submitted_token, 3600, TRUE);
+
+        // make sure message UIDs are sanitized (BIGINT)
+        foreach ($aUid as $i => $uid)
+           $aUid[$i] = (preg_match('/^[0-9]+$/', $uid) ? $uid : '0');
+
         $aUpdatedMsgs = false;
         $bExpunge = false;
         switch ($sButton) {
@@ -1299,18 +1345,20 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
             if (count($aMsgHeaders)) {
                 $composesession = attachSelectedMessages($imapConnection,$aMsgHeaders);
                 // dirty hack, add info to $aMailbox
-                $aMailbox['FORWARD_SESSION'] = $composesession;
+                $aMailbox['FORWARD_SESSION']['SESSION_NUMBER'] = $composesession;
+                $aMailbox['FORWARD_SESSION']['UIDS'] = $aUid;
             }
             break;
           default:
              // Hook for plugin buttons
-             do_hook_function('mailbox_display_button_action', $aUid);
+             $temp = array(&$sButton, &$aMailbox, $iAccount, $aMailbox['NAME'], &$aUid);
+             do_hook('mailbox_display_button_action', $temp);
              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
@@ -1324,6 +1372,34 @@ 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_forwarded = 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) == '$forwarded' && $value)
+                                $message->is_forwarded = 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
@@ -1388,7 +1464,15 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
             sqimap_run_command($imapConnection,'CLOSE',false,$result,$message);
             $aMailbox = sqm_api_mailbox_select($imapConnection,$iAccount, $aMailbox['NAME'],array(),array());
         } else {
-            if ($sButton) {
+            // this is the same hook as above, but here it is called in the
+            // context of not having had any messages selected and if any
+            // plugin handles the situation, it should return TRUE so we
+            // know this was not an erroneous user action
+            //
+            global $null;
+            $temp = array(&$sButton, &$aMailbox, $iAccount, $aMailbox['NAME'], $null);
+            if (!boolean_hook_function('mailbox_display_button_action', $temp, 1)
+             && $sButton) {
                 $sError = _("No messages were selected.");
             }
         }
@@ -1405,9 +1489,6 @@ function handleMessageListForm($imapConnection,&$aMailbox,$sButton='',$aUid = ar
  * @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);
@@ -1424,8 +1505,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;
@@ -1445,14 +1524,25 @@ 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);
+
+            // create subject for new message
+            //
+            $subject = decodeHeader($subject,false,false,true);
+            $subject = str_replace('"', "'", $subject);
+            $subject = trim($subject);
+            if (substr(strtolower($subject), 0, 4) != 'fwd:') {
+                $subject = 'Fwd: ' . $subject;
+            }
+            $composeMessage->rfc822_header->subject = $subject;
+
         }
     }
 
@@ -1461,4 +1551,3 @@ function attachSelectedMessages($imapConnection,$aMsgHeaders) {
     return $composesession;
 }
 
-?>