mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-07 16:37:02 +03:00
RFC 5464 Metadata improvements as for #152
This commit is contained in:
parent
73eb89b4ee
commit
474c2b9f95
7 changed files with 164 additions and 47 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue