diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderResponseStatus.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderResponseStatus.php index 8a602bbec..f6259eb69 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderResponseStatus.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderResponseStatus.php @@ -26,4 +26,8 @@ abstract class FolderResponseStatus const UNSEEN = 'UNSEEN'; // rfc4551 const HIGHESTMODSEQ = 'HIGHESTMODSEQ'; + // rfc7889 + const APPENDLIMIT = 'APPENDLIMIT'; + // rfc8474 + const MAILBOXID = 'MAILBOXID'; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderStatus.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderStatus.php index 4f44cd886..2b827f1d5 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderStatus.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderStatus.php @@ -26,6 +26,8 @@ abstract class FolderStatus const UNSEEN = 'UNSEEN'; // rfc4551 const HIGHESTMODSEQ = 'HIGHESTMODSEQ'; + // rfc7889 + const APPENDLIMIT = 'APPENDLIMIT'; // rfc8474 const MAILBOXID = 'MAILBOXID'; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/FetchResponse.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/FetchResponse.php index 00a1d0b75..21d6d89a5 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/FetchResponse.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/FetchResponse.php @@ -54,12 +54,12 @@ class FetchResponse { $oResult = null; $aEmails = $this->GetFetchEnvelopeValue($iIndex); - if (is_array($aEmails) && 0 < count($aEmails)) + if (\is_array($aEmails) && \count($aEmails)) { $oResult = new \MailSo\Mime\EmailCollection; foreach ($aEmails as $aEmailItem) { - if (is_array($aEmailItem) && 4 === count($aEmailItem)) + if (\is_array($aEmailItem) && 4 === \count($aEmailItem)) { $sDisplayName = \MailSo\Base\Utils::DecodeHeaderValue( self::findEnvelopeIndex($aEmailItem, 0, ''), $sParentCharset); @@ -70,7 +70,7 @@ class FetchResponse $sLocalPart = self::findEnvelopeIndex($aEmailItem, 2, ''); $sDomainPart = self::findEnvelopeIndex($aEmailItem, 3, ''); - if (0 < strlen($sLocalPart) && 0 < strlen($sDomainPart)) + if (\strlen($sLocalPart) && \strlen($sDomainPart)) { $oResult->append( new \MailSo\Mime\Email($sLocalPart.'@'.$sDomainPart, $sDisplayName) @@ -87,9 +87,9 @@ class FetchResponse { $aBodyStructureArray = $this->GetFetchValue(Enumerations\FetchType::BODYSTRUCTURE); - if (is_array($aBodyStructureArray)) + if (\is_array($aBodyStructureArray)) { - if (0 < strlen($sRfc822SubMimeIndex)) + if (\strlen($sRfc822SubMimeIndex)) { return BodyStructure::NewInstanceFromRfc822SubPart($aBodyStructureArray, $sRfc822SubMimeIndex); } @@ -100,6 +100,7 @@ class FetchResponse } /** + * Like: UID, RFC822.SIZE, MODSEQ, INTERNALDATE, FLAGS, BODYSTRUCTURE * @return mixed */ public function GetFetchValue(string $sFetchItemName) @@ -124,11 +125,15 @@ class FetchResponse return null; } + /** + * Like: BODY[HEADER.FIELDS (RETURN-PATH RECEIVED MIME-VERSION MESSAGE-ID CONTENT-TYPE FROM TO CC BCC SENDER REPLY-TO DELIVERED-TO IN-REPLY-TO REFERENCES DATE SUBJECT SENSITIVITY X-MSMAIL-PRIORITY IMPORTANCE X-PRIORITY X-DRAFT-INFO RETURN-RECEIPT-TO DISPOSITION-NOTIFICATION-TO X-CONFIRM-READING-TO AUTHENTICATION-RESULTS X-DKIM-AUTHENTICATION-RESULTS LIST-UNSUBSCRIBE X-SPAM-STATUS X-SPAMD-RESULT X-BOGOSITY X-VIRUS X-VIRUS-SCANNED X-VIRUS-STATUS)] + * @return mixed + */ public function GetHeaderFieldsValue(string $sRfc822SubMimeIndex = '') : string { $bNextIsValue = false; - $sRfc822SubMimeIndex = 0 < \strlen($sRfc822SubMimeIndex) ? ''.$sRfc822SubMimeIndex.'.' : ''; + $sRfc822SubMimeIndex = \strlen($sRfc822SubMimeIndex) ? ''.$sRfc822SubMimeIndex.'.' : ''; if (isset($this->oImapResponse->ResponseList[3]) && \is_array($this->oImapResponse->ResponseList[3])) { diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 37b12bd0f..74e9ad67b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -334,6 +334,25 @@ class ImapClient extends \MailSo\Net\NetClient } } + /** + * RFC 7889 + * APPENDLIMIT= indicates that the IMAP server has the same upload limit for all mailboxes. + * APPENDLIMIT without any value indicates that the IMAP server supports this extension, + * and that the client will need to discover upload limits for each mailbox, + * as they might differ from mailbox to mailbox. + */ + public function AppendLimit() : ?int + { + if ($this->aCapabilityItems) { + foreach ($this->aCapabilityItems as $string) { + if ('APPENDLIMIT=' === \substr($string, 0, 12)) { + return (int) \substr($string, 12); + } + } + } + return null; + } + /** * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception @@ -416,21 +435,36 @@ class ImapClient extends \MailSo\Net\NetClient */ public function FolderStatus(string $sFolderName, array $aStatusItems) : ?array { + if (!\count($aStatusItems)) { + return null; + } $oFolderInfo = $this->oCurrentFolderInfo; + $bReselect = false; + $bWritable = false; if ($oFolderInfo && $sFolderName === $oFolderInfo->FolderName) { - // Not supported by Outlook/Hotmail/Office365 + /** + * There's a long standing IMAP CLIENTBUG where STATUS command is executed + * after SELECT/EXAMINE on same folder (it should not). + * So we must unselect the folder to be able to get the APPENDLIMIT and UNSEEN. + */ +/* if ($this->IsSupported('ESEARCH')) { $aResult = $oFolderInfo->getStatusItems(); // SELECT or EXAMINE command then UNSEEN is the message sequence number of the first unseen message $aResult['UNSEEN'] = $this->simpleESearchOrESortHelper(false, 'UNSEEN', ['COUNT'])['COUNT']; return $aResult; } -// $this->FolderUnSelect(); +*/ + $bWritable = $oFolderInfo->IsWritable; + $bReselect = true; + $this->FolderUnSelect(); } - return \count($aStatusItems) - ? $this->SendRequestGetResponse('STATUS', array($this->EscapeString($sFolderName), $aStatusItems)) - ->getStatusFolderInformationResult() - : null; + $aResult = $this->SendRequestGetResponse('STATUS', array($this->EscapeString($sFolderName), $aStatusItems)) + ->getStatusFolderInformationResult(); + if ($bReselect) { + $this->selectOrExamineFolder($sFolderName, $bWritable, false); + } + return $aResult; } /** @@ -461,18 +495,26 @@ class ImapClient extends \MailSo\Net\NetClient $aParameters[] = $this->EscapeString($sParentFolderName); $aParameters[] = $this->EscapeString(\strlen(\trim($sListPattern)) ? $sListPattern : '*'); + // RFC 5819 if ($bUseListStatus && !$bIsSubscribeList && $this->IsSupported('LIST-STATUS')) { - // RFC 5819 $aL = array( Enumerations\FolderStatus::MESSAGES, Enumerations\FolderStatus::UNSEEN, Enumerations\FolderStatus::UIDNEXT ); - + // RFC 4551 if ($this->IsSupported('CONDSTORE')) { $aL[] = Enumerations\FolderStatus::HIGHESTMODSEQ; } + // RFC 7889 + if ($this->IsSupported('APPENDLIMIT')) { + $aL[] = Enumerations\FolderStatus::APPENDLIMIT; + } + // RFC 8474 + if ($this->IsSupported('OBJECTID')) { + $aTypes[] = Enumerations\FolderStatus::MAILBOXID; + } $aReturnParams[] = 'STATUS'; $aReturnParams[] = $aL; @@ -560,11 +602,17 @@ class ImapClient extends \MailSo\Net\NetClient throw new \MailSo\Base\Exceptions\InvalidArgumentException; } + $aParams = array($this->EscapeString($sFolderName)); +/* + if ($this->IsSupported('CONDSTORE')) { + $aParams[] = ['CONDSTORE']; + } +*/ /** * IMAP4rev2 SELECT/EXAMINE are now required to return an untagged LIST response. */ $this->oCurrentFolderInfo = $this->SendRequestGetResponse($bIsWritable ? 'SELECT' : 'EXAMINE', - array($this->EscapeString($sFolderName))) + $aParams) ->getCurrentFolderInformation($sFolderName, $bIsWritable); return $this; diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Traits/Status.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Traits/Status.php index 659a3e9a0..d3be7187b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Traits/Status.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Traits/Status.php @@ -70,6 +70,13 @@ trait Status */ $HIGHESTMODSEQ, + /** + * RFC7889 + * Message upload size limit. + * @var int + */ + $APPENDLIMIT, + /** * RFC8474 * A server-allocated unique identifier for the mailbox. @@ -81,7 +88,7 @@ trait Status public function getStatusItems() : array { return \array_filter(\get_object_vars($this), function($v, $k){ - return isset($v) && \property_exists(__TRAIT__, $k); + return \property_exists(__TRAIT__, $k); }, ARRAY_FILTER_USE_BOTH); } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index 656a727d9..051d6969f 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -519,8 +519,7 @@ class MailClient return $this; } - protected function initFolderValues(string $sFolderName, int &$iCount, int &$iUnseenCount, - int &$iUidNext, int &$iHighestModSeq) : void + protected function initFolderValues(string $sFolderName) : array { $aTypes = array( FolderResponseStatus::MESSAGES, @@ -528,24 +527,31 @@ class MailClient FolderResponseStatus::UIDNEXT ); - if ($this->oImapClient->IsSupported('CONDSTORE')) - { + if ($this->oImapClient->IsSupported('CONDSTORE')) { $aTypes[] = FolderResponseStatus::HIGHESTMODSEQ; } + if ($this->oImapClient->IsSupported('APPENDLIMIT')) { + $aTypes[] = FolderResponseStatus::APPENDLIMIT; + } + if ($this->oImapClient->IsSupported('OBJECTID')) { + $aTypes[] = FolderResponseStatus::MAILBOXID; + } $aFolderStatus = $this->oImapClient->FolderStatus($sFolderName, $aTypes); - $iCount = (int) $aFolderStatus[FolderResponseStatus::MESSAGES] ?? 0; + return [ + $aFolderStatus[FolderResponseStatus::MESSAGES] ?: 0, - $iUnseenCount = (int) $aFolderStatus[FolderResponseStatus::UNSEEN] ?? 0; + $aFolderStatus[FolderResponseStatus::UNSEEN] ?: 0, - $iUidNext = (int) $aFolderStatus[FolderResponseStatus::UIDNEXT] ?? 0; + $aFolderStatus[FolderResponseStatus::UIDNEXT] ?: 0, - if (isset($aFolderStatus[FolderResponseStatus::HIGHESTMODSEQ])) { - $iHighestModSeq = (int) $aFolderStatus[FolderResponseStatus::HIGHESTMODSEQ]; - } else { - $iHighestModSeq = 0; - } + $aFolderStatus[FolderResponseStatus::HIGHESTMODSEQ] ?: 0, + + $aFolderStatus[FolderResponseStatus::APPENDLIMIT] ?: $this->oImapClient->AppendLimit(), + + $aFolderStatus[FolderResponseStatus::MAILBOXID] ?: '' + ]; } public function GenerateImapClientHash() : string @@ -652,12 +658,7 @@ class MailClient } } - $iCount = 0; - $iUnseenCount = 0; - $iUidNext = 0; - $iHighestModSeq = 0; - - $this->initFolderValues($sFolderName, $iCount, $iUnseenCount, $iUidNext, $iHighestModSeq); + list($iCount, $iUnseenCount, $iUidNext, $iHighestModSeq, $iAppendLimit, $sMailboxId) = $this->initFolderValues($sFolderName); return array( 'Folder' => $sFolderName, @@ -667,6 +668,8 @@ class MailClient 'UidNext' => $iUidNext, 'MessageFlags' => $aFlags, 'HighestModSeq' => $iHighestModSeq, + 'AppendLimit' => $iAppendLimit, + 'MailboxId' => $sMailboxId, 'NewMessages' => 'INBOX' === $sFolderName && \MailSo\Config::$CheckNewMessages ? $this->getFolderNextMessageInformation($sFolderName, $iPrevUidNext, $iUidNext) : array() ); @@ -679,12 +682,7 @@ class MailClient */ public function FolderHash(string $sFolderName) : string { - $iCount = 0; - $iUnseenCount = 0; - $iUidNext = 0; - $iHighestModSeq = 0; - - $this->initFolderValues($sFolderName, $iCount, $iUnseenCount, $iUidNext, $iHighestModSeq); + list($iCount, $iUnseenCount, $iUidNext, $iHighestModSeq) = $this->initFolderValues($sFolderName); return $this->GenerateFolderHash($sFolderName, $iCount, $iUidNext, $iHighestModSeq); } @@ -1555,11 +1553,6 @@ class MailClient $mAllSortedUids = null; $mAllThreads = null; - $iMessageRealCount = 0; - $iMessageUnseenCount = 0; - $iUidNext = 0; - $iHighestModSeq = 0; - $bUseSortIfSupported = $bUseSortIfSupported && $this->oImapClient->IsSupported('SORT'); $bUseThreadSortIfSupported = $bUseThreadSortIfSupported ? @@ -1575,7 +1568,7 @@ class MailClient $oCacher = null; } - $this->initFolderValues($sFolderName, $iMessageRealCount, $iMessageUnseenCount, $iUidNext, $iHighestModSeq); + list($iMessageRealCount, $iMessageUnseenCount, $iUidNext, $iHighestModSeq) = $this->initFolderValues($sFolderName); if ($bUseFilter) {