File: //usr/local/CyberCP/public/snappymail/snappymail/v/2.38.2/app/libraries/snappymail/imap/sync.php
<?php
namespace SnappyMail\Imap;
use MailSo\Imap\Enumerations\FetchType;
use MailSo\Imap\Enumerations\MessageFlag;
use MailSo\Mime\Enumerations\Header;
class Sync
{
public \MailSo\Imap\ImapClient $oImapSource;
public \MailSo\Imap\ImapClient $oImapTarget;
function import(string $sTargetRootFolderName = '')
{
$sParent = '';
$sListPattern = '*';
$this->oImapSource->TAG_PREFIX = 'S';
$this->oImapTarget->TAG_PREFIX = 'T';
// $this->oImapTarget->logWrite('Get oImapTarget->FolderList');
\SnappyMail\Log::notice('SYNC', 'Get oImapTarget->FolderList');
$aTargetFolders = $this->oImapTarget->FolderList($sParent, $sListPattern);
if (!$aTargetFolders) {
return null;
}
$sTargetDelimiter = '';
$sTargetRoles = [
'inbox' => false,
'sent' => false,
'drafts' => false,
'junk' => false,
'trash' => false,
'archive' => false
];
foreach ($aTargetFolders as $sFullName => $oImapFolder) {
$role = $oImapFolder->Role();
if ($role && empty($sTargetRoles[$role])) {
$sTargetRoles[$role] = $sFullName;
}
if (!$sTargetDelimiter) {
$sTargetDelimiter = $oImapFolder->Delimiter();
}
}
\SnappyMail\Log::notice('SYNC', 'Get oImapSource->FolderList');
$bUseListStatus = $this->oImapSource->hasCapability('LIST-EXTENDED');
$aSourceFolders = $this->oImapSource->FolderList($sParent, $sListPattern, false, $bUseListStatus);
if (!$aSourceFolders) {
return null;
}
$isCli = false !== \stripos(\php_sapi_name(), 'cli');
if ($isCli) {
echo 'folders: ' . \count($aSourceFolders) . "\n";
} else {
\SnappyMail\HTTP\Stream::start();
\SnappyMail\HTTP\Stream::JSON([
'folders' => \count($aSourceFolders)
]);
}
$fi = 0;
\ignore_user_abort(true);
foreach ($aSourceFolders as $sSourceFolderName => $oImapFolder) {
++$fi;
if ($oImapFolder->Selectable()) {
$role = $oImapFolder->Role();
if ('all' === $role) {
// Don't duplicate all mail
continue;
}
// Detect mailbox name based on role
if (!$sTargetRootFolderName && $role && !empty($sTargetRoles[$role])) {
$sTargetFolderName = $sTargetRoles[$role];
}
// Else just do a name match
else {
if ($sTargetDelimiter) {
$sTargetFolderName = \str_replace($oImapFolder->Delimiter(), $sTargetDelimiter, $sSourceFolderName);
$sTargetRootFolderName = \str_replace($sTargetDelimiter, '-', $sTargetRootFolderName);
} else {
$sTargetFolderName = $sSourceFolderName;
}
if ($sTargetRootFolderName) {
$sTargetFolderName = $sTargetRootFolderName . ($sTargetDelimiter?:'-') . $sTargetFolderName;
}
}
if ($isCli) {
echo \str_pad($fi, 3, ' ', STR_PAD_LEFT) . " folder: {$sSourceFolderName} => {$sTargetFolderName}\n";
} else {
\SnappyMail\HTTP\Stream::JSON([
'index' => $fi,
'folder' => $sSourceFolderName,
'target' => $sTargetFolderName
]);
}
// Create mailbox if not exists
if (!isset($aTargetFolders[$sTargetFolderName])) {
$this->oImapTarget->FolderCreate(
$sTargetFolderName,
!$bUseListStatus || $oImapFolder->IsSubscribed()
);
} else if (!$aTargetFolders[$sTargetFolderName]->Selectable()) {
// Can't copy messages
continue;
}
// Set Source metadata on target
if ($aMetadata = $oImapFolder->Metadata()) {
$this->oImapTarget->FolderSetMetadata($sTargetFolderName, $aMetadata);
}
$oSourceInfo = $this->oImapSource->FolderSelect($sSourceFolderName);
if ($oSourceInfo->MESSAGES) {
if ($isCli) {
echo \str_pad($fi, 3, ' ', STR_PAD_LEFT)
. " messages: [" . \str_repeat(' ', 50) . "] 0/{$oSourceInfo->MESSAGES}";
} else {
\SnappyMail\HTTP\Stream::JSON([
'index' => $fi,
'messages' => $oSourceInfo->MESSAGES
]);
}
// All id's to skip from source
$oTargetInfo = $this->oImapTarget->FolderSelect($sTargetFolderName);
if ($oTargetInfo->MESSAGES) {
// Get all existing message id's from target to skip
$aTargetMessageIDs = [];
$this->oImapTarget->SendRequest('FETCH', [
'1:*', [FetchType::BuildBodyCustomHeaderRequest([Header::MESSAGE_ID], true)]
]);
foreach ($this->oImapTarget->yieldUntaggedResponses() as $oResponse) {
if ('FETCH' === $oResponse->ResponseList[2]) {
// $oResponse->ResponseList[3][0] == 'BODY[HEADER.FIELDS (MESSAGE-ID)]'
// 'Message-ID: ...'
$aTargetMessageIDs[] = $oResponse->ResponseList[3][1];
}
}
}
// Set all existing id's from source to skip and get all flags
$aSourceSkipIDs = [];
$aSourceFlags = [];
$this->oImapSource->SendRequest('FETCH', [
'1:*', [FetchType::FLAGS, FetchType::BuildBodyCustomHeaderRequest([Header::MESSAGE_ID], true)]
]);
foreach ($this->oImapSource->yieldUntaggedResponses() as $oResponse) {
if ('FETCH' === $oResponse->ResponseList[2]
&& isset($oResponse->ResponseList[3])
&& \is_array($oResponse->ResponseList[3])
) {
$id = $oResponse->ResponseList[1];
foreach ($oResponse->ResponseList[3] as $i => $mItem) {
if ('FLAGS' === $mItem) {
$aSourceFlags[$id] = $oResponse->ResponseList[3][$i+1];
} else if ('MESSAGE-ID' === $mItem && \in_array($oResponse->ResponseList[3][$i+1], $aTargetMessageIDs)) {
$aSourceSkipIDs[] = $id;
}
}
}
}
$aTargetMessageIDs = [];
// Now copy each message from source to target
for ($i = 1; $i <= $oSourceInfo->MESSAGES; ++$i) {
if (!\in_array($i, $aSourceSkipIDs)) {
$sPeek = $this->oImapSource->hasCapability('BINARY')
? FetchType::BINARY_PEEK
: FetchType::BODY_PEEK;
$iAppendUid = 0;
$aFetchResponse = $this->oImapSource->Fetch(array(
array(
$sPeek.'[]',
function ($sParent, $sLiteralAtomUpperCase, $rLiteralStream, $iLiteralLen)
use ($sTargetFolderName, &$iAppendUid, $aSourceFlags, $i) {
if (\strlen($sLiteralAtomUpperCase) && \is_resource($rLiteralStream) && 'FETCH' === $sParent) {
// $sMessage = \stream_get_contents($rLiteralStream);
$iAppendUid = $this->oImapTarget->MessageAppendStream(
$sTargetFolderName,
$rLiteralStream,
$iLiteralLen,
isset($aSourceFlags[$i]) ? $aSourceFlags[$i] : []
);
}
}
)), $i, false);
/*
$aFlags = $aFetchResponse[0]->GetFetchValue('FLAGS');
$iAppendUid = $this->oImapTarget->MessageAppendStream(
$sTargetFolderName,
$rLiteralStream,
$iLiteralLen,
$aFlags
);
if ($iAppendUid && $aFlags) {
$this->MessageStoreFlag(
new SequenceSet($iAppendUid),
$aFlags,
\MailSo\Imap\Enumerations\StoreAction::ADD_FLAGS_SILENT
);
}
*/
}
if ($isCli) {
// Clear line
echo "\x1b[2K\x1b[1G";
// Echo same line with progress
$p = \floor(50 * $i / $oSourceInfo->MESSAGES);
echo \str_pad($fi, 3, ' ', STR_PAD_LEFT)
. " messages: [" . \str_repeat('=', $p) . \str_repeat(' ', 50 - $p)
. "] {$i}/{$oSourceInfo->MESSAGES}";
} else {
\SnappyMail\HTTP\Stream::JSON([
'index' => $fi,
'message' => $i
]);
}
}
if ($isCli) {
echo "\n";
}
}
} else if ($isCli) {
echo \str_pad($fi, 3, ' ', STR_PAD_LEFT) . " folder: {$sSourceFolderName}\n";
} else {
\SnappyMail\HTTP\Stream::JSON([
'index' => $fi,
'folder' => $sSourceFolderName
]);
}
}
}
}