Resolve #744 (not active!!)

This commit is contained in:
the-djmaze 2022-12-07 12:24:31 +01:00
parent 8d1290e2c4
commit 94c3fa464d
12 changed files with 276 additions and 41 deletions

View file

@ -22,4 +22,22 @@ export class AccountModel extends AbstractModel {
});
}
/**
* Imports all mail to main account
*//*
importAll(account) {
rl.app.Remote.streamPerLine(line => {
try {
line = JSON.parse(line);
console.dir(line);
} catch (e) {
// OOPS
}
}, 'AccountImport', {
Action: 'AccountImport',
email: account.email
});
}
*/
}

View file

@ -287,6 +287,7 @@ trait Messages
* @throws \MailSo\RuntimeException
* @throws \MailSo\Net\Exceptions\*
* @throws \MailSo\Imap\Exceptions\*
* $sStoreAction = \MailSo\Imap\Enumerations\StoreAction::ADD_FLAGS_SILENT
*/
public function MessageStoreFlag(SequenceSet $oRange, array $aInputStoreItems, string $sStoreAction) : ?ResponseCollection
{

View file

@ -20,20 +20,14 @@ class Folder
// RFC5258 Response data STATUS items when using LIST-EXTENDED
use Traits\Status;
/**
* @var string
*/
private $sDelimiter;
private ?string $sDelimiter;
/**
* @var array
*/
private $aFlagsLowerCase;
private array $aFlagsLowerCase;
/**
* RFC 5464
*/
private $aMetadata = array();
private array $aMetadata = array();
/**
* @throws \InvalidArgumentException
@ -57,7 +51,7 @@ class Folder
public function setFlags(array $aFlags) : void
{
$this->aFlagsLowerCase = \array_map('strtolower', $aFlags);
$this->aFlagsLowerCase = \array_map('mb_strtolower', $aFlags);
}
public function setDelimiter(?string $sDelimiter) : void

View file

@ -26,8 +26,8 @@ class ImapClient extends \MailSo\Net\NetClient
use Commands\Metadata;
use Commands\Quota;
const
TAG_PREFIX = 'TAG';
public
$TAG_PREFIX = 'TAG';
/**
* @var int
@ -606,7 +606,7 @@ class ImapClient extends \MailSo\Net\NetClient
protected function getCurrentTag() : string
{
return self::TAG_PREFIX.$this->iTagCount;
return $this->TAG_PREFIX.$this->iTagCount;
}
public function EscapeString(?string $sStringForEscape) : string

View file

@ -401,8 +401,7 @@ trait ResponseParser
return false;
}
$rImapLiteralStream =
\MailSo\Base\StreamWrappers\Literal::CreateStream($this->ConnectionResource(), $iLiteralLen);
$rImapLiteralStream = \MailSo\Base\StreamWrappers\Literal::CreateStream($this->ConnectionResource(), $iLiteralLen);
$this->writeLog('Start Callback for '.$sParent.' / '.$sLiteralAtomUpperCase.
' - try to read '.$iLiteralLen.' bytes.', \LOG_INFO);
@ -411,7 +410,7 @@ trait ResponseParser
try
{
$this->aFetchCallbacks[$sFetchKey]($sParent, $sLiteralAtomUpperCase, $rImapLiteralStream);
$this->aFetchCallbacks[$sFetchKey]($sParent, $sLiteralAtomUpperCase, $rImapLiteralStream, $iLiteralLen);
}
catch (\Throwable $oException)
{
@ -445,12 +444,10 @@ trait ResponseParser
\fclose($rImapLiteralStream);
if (0 < $iNotReadLiteralLen) {
$this->writeLog('Not read literal size is '.$iNotReadLiteralLen.' bytes.',
\LOG_WARNING);
$this->writeLog('Not read literal size is '.$iNotReadLiteralLen.' bytes.', \LOG_WARNING);
}
} else {
$this->writeLog('Literal stream is not resource after callback.',
\LOG_WARNING);
$this->writeLog('Literal stream is not resource after callback.', \LOG_WARNING);
}
$this->bRunningCallback = false;

View file

@ -20,20 +20,11 @@ use MailSo\Imap\Enumerations\MetadataKeys;
*/
class Folder implements \JsonSerializable
{
/**
* @var bool
*/
private $bExists;
private bool $bExists;
/**
* @var bool
*/
private $bSubscribed;
private bool $bSubscribed;
/**
* @var \MailSo\Imap\Folder
*/
private $oImapFolder;
private \MailSo\Imap\Folder $oImapFolder;
/**
* @throws \InvalidArgumentException
@ -70,7 +61,7 @@ class Folder implements \JsonSerializable
return $this->oImapFolder->NameRaw();
}
public function Delimiter() : string
public function Delimiter() : ?string
{
return $this->oImapFolder->Delimiter();
}

View file

@ -1096,7 +1096,7 @@ class Actions
if (!$this->MailClient()->IsLoggined()) {
try {
$oAccount->ImapConnectAndLoginHelper($this->oPlugins, $this->MailClient(), $this->oConfig);
$oAccount->ImapConnectAndLoginHelper($this->oPlugins, $this->MailClient()->ImapClient(), $this->oConfig);
} catch (\MailSo\Net\Exceptions\ConnectionException $oException) {
throw new Exceptions\ClientException(Notifications::ConnectionError, $oException);
} catch (\Throwable $oException) {

View file

@ -110,6 +110,47 @@ trait Accounts
return $this->TrueResponse(__FUNCTION__);
}
/**
* Imports all mail from AdditionalAccount into MainAccount
*/
public function DoAccountImport(): array
{
$sEmail = \MailSo\Base\Utils::IdnToAscii(\trim($this->GetActionParam('email', '')), true);
if (!\strlen($sEmail)) {
throw new ClientException(Notifications::AccountDoesNotExist);
}
$oMainAccount = $this->getMainAccountFromToken();
$oLogger = $this->Logger();
$aAccounts = $this->GetAccounts($oMainAccount);
if (!isset($aAccounts[$sEmail])) {
throw new ClientException(Notifications::AccountDoesNotExist);
}
$oAccount = AdditionalAccount::NewInstanceFromTokenArray(
$this, $aAccounts[$sEmail]
);
if (!$oAccount) {
throw new ClientException(Notifications::AccountDoesNotExist);
}
$oImapTarget = new \MailSo\Imap\ImapClient;
$oImapTarget->SetLogger($oLogger);
$this->imapConnect($oMainAccount, false, $oImapTarget);
$oImapSource = new \MailSo\Imap\ImapClient;
$oImapSource->SetLogger($oLogger);
$this->imapConnect($oAccount, false, $oImapSource);
$oSync = new \SnappyMail\Imap\Sync;
$oSync->oImapSource = $oImapSource;
$oSync->oImapTarget = $oImapTarget;
$rootfolder = $this->GetActionParam('rootfolder', '') ?: $sEmail;
$oSync->import($rootfolder);
exit;
}
/**
* @throws \MailSo\RuntimeException
*/

View file

@ -139,7 +139,7 @@ trait UserAuth
}
try {
$this->CheckMailConnection($oAccount, true);
$this->imapConnect($oAccount, true);
if ($bMainAccount) {
$bSignMe && $this->SetSignMeToken($oAccount);
$this->StorageProvider()->Put($oAccount, StorageType::SESSION, Utils::GetSessionToken(), 'true');
@ -188,7 +188,8 @@ trait UserAuth
}
// Test the login
$this->CheckMailConnection($oAccount);
$oImapClient = new \MailSo\Imap\ImapClient;
$this->imapConnect($oAccount, false, $oImapClient);
$this->SetAdditionalAuthToken($oAccount);
return true;
@ -380,7 +381,7 @@ trait UserAuth
if (!$oAccount) {
throw new \RuntimeException('token has no account');
}
$this->CheckMailConnection($oAccount);
$this->imapConnect($oAccount);
// Update lifetime
$this->SetSignMeToken($oAccount);
return $oAccount;
@ -440,10 +441,13 @@ trait UserAuth
/**
* @throws \RainLoop\Exceptions\ClientException
*/
protected function CheckMailConnection(Account $oAccount, bool $bAuthLog = false): void
protected function imapConnect(Account $oAccount, bool $bAuthLog = false, \MailSo\Imap\ImapClient $oImapClient = null): void
{
try {
$oAccount->ImapConnectAndLoginHelper($this->Plugins(), $this->MailClient(), $this->Config());
if (!$oImapClient) {
$oImapClient = $this->MailClient()->ImapClient();
}
$oAccount->ImapConnectAndLoginHelper($this->Plugins(), $oImapClient, $this->Config());
} catch (ClientException $oException) {
throw $oException;
} catch (\MailSo\Net\Exceptions\ConnectionException $oException) {

View file

@ -216,9 +216,8 @@ abstract class Account implements \JsonSerializable
return $oAccount;
}
public function ImapConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Mail\MailClient $oMailClient, \RainLoop\Config\Application $oConfig) : bool
public function ImapConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Imap\ImapClient $oImapClient, \RainLoop\Config\Application $oConfig) : bool
{
$oImapClient = $oMailClient->ImapClient();
$oImapClient->__FORCE_SELECT_ON_EXAMINE__ = !!$oConfig->Get('imap', 'use_force_selection');
$oImapClient->__DISABLE_METADATA = !!$oConfig->Get('imap', 'disable_metadata');

View file

@ -0,0 +1,187 @@
<?php
namespace SnappyMail\Imap;
use MailSo\Imap\Enumerations\FetchType;
use MailSo\Imap\Enumerations\MessageFlag;
use MailSo\Imap\FetchResponse;
use MailSo\Mime\Enumerations\Header;
class Sync
{
public
$oImapSource,
$oImapTarget;
function import(string $sTargetRootFolderName = '')
{
$sParent = '';
$sListPattern = '*';
$this->oImapSource->TAG_PREFIX = 'S';
$this->oImapTarget->TAG_PREFIX = 'T';
// $this->oImapTarget->Logger()->Write('Get oImapTarget->FolderList');
\SnappyMail\Log::notice('SYNC', 'Get oImapTarget->FolderList');
$aTargetFolders = $this->oImapTarget->FolderList($sParent, $sListPattern);
if (!$aTargetFolders) {
return null;
}
$sTargetINBOX = 'INBOX';
$sTargetDelimiter = '';
foreach ($aTargetFolders as $sFullName => $oImapFolder) {
if ($oImapFolder->IsInbox()) {
$sTargetINBOX = $sFullName;
}
if (!$sTargetDelimiter) {
$sTargetDelimiter = $oImapFolder->Delimiter();
}
}
\SnappyMail\Log::notice('SYNC', 'Get oImapSource->FolderList');
$bUseListStatus = $this->oImapSource->IsSupported('LIST-EXTENDED');
$aSourceFolders = $this->oImapSource->FolderList($sParent, $sListPattern, false, $bUseListStatus);
if (!$aSourceFolders) {
return null;
}
$sSourceINBOX = 'INBOX';
\SnappyMail\HTTP\Stream::start();
\SnappyMail\HTTP\Stream::JSON([
'folders' => \count($aSourceFolders)
]);
$fi = 0;
foreach ($aSourceFolders as $sSourceFolderName => $oImapFolder) {
++$fi;
\SnappyMail\HTTP\Stream::JSON([
'index' => $fi,
'folder' => $sSourceFolderName
]);
if ($oImapFolder->IsSelectable()) {
if ($oImapFolder->IsInbox()) {
$sSourceINBOX = $sSourceFolderName;
}
// Set mailbox delimiter
$sTargetFolderName = $sSourceFolderName;
if ($sTargetDelimiter) {
$sTargetFolderName = \str_replace($oImapFolder->Delimiter(), $sTargetDelimiter, $sTargetFolderName);
$sTargetRootFolderName = \str_replace($sTargetDelimiter, '-', $sTargetRootFolderName);
}
if ($sTargetRootFolderName) {
$sTargetFolderName = $sTargetRootFolderName . ($sTargetDelimiter?:'-') . $sTargetFolderName;
}
// Create mailbox if not exists
if (!isset($aTargetFolders[$sTargetFolderName])) {
$this->oImapTarget->FolderCreate($sTargetFolderName);
if (!$bUseListStatus || \in_array('\\subscribed', $oImapFolder->FlagsLowerCase())) {
$this->oImapTarget->FolderSubscribe($sTargetFolderName);
}
} else if (!$aTargetFolders[$sTargetFolderName]->IsSelectable()) {
// 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) {
\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->IsSupported('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
);
}
*/
}
\SnappyMail\HTTP\Stream::JSON([
'index' => $fi,
'message' => $i
]);
}
}
}
}
}
}

View file

@ -22,6 +22,9 @@
<td class="e-action">
<span class="account-name" data-bind="text: displayName"></span>
</td>
<!--
<td><span class="icon-import" data-bind="click: importAll"></span></td>
-->
<td>
<a class="btn btn-small btn-danger button-confirm-delete" data-bind="css: {'delete-access': askDelete}, click: function(oAccount) { $root.deleteAccount(oAccount); }"
data-i18n="GLOBAL/ARE_YOU_SURE"></a>