* This implements functions that manipulate messages
* NOTE: Quite a few functions in this file are obsolete
*
- * @copyright © 1999-2007 The SquirrelMail Project Team
+ * @copyright 1999-2022 The SquirrelMail Project Team
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version $Id$
* @package squirrelmail
if ($source_mailbox!==false && $source_mailbox==$mailbox) {
return false;
}
- $msgs_id = sqimap_message_list_squisher($id);
if (sqimap_msgs_list_copy ($imap_stream, $id, $mailbox, $handle_errors)) {
return sqimap_toggle_flag($imap_stream, $id, '\\Deleted', true, true);
} else {
* @since 1.4.0
*/
function sqimap_msgs_list_delete($imap_stream, $mailbox, $id, $bypass_trash=false) {
- // FIX ME, remove globals by introducing an associative array with properties
- // as 4th argument as replacement for the bypass_trash var
+ // FIXME: Remove globals by introducing an associative array with properties as 4th argument as replacement for the $bypass_trash variable.
global $move_to_trash, $trash_folder;
if (($move_to_trash == true) && ($bypass_trash != true) &&
(sqimap_mailbox_exists($imap_stream, $trash_folder) && ($mailbox != $trash_folder)) ) {
/**
* Set a flag on the provided uid list
* @param resource imap connection
- * @param array $id list with uid's
+ * @param mixed $id Normally an array which is a list with message UIDs to be flagged
+ * or a string range such as "1:*"
* @param string $flag Flags to set/unset flags can be i.e.'\Seen', '\Answered', '\Seen \Answered'
* @param bool $set add (true) or remove (false) the provided flag
* @param bool $handle_errors Show error messages in case of a NO, BAD or BYE response
$msgs_id = sqimap_message_list_squisher($id);
$set_string = ($set ? '+' : '-');
- for ($i=0; $i<sizeof($id); $i++) {
- $aMessageList["$id[$i]"] = array();
+ $aMessageList = array();
+ // TODO: There doesn't seem to be a reason to set up $aMessageList anyway because an empty array for each message doesn't add anything to the parseFetch() return value, so this code block could be simply deleted:
+ if (!is_string($id)) {
+ for ($i=0; $i<sizeof($id); $i++) {
+ $aMessageList["$id[$i]"] = array();
+ }
}
$aResponse = sqimap_run_command_list($imap_stream, "STORE $msgs_id ".$set_string."FLAGS ($flag)", $handle_errors, $response, $message, TRUE);
// some broken IMAP servers do not return UID elements on UID STORE
// if this is the case, then we need to do a UID FETCH
- $testkey=$id[0];
- if (!isset($parseFetchResults[$testkey]['UID'])) {
+ if (!empty($parseFetchResults)
+ && !isset(reset($parseFetchResults)['UID'])) {
$aResponse = sqimap_run_command_list($imap_stream, "FETCH $msgs_id (FLAGS)", $handle_errors, $response, $message, TRUE);
$parseFetchResults = parseFetch($aResponse,$aMessageList);
}
$sSortField = 'REVERSE '.$sSortField;
}
$query = "SORT ($sSortField) ".strtoupper($default_charset)." $search";
- // FIX ME sqimap_run_command should return the parsed data accessible by $aDATA['SORT']
- // use sqimap_run_command_list in case of unsollicited responses. If we don't we could loose the SORT response
+ // FIXME: sqimap_run_command() should return the parsed data accessible by $aDATA['SORT']
+ // use sqimap_run_command_list() in case of unsolicited responses. If we don't we could loose the SORT response.
$aData = sqimap_run_command_list ($imap_stream, $query, false, $response, $message, TRUE);
/* fallback to default charset */
if ($response == 'NO') {
for ($i=0,$iCnt=count($aData);$i<$iCnt;++$i) {
for ($j=0,$jCnt=count($aData[$i]);$j<$jCnt;++$j) {
if (preg_match("/^\* $sCommand (.+)$/", $aData[$i][$j], $aMatch)) {
- $aUid += preg_split("/ /", trim($aMatch[1]));
+ $aUid += explode(' ', trim($aMatch[1]));
}
}
}
$msgs = sqimap_get_small_header_list($imap_stream, $aUid,
array(), array($sSortField));
}
+
+ // sqimap_get_small_header (see above) returns fields in lower case,
+ // but the code below uses all upper case
+ foreach ($msgs as $k => $v)
+ if (isset($msgs[$k][strtolower($sSortField)]))
+ $msgs[$k][strtoupper($sSortField)] = $msgs[$k][strtolower($sSortField)];
+
$aUid = array();
$walk = false;
switch ($sSortField) {
case 'TO':
case 'CC':
if(!$walk) {
- array_walk($msgs, create_function('&$v,&$k,$f',
- '$v[$f] = (isset($v[$f])) ? $v[$f] : "";
- $addr = reset(parseRFC822Address($v[$f],1));
- $sPersonal = (isset($addr[SQM_ADDR_PERSONAL]) && $addr[SQM_ADDR_PERSONAL]) ?
- $addr[SQM_ADDR_PERSONAL] : "";
- $sEmail = ($addr[SQM_ADDR_HOST]) ?
- $addr[SQM_ADDR_MAILBOX] . "@".$addr[SQM_ADDR_HOST] :
- $addr[SQM_ADDR_HOST];
- $v[$f] = ($sPersonal) ? decodeHeader($sPersonal):$sEmail;'),$sSortField);
+ if (check_php_version(5, 3, 0))
+ $walk_function = function(&$v,&$k,$f) {
+ $v[$f] = (isset($v[$f])) ? $v[$f] : "";
+ $addr = reset(parseRFC822Address($v[$f],1));
+ $sPersonal = (isset($addr[SQM_ADDR_PERSONAL]) && $addr[SQM_ADDR_PERSONAL]) ?
+ $addr[SQM_ADDR_PERSONAL] : "";
+ $sEmail = ($addr[SQM_ADDR_HOST]) ?
+ $addr[SQM_ADDR_MAILBOX] . "@".$addr[SQM_ADDR_HOST] :
+ $addr[SQM_ADDR_HOST];
+ $v[$f] = ($sPersonal) ? decodeHeader($sPersonal, true, false):$sEmail;
+ };
+ else
+ $walk_function = create_function('&$v,&$k,$f',
+ '$v[$f] = (isset($v[$f])) ? $v[$f] : "";
+ $addr = reset(parseRFC822Address($v[$f],1));
+ $sPersonal = (isset($addr[SQM_ADDR_PERSONAL]) && $addr[SQM_ADDR_PERSONAL]) ?
+ $addr[SQM_ADDR_PERSONAL] : "";
+ $sEmail = ($addr[SQM_ADDR_HOST]) ?
+ $addr[SQM_ADDR_MAILBOX] . "@".$addr[SQM_ADDR_HOST] :
+ $addr[SQM_ADDR_HOST];
+ $v[$f] = ($sPersonal) ? decodeHeader($sPersonal, true, false):$sEmail;');
+ array_walk($msgs, $walk_function, $sSortField);
$walk = true;
}
// nobreak
case 'SUBJECT':
if(!$walk) {
- array_walk($msgs, create_function('&$v,&$k,$f',
- '$v[$f] = (isset($v[$f])) ? $v[$f] : "";
- $v[$f] = strtolower(decodeHeader(trim($v[$f])));
- $v[$f] = (preg_match("/^(vedr|sv|re|aw|\[\w\]):\s*(.*)$/si", $v[$f], $matches)) ?
- $matches[2] : $v[$f];'),$sSortField);
+ if (check_php_version(5, 3, 0))
+ $walk_function = function(&$v,&$k,$f) {
+ $v[$f] = (isset($v[$f])) ? $v[$f] : "";
+ $v[$f] = strtolower(decodeHeader(trim($v[$f]), true, false));
+ $v[$f] = (preg_match("/^(?:(?:vedr|sv|re|aw|fw|fwd|\[\w\]):\s*)*\s*(.*)$/si", $v[$f], $matches)) ?
+ $matches[1] : $v[$f];
+ };
+ else
+ $walk_function = create_function('&$v,&$k,$f',
+ '$v[$f] = (isset($v[$f])) ? $v[$f] : "";
+ $v[$f] = strtolower(decodeHeader(trim($v[$f]), true, false));
+ $v[$f] = (preg_match("/^(?:(?:vedr|sv|re|aw|fw|fwd|\[\w\]):\s*)*\s*(.*)$/si", $v[$f], $matches)) ?
+ $matches[1] : $v[$f];');
+ array_walk($msgs, $walk_function, $sSortField);
$walk = true;
}
foreach ($msgs as $item) {
case 'DATE':
case 'INTERNALDATE':
if(!$walk) {
- array_walk($msgs, create_function('&$v,$k,$f',
- '$v[$f] = (isset($v[$f])) ? $v[$f] : "";
- $v[$f] = getTimeStamp(explode(" ",$v[$f]));'),$sSortField);
+ if (check_php_version(5, 3, 0))
+ $walk_function = function(&$v,$k,$f) {
+ $v[$f] = (isset($v[$f])) ? $v[$f] : "";
+ $v[$f] = getTimeStamp(explode(" ",$v[$f]));
+ };
+ else
+ $walk_function = create_function('&$v,$k,$f',
+ '$v[$f] = (isset($v[$f])) ? $v[$f] : "";
+ $v[$f] = getTimeStamp(explode(" ",$v[$f]));');
+ array_walk($msgs, $walk_function, $sSortField);
$walk = true;
}
// nobreak;
} elseif ($response == 'BAD') {
sqm_trigger_imap_error('SQM_IMAP_NO_THREAD',$query, $response, $message);
}
+ $sThreadResponse = '';
if (isset($sRead[0])) {
for ($i=0,$iCnt=count($sRead);$i<$iCnt;++$i) {
if (preg_match("/^\* THREAD (.+)$/", $sRead[$i], $aMatch)) {
break;
}
}
- } else {
- $sThreadResponse = "";
}
unset($sRead);
if ($sThreadResponse) {
for ($i=0,$iCnt = strlen($sThreadResponse);$i<$iCnt;++$i) {
- $cChar = $sThreadResponse{$i};
+ $cChar = $sThreadResponse[$i];
switch ($cChar) {
case '(': // new sub thread
// correction for a subthread of a thread with no parents in thread
return $timepassed;
}
-
-/**
- * Normalise the different Priority headers into a uniform value,
- * namely that of the X-Priority header (1, 3, 5). Supports:
- * Prioirty, X-Priority, Importance.
- * X-MS-Mail-Priority is not parsed because it always coincides
- * with one of the other headers.
- *
- * FIXME: DUPLICATE CODE ALERT:
- * NOTE: this is actually a duplicate from the function in
- * class/mime/Rfc822Header.php.
- * @todo obsolate function or use it instead of code block in parseFetch()
- */
-function parsePriority($sValue) {
- $aValue = split('/\w/',trim($sValue));
- $value = strtolower(array_shift($aValue));
- if ( is_numeric($value) ) {
- return $value;
- }
- if ( $value == 'urgent' || $value == 'high' ) {
- return 1;
- } elseif ( $value == 'non-urgent' || $value == 'low' ) {
- return 5;
- }
- return 3;
-}
-
/**
* Parses a string in an imap response. String starts with " or { which means it
* can handle double quoted strings and literal strings
* @return string $s parsed string without the double quotes or literal count
*/
function parseString($read,&$i) {
- $char = $read{$i};
+ $char = $read[$i];
$s = '';
if ($char == '"') {
$iPos = ++$i;
while (true) {
$iPos = strpos($read,'"',$iPos);
if (!$iPos) break;
- if ($iPos && $read{$iPos -1} != '\\') {
+ if ($iPos && $read[$iPos -1] != '\\') {
$s = substr($read,$i,($iPos-$i));
$i = $iPos;
break;
$aMsg['ID'] = $id;
$fetch = substr($read,$i_space+1,5);
if (!is_numeric($id) && $fetch !== 'FETCH') {
- $aMsg['ERROR'] = $read; // htmlspecialchars should be done just before display. this is backend code
+ $aMsg['ERROR'] = $read; // sm_encode_html_special_chars should be done just before display. this is backend code
break;
}
$i = strpos($read,'(',$i_space+5);
case 'BODYSTRUCTURE':
break; // to be implemented, moving imap code out of the Message class
case 'INTERNALDATE':
- $aMsg['INTERNALDATE'] = trim(str_replace(' ', ' ',parseString($read,$i)));
+ $aMsg['INTERNALDATE'] = trim(preg_replace('/\s+/', ' ',parseString($read,$i)));
break;
case 'BODY.PEEK[HEADER.FIELDS':
case 'BODY[HEADER.FIELDS':
$value = trim(substr($line, $pos+1));
switch($field) {
case 'date':
- $aMsg['date'] = trim(str_replace(' ', ' ', $value));
+ $aMsg['date'] = trim(preg_replace('/\s+/', ' ', $value));
break;
- case 'x-priority': $aMsg['x-priority'] = ($value) ? (int) $value{0} : 3; break;
+ case 'x-priority': $aMsg['x-priority'] = ($value) ? (int) $value[0] : 3; break;
case 'priority':
case 'importance':
+ // duplicate code with Rfc822Header.cls:parsePriority()
if (!isset($aMsg['x-priority'])) {
- $aPrio = split('/\w/',trim($value));
+ $aPrio = preg_split('/\s/',trim($value));
$sPrio = strtolower(array_shift($aPrio));
if (is_numeric($sPrio)) {
$iPrio = (int) $sPrio;
} elseif ( $sPrio == 'non-urgent' || $sPrio == 'low' ) {
- $iPrio = 3;
+ $iPrio = 5;
} elseif ( $sPrio == 'urgent' || $sPrio == 'high' ) {
$iPrio = 1;
} else {
$arg_no = 0;
$arg_a = array();
++$i;
- for ($cnt = strlen($read); ($i < $cnt) && ($read{$i} != ')'); ++$i) {
- $char = strtoupper($read{$i});
+ for ($cnt = strlen($read); ($i < $cnt) && ($read[$i] != ')'); ++$i) {
+ $char = strtoupper($read[$i]);
switch ($char) {
case '{':
case '"':
$addr_a = array();
$group = '';
$a=0;
- for (; $i < $cnt && $read{$i} != ')'; ++$i) {
- if ($read{$i} == '(') {
+ for (; $i < $cnt && $read[$i] != ')'; ++$i) {
+ if ($read[$i] == '(') {
$addr = sqimap_parse_address($read, $i);
if (($addr[3] == '') && ($addr[2] != '')) {
/* start of group */
*/
function sqimap_parse_address($read, &$i) {
$arg_a = array();
- for (; $read{$i} != ')'; ++$i) {
- $char = strtoupper($read{$i});
+ for (; $read[$i] != ')'; ++$i) {
+ $char = strtoupper($read[$i]);
switch ($char) {
case '{':
case '"': $arg_a[] = parseString($read,$i); break;
*/
function sqimap_get_message($imap_stream, $id, $mailbox, $hide=0) {
// typecast to int to prohibit 1:* msgs sets
- $id = (int) $id;
+ // Update: $id should always be sanitized into a BIGINT so this
+ // is being removed; leaving this code here in case something goes
+ // wrong, however
+ //$id = (int) $id;
$flags = array();
$read = sqimap_run_command($imap_stream, "FETCH $id (FLAGS BODYSTRUCTURE)", true, $response, $message, TRUE);
if ($read) {
if ($hide == 2) return FALSE;
/* the message was not found, maybe the mailbox was modified? */
- global $sort, $startMessage, $color;
+ global $sort, $startMessage;
$errmessage = _("The server couldn't find the message you requested.");
$errmessage .= '<p>'._("Most probably your message list was out of date and the message has been moved away or deleted (perhaps by another program accessing the same mailbox).");
/* this will include a link back to the message list */
- error_message($errmessage, $mailbox, $sort, (int) $startMessage, $color);
+ error_message($errmessage, $mailbox, $sort, (int) $startMessage);
exit;
}
$bodystructure = implode('',$read);
$rfc822_header = new Rfc822Header();
$rfc822_header->parseHeader($read);
$msg->rfc822_header = $rfc822_header;
+
+ parse_message_entities($msg, $id, $imap_stream);
return $msg;
+ }
+
+
+/**
+ * Recursively parse embedded messages (if any) in the given
+ * message, building correct rfc822 headers for each one
+ *
+ * @param object $msg The message object to scan for attached messages
+ * NOTE: this is passed by reference! Changes made
+ * within will affect the caller's copy of $msg!
+ * @param int $id The top-level message UID on the IMAP server, even
+ * if the $msg being passed in is only an attached entity
+ * thereof.
+ * @param resource $imap_stream A live connection to the IMAP server.
+ *
+ * @return void
+ *
+ * @since 1.5.2
+ *
+ */
+function parse_message_entities(&$msg, $id, $imap_stream) {
+ if (!empty($msg->entities)) foreach ($msg->entities as $i => $entity) {
+ if (is_object($entity) && strtolower(get_class($entity)) == 'message') {
+ if (!empty($entity->rfc822_header)) {
+ $read = sqimap_run_command($imap_stream, "FETCH $id BODY[". $entity->entity_id .".HEADER]", true, $response, $message, TRUE);
+ $rfc822_header = new Rfc822Header();
+ $rfc822_header->parseHeader($read);
+ $msg->entities[$i]->rfc822_header = $rfc822_header;
+ }
+ parse_message_entities($msg->entities[$i], $id, $imap_stream);
+ }
+ }
}