RFC 5464 Metadata improvements as for #152

This commit is contained in:
djmaze 2021-10-13 10:43:43 +02:00
parent 73eb89b4ee
commit 474c2b9f95
7 changed files with 164 additions and 47 deletions

View file

@ -28,4 +28,13 @@ abstract class FolderType
const FLAGGED = 11; const FLAGGED = 11;
const ARCHIVE = 12; const ARCHIVE = 12;
const ALL = 13; 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;
} }

View file

@ -0,0 +1,39 @@
<?php
/*
* This file is part of MailSo.
*
* (c) 2021 DJMaze
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MailSo\Imap\Enumerations;
/**
* @category MailSo
* @package Imap
* @subpackage Enumerations
*/
abstract class MetadataKeys
{
const
// RFC 5464
ADMIN_SHARED = '/shared/admin', // Server
COMMENT = '/private/comment', // Mailbox
COMMENT_SHARED = '/shared/comment', // Server & Mailbox
// RFC 6154
SPECIALUSE = '/private/specialuse',
// Kolab
KOLAB_CTYPE = '/private/vendor/kolab/folder-type',
KOLAB_CTYPE_SHARED = '/shared/vendor/kolab/folder-type',
KOLAB_COLOR = '/private/vendor/kolab/color',
KOLAB_COLOR_SHARED = '/shared/vendor/kolab/color',
KOLAB_NAME = '/private/vendor/kolab/displayname',
KOLAB_NAME_SHARED = '/shared/vendor/kolab/displayname',
KOLAB_UID_SHARED = '/shared/vendor/kolab/uniqueid',
CYRUS_UID_SHARED = '/shared/vendor/cmu/cyrus-imapd/uniqueid';
}

View file

@ -160,4 +160,12 @@ class Folder
{ {
return isset($this->aMetadata[$sName]) ? $this->aMetadata[$sName] : null; return isset($this->aMetadata[$sName]) ? $this->aMetadata[$sName] : null;
} }
/**
* @return array
*/
public function Metadata() : array
{
return $this->aMetadata;
}
} }

View file

@ -437,7 +437,7 @@ class ImapClient extends \MailSo\Net\NetClient
$sCmd = 'LIST'; $sCmd = 'LIST';
} }
$sListPattern = 0 === strlen(trim($sListPattern)) ? '*' : $sListPattern; $sListPattern = \strlen(\trim($sListPattern)) ? $sListPattern : '*';
$aParameters = array( $aParameters = array(
$this->EscapeString($sParentFolderName), $this->EscapeString($sParentFolderName),
@ -470,7 +470,7 @@ class ImapClient extends \MailSo\Net\NetClient
// RFC 5464 // RFC 5464
if ($this->IsSupported('METADATA')) { if ($this->IsSupported('METADATA')) {
foreach ($aReturn as $oFolder) { 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); $oFolder->SetMetadata($key, $value);
} }
} }
@ -1754,28 +1754,8 @@ class ImapClient extends \MailSo\Net\NetClient
* RFC 5464 * RFC 5464
*/ */
const private function getMetadata(string $sFolderName, array $aEntries, array $aOptions = []) : array
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
{ {
if (!$this->IsSupported('METADATA') && !(!\strlen($sFolderName) && $this->IsSupported('METADATA-SERVER'))) {
return [];
}
$arguments = []; $arguments = [];
if ($aOptions) { if ($aOptions) {
@ -1801,25 +1781,36 @@ class ImapClient extends \MailSo\Net\NetClient
return $this->SendRequestGetResponse('GETMETADATA', $arguments)->getFolderMetadataResult(); return $this->SendRequestGetResponse('GETMETADATA', $arguments)->getFolderMetadataResult();
} }
public function FolderSetMetadata(string $sFolderName, array $aEntries) public function ServerGetMetadata(array $aEntries, array $aOptions = []) : array
{ {
if (!$aEntries) { return $this->IsSupported('METADATA-SERVER')
throw new \MailSo\Base\Exceptions\InvalidArgumentException("Wrong argument for SETMETADATA command"); ? $this->getMetadata('', $aEntries, $aOptions)
} : [];
$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);
} }
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);
}
} }
} }

View file

@ -131,7 +131,9 @@ class ResponseCollection extends \MailSo\Base\Collection
$c = \count($oResponse->ResponseList[3]); $c = \count($oResponse->ResponseList[3]);
for ($i = 0; $i < $c; $i += 2) { for ($i = 0; $i < $c; $i += 2) {
$value = $oResponse->ResponseList[3][$i+1]; $value = $oResponse->ResponseList[3][$i+1];
$aReturn[$oResponse->ResponseList[3][$i]] = ('NIL' === $value) ? null : $value; if ('NIL' !== $value) {
$aReturn[$oResponse->ResponseList[3][$i]] = $value;
}
} }
} }
} }

View file

@ -11,6 +11,8 @@
namespace MailSo\Mail; namespace MailSo\Mail;
use MailSo\Imap\Enumerations\MetadataKeys;
/** /**
* @category MailSo * @category MailSo
* @package Mail * @package Mail
@ -188,6 +190,7 @@ class Folder implements \JsonSerializable
public function GetFolderListType() : int public function GetFolderListType() : int
{ {
$aFlags = $this->oImapFolder->FlagsLowerCase(); $aFlags = $this->oImapFolder->FlagsLowerCase();
// $aFlags[] = \strtolower($this->oImapFolder->GetMetadata(MetadataKeys::SPECIALUSE));
switch (true) switch (true)
{ {
@ -224,9 +227,49 @@ class Folder implements \JsonSerializable
return \MailSo\Imap\Enumerations\FolderType::ALL; 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 \MailSo\Imap\Enumerations\FolderType::USER;
} }
/**
* @return mixed
*/
public function GetMetadata(string $sName) : ?string
{
return $this->oImapFolder->GetMetadata($sName);
}
public function jsonSerialize() public function jsonSerialize()
{ {
return array( return array(
@ -239,7 +282,8 @@ class Folder implements \JsonSerializable
'Subscribed' => $this->bSubscribed, 'Subscribed' => $this->bSubscribed,
'Exists' => $this->bExists, 'Exists' => $this->bExists,
'Selectable' => $this->IsSelectable(), 'Selectable' => $this->IsSelectable(),
'Flags' => $this->FlagsLowerCase() 'Flags' => $this->FlagsLowerCase(),
'Metadata' => $this->oImapFolder->Metadata()
); );
} }
} }

View file

@ -182,7 +182,7 @@ class MailClient
{ {
if (!$bSkipUnsupportedFlag) 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) 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 else
@ -2095,7 +2095,7 @@ class MailClient
if (!$aFolders) if (!$aFolders)
{ {
// TODO: Translate // TODO: Translate
throw new \MailSo\Mail\Exceptions\RuntimeException( throw new Exceptions\RuntimeException(
\strlen($sFolderParentFullNameRaw) \strlen($sFolderParentFullNameRaw)
? 'Cannot create folder in non-existent parent folder.' ? 'Cannot create folder in non-existent parent folder.'
: 'Cannot get folder delimiter.'); : 'Cannot get folder delimiter.');
@ -2115,7 +2115,7 @@ class MailClient
if (\strlen($sDelimiter) && false !== \strpos($sFullNameRawToCreate, $sDelimiter)) if (\strlen($sDelimiter) && false !== \strpos($sFullNameRawToCreate, $sDelimiter))
{ {
// TODO: Translate // TODO: Translate
throw new \MailSo\Mail\Exceptions\RuntimeException( throw new Exceptions\RuntimeException(
'New folder name contains delimiter.'); 'New folder name contains delimiter.');
} }
@ -2161,7 +2161,7 @@ class MailClient
if (!$aFolders) if (!$aFolders)
{ {
// TODO: Translate // 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(); $sDelimiter = $aFolders[0]->Delimiter();
@ -2186,7 +2186,7 @@ class MailClient
if (\strlen($sDelimiter) && false !== \strpos($sNewFolderFullNameRaw, $sDelimiter)) if (\strlen($sDelimiter) && false !== \strpos($sNewFolderFullNameRaw, $sDelimiter))
{ {
// TODO: Translate // 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); $sFolderParentFullNameRaw = false === $iLast ? '' : \substr($sPrevFolderFullNameRaw, 0, $iLast + 1);
@ -2226,7 +2226,7 @@ class MailClient
$aIndexOrUids = $this->oImapClient->MessageSimpleSearch('ALL'); $aIndexOrUids = $this->oImapClient->MessageSimpleSearch('ALL');
if (\count($aIndexOrUids)) if (\count($aIndexOrUids))
{ {
throw new \MailSo\Mail\Exceptions\NonEmptyFolder; throw new Exceptions\NonEmptyFolder;
} }
$this->oImapClient->FolderExamine('INBOX'); $this->oImapClient->FolderExamine('INBOX');
@ -2292,4 +2292,28 @@ class MailClient
return $this; 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));
}
} }