X-Git-Url: https://vcs.fsf.org/?p=squirrelmail.git;a=blobdiff_plain;f=functions%2Fmailbox_display.php;h=29a3b340e178cf25fdcf279eb6956b2e2d02eb55;hp=47e70ba881c292f233738638b0e041c0d027fdcb;hb=54156b1cfd60ef726191e2209fbd4306a980a03c;hpb=91c27aee3c2f6c883b2996827567999e291cfd19 diff --git a/functions/mailbox_display.php b/functions/mailbox_display.php index 47e70ba8..29a3b340 100644 --- a/functions/mailbox_display.php +++ b/functions/mailbox_display.php @@ -1,26 +1,16 @@ $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('&',$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
"; + foreach ($highlight_list as $highlight_list_part) { if (trim($highlight_list_part['value'])) { $high_val = strtolower($highlight_list_part['value']); @@ -627,6 +715,10 @@ function highlightMessage($sCol, $sVal, $highlight_list, &$aFormat) { } } if ($hlt_color) { + // Bug in highlight color??? + if ($hlt_color{0} != '#') { + $hlt_color = '#'. $hlt_color; + } $aFormat['row']['color'] = $hlt_color; return true; } else { @@ -640,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'; @@ -694,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: @@ -743,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]); @@ -806,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; } } @@ -830,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 { @@ -848,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; @@ -874,11 +988,11 @@ function showMessagesForMailbox($imapConnection, &$aMailbox,$aProps, &$iError) { $thread_name = _("Thread View"); $newsort = $aMailbox['SORT'] + SQSORT_THREAD; } - $thread_link_str = '[' . $thread_name - . ']'; + $thread_link_uri = $baseurl . '&srt=' . $newsort + . '&startMessage=1'; } else { - $thread_link_str =''; + $thread_link_uri =''; + $thread_name = ''; } $sort = $aMailbox['SORT']; @@ -887,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. @@ -964,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; @@ -995,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; @@ -1018,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('&','&',$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; @@ -1028,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; @@ -1098,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 = ''; @@ -1151,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) { @@ -1168,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 @@ -1188,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 @@ -1243,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 @@ -1260,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); @@ -1280,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; @@ -1301,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); } } @@ -1317,4 +1558,3 @@ function attachSelectedMessages($imapConnection,$aMsgHeaders) { return $composesession; } -?>