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 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;
}

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 array
*/
public function Metadata() : array
{
return $this->aMetadata;
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -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()
);
}
}

View file

@ -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));
}
}