Support RFC 6855 / RFC 5738 (UTF8)

This commit is contained in:
djmaze 2021-11-22 21:01:30 +01:00
parent c9a497eb9d
commit 299ec7faf8
25 changed files with 249 additions and 505 deletions

View file

@ -299,8 +299,8 @@ class AppUser extends AbstractApp {
} }
]); ]);
} else if (oMoveFolder) { } else if (oMoveFolder) {
this.messagesMoveHelper(sFromFolderFullNameRaw, oMoveFolder.fullNameRaw, aUidForRemove); this.messagesMoveHelper(sFromFolderFullNameRaw, oMoveFolder.fullName, aUidForRemove);
MessageUserStore.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove, oMoveFolder.fullNameRaw); MessageUserStore.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove, oMoveFolder.fullName);
} }
} }
@ -317,12 +317,12 @@ class AppUser extends AbstractApp {
if (oFromFolder && oToFolder) { if (oFromFolder && oToFolder) {
if (undefined === bCopy ? false : !!bCopy) { if (undefined === bCopy ? false : !!bCopy) {
this.messagesCopyHelper(oFromFolder.fullNameRaw, oToFolder.fullNameRaw, aUidForMove); this.messagesCopyHelper(oFromFolder.fullName, oToFolder.fullName, aUidForMove);
} else { } else {
this.messagesMoveHelper(oFromFolder.fullNameRaw, oToFolder.fullNameRaw, aUidForMove); this.messagesMoveHelper(oFromFolder.fullName, oToFolder.fullName, aUidForMove);
} }
MessageUserStore.removeMessagesFromList(oFromFolder.fullNameRaw, aUidForMove, oToFolder.fullNameRaw, bCopy); MessageUserStore.removeMessagesFromList(oFromFolder.fullName, aUidForMove, oToFolder.fullName, bCopy);
return true; return true;
} }
} }
@ -487,12 +487,12 @@ class AppUser extends AbstractApp {
folderFromCache.messageCountUnread(result.MessageUnseenCount); folderFromCache.messageCountUnread(result.MessageUnseenCount);
if (unreadCountChange) { if (unreadCountChange) {
MessageFlagsCache.clearFolder(folderFromCache.fullNameRaw); MessageFlagsCache.clearFolder(folderFromCache.fullName);
} }
if (result.Flags.length) { if (result.Flags.length) {
result.Flags.forEach(flags => result.Flags.forEach(flags =>
MessageFlagsCache.storeByFolderAndUid(folderFromCache.fullNameRaw, flags.Uid.toString(), [ MessageFlagsCache.storeByFolderAndUid(folderFromCache.fullName, flags.Uid.toString(), [
!!flags.IsUnseen, !!flags.IsUnseen,
!!flags.IsFlagged, !!flags.IsFlagged,
!!flags.IsAnswered, !!flags.IsAnswered,
@ -505,15 +505,15 @@ class AppUser extends AbstractApp {
} }
MessageUserStore.initUidNextAndNewMessages( MessageUserStore.initUidNextAndNewMessages(
folderFromCache.fullNameRaw, folderFromCache.fullName,
result.UidNext, result.UidNext,
result.NewMessages result.NewMessages
); );
if (!hash || unreadCountChange || result.Hash !== hash) { if (!hash || unreadCountChange || result.Hash !== hash) {
if (folderFromCache.fullNameRaw === FolderUserStore.currentFolderFullNameRaw()) { if (folderFromCache.fullName === FolderUserStore.currentFolderFullNameRaw()) {
this.reloadMessageList(); this.reloadMessageList();
} else if (getFolderInboxName() === folderFromCache.fullNameRaw) { } else if (getFolderInboxName() === folderFromCache.fullName) {
Remote.messageList(null, {Folder: getFolderInboxName()}, true); Remote.messageList(null, {Folder: getFolderInboxName()}, true);
} }
} }
@ -551,17 +551,17 @@ class AppUser extends AbstractApp {
folder.messageCountUnread(item.MessageUnseenCount); folder.messageCountUnread(item.MessageUnseenCount);
if (unreadCountChange) { if (unreadCountChange) {
MessageFlagsCache.clearFolder(folder.fullNameRaw); MessageFlagsCache.clearFolder(folder.fullName);
} }
if (!hash || item.Hash !== hash) { if (!hash || item.Hash !== hash) {
if (folder.fullNameRaw === FolderUserStore.currentFolderFullNameRaw()) { if (folder.fullName === FolderUserStore.currentFolderFullNameRaw()) {
this.reloadMessageList(); this.reloadMessageList();
} }
} else if (unreadCountChange } else if (unreadCountChange
&& folder.fullNameRaw === FolderUserStore.currentFolderFullNameRaw() && folder.fullName === FolderUserStore.currentFolderFullNameRaw()
&& MessageUserStore.list.length) { && MessageUserStore.list.length) {
this.folderInformation(folder.fullNameRaw, MessageUserStore.list()); this.folderInformation(folder.fullName, MessageUserStore.list());
} }
} }
}); });

View file

@ -200,14 +200,14 @@ folderListOptionsBuilder = (
folders.forEach(oItem => { folders.forEach(oItem => {
if (showUnsubscribed || oItem.hasSubscriptions() || !oItem.exists) { if (showUnsubscribed || oItem.hasSubscriptions() || !oItem.exists) {
aResult.push({ aResult.push({
id: oItem.fullNameRaw, id: oItem.fullName,
name: name:
sDeepPrefix.repeat(oItem.deep) + sDeepPrefix.repeat(oItem.deep) +
fRenameCallback(oItem), fRenameCallback(oItem),
system: false, system: false,
disabled: !bNoSelectSelectable && ( disabled: !bNoSelectSelectable && (
!oItem.selectable() || !oItem.selectable() ||
aDisabled.includes(oItem.fullNameRaw) || aDisabled.includes(oItem.fullName) ||
fDisableCallback(oItem)) fDisableCallback(oItem))
}); });
} }

View file

@ -145,7 +145,7 @@ ko.bindingHandlers.dropmessages = {
if ('messages' === getDragAction(e) && ['move','copy'].includes(e.dataTransfer.effectAllowed)) { if ('messages' === getDragAction(e) && ['move','copy'].includes(e.dataTransfer.effectAllowed)) {
let data = dragData.data; let data = dragData.data;
if (folder && data && data.folder && isArray(data.uids)) { if (folder && data && data.folder && isArray(data.uids)) {
rl.app.moveMessagesToFolder(data.folder, data.uids, folder.fullNameRaw, data.copy && e.ctrlKey); rl.app.moveMessagesToFolder(data.folder, data.uids, folder.fullName, data.copy && e.ctrlKey);
} }
} }
}); });

View file

@ -69,7 +69,7 @@ export class FolderCollectionModel extends AbstractCollectionModel
} }
return super.reviveFromJson(object, oFolder => { return super.reviveFromJson(object, oFolder => {
let oCacheFolder = Cache.getFolderFromCacheList(oFolder.FullNameRaw); let oCacheFolder = Cache.getFolderFromCacheList(oFolder.FullName);
if (oCacheFolder) { if (oCacheFolder) {
oFolder.SubFolders = FolderCollectionModel.reviveFromJson(oFolder.SubFolders); oFolder.SubFolders = FolderCollectionModel.reviveFromJson(oFolder.SubFolders);
@ -79,14 +79,14 @@ export class FolderCollectionModel extends AbstractCollectionModel
if (!oCacheFolder) if (!oCacheFolder)
return null; return null;
if (1 == SystemFolders.indexOf(oFolder.FullNameRaw)) { if (1 == SystemFolders.indexOf(oFolder.FullName)) {
oCacheFolder.type(FolderType.Inbox); oCacheFolder.type(FolderType.Inbox);
Cache.setFolderInboxName(oFolder.FullNameRaw); Cache.setFolderInboxName(oFolder.FullName);
} }
Cache.setFolder(oCacheFolder.fullNameHash, oFolder.FullNameRaw, oCacheFolder); Cache.setFolder(oCacheFolder.fullNameHash, oFolder.FullName, oCacheFolder);
} }
let type = SystemFolders.indexOf(oFolder.FullNameRaw); let type = SystemFolders.indexOf(oFolder.FullName);
if (1 < type) { if (1 < type) {
oCacheFolder.type(type); oCacheFolder.type(type);
} }
@ -97,7 +97,7 @@ export class FolderCollectionModel extends AbstractCollectionModel
if (oFolder.Extended) { if (oFolder.Extended) {
if (oFolder.Extended.Hash) { if (oFolder.Extended.Hash) {
Cache.setFolderHash(oCacheFolder.fullNameRaw, oFolder.Extended.Hash); Cache.setFolderHash(oCacheFolder.fullName, oFolder.Extended.Hash);
} }
if (null != oFolder.Extended.MessageCount) { if (null != oFolder.Extended.MessageCount) {
@ -196,7 +196,6 @@ export class FolderModel extends AbstractModel {
super(); super();
this.fullName = ''; this.fullName = '';
this.fullNameRaw = '';
this.fullNameHash = ''; this.fullNameHash = '';
this.delimiter = ''; this.delimiter = '';
this.namespace = ''; this.namespace = '';
@ -244,7 +243,7 @@ export class FolderModel extends AbstractModel {
static reviveFromJson(json) { static reviveFromJson(json) {
const folder = super.reviveFromJson(json); const folder = super.reviveFromJson(json);
if (folder) { if (folder) {
folder.deep = json.FullNameRaw.split(folder.delimiter).length - 1; folder.deep = json.FullName.split(folder.delimiter).length - 1;
let type = (folder.metadata[FolderMetadataKeys.KolabFolderType] let type = (folder.metadata[FolderMetadataKeys.KolabFolderType]
|| folder.metadata[FolderMetadataKeys.KolabFolderTypeShared] || folder.metadata[FolderMetadataKeys.KolabFolderTypeShared]

View file

@ -19,10 +19,19 @@ import { AbstractFetchRemote } from 'Remote/AbstractFetch';
import { FolderCollectionModel } from 'Model/FolderCollection'; import { FolderCollectionModel } from 'Model/FolderCollection';
const urlSafeJSON = data => btoa(JSON.stringify(data)) // unescape(encodeURIComponent()) makes the UTF-16 DOMString to an UTF-8 string
const urlSafeJSON = data => btoa(unescape(encodeURIComponent(JSON.stringify(data))))
.replace(/\+/g, '-') .replace(/\+/g, '-')
.replace(/\//g, '_') .replace(/\//g, '_')
.replace(/=+$/, ''); .replace(/=+$/, '');
/* Withous deprecated 'unescape':
const urlSafeJSON = data => btoa(encodeURIComponent(JSON.stringify(data)).replace(
/%([0-9A-F]{2})/g, (match, p1) => String.fromCharCode('0x' + p1)
))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
*/
class RemoteUserFetch extends AbstractFetchRemote { class RemoteUserFetch extends AbstractFetchRemote {

View file

@ -73,11 +73,11 @@ export class FoldersUserSettings /*extends AbstractViewSettings*/ {
Local.set(ClientSideKeyName.FoldersLashHash, ''); Local.set(ClientSideKeyName.FoldersLashHash, '');
rl.app.foldersPromisesActionHelper( rl.app.foldersPromisesActionHelper(
Remote.folderRename(folder.fullNameRaw, nameToEdit), Remote.folderRename(folder.fullName, nameToEdit),
Notification.CantRenameFolder Notification.CantRenameFolder
); );
removeFolderFromCacheList(folder.fullNameRaw); removeFolderFromCacheList(folder.fullName);
folder.name(nameToEdit); folder.name(nameToEdit);
} }
@ -120,11 +120,11 @@ export class FoldersUserSettings /*extends AbstractViewSettings*/ {
// rl.app.foldersPromisesActionHelper // rl.app.foldersPromisesActionHelper
Remote.abort('Folders') Remote.abort('Folders')
.folderDelete(folderToRemove.fullNameRaw) .folderDelete(folderToRemove.fullName)
.then( .then(
() => { () => {
folderToRemove.selectable(false) folderToRemove.selectable(false)
removeFolderFromCacheList(folderToRemove.fullNameRaw); removeFolderFromCacheList(folderToRemove.fullName);
FolderUserStore.folderList(FolderUserStore.folderList.filter(folder => folder !== folderToRemove)); FolderUserStore.folderList(FolderUserStore.folderList.filter(folder => folder !== folderToRemove));
}, },
error => { error => {
@ -143,20 +143,20 @@ export class FoldersUserSettings /*extends AbstractViewSettings*/ {
toggleFolderKolabType(folder, event) { toggleFolderKolabType(folder, event) {
let type = event.target.value; let type = event.target.value;
// TODO: append '.default' ? // TODO: append '.default' ?
Remote.folderSetMetadata(null, folder.fullNameRaw, FolderMetadataKeys.KolabFolderType, type); Remote.folderSetMetadata(null, folder.fullName, FolderMetadataKeys.KolabFolderType, type);
folder.kolabType(type); folder.kolabType(type);
} }
toggleFolderSubscription(folder) { toggleFolderSubscription(folder) {
let subscribe = !folder.subscribed(); let subscribe = !folder.subscribed();
Local.set(ClientSideKeyName.FoldersLashHash, ''); Local.set(ClientSideKeyName.FoldersLashHash, '');
Remote.folderSetSubscribe(null, folder.fullNameRaw, subscribe); Remote.folderSetSubscribe(null, folder.fullName, subscribe);
folder.subscribed(subscribe); folder.subscribed(subscribe);
} }
toggleFolderCheckable(folder) { toggleFolderCheckable(folder) {
let checkable = !folder.checkable(); let checkable = !folder.checkable();
Remote.folderSetCheckable(null, folder.fullNameRaw, checkable); Remote.folderSetCheckable(null, folder.fullName, checkable);
folder.checkable(checkable); folder.checkable(checkable);
} }
} }

View file

@ -58,7 +58,7 @@ export const FolderUserStore = new class {
draftFolderNotEnabled: () => !self.draftFolder() || UNUSED_OPTION_VALUE === self.draftFolder(), draftFolderNotEnabled: () => !self.draftFolder() || UNUSED_OPTION_VALUE === self.draftFolder(),
currentFolderFullNameRaw: () => (self.currentFolder() ? self.currentFolder().fullNameRaw : ''), currentFolderFullNameRaw: () => (self.currentFolder() ? self.currentFolder().fullName : ''),
currentFolderFullName: () => (self.currentFolder() ? self.currentFolder().fullName : ''), currentFolderFullName: () => (self.currentFolder() ? self.currentFolder().fullName : ''),
currentFolderFullNameHash: () => (self.currentFolder() ? self.currentFolder().fullNameHash : ''), currentFolderFullNameHash: () => (self.currentFolder() ? self.currentFolder().fullNameHash : ''),
@ -136,7 +136,7 @@ export const FolderUserStore = new class {
timeout > folder.expires && timeout > folder.expires &&
(folder.isSystemFolder() || (folder.subscribed() && (folder.checkable() || !bDisplaySpecSetting))) (folder.isSystemFolder() || (folder.subscribed() && (folder.checkable() || !bDisplaySpecSetting)))
) { ) {
timeouts.push([folder.expires, folder.fullNameRaw]); timeouts.push([folder.expires, folder.fullName]);
} }
if (folder && folder.subFolders.length) { if (folder && folder.subFolders.length) {

View file

@ -308,7 +308,7 @@ export const MessageUserStore = new class {
} }
if (toFolder) { if (toFolder) {
if (trashFolder === toFolder.fullNameRaw || spamFolder === toFolder.fullNameRaw) { if (trashFolder === toFolder.fullName || spamFolder === toFolder.fullName) {
unseenCount = 0; unseenCount = 0;
} }
@ -661,13 +661,13 @@ export const MessageUserStore = new class {
if (null != collection.MessageUnseenCount) { if (null != collection.MessageUnseenCount) {
if (pInt(folder.messageCountUnread()) !== pInt(collection.MessageUnseenCount)) { if (pInt(folder.messageCountUnread()) !== pInt(collection.MessageUnseenCount)) {
unreadCountChange = true; unreadCountChange = true;
MessageFlagsCache.clearFolder(folder.fullNameRaw); MessageFlagsCache.clearFolder(folder.fullName);
} }
folder.messageCountUnread(collection.MessageUnseenCount); folder.messageCountUnread(collection.MessageUnseenCount);
} }
this.initUidNextAndNewMessages(folder.fullNameRaw, collection.UidNext, collection.NewMessages); this.initUidNextAndNewMessages(folder.fullName, collection.UidNext, collection.NewMessages);
} }
this.listCount(iCount); this.listCount(iCount);
@ -688,7 +688,7 @@ export const MessageUserStore = new class {
clearNewMessageCache(); clearNewMessageCache();
if (folder && (cached || unreadCountChange || SettingsUserStore.useThreads())) { if (folder && (cached || unreadCountChange || SettingsUserStore.useThreads())) {
rl.app.folderInformation(folder.fullNameRaw, collection); rl.app.folderInformation(folder.fullName, collection);
} }
} else { } else {
this.listCount(0); this.listCount(0);

View file

@ -52,7 +52,7 @@ class FolderClearPopupView extends AbstractViewPopup {
folderToClear.messageCountAll(0); folderToClear.messageCountAll(0);
folderToClear.messageCountUnread(0); folderToClear.messageCountUnread(0);
setFolderHash(folderToClear.fullNameRaw, ''); setFolderHash(folderToClear.fullName, '');
Remote.folderClear(iError => { Remote.folderClear(iError => {
this.clearingProcess(false); this.clearingProcess(false);
@ -62,7 +62,7 @@ class FolderClearPopupView extends AbstractViewPopup {
rl.app.reloadMessageList(true); rl.app.reloadMessageList(true);
this.cancelCommand(); this.cancelCommand();
} }
}, folderToClear.fullNameRaw); }, folderToClear.fullName);
} }
} }

View file

@ -30,7 +30,7 @@ class FolderCreatePopupView extends AbstractViewPopup {
oItem => oItem =>
oItem ? (oItem.isSystemFolder() ? oItem.name() + ' ' + oItem.manageFolderSystemName() : oItem.name()) : '', oItem ? (oItem.isSystemFolder() ? oItem.name() + ' ' + oItem.manageFolderSystemName() : oItem.name()) : '',
FolderUserStore.namespace FolderUserStore.namespace
? item => FolderUserStore.namespace !== item.fullNameRaw.substr(0, FolderUserStore.namespace.length) ? item => FolderUserStore.namespace !== item.fullName.substr(0, FolderUserStore.namespace.length)
: null, : null,
true true
) )

View file

@ -88,7 +88,7 @@ export class MailFolderList extends AbstractViewLeft {
rl.app.moveMessagesToFolder( rl.app.moveMessagesToFolder(
FolderUserStore.currentFolderFullNameRaw(), FolderUserStore.currentFolderFullNameRaw(),
MessageUserStore.listCheckedOrSelectedUidsWithSubMails(), MessageUserStore.listCheckedOrSelectedUidsWithSubMails(),
folder.fullNameRaw, folder.fullName,
event.ctrlKey event.ctrlKey
); );
} else { } else {
@ -96,8 +96,8 @@ export class MailFolderList extends AbstractViewLeft {
MessageUserStore.message(null); MessageUserStore.message(null);
} }
if (folder.fullNameRaw === FolderUserStore.currentFolderFullNameRaw()) { if (folder.fullName === FolderUserStore.currentFolderFullNameRaw()) {
setFolderHash(folder.fullNameRaw, ''); setFolderHash(folder.fullName, '');
} }
rl.route.setHash( rl.route.setHash(

View file

@ -1374,239 +1374,16 @@ END;
public static function Utf7ModifiedToUtf8(string $sStr) : string public static function Utf7ModifiedToUtf8(string $sStr) : string
{ {
$aArray = array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, return \is_callable('imap_mutf7_to_utf8')
-1,-1,-1,-1,-1,-1,-1,-1,-1,62, 63,-1,-1,-1,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9, ? \imap_mutf7_to_utf8($sStr)
10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, : \mb_convert_encoding($sStr, 'UTF-8', 'UTF7-IMAP');
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1);
$sResult = '';
$bError = false;
$iLen = \strlen($sStr);
for ($iIndex = 0; $iLen > 0; $iIndex++, $iLen--)
{
$sChar = $sStr[$iIndex];
if ($sChar == '&')
{
$iIndex++;
$iLen--;
$sChar = isset($sStr[$iIndex]) ? $sStr[$iIndex] : null;
if ($sChar === null)
{
break;
}
if ($iLen && $sChar == '-')
{
$sResult .= '&';
continue;
}
$iCh = 0;
$iK = 10;
for (; $iLen > 0; $iIndex++, $iLen--)
{
$sChar = $sStr[$iIndex];
$iB = $aArray[\ord($sChar)];
if ((\ord($sChar) & 0x80) || $iB == -1)
{
break;
}
if ($iK > 0)
{
$iCh |= $iB << $iK;
$iK -= 6;
}
else
{
$iCh |= $iB >> (-$iK);
if ($iCh < 0x80)
{
if (0x20 <= $iCh && $iCh < 0x7f)
{
return $bError;
}
$sResult .= \chr($iCh);
}
else if ($iCh < 0x800)
{
$sResult .= \chr(0xc0 | ($iCh >> 6));
$sResult .= \chr(0x80 | ($iCh & 0x3f));
}
else
{
$sResult .= \chr(0xe0 | ($iCh >> 12));
$sResult .= \chr(0x80 | (($iCh >> 6) & 0x3f));
$sResult .= \chr(0x80 | ($iCh & 0x3f));
}
$iCh = ($iB << (16 + $iK)) & 0xffff;
$iK += 10;
}
}
if (($iCh || $iK < 6) ||
(!$iLen || $sChar != '-') ||
($iLen > 2 && '&' === $sStr[$iIndex+1] && '-' !== $sStr[$iIndex+2]))
{
return $bError;
}
}
else if (\ord($sChar) < 0x20 || \ord($sChar) >= 0x7f)
{
return $bError;
}
else
{
$sResult .= $sChar;
}
}
return $sResult;
} }
public static function Utf8ToUtf7Modified(string $sStr) : string public static function Utf8ToUtf7Modified(string $sStr) : string
{ {
$sArray = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S', return \is_callable('imap_utf8_to_mutf7')
'T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', ? \imap_utf8_to_mutf7($sStr)
'p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9','+',','); : \mb_convert_encoding($sStr, 'UTF7-IMAP', 'UTF-8');
$sLen = \strlen($sStr);
$bIsB = false;
$iIndex = $iN = 0;
$sReturn = '';
$bError = false;
$iCh = $iB = $iK = 0;
while ($sLen)
{
$iC = \ord($sStr[$iIndex]);
if ($iC < 0x80)
{
$iCh = $iC;
$iN = 0;
}
else if ($iC < 0xc2)
{
return $bError;
}
else if ($iC < 0xe0)
{
$iCh = $iC & 0x1f;
$iN = 1;
}
else if ($iC < 0xf0)
{
$iCh = $iC & 0x0f;
$iN = 2;
}
else if ($iC < 0xf8)
{
$iCh = $iC & 0x07;
$iN = 3;
}
else if ($iC < 0xfc)
{
$iCh = $iC & 0x03;
$iN = 4;
}
else if ($iC < 0xfe)
{
$iCh = $iC & 0x01;
$iN = 5;
}
else
{
return $bError;
}
$iIndex++;
$sLen--;
if ($iN > $sLen)
{
return $bError;
}
for ($iJ = 0; $iJ < $iN; $iJ++)
{
$iO = \ord($sStr[$iIndex+$iJ]);
if (($iO & 0xc0) != 0x80)
{
return $bError;
}
$iCh = ($iCh << 6) | ($iO & 0x3f);
}
if ($iN > 1 && !($iCh >> ($iN * 5 + 1)))
{
return $bError;
}
$iIndex += $iN;
$sLen -= $iN;
if ($iCh < 0x20 || $iCh >= 0x7f)
{
if (!$bIsB)
{
$sReturn .= '&';
$bIsB = true;
$iB = 0;
$iK = 10;
}
if ($iCh & ~0xffff)
{
$iCh = 0xfffe;
}
$sReturn .= $sArray[($iB | $iCh >> $iK)];
$iK -= 6;
for (; $iK >= 0; $iK -= 6)
{
$sReturn .= $sArray[(($iCh >> $iK) & 0x3f)];
}
$iB = ($iCh << (-$iK)) & 0x3f;
$iK += 16;
}
else
{
if ($bIsB)
{
if ($iK > 10)
{
$sReturn .= $sArray[$iB];
}
$sReturn .= '-';
$bIsB = false;
}
$sReturn .= \chr($iCh);
if ('&' === \chr($iCh))
{
$sReturn .= '-';
}
}
}
if ($bIsB)
{
if ($iK > 10)
{
$sReturn .= $sArray[$iB];
}
$sReturn .= '-';
}
return $sReturn;
} }
public static function FunctionExistsAndEnabled($mFunctionNameOrNames) : bool public static function FunctionExistsAndEnabled($mFunctionNameOrNames) : bool

View file

@ -45,7 +45,7 @@ trait Metadata
for ($i = 0; $i < $c; $i += 2) { for ($i = 0; $i < $c; $i += 2) {
$aMetadata[$oResponse->ResponseList[3][$i]] = $oResponse->ResponseList[3][$i+1]; $aMetadata[$oResponse->ResponseList[3][$i]] = $oResponse->ResponseList[3][$i+1];
} }
$aReturn[$oResponse->ResponseList[2]] = $aMetadata; $aReturn[$this->toUTF8($oResponse->ResponseList[2])] = $aMetadata;
} }
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
@ -75,7 +75,7 @@ trait Metadata
} }
} }
$arguments[] = $this->EscapeString($sFolderName); $arguments[] = $this->EscapeFolderName($sFolderName);
$arguments[] = '(' . \implode(' ', \array_map([$this, 'EscapeString'], $aEntries)) . ')'; $arguments[] = '(' . \implode(' ', \array_map([$this, 'EscapeString'], $aEntries)) . ')';
@ -118,7 +118,7 @@ trait Metadata
throw new \MailSo\Base\Exceptions\InvalidArgumentException("Wrong argument for SETMETADATA command"); throw new \MailSo\Base\Exceptions\InvalidArgumentException("Wrong argument for SETMETADATA command");
} }
$arguments = [$this->EscapeString($sFolderName)]; $arguments = [$this->EscapeFolderName($sFolderName)];
\array_walk($aEntries, function(&$v, $k){ \array_walk($aEntries, function(&$v, $k){
$v = $this->EscapeString($k) . ' ' . $this->EscapeString($v); $v = $this->EscapeString($k) . ' ' . $this->EscapeString($v);

View file

@ -29,7 +29,7 @@ trait Quota
public function Quota(string $sRootName = '') : ?array public function Quota(string $sRootName = '') : ?array
{ {
return $this->IsSupported('QUOTA') return $this->IsSupported('QUOTA')
? $this->SendRequestGetResponse("GETQUOTA {$this->EscapeString($sRootName)}")->getQuotaResult() ? $this->getQuotaResult($this->SendRequestGetResponse("GETQUOTA {$this->EscapeFolderName($sRootName)}"))
: null; : null;
} }
@ -42,7 +42,45 @@ trait Quota
public function QuotaRoot(string $sFolderName = 'INBOX') : ?array public function QuotaRoot(string $sFolderName = 'INBOX') : ?array
{ {
return $this->IsSupported('QUOTA') return $this->IsSupported('QUOTA')
? $this->SendRequestGetResponse("GETQUOTAROOT {$this->EscapeString($sFolderName)}")->getQuotaResult() ? $this->getQuotaResult($this->SendRequestGetResponse("GETQUOTAROOT {$this->EscapeFolderName($sFolderName)}"))
: null; : null;
} }
private function getQuotaResult(\MailSo\Imap\ResponseCollection $oResponseCollection) : array
{
$aReturn = array(0, 0);
foreach ($oResponseCollection as $oResponse) {
if (Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType
&& 'QUOTA' === $oResponse->StatusOrIndex
&& \is_array($oResponse->ResponseList)
&& isset($oResponse->ResponseList[3])
&& \is_array($oResponse->ResponseList[3])
&& 2 < \count($oResponse->ResponseList[3])
&& 'STORAGE' === \strtoupper($oResponse->ResponseList[3][0])
&& \is_numeric($oResponse->ResponseList[3][1])
&& \is_numeric($oResponse->ResponseList[3][2])
)
{
$aReturn = array(
(int) $oResponse->ResponseList[3][1],
(int) $oResponse->ResponseList[3][2],
0,
0
);
if (5 < \count($oResponse->ResponseList[3])
&& 'MESSAGE' === \strtoupper($oResponse->ResponseList[3][3])
&& \is_numeric($oResponse->ResponseList[3][4])
&& \is_numeric($oResponse->ResponseList[3][5])
)
{
$aReturn[2] = (int) $oResponse->ResponseList[3][4];
$aReturn[3] = (int) $oResponse->ResponseList[3][5];
}
}
}
return $aReturn;
}
} }

View file

@ -23,12 +23,7 @@ class Folder
/** /**
* @var string * @var string
*/ */
private $sNameRaw; private $sFullName;
/**
* @var string
*/
private $sFullNameRaw;
/** /**
* @var string * @var string
@ -48,15 +43,20 @@ class Folder
/** /**
* @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Base\Exceptions\InvalidArgumentException
*/ */
function __construct(string $sFullNameRaw, string $sDelimiter = null, array $aFlags = array()) function __construct(string $sFullName, string $sDelimiter = null, array $aFlags = array())
{ {
if (!\strlen($sFullNameRaw)) { if (!\strlen($sFullName)) {
throw new \MailSo\Base\Exceptions\InvalidArgumentException; throw new \MailSo\Base\Exceptions\InvalidArgumentException;
} }
$this->sFullNameRaw = $sFullNameRaw; $this->sFullName = $sFullName;
$this->setDelimiter($sDelimiter); $this->setDelimiter($sDelimiter);
$this->setFlags($aFlags); $this->setFlags($aFlags);
/*
if (\in_array('\\noutf8', $this->aFlagsLowerCase)) {
}
if (\in_array('\\utf8only', $this->aFlagsLowerCase)) {
}
*/
} }
public function setFlags(array $aFlags) : void public function setFlags(array $aFlags) : void
@ -67,22 +67,21 @@ class Folder
public function setDelimiter(?string $sDelimiter) : void public function setDelimiter(?string $sDelimiter) : void
{ {
$this->sDelimiter = $sDelimiter; $this->sDelimiter = $sDelimiter;
if ($sDelimiter) { }
$aNames = \explode($this->sDelimiter, $this->sFullNameRaw);
$this->sNameRaw = \end($aNames); public function Name() : string
} else { {
$this->sNameRaw = $this->sFullNameRaw; $sNameRaw = $this->sFullName;
if ($this->sDelimiter) {
$aNames = \explode($this->sDelimiter, $sNameRaw);
return \end($aNames);
} }
return $sNameRaw;
} }
public function NameRaw() : string public function FullName() : string
{ {
return $this->sNameRaw; return $this->sFullName;
}
public function FullNameRaw() : string
{
return $this->sFullNameRaw;
} }
public function Delimiter() : ?string public function Delimiter() : ?string
@ -102,7 +101,7 @@ class Folder
public function IsInbox() : bool public function IsInbox() : bool
{ {
return 'INBOX' === \strtoupper($this->sFullNameRaw) || \in_array('\\inbox', $this->aFlagsLowerCase); return 'INBOX' === \strtoupper($this->sFullName) || \in_array('\\inbox', $this->aFlagsLowerCase);
} }
/** /**

View file

@ -66,6 +66,11 @@ class ImapClient extends \MailSo\Net\NetClient
*/ */
public $__FORCE_SELECT_ON_EXAMINE__ = false; public $__FORCE_SELECT_ON_EXAMINE__ = false;
/**
* @var bool
*/
private $UTF8 = false;
function __construct() function __construct()
{ {
\ini_set('xdebug.max_nesting_level', 500); \ini_set('xdebug.max_nesting_level', 500);
@ -248,11 +253,12 @@ class ImapClient extends \MailSo\Net\NetClient
if ($this->IsSupported('IMAP4rev2')) { if ($this->IsSupported('IMAP4rev2')) {
$this->SendRequestGetResponse('ENABLE', array('IMAP4rev1')); $this->SendRequestGetResponse('ENABLE', array('IMAP4rev1'));
} }
// TODO: RFC 6855 */
if ($this->IsSupported('UTF8=ONLY')) { // RFC 6855 || RFC 5738
$this->UTF8 = $this->IsSupported('UTF8=ONLY') || $this->IsSupported('UTF8=ACCEPT');
if ($this->UTF8) {
$this->SendRequestGetResponse('ENABLE', array('UTF8=ACCEPT')); $this->SendRequestGetResponse('ENABLE', array('UTF8=ACCEPT'));
} }
*/
} }
catch (Exceptions\NegativeResponseException $oException) catch (Exceptions\NegativeResponseException $oException)
{ {
@ -377,7 +383,7 @@ class ImapClient extends \MailSo\Net\NetClient
public function FolderCreate(string $sFolderName) : self public function FolderCreate(string $sFolderName) : self
{ {
$this->SendRequestGetResponse('CREATE', $this->SendRequestGetResponse('CREATE',
array($this->EscapeString($sFolderName))); array($this->EscapeFolderName($sFolderName)));
return $this; return $this;
} }
@ -391,7 +397,7 @@ class ImapClient extends \MailSo\Net\NetClient
// Uncomment will work issue #124 ? // Uncomment will work issue #124 ?
// $this->selectOrExamineFolder($sFolderName, true); // $this->selectOrExamineFolder($sFolderName, true);
$this->SendRequestGetResponse('DELETE', $this->SendRequestGetResponse('DELETE',
array($this->EscapeString($sFolderName))); array($this->EscapeFolderName($sFolderName)));
// $this->FolderCheck(); // $this->FolderCheck();
// $this->FolderUnSelect(); // $this->FolderUnSelect();
return $this; return $this;
@ -405,7 +411,7 @@ class ImapClient extends \MailSo\Net\NetClient
public function FolderSubscribe(string $sFolderName) : self public function FolderSubscribe(string $sFolderName) : self
{ {
$this->SendRequestGetResponse('SUBSCRIBE', $this->SendRequestGetResponse('SUBSCRIBE',
array($this->EscapeString($sFolderName))); array($this->EscapeFolderName($sFolderName)));
return $this; return $this;
} }
@ -417,7 +423,7 @@ class ImapClient extends \MailSo\Net\NetClient
public function FolderUnSubscribe(string $sFolderName) : self public function FolderUnSubscribe(string $sFolderName) : self
{ {
$this->SendRequestGetResponse('UNSUBSCRIBE', $this->SendRequestGetResponse('UNSUBSCRIBE',
array($this->EscapeString($sFolderName))); array($this->EscapeFolderName($sFolderName)));
return $this; return $this;
} }
@ -429,8 +435,8 @@ class ImapClient extends \MailSo\Net\NetClient
public function FolderRename(string $sOldFolderName, string $sNewFolderName) : self public function FolderRename(string $sOldFolderName, string $sNewFolderName) : self
{ {
$this->SendRequestGetResponse('RENAME', array( $this->SendRequestGetResponse('RENAME', array(
$this->EscapeString($sOldFolderName), $this->EscapeFolderName($sOldFolderName),
$this->EscapeString($sNewFolderName))); $this->EscapeFolderName($sNewFolderName)));
return $this; return $this;
} }
@ -465,8 +471,14 @@ class ImapClient extends \MailSo\Net\NetClient
$bReselect = true; $bReselect = true;
$this->FolderUnSelect(); $this->FolderUnSelect();
} }
$aResult = $this->SendRequestGetResponse('STATUS', array($this->EscapeString($sFolderName), $aStatusItems))
->getStatusFolderInformationResult(); $oResponseCollection = $this->SendRequestGetResponse('STATUS', array($this->EscapeFolderName($sFolderName), $aStatusItems));
$oInfo = new FolderInformation($sFolderName, false);
foreach ($oResponseCollection as $oResponse) {
$oInfo->setStatusFromResponse($oResponse);
}
$aResult = $oInfo->getStatusItems();
if ($bReselect) { if ($bReselect) {
$this->selectOrExamineFolder($sFolderName, $bWritable, false); $this->selectOrExamineFolder($sFolderName, $bWritable, false);
} }
@ -502,7 +514,7 @@ class ImapClient extends \MailSo\Net\NetClient
} }
} }
$aParameters[] = $this->EscapeString($sParentFolderName); $aParameters[] = $this->EscapeFolderName($sParentFolderName);
$aParameters[] = $this->EscapeString(\strlen(\trim($sListPattern)) ? $sListPattern : '*'); $aParameters[] = $this->EscapeString(\strlen(\trim($sListPattern)) ? $sListPattern : '*');
// RFC 5819 // RFC 5819
@ -529,7 +541,11 @@ class ImapClient extends \MailSo\Net\NetClient
$aReturnParams[] = 'STATUS'; $aReturnParams[] = 'STATUS';
$aReturnParams[] = $aL; $aReturnParams[] = $aL;
} }
/*
if ($this->UTF8) {
$aReturnParams[] = 'UTF8'; // 'UTF8ONLY';
}
*/
if ($aReturnParams) { if ($aReturnParams) {
$aParameters[] = 'RETURN'; $aParameters[] = 'RETURN';
$aParameters[] = $aReturnParams; $aParameters[] = $aReturnParams;
@ -540,7 +556,7 @@ class ImapClient extends \MailSo\Net\NetClient
if ($bPassthru) { if ($bPassthru) {
$this->streamResponse(); $this->streamResponse();
} else { } else {
$aReturn = $this->getResponse()->getFoldersResult($sCmd); $aReturn = $this->getResponse()->getFoldersResult($sCmd, $this);
} }
// RFC 5464 // RFC 5464
@ -549,15 +565,15 @@ class ImapClient extends \MailSo\Net\NetClient
$aMetadata = $this->getAllMetadata(); $aMetadata = $this->getAllMetadata();
if ($aMetadata) { if ($aMetadata) {
foreach ($aReturn as $oFolder) { foreach ($aReturn as $oFolder) {
if (isset($aMetadata[$oFolder->FullNameRaw()])) { if (isset($aMetadata[$oFolder->FullName()])) {
$oFolder->SetAllMetadata($aMetadata[$oFolder->FullNameRaw()]); $oFolder->SetAllMetadata($aMetadata[$oFolder->FullName()]);
} }
} }
} else { } else {
foreach ($aReturn as $oFolder) { foreach ($aReturn as $oFolder) {
try { try {
$oFolder->SetAllMetadata( $oFolder->SetAllMetadata(
$this->getMetadata($oFolder->FullNameRaw(), ['/shared', '/private'], ['DEPTH'=>'infinity']) $this->getMetadata($oFolder->FullName(), ['/shared', '/private'], ['DEPTH'=>'infinity'])
); );
} catch (\Throwable $e) { } catch (\Throwable $e) {
// Ignore error // Ignore error
@ -602,7 +618,7 @@ class ImapClient extends \MailSo\Net\NetClient
*/ */
public function FolderHierarchyDelimiter(string $sFolderName = '') : ?string public function FolderHierarchyDelimiter(string $sFolderName = '') : ?string
{ {
$oResponse = $this->SendRequestGetResponse('LIST', ['""', $this->EscapeString($sFolderName)]); $oResponse = $this->SendRequestGetResponse('LIST', ['""', $this->EscapeFolderName($sFolderName)]);
return ('LIST' === $oResponse[0]->ResponseList[1]) ? $oResponse[0]->ResponseList[3] : null; return ('LIST' === $oResponse[0]->ResponseList[1]) ? $oResponse[0]->ResponseList[3] : null;
} }
@ -628,18 +644,57 @@ class ImapClient extends \MailSo\Net\NetClient
throw new \MailSo\Base\Exceptions\InvalidArgumentException; throw new \MailSo\Base\Exceptions\InvalidArgumentException;
} }
$aParams = array($this->EscapeString($sFolderName)); $aSelectParams = array();
/* /*
if ($this->IsSupported('CONDSTORE')) { if ($this->IsSupported('CONDSTORE')) {
$aParams[] = ['CONDSTORE']; $aSelectParams[] = 'CONDSTORE';
}
if ($this->UTF8) {
$aSelectParams[] = 'UTF8';
} }
*/ */
$aParams = array(
$this->EscapeFolderName($sFolderName)
);
if ($aSelectParams) {
$aParams[] = $aSelectParams;
}
/** /**
* IMAP4rev2 SELECT/EXAMINE are now required to return an untagged LIST response. * IMAP4rev2 SELECT/EXAMINE are now required to return an untagged LIST response.
*/ */
$this->oCurrentFolderInfo = $this->SendRequestGetResponse($bIsWritable ? 'SELECT' : 'EXAMINE', $oResponseCollection = $this->SendRequestGetResponse($bIsWritable ? 'SELECT' : 'EXAMINE', $aParams);
$aParams) $oResult = new FolderInformation($sFolderName, $bIsWritable);
->getCurrentFolderInformation($sFolderName, $bIsWritable); foreach ($oResponseCollection as $oResponse) {
if (Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType) {
if (!$oResult->setStatusFromResponse($oResponse)) {
// OK untagged responses
if (\is_array($oResponse->OptionalResponse)) {
$key = $oResponse->OptionalResponse[0];
if (\count($oResponse->OptionalResponse) > 1) {
if ('PERMANENTFLAGS' === $key && \is_array($oResponse->OptionalResponse[1])) {
$oResult->PermanentFlags = $oResponse->OptionalResponse[1];
}
} else if ('READ-ONLY' === $key) {
// $oResult->IsWritable = false;
} else if ('READ-WRITE' === $key) {
// $oResult->IsWritable = true;
} else if ('NOMODSEQ' === $key) {
// https://datatracker.ietf.org/doc/html/rfc4551#section-3.1.2
}
}
// untagged responses
else if (\count($oResponse->ResponseList) > 2
&& 'FLAGS' === $oResponse->ResponseList[1]
&& \is_array($oResponse->ResponseList[2])) {
$oResult->Flags = $oResponse->ResponseList[2];
}
}
}
}
$this->oCurrentFolderInfo = $oResult;
return $this; return $this;
} }
@ -885,7 +940,7 @@ class ImapClient extends \MailSo\Net\NetClient
} }
$this->SendRequestGetResponse($bIndexIsUid ? 'UID COPY' : 'COPY', $this->SendRequestGetResponse($bIndexIsUid ? 'UID COPY' : 'COPY',
array($sIndexRange, $this->EscapeString($sToFolder))); array($sIndexRange, $this->EscapeFolderName($sToFolder)));
return $this; return $this;
} }
@ -911,7 +966,7 @@ class ImapClient extends \MailSo\Net\NetClient
} }
$this->SendRequestGetResponse($bIndexIsUid ? 'UID MOVE' : 'MOVE', $this->SendRequestGetResponse($bIndexIsUid ? 'UID MOVE' : 'MOVE',
array($sIndexRange, $this->EscapeString($sToFolder))); array($sIndexRange, $this->EscapeFolderName($sToFolder)));
return $this; return $this;
} }
@ -972,7 +1027,7 @@ class ImapClient extends \MailSo\Net\NetClient
*/ */
public function MessageAppendStream(string $sFolderName, $rMessageAppendStream, int $iStreamSize, array $aAppendFlags = null, int &$iUid = null, int $iDateTime = 0) : self public function MessageAppendStream(string $sFolderName, $rMessageAppendStream, int $iStreamSize, array $aAppendFlags = null, int &$iUid = null, int $iDateTime = 0) : self
{ {
$aData = array($this->EscapeString($sFolderName), $aAppendFlags); $aData = array($this->EscapeFolderName($sFolderName), $aAppendFlags);
if (0 < $iDateTime) if (0 < $iDateTime)
{ {
$aData[] = $this->EscapeString(\gmdate('d-M-Y H:i:s', $iDateTime).' +0000'); $aData[] = $this->EscapeString(\gmdate('d-M-Y H:i:s', $iDateTime).' +0000');
@ -1239,4 +1294,14 @@ class ImapClient extends \MailSo\Net\NetClient
return 'UNKNOWN'; return 'UNKNOWN';
} }
public function EscapeFolderName(string $sFolderName) : string
{
return $this->EscapeString($this->UTF8 ? $sFolderName : \MailSo\Base\Utils::Utf8ToUtf7Modified($sFolderName));
}
public function toUTF8(string $sText) : string
{
return $this->UTF8 ? $sText : \MailSo\Base\Utils::Utf7ModifiedToUtf8($sText);
}
} }

View file

@ -20,42 +20,32 @@ class NamespaceResult
/** /**
* @var string * @var string
*/ */
private $sPersonal; private $sPersonal = '';
/** /**
* @var string * @var string
*/ */
private $sPersonalDelimiter; private $sPersonalDelimiter = '';
/** /**
* @var string * @var string
*/ */
private $sOtherUser; private $sOtherUser = '';
/** /**
* @var string * @var string
*/ */
private $sOtherUserDelimiter; private $sOtherUserDelimiter = '';
/** /**
* @var string * @var string
*/ */
private $sShared; private $sShared = '';
/** /**
* @var string * @var string
*/ */
private $sSharedDelimiter; private $sSharedDelimiter = '';
function __construct()
{
$this->sPersonal = '';
$this->sPersonalDelimiter = '';
$this->sOtherUser = '';
$this->sOtherUserDelimiter = '';
$this->sShared = '';
$this->sSharedDelimiter = '';
}
public function InitByImapResponse(\MailSo\Imap\Response $oImapResponse) : self public function InitByImapResponse(\MailSo\Imap\Response $oImapResponse) : self
{ {
@ -103,8 +93,4 @@ class NamespaceResult
return $this->sPersonal; return $this->sPersonal;
} }
public function GetPersonalNamespaceDelimiter() : string
{
return $this->sPersonalDelimiter;
}
} }

View file

@ -88,7 +88,7 @@ class ResponseCollection extends \MailSo\Base\Collection
return $aReturn; return $aReturn;
} }
public function getFoldersResult(string $sStatus) : array public function getFoldersResult(string $sStatus, ImapClient $oImapClient) : array
{ {
$aReturn = array(); $aReturn = array();
@ -99,7 +99,7 @@ class ResponseCollection extends \MailSo\Base\Collection
continue; continue;
} }
if ('STATUS' === $oResponse->StatusOrIndex && isset($oResponse->ResponseList[2])) { if ('STATUS' === $oResponse->StatusOrIndex && isset($oResponse->ResponseList[2])) {
$sFullNameRaw = $oResponse->ResponseList[2]; $sFullNameRaw = $oImapClient->toUTF8($oResponse->ResponseList[2]);
if (!isset($aReturn[$sFullNameRaw])) { if (!isset($aReturn[$sFullNameRaw])) {
$aReturn[$sFullNameRaw] = new Folder($sFullNameRaw); $aReturn[$sFullNameRaw] = new Folder($sFullNameRaw);
} }
@ -108,14 +108,14 @@ class ResponseCollection extends \MailSo\Base\Collection
else if ($sStatus === $oResponse->StatusOrIndex && 5 == \count($oResponse->ResponseList)) { else if ($sStatus === $oResponse->StatusOrIndex && 5 == \count($oResponse->ResponseList)) {
try try
{ {
$sFullNameRaw = $oResponse->ResponseList[4]; $sFullNameRaw = $oImapClient->toUTF8($oResponse->ResponseList[4]);
/** /**
* $oResponse->ResponseList[0] = * * $oResponse->ResponseList[0] = *
* $oResponse->ResponseList[1] = LIST (all) | LSUB (subscribed) * $oResponse->ResponseList[1] = LIST (all) | LSUB (subscribed)
* $oResponse->ResponseList[2] = Flags * $oResponse->ResponseList[2] = Flags
* $oResponse->ResponseList[3] = Delimiter * $oResponse->ResponseList[3] = Delimiter
* $oResponse->ResponseList[4] = FullNameRaw * $oResponse->ResponseList[4] = FullName
*/ */
if (!isset($aReturn[$sFullNameRaw])) { if (!isset($aReturn[$sFullNameRaw])) {
$oFolder = new Folder($sFullNameRaw, $oFolder = new Folder($sFullNameRaw,
@ -220,45 +220,6 @@ class ResponseCollection extends \MailSo\Base\Collection
return $aReturn; return $aReturn;
} }
/**
* Called by selectOrExamineFolder
*/
public function getCurrentFolderInformation(string $sFolderName, bool $bIsWritable) : FolderInformation
{
$oResult = new FolderInformation($sFolderName, $bIsWritable);
foreach ($this as $oResponse) {
if (Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType) {
if (!$oResult->setStatusFromResponse($oResponse)) {
// OK untagged responses
if (\is_array($oResponse->OptionalResponse)) {
$key = $oResponse->OptionalResponse[0];
if (\count($oResponse->OptionalResponse) > 1) {
if ('PERMANENTFLAGS' === $key && \is_array($oResponse->OptionalResponse[1])) {
$oResult->PermanentFlags = $oResponse->OptionalResponse[1];
}
} else if ('READ-ONLY' === $key) {
// $oResult->IsWritable = false;
} else if ('READ-WRITE' === $key) {
// $oResult->IsWritable = true;
} else if ('NOMODSEQ' === $key) {
// https://datatracker.ietf.org/doc/html/rfc4551#section-3.1.2
}
}
// untagged responses
else if (\count($oResponse->ResponseList) > 2) {
if ('FLAGS' === $oResponse->ResponseList[1] && \is_array($oResponse->ResponseList[2]))
{
$oResult->Flags = $oResponse->ResponseList[2];
}
}
}
}
}
return $oResult;
}
public function getNamespaceResult() : NamespaceResult public function getNamespaceResult() : NamespaceResult
{ {
foreach ($this as $oResponse) { foreach ($this as $oResponse) {
@ -273,43 +234,6 @@ class ResponseCollection extends \MailSo\Base\Collection
throw new Exceptions\ResponseException; throw new Exceptions\ResponseException;
} }
public function getQuotaResult() : array
{
$aReturn = array(0, 0);
foreach ($this as $oResponse) {
if (Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType
&& 'QUOTA' === $oResponse->StatusOrIndex
&& \is_array($oResponse->ResponseList)
&& isset($oResponse->ResponseList[3])
&& \is_array($oResponse->ResponseList[3])
&& 2 < \count($oResponse->ResponseList[3])
&& 'STORAGE' === \strtoupper($oResponse->ResponseList[3][0])
&& \is_numeric($oResponse->ResponseList[3][1])
&& \is_numeric($oResponse->ResponseList[3][2])
)
{
$aReturn = array(
(int) $oResponse->ResponseList[3][1],
(int) $oResponse->ResponseList[3][2],
0,
0
);
if (5 < \count($oResponse->ResponseList[3])
&& 'MESSAGE' === \strtoupper($oResponse->ResponseList[3][3])
&& \is_numeric($oResponse->ResponseList[3][4])
&& \is_numeric($oResponse->ResponseList[3][5])
)
{
$aReturn[2] = (int) $oResponse->ResponseList[3][4];
$aReturn[3] = (int) $oResponse->ResponseList[3][5];
}
}
}
return $aReturn;
}
public function getSimpleESearchOrESortResult(string $sRequestTag, bool $bReturnUid) : array public function getSimpleESearchOrESortResult(string $sRequestTag, bool $bReturnUid) : array
{ {
$aResult = array(); $aResult = array();
@ -340,15 +264,6 @@ class ResponseCollection extends \MailSo\Base\Collection
return $aResult; return $aResult;
} }
public function getStatusFolderInformationResult() : array
{
$oInfo = new FolderInformation('', false);
foreach ($this as $oResponse) {
$oInfo->setStatusFromResponse($oResponse);
}
return $oInfo->getStatusItems();
}
/** /**
* @param mixed $mValue * @param mixed $mValue
* *

View file

@ -3,7 +3,7 @@
/* /*
* This file is part of MailSo. * This file is part of MailSo.
* *
* (c) 2014 Usenko Timur * (c) 2021 DJMaze
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.

View file

@ -112,10 +112,10 @@ trait Status
* EXAMINE https://datatracker.ietf.org/doc/html/rfc3501#section-6.3.2 * EXAMINE https://datatracker.ietf.org/doc/html/rfc3501#section-6.3.2
* STATUS https://datatracker.ietf.org/doc/html/rfc3501#section-6.3.10 * STATUS https://datatracker.ietf.org/doc/html/rfc3501#section-6.3.10
* *
* getCurrentFolderInformation * selectOrExamineFolder
* ResponseList[2] => EXISTS | RECENT * ResponseList[2] => EXISTS | RECENT
* OptionalResponse[0] => UNSEEN * OptionalResponse[0] => UNSEEN
* getStatusFolderInformationResult * FolderStatus
* OptionalResponse[0] => HIGHESTMODSEQ * OptionalResponse[0] => HIGHESTMODSEQ
* ResponseList[1] => STATUS * ResponseList[1] => STATUS
* getFoldersResult LIST-EXTENDED * getFoldersResult LIST-EXTENDED

View file

@ -20,16 +20,6 @@ use MailSo\Imap\Enumerations\MetadataKeys;
*/ */
class Folder implements \JsonSerializable class Folder implements \JsonSerializable
{ {
/**
* @var string
*/
private $sParentFullNameRaw;
/**
* @var int
*/
private $iNestingLevel;
/** /**
* @var bool * @var bool
*/ */
@ -48,7 +38,7 @@ class Folder implements \JsonSerializable
/** /**
* @var \MailSo\Mail\FolderCollection * @var \MailSo\Mail\FolderCollection
*/ */
private $oSubFolders; private $oSubFolders = null;
/** /**
* @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Base\Exceptions\InvalidArgumentException
@ -56,18 +46,6 @@ class Folder implements \JsonSerializable
function __construct(\MailSo\Imap\Folder $oImapFolder, bool $bSubscribed = true, bool $bExists = true) function __construct(\MailSo\Imap\Folder $oImapFolder, bool $bSubscribed = true, bool $bExists = true)
{ {
$this->oImapFolder = $oImapFolder; $this->oImapFolder = $oImapFolder;
$this->oSubFolders = null;
$aNames = \explode($this->oImapFolder->Delimiter(), $this->oImapFolder->FullNameRaw());
$this->iNestingLevel = \count($aNames);
$this->sParentFullNameRaw = '';
if (1 < $this->iNestingLevel)
{
\array_pop($aNames);
$this->sParentFullNameRaw = \implode($this->oImapFolder->Delimiter(), $aNames);
}
$this->bSubscribed = $bSubscribed || \in_array('\\subscribed', $oImapFolder->FlagsLowerCase()); $this->bSubscribed = $bSubscribed || \in_array('\\subscribed', $oImapFolder->FlagsLowerCase());
$this->bExists = $bExists; $this->bExists = $bExists;
} }
@ -84,16 +62,12 @@ class Folder implements \JsonSerializable
public function Name() : string public function Name() : string
{ {
return \MailSo\Base\Utils::ConvertEncoding($this->NameRaw(), return $this->oImapFolder->Name();
\MailSo\Base\Enumerations\Charset::UTF_7_IMAP,
\MailSo\Base\Enumerations\Charset::UTF_8);
} }
public function FullName() : string public function FullName() : string
{ {
return \MailSo\Base\Utils::ConvertEncoding($this->FullNameRaw(), return $this->oImapFolder->FullName();
\MailSo\Base\Enumerations\Charset::UTF_7_IMAP,
\MailSo\Base\Enumerations\Charset::UTF_8);
} }
public function NameRaw() : string public function NameRaw() : string
@ -101,23 +75,6 @@ class Folder implements \JsonSerializable
return $this->oImapFolder->NameRaw(); return $this->oImapFolder->NameRaw();
} }
public function FullNameRaw() : string
{
return $this->oImapFolder->FullNameRaw();
}
public function ParentFullName() : string
{
return \MailSo\Base\Utils::ConvertEncoding($this->sParentFullNameRaw,
\MailSo\Base\Enumerations\Charset::UTF_7_IMAP,
\MailSo\Base\Enumerations\Charset::UTF_8);
}
public function ParentFullNameRaw() : string
{
return $this->sParentFullNameRaw;
}
public function Delimiter() : string public function Delimiter() : string
{ {
return $this->oImapFolder->Delimiter(); return $this->oImapFolder->Delimiter();
@ -188,7 +145,7 @@ class Folder implements \JsonSerializable
switch (true) switch (true)
{ {
case \in_array('\\inbox', $aFlags) || 'INBOX' === \strtoupper($this->FullNameRaw()): case \in_array('\\inbox', $aFlags) || 'INBOX' === \strtoupper($this->FullName()):
return FolderType::INBOX; return FolderType::INBOX;
case \in_array('\\sent', $aFlags): case \in_array('\\sent', $aFlags):
@ -221,7 +178,7 @@ class Folder implements \JsonSerializable
return FolderType::ALL; return FolderType::ALL;
// TODO // TODO
// case 'Templates' === $this->FullNameRaw(): // case 'Templates' === $this->FullName():
// return FolderType::TEMPLATES; // return FolderType::TEMPLATES;
} }
@ -287,7 +244,7 @@ class Folder implements \JsonSerializable
'MessageUnseenCount' => (int) $aStatus['UNSEEN'], 'MessageUnseenCount' => (int) $aStatus['UNSEEN'],
'UidNext' => (int) $aStatus['UIDNEXT'], 'UidNext' => (int) $aStatus['UIDNEXT'],
// 'Hash' => $this->MailClient()->GenerateFolderHash( // 'Hash' => $this->MailClient()->GenerateFolderHash(
// $this->FullNameRaw(), $aStatus['MESSAGES'], $aStatus['UIDNEXT'], // $this->FullName(), $aStatus['MESSAGES'], $aStatus['UIDNEXT'],
// empty($aStatus['HIGHESTMODSEQ']) ? 0 : $aStatus['HIGHESTMODSEQ']) // empty($aStatus['HIGHESTMODSEQ']) ? 0 : $aStatus['HIGHESTMODSEQ'])
); );
} }
@ -296,7 +253,6 @@ class Folder implements \JsonSerializable
'@Object' => 'Object/Folder', '@Object' => 'Object/Folder',
'Name' => $this->Name(), 'Name' => $this->Name(),
'FullName' => $this->FullName(), 'FullName' => $this->FullName(),
'FullNameRaw' => $this->FullNameRaw(),
'Delimiter' => (string) $this->Delimiter(), 'Delimiter' => (string) $this->Delimiter(),
// 'HasVisibleSubFolders' => $this->HasVisibleSubFolders(), // 'HasVisibleSubFolders' => $this->HasVisibleSubFolders(),
'Subscribed' => $this->bSubscribed, 'Subscribed' => $this->bSubscribed,

View file

@ -57,7 +57,7 @@ class FolderCollection extends \MailSo\Base\Collection
$mResult = null; $mResult = null;
foreach ($this as $oFolder) foreach ($this as $oFolder)
{ {
if ($oFolder->FullNameRaw() === $sFullNameRaw) if ($oFolder->FullName() === $sFullNameRaw)
{ {
$mResult = $oFolder; $mResult = $oFolder;
break; break;
@ -119,7 +119,7 @@ class FolderCollection extends \MailSo\Base\Collection
{ {
foreach ($this as $oItemFolder) foreach ($this as $oItemFolder)
{ {
if (0 === \strpos($oMailFolder->FullNameRaw(), $oItemFolder->FullNameRaw().$oItemFolder->Delimiter())) if (0 === \strpos($oMailFolder->FullName(), $oItemFolder->FullName().$oItemFolder->Delimiter()))
{ {
$oItemFolder->SubFolders(true)->AddWithPositionSearch($oMailFolder); $oItemFolder->SubFolders(true)->AddWithPositionSearch($oMailFolder);
return; return;

View file

@ -1219,7 +1219,7 @@ class MailClient
$aImapSubscribedFoldersHelper = array(); $aImapSubscribedFoldersHelper = array();
foreach ($aSubscribedFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) foreach ($aSubscribedFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder)
{ {
$aImapSubscribedFoldersHelper[] = $oImapFolder->FullNameRaw(); $aImapSubscribedFoldersHelper[] = $oImapFolder->FullName();
} }
} }
catch (\Throwable $oException) catch (\Throwable $oException)
@ -1253,13 +1253,13 @@ class MailClient
foreach ($aFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) foreach ($aFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder)
{ {
$oMailFolder = new Folder($oImapFolder, $oMailFolder = new Folder($oImapFolder,
($bUseListSubscribeStatus && (null === $aImapSubscribedFoldersHelper || \in_array($oImapFolder->FullNameRaw(), $aImapSubscribedFoldersHelper))) ($bUseListSubscribeStatus && (null === $aImapSubscribedFoldersHelper || \in_array($oImapFolder->FullName(), $aImapSubscribedFoldersHelper)))
|| $oImapFolder->IsInbox() || $oImapFolder->IsInbox()
); );
if ($oImapFolder->IsInbox()) { if ($oImapFolder->IsInbox()) {
$sINBOX = $oMailFolder->FullNameRaw(); $sINBOX = $oMailFolder->FullName();
} }
$aSortedByLenImapFolders[$oMailFolder->FullNameRaw()] = $oMailFolder; $aSortedByLenImapFolders[$oMailFolder->FullName()] = $oMailFolder;
} }
// Add NonExistent folders // Add NonExistent folders
@ -1267,7 +1267,7 @@ class MailClient
foreach ($aSortedByLenImapFolders as /* @var $oMailFolder Folder */ $oMailFolder) foreach ($aSortedByLenImapFolders as /* @var $oMailFolder Folder */ $oMailFolder)
{ {
$sDelimiter = $oMailFolder->Delimiter(); $sDelimiter = $oMailFolder->Delimiter();
$aFolderExplode = \explode($sDelimiter, $oMailFolder->FullNameRaw()); $aFolderExplode = \explode($sDelimiter, $oMailFolder->FullName());
if (1 < \count($aFolderExplode)) if (1 < \count($aFolderExplode))
{ {
@ -1418,7 +1418,7 @@ class MailClient
$aSubscribeFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullNameRaw, '*'); $aSubscribeFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullNameRaw, '*');
foreach ($aSubscribeFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) foreach ($aSubscribeFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder)
{ {
$this->oImapClient->FolderUnSubscribe($oFolder->FullNameRaw()); $this->oImapClient->FolderUnSubscribe($oFolder->FullName());
} }
} }
@ -1442,7 +1442,7 @@ class MailClient
foreach ($aSubscribeFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) foreach ($aSubscribeFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder)
{ {
$sFolderFullNameRawForResubscrine = $oFolder->FullNameRaw(); $sFolderFullNameRawForResubscrine = $oFolder->FullName();
if (0 === \strpos($sFolderFullNameRawForResubscrine, $sPrevFolderFullNameRaw)) if (0 === \strpos($sFolderFullNameRawForResubscrine, $sPrevFolderFullNameRaw))
{ {
$sNewFolderFullNameRawForResubscrine = $sNewFolderFullNameRaw. $sNewFolderFullNameRawForResubscrine = $sNewFolderFullNameRaw.

View file

@ -460,7 +460,7 @@ trait Folders
{ {
foreach ($oFolders as $oFolder) foreach ($oFolders as $oFolder)
{ {
$aResult[] = $oFolder->FullNameRaw()."|". $aResult[] = $oFolder->FullName()."|".
implode("|", $oFolder->FlagsLowerCase()).($oFolder->IsSubscribed() ? '1' : '0'); implode("|", $oFolder->FlagsLowerCase()).($oFolder->IsSubscribed() ? '1' : '0');
$oSub = $oFolder->SubFolders(); $oSub = $oFolder->SubFolders();
@ -492,7 +492,7 @@ trait Folders
FolderType::ARCHIVE FolderType::ARCHIVE
))) )))
{ {
$aResult[$iFolderType] = $oFolder->FullNameRaw(); $aResult[$iFolderType] = $oFolder->FullName();
} }
} }
@ -524,7 +524,7 @@ trait Folders
FolderType::ARCHIVE FolderType::ARCHIVE
))) )))
{ {
$aResult[$iFolderType] = $oFolder->FullNameRaw(); $aResult[$iFolderType] = $oFolder->FullName();
} }
} }
} }

View file

@ -373,7 +373,7 @@ trait Response
'MessageUnseenCount' => (int) $aStatus['UNSEEN'], 'MessageUnseenCount' => (int) $aStatus['UNSEEN'],
'UidNext' => (int) $aStatus['UIDNEXT'], 'UidNext' => (int) $aStatus['UIDNEXT'],
'Hash' => $this->MailClient()->GenerateFolderHash( 'Hash' => $this->MailClient()->GenerateFolderHash(
$mResponse->FullNameRaw(), $aStatus['MESSAGES'], $aStatus['UIDNEXT'], $mResponse->FullName(), $aStatus['MESSAGES'], $aStatus['UIDNEXT'],
empty($aStatus['HIGHESTMODSEQ']) ? 0 : $aStatus['HIGHESTMODSEQ']) empty($aStatus['HIGHESTMODSEQ']) ? 0 : $aStatus['HIGHESTMODSEQ'])
); );
} }
@ -394,8 +394,8 @@ trait Response
return \array_merge( return \array_merge(
$mResponse->jsonSerialize(), $mResponse->jsonSerialize(),
array( array(
'FullNameHash' => $this->hashFolderFullName($mResponse->FullNameRaw(), $mResponse->FullName()), 'FullNameHash' => $this->hashFolderFullName($mResponse->FullName(), $mResponse->FullName()),
'Checkable' => \in_array($mResponse->FullNameRaw(), $this->aCheckableFolder), 'Checkable' => \in_array($mResponse->FullName(), $this->aCheckableFolder),
'Extended' => $aExtended, 'Extended' => $aExtended,
'SubFolders' => $this->responseObject($mResponse->SubFolders(), $sParent, $aParameters) 'SubFolders' => $this->responseObject($mResponse->SubFolders(), $sParent, $aParameters)
) )