From 474c2b9f95fe2e0294c19769c0ac3b88cbb8173b Mon Sep 17 00:00:00 2001 From: djmaze Date: Wed, 13 Oct 2021 10:43:43 +0200 Subject: [PATCH] RFC 5464 Metadata improvements as for #152 --- .../MailSo/Imap/Enumerations/FolderType.php | 9 +++ .../MailSo/Imap/Enumerations/MetadataKeys.php | 39 +++++++++++ .../app/libraries/MailSo/Imap/Folder.php | 8 +++ .../app/libraries/MailSo/Imap/ImapClient.php | 67 ++++++++----------- .../MailSo/Imap/ResponseCollection.php | 4 +- .../app/libraries/MailSo/Mail/Folder.php | 46 ++++++++++++- .../app/libraries/MailSo/Mail/MailClient.php | 38 +++++++++-- 7 files changed, 164 insertions(+), 47 deletions(-) create mode 100644 snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/MetadataKeys.php diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderType.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderType.php index 10628b3f0..f3d87be3a 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderType.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/FolderType.php @@ -28,4 +28,13 @@ abstract class FolderType const FLAGGED = 11; const ARCHIVE = 12; const ALL = 13; + + // Kolab + const CONFIGURATION = 20; + const CALENDAR = 21; + const CONTACTS = 22; + const TASKS = 23; + const NOTES = 24; + const FILES = 25; + const JOURNAL = 26; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/MetadataKeys.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/MetadataKeys.php new file mode 100644 index 000000000..961057578 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/MetadataKeys.php @@ -0,0 +1,39 @@ +aMetadata[$sName]) ? $this->aMetadata[$sName] : null; } + + /** + * @return array + */ + public function Metadata() : array + { + return $this->aMetadata; + } } 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 e99626b81..9118fb39e 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 @@ -437,7 +437,7 @@ class ImapClient extends \MailSo\Net\NetClient $sCmd = 'LIST'; } - $sListPattern = 0 === strlen(trim($sListPattern)) ? '*' : $sListPattern; + $sListPattern = \strlen(\trim($sListPattern)) ? $sListPattern : '*'; $aParameters = array( $this->EscapeString($sParentFolderName), @@ -470,7 +470,7 @@ class ImapClient extends \MailSo\Net\NetClient // RFC 5464 if ($this->IsSupported('METADATA')) { foreach ($aReturn as $oFolder) { - foreach ($this->FolderGetMetadata($oFolder->FullNameRaw(), ['/shared', '/private'], ['DEPTH'=>'infinity']) as $key => $value) { + foreach ($this->getMetadata($oFolder->FullNameRaw(), ['/shared', '/private'], ['DEPTH'=>'infinity']) as $key => $value) { $oFolder->SetMetadata($key, $value); } } @@ -1754,28 +1754,8 @@ class ImapClient extends \MailSo\Net\NetClient * RFC 5464 */ - const - ADMIN_SHARED = '/shared/admin', // Server - COMMENT_SHARED = '/shared/comment', // Server & Mailbox - COMMENT_PRIVATE = '/private/comment', // Mailbox - // RFC 6154 - SPECIALUSE_PRIVATE = '/private/specialuse', - // Kolab - KOLAB_CTYPE_KEY_SHARED = '/shared/vendor/kolab/folder-type', - KOLAB_CTYPE_KEY_PRIVATE = '/private/vendor/kolab/folder-type', - KOLAB_COLOR_KEY_SHARED = '/shared/vendor/kolab/color', - KOLAB_COLOR_KEY_PRIVATE = '/private/vendor/kolab/color', - KOLAB_NAME_KEY_SHARED = '/shared/vendor/kolab/displayname', - KOLAB_NAME_KEY_PRIVATE = '/private/vendor/kolab/displayname', - KOLAB_UID_KEY_SHARED = '/shared/vendor/kolab/uniqueid', - CYRUS_UID_KEY_SHARED = '/shared/vendor/cmu/cyrus-imapd/uniqueid'; - - public function FolderGetMetadata(string $sFolderName, array $aEntries, array $aOptions = []) : array + private function getMetadata(string $sFolderName, array $aEntries, array $aOptions = []) : array { - if (!$this->IsSupported('METADATA') && !(!\strlen($sFolderName) && $this->IsSupported('METADATA-SERVER'))) { - return []; - } - $arguments = []; if ($aOptions) { @@ -1801,25 +1781,36 @@ class ImapClient extends \MailSo\Net\NetClient return $this->SendRequestGetResponse('GETMETADATA', $arguments)->getFolderMetadataResult(); } - public function FolderSetMetadata(string $sFolderName, array $aEntries) + public function ServerGetMetadata(array $aEntries, array $aOptions = []) : array { - if (!$aEntries) { - throw new \MailSo\Base\Exceptions\InvalidArgumentException("Wrong argument for SETMETADATA command"); - } - - $arguments = [$this->EscapeString($sFolderName)]; - - \array_walk($aEntries, function(&$v, $k){ - $v = $this->EscapeString($k) . ' ' . $this->EscapeString($v); - }); - $arguments[] = '(' . \implode(' ', $aEntries) . ')'; - - $result = $this->SendRequestGetResponse('SETMETADATA', $arguments); + return $this->IsSupported('METADATA-SERVER') + ? $this->getMetadata('', $aEntries, $aOptions) + : []; } - public function FolderDeleteMetadata($sFolderName, array $aEntries) + public function FolderGetMetadata(string $sFolderName, array $aEntries, array $aOptions = []) : array { - $this->SetMetadata($sFolderName, \array_fill_keys(\array_keys($aEntries), null)); + return $this->IsSupported('METADATA') + ? $this->getMetadata($sFolderName, $aEntries, $aOptions) + : []; + } + + public function FolderSetMetadata(string $sFolderName, array $aEntries) : void + { + if ($this->IsSupported('METADATA')) { + if (!$aEntries) { + throw new \MailSo\Base\Exceptions\InvalidArgumentException("Wrong argument for SETMETADATA command"); + } + + $arguments = [$this->EscapeString($sFolderName)]; + + \array_walk($aEntries, function(&$v, $k){ + $v = $this->EscapeString($k) . ' ' . $this->EscapeString($v); + }); + $arguments[] = '(' . \implode(' ', $aEntries) . ')'; + + $this->SendRequestGetResponse('SETMETADATA', $arguments); + } } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php index d7e37a3a3..932433203 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php @@ -131,7 +131,9 @@ class ResponseCollection extends \MailSo\Base\Collection $c = \count($oResponse->ResponseList[3]); for ($i = 0; $i < $c; $i += 2) { $value = $oResponse->ResponseList[3][$i+1]; - $aReturn[$oResponse->ResponseList[3][$i]] = ('NIL' === $value) ? null : $value; + if ('NIL' !== $value) { + $aReturn[$oResponse->ResponseList[3][$i]] = $value; + } } } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php index 029e5d78d..066c12ee4 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php @@ -11,6 +11,8 @@ namespace MailSo\Mail; +use MailSo\Imap\Enumerations\MetadataKeys; + /** * @category MailSo * @package Mail @@ -188,6 +190,7 @@ class Folder implements \JsonSerializable public function GetFolderListType() : int { $aFlags = $this->oImapFolder->FlagsLowerCase(); +// $aFlags[] = \strtolower($this->oImapFolder->GetMetadata(MetadataKeys::SPECIALUSE)); switch (true) { @@ -224,9 +227,49 @@ class Folder implements \JsonSerializable return \MailSo\Imap\Enumerations\FolderType::ALL; } +/* + // TODO: Kolab + switch ($this->oImapFolder->GetMetadata(MetadataKeys::KOLAB_CTYPE) ?: $this->oImapFolder->GetMetadata(MetadataKeys::KOLAB_CTYPE_SHARED)) { + { + case 'event': + return \MailSo\Imap\Enumerations\FolderType::CALENDAR; + case 'contact': + return \MailSo\Imap\Enumerations\FolderType::CONTACTS; + case 'task': + return \MailSo\Imap\Enumerations\FolderType::TASKS; + case 'note': + return \MailSo\Imap\Enumerations\FolderType::NOTES; + case 'file': + return \MailSo\Imap\Enumerations\FolderType::FILES; + case 'configuration': + return \MailSo\Imap\Enumerations\FolderType::CONFIGURATION; + case 'journal': + return \MailSo\Imap\Enumerations\FolderType::JOURNAL; + case 'mail.inbox': + return \MailSo\Imap\Enumerations\FolderType::INBOX; + case 'mail.drafts': + return \MailSo\Imap\Enumerations\FolderType::DRAFTS; + case 'mail.sentitems': + return \MailSo\Imap\Enumerations\FolderType::SENT; + case 'mail.wastebasket': + return \MailSo\Imap\Enumerations\FolderType::TRASH; +// case 'mail.outbox': + case 'mail.junkemail': + return \MailSo\Imap\Enumerations\FolderType::JUNK; + } +*/ + return \MailSo\Imap\Enumerations\FolderType::USER; } + /** + * @return mixed + */ + public function GetMetadata(string $sName) : ?string + { + return $this->oImapFolder->GetMetadata($sName); + } + public function jsonSerialize() { return array( @@ -239,7 +282,8 @@ class Folder implements \JsonSerializable 'Subscribed' => $this->bSubscribed, 'Exists' => $this->bExists, 'Selectable' => $this->IsSelectable(), - 'Flags' => $this->FlagsLowerCase() + 'Flags' => $this->FlagsLowerCase(), + 'Metadata' => $this->oImapFolder->Metadata() ); } } 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 ad9fbaeea..346c0d6c2 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 @@ -182,7 +182,7 @@ class MailClient { if (!$bSkipUnsupportedFlag) { - throw new \MailSo\Mail\Exceptions\RuntimeException('Message flag "'.$sMessageFlag.'" is not supported.'); + throw new Exceptions\RuntimeException('Message flag "'.$sMessageFlag.'" is not supported.'); } } @@ -222,7 +222,7 @@ class MailClient { if (!$bSkipUnsupportedFlag) { - throw new \MailSo\Mail\Exceptions\RuntimeException('Message flag "'.$sMessageFlag.'" is not supported.'); + throw new Exceptions\RuntimeException('Message flag "'.$sMessageFlag.'" is not supported.'); } } else @@ -2095,7 +2095,7 @@ class MailClient if (!$aFolders) { // TODO: Translate - throw new \MailSo\Mail\Exceptions\RuntimeException( + throw new Exceptions\RuntimeException( \strlen($sFolderParentFullNameRaw) ? 'Cannot create folder in non-existent parent folder.' : 'Cannot get folder delimiter.'); @@ -2115,7 +2115,7 @@ class MailClient if (\strlen($sDelimiter) && false !== \strpos($sFullNameRawToCreate, $sDelimiter)) { // TODO: Translate - throw new \MailSo\Mail\Exceptions\RuntimeException( + throw new Exceptions\RuntimeException( 'New folder name contains delimiter.'); } @@ -2161,7 +2161,7 @@ class MailClient if (!$aFolders) { // TODO: Translate - throw new \MailSo\Mail\Exceptions\RuntimeException('Cannot '.($bRename?'rename':'move').' non-existent folder.'); + throw new Exceptions\RuntimeException('Cannot '.($bRename?'rename':'move').' non-existent folder.'); } $sDelimiter = $aFolders[0]->Delimiter(); @@ -2186,7 +2186,7 @@ class MailClient if (\strlen($sDelimiter) && false !== \strpos($sNewFolderFullNameRaw, $sDelimiter)) { // TODO: Translate - throw new \MailSo\Mail\Exceptions\RuntimeException('New folder name contains delimiter.'); + throw new Exceptions\RuntimeException('New folder name contains delimiter.'); } $sFolderParentFullNameRaw = false === $iLast ? '' : \substr($sPrevFolderFullNameRaw, 0, $iLast + 1); @@ -2226,7 +2226,7 @@ class MailClient $aIndexOrUids = $this->oImapClient->MessageSimpleSearch('ALL'); if (\count($aIndexOrUids)) { - throw new \MailSo\Mail\Exceptions\NonEmptyFolder; + throw new Exceptions\NonEmptyFolder; } $this->oImapClient->FolderExamine('INBOX'); @@ -2292,4 +2292,28 @@ class MailClient return $this; } + + /** + * RFC 5464 + */ + + public function ServerGetMetadata(array $aEntries, array $aOptions = []) : array + { + return $this->oImapClient->ServerGetMetadata($aEntries, $aOptions); + } + + public function FolderGetMetadata(string $sFolderName, array $aEntries, array $aOptions = []) : array + { + return $this->oImapClient->FolderGetMetadata($sFolderName, $aEntries, $aOptions); + } + + public function FolderSetMetadata(string $sFolderName, array $aEntries) : void + { + $this->oImapClient->FolderSetMetadata($sFolderName, $aEntries); + } + + public function FolderDeleteMetadata($sFolderName, array $aEntries) : void + { + $this->oImapClient->FolderSetMetadata($sFolderName, \array_fill_keys(\array_keys($aEntries), null)); + } }