From c44e3f08fadba438af5cec42acffc2dceb363bb2 Mon Sep 17 00:00:00 2001 From: ralf Date: Tue, 5 Mar 2024 14:54:29 +0200 Subject: [PATCH] * eSync: fix syncing forwarded and replied flags to client incl. push using highestmodseq, if supported by IMAP server --- api/src/Mail.php | 47 +++++++++++++++++++++---------- api/src/Mail/Imap.php | 11 +++++--- mail/inc/class.mail_zpush.inc.php | 15 +++++----- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/api/src/Mail.php b/api/src/Mail.php index 68d3b392b7..ca2bc04038 100644 --- a/api/src/Mail.php +++ b/api/src/Mail.php @@ -1162,12 +1162,12 @@ class Mail * * @param folderName string the foldername * @param ignoreStatusCache bool ignore the cache used for counters - * + * @param bool $getModSeq true: query highestmodseq (returned in uppercase!) * @return array * * @throws Exception */ - function _getStatus($folderName,$ignoreStatusCache=false) + function _getStatus($folderName,$ignoreStatusCache=false,bool $getModSeq=false) { static $folderStatus = null; if (!$ignoreStatusCache && isset($folderStatus[$this->icServer->ImapServerId][$folderName])) @@ -1177,7 +1177,7 @@ class Mail } try { - $folderStatus[$this->icServer->ImapServerId][$folderName] = $this->icServer->getStatus($folderName,$ignoreStatusCache); + $folderStatus[$this->icServer->ImapServerId][$folderName] = $this->icServer->getStatus($folderName,$ignoreStatusCache,$getModSeq); } catch (\Exception $e) { @@ -1191,13 +1191,14 @@ class Mail * * returns an array information about the imap folder, may be used as wrapper to retrieve results from cache * - * @param _folderName string the foldername - * @param ignoreStatusCache bool ignore the cache used for counters - * @param basicInfoOnly bool retrieve only names and stuff returned by getMailboxes - * @param fetchSubscribedInfo bool fetch Subscribed Info on folder + * @param $_folderName string the foldername + * @param $ignoreStatusCache bool ignore the cache used for counters + * @param $basicInfoOnly bool retrieve only names and stuff returned by getMailboxes + * @param $fetchSubscribedInfo bool fetch Subscribed Info on folder + * @param bool $getModSeq true: return highestmodseq with key "highestmodseq" * @return array|false */ - function getFolderStatus($_folderName,$ignoreStatusCache=false,$basicInfoOnly=false,$fetchSubscribedInfo=true) + function getFolderStatus($_folderName,$ignoreStatusCache=false,$basicInfoOnly=false,$fetchSubscribedInfo=true, bool $getModSeq=false) { if (self::$debug) error_log(__METHOD__.' ('.__LINE__.') '." called with:$_folderName,$ignoreStatusCache,$basicInfoOnly"); if (!is_string($_folderName) || empty($_folderName)||(isset(self::$profileDefunct[$this->profileID]) && strlen(self::$profileDefunct[$this->profileID]))) @@ -1246,7 +1247,7 @@ class Mail if($ignoreStatusCache||!$folderInfo|| !is_array($folderInfo)) { try { - $folderInfo = $this->_getStatus($_folderName,$ignoreStatusCache); + $folderInfo = $this->_getStatus($_folderName,$ignoreStatusCache,$getModSeq); } catch (\Exception $e) { @@ -1283,6 +1284,10 @@ class Mail } if ($folderInfo) $folderBasicInfo[$this->profileID][$_folderName]=$retValue; //error_log(__METHOD__.' ('.__LINE__.') '.' '.$_folderName.array2string($retValue['attributes'])); + if ($getModSeq) + { + $retValue['highestmodseq'] = $folderInfo['HIGHESTMODSEQ'] ?? 0; + } if ($basicInfoOnly || (isset($retValue['attributes']) && stripos(array2string($retValue['attributes']),'noselect')!==false)) { return $retValue; @@ -1317,12 +1322,16 @@ class Mail try { //$folderStatus = $this->_getStatus($_folderName,$ignoreStatusCache); - $folderStatus = $this->getMailBoxCounters($_folderName,false); + $folderStatus = $this->getMailBoxCounters($_folderName,false, empty($retValue['highestmodseq']) && $getModSeq); $retValue['messages'] = $folderStatus['MESSAGES']; $retValue['recent'] = $folderStatus['RECENT']; $retValue['uidnext'] = $folderStatus['UIDNEXT']; $retValue['uidvalidity'] = $folderStatus['UIDVALIDITY']; $retValue['unseen'] = $folderStatus['UNSEEN']; + if (empty($retValue['highestmodseq']) && $getModSeq) + { + $retValue['highestmodseq'] = $folderStatus['HIGHESTMODSEQ']; + } if (//$retValue['unseen']==0 && (isset($this->mailPreferences['trustServersUnseenInfo']) && // some servers dont serve the UNSEEN information $this->mailPreferences['trustServersUnseenInfo']==false) || @@ -3384,13 +3393,14 @@ class Mail * function to retrieve the counters for a given folder * @param string $folderName * @param boolean $_returnObject return the counters as object rather than an array + * @param bool $getModSeq true: query highestmodseq (returned in uppercase!) * @return mixed false or array of counters array(MESSAGES,UNSEEN,RECENT,UIDNEXT,UIDVALIDITY) or object */ - function getMailBoxCounters($folderName,$_returnObject=true) + function getMailBoxCounters($folderName,$_returnObject=true, bool $getModSeq=false) { try { - $folderStatus = $this->icServer->getMailboxCounters($folderName); + $folderStatus = $this->icServer->getMailboxCounters($folderName, $getModSeq); //error_log(__METHOD__.' ('.__LINE__.') '.$folderName.": FolderStatus:".array2string($folderStatus).function_backtrace()); } catch (\Exception $e) @@ -3398,15 +3408,22 @@ class Mail if (self::$debug) error_log(__METHOD__." returned FolderStatus for Folder $folderName:".$e->getMessage()); return false; } - if(is_array($folderStatus)) { - if ($_returnObject===false) return $folderStatus; + if(is_array($folderStatus)) + { + if (!$_returnObject) + { + return $folderStatus; + } $status = new \stdClass; $status->messages = $folderStatus['MESSAGES']; $status->unseen = $folderStatus['UNSEEN']; $status->recent = $folderStatus['RECENT']; $status->uidnext = $folderStatus['UIDNEXT']; $status->uidvalidity = $folderStatus['UIDVALIDITY']; - + if ($getModSeq) + { + $status->highestmodseq = $folderStatus['HIGHESTMODSEQ']; + } return $status; } return false; diff --git a/api/src/Mail/Imap.php b/api/src/Mail/Imap.php index f8676d68da..323c9d56bb 100644 --- a/api/src/Mail/Imap.php +++ b/api/src/Mail/Imap.php @@ -558,13 +558,14 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface * getMailboxCounters * * @param array|string $mailbox + * @param bool $getModSeq true: query highestmodseq (returned in uppercase!) * @return array|false with counters */ - function getMailboxCounters($mailbox) + function getMailboxCounters($mailbox, bool $getModSeq=false) { try { - $status = $this->status($mailbox); + $status = $this->status($mailbox, Horde_Imap_Client::STATUS_ALL | ($getModSeq ? Horde_Imap_Client::STATUS_HIGHESTMODSEQ : 0)); foreach ($status as $key => $v) { $_status[strtoupper($key)]=$v; @@ -587,10 +588,11 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface * getStatus * * @param string $mailbox - * @param bool ignoreStatusCache ignore the cache used for counters + * @param bool $ignoreStatusCache ignore the cache used for counters + * @param bool $getModSeq true: return highestmodseq with key "modseq" * @return array with counters */ - function getStatus($mailbox, $ignoreStatusCache=false) + function getStatus($mailbox, $ignoreStatusCache=false, bool $getModSeq=false) { $mailboxes = $this->listMailboxes($mailbox,Horde_Imap_Client::MBOX_ALL_SUBSCRIBED, array( 'attributes'=>true, @@ -601,6 +603,7 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface $flags = Horde_Imap_Client::STATUS_ALL; if ($ignoreStatusCache) $flags |= Horde_Imap_Client::STATUS_FORCE_REFRESH; + if ($getModSeq) $flags |= Horde_Imap_Client::STATUS_HIGHESTMODSEQ; $mboxes = new Horde_Imap_Client_Mailbox_List($mailboxes); //error_log(__METHOD__.__LINE__.array2string($mboxes->count())); diff --git a/mail/inc/class.mail_zpush.inc.php b/mail/inc/class.mail_zpush.inc.php index 85d48f1bfe..2393bd40f6 100644 --- a/mail/inc/class.mail_zpush.inc.php +++ b/mail/inc/class.mail_zpush.inc.php @@ -1076,13 +1076,13 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, } else { $output->flag->flagstatus = 0; } - if (!empty($headers['answered'])) + if (!empty($headers['forwarded'])) { - $output->lastverexecuted = AS_REPLYTOSENDER; + $output->lastverbexecuted = AS_FORWARD; } - elseif (!empty($headers['forwarded'])) + elseif (!empty($headers['answered'])) { - $output->lastverexecuted = AS_FORWARD; + $output->lastverbexecuted = AS_REPLYTOSENDER; } $output->subject = $headers['subject']; $output->importance = $headers['priority'] > 3 ? 0 : @@ -1799,18 +1799,19 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, if (!$this->mail->folderIsSelectable($folder)) { ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": could not select folder $folder returning fake state"); - $syncstate = "M:".'0'."-R:".'0'."-U:".'0'."-NUID:".'0'."-UIDV:".'0'; + $syncstate = "M:".'0'."-R:".'0'."-U:".'0'."-NUID:".'0'."-UIDV:".'0'."-MODSEQ:".'0'; return array(); } $this->mail->reopen($folder); - if (!($status = $this->mail->getFolderStatus($folder,$ignoreStatusCache=true))) + if (!($status = $this->mail->getFolderStatus($folder, true, false, true, true))) { ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": could not stat folder $folder "); return false; } - $syncstate = "M:". $status['messages'] ."-R:". $status['recent'] ."-U:". $status['unseen']."-NUID:".$status['uidnext']."-UIDV:".$status['uidvalidity']; + $syncstate = "M:".$status['messages']."-R:".$status['recent']."-U:".$status['unseen']. + "-NUID:".$status['uidnext']."-UIDV:".$status['uidvalidity']."-MODSEQ:".($status['highestmodseq'] ?? '0'); if ($this->debugLevel) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($folderid, ...) $folder ($account) returning ".array2string($syncstate)); return array();