mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-27 11:09:20 +03:00
Move IMAP response parser to trait for readability
This commit is contained in:
parent
9fbde56e01
commit
e289532699
2 changed files with 629 additions and 598 deletions
|
|
@ -17,16 +17,13 @@ namespace MailSo\Imap;
|
|||
*/
|
||||
class ImapClient extends \MailSo\Net\NetClient
|
||||
{
|
||||
use Traits\ResponseParser;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const TAG_PREFIX = 'TAG';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $iResponseBufParsedPos;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
|
@ -52,11 +49,6 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
*/
|
||||
private $aFetchCallbacks;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bNeedNext = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
|
@ -220,7 +212,7 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
$aR = $this->parseResponseWithValidation();
|
||||
if (\is_array($aR) && \count($aR) && isset($aR[\count($aR) - 1])) {
|
||||
$oR = $aR[\count($aR) - 1];
|
||||
if (\MailSo\Imap\Enumerations\ResponseType::CONTINUATION === $oR->ResponseType) {
|
||||
if (Enumerations\ResponseType::CONTINUATION === $oR->ResponseType) {
|
||||
if (!empty($oR->ResponseList[1]) && preg_match('/^[a-zA-Z0-9=+\/]+$/', $oR->ResponseList[1])) {
|
||||
$this->Logger()->Write(\base64_decode($oR->ResponseList[1]),
|
||||
\MailSo\Log\Enumerations\Type::WARNING);
|
||||
|
|
@ -1123,24 +1115,18 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
$oResult = new ResponseCollection;
|
||||
|
||||
if (\is_resource($this->ConnectionResource())) {
|
||||
$sEndTag = (null === $sEndTag) ? $this->getCurrentTag() : $sEndTag;
|
||||
$sEndTag = $sEndTag ?: $this->getCurrentTag();
|
||||
|
||||
while (true) {
|
||||
$oResponse = new Response;
|
||||
|
||||
$this->partialParseResponseBranch($oResponse);
|
||||
|
||||
if (Enumerations\ResponseType::UNKNOWN === $oResponse->ResponseType) {
|
||||
throw new Exceptions\ResponseNotFoundException;
|
||||
}
|
||||
$oResponse = $this->partialParseResponse($oResponse);
|
||||
$oResult->append($oResponse);
|
||||
|
||||
// RFC 5530
|
||||
if (\is_array($oResponse->OptionalResponse) && 'CLIENTBUG' === $oResponse->OptionalResponse[0]) {
|
||||
if ($sEndTag === $oResponse->Tag && \is_array($oResponse->OptionalResponse) && 'CLIENTBUG' === $oResponse->OptionalResponse[0]) {
|
||||
// The server has detected a client bug.
|
||||
// \error_log("IMAP {$oResponse->OptionalResponse[0]}: {$this->lastCommand}");
|
||||
}
|
||||
|
||||
$oResult->append($oResponse);
|
||||
if ($sEndTag === $oResponse->Tag || Enumerations\ResponseType::CONTINUATION === $oResponse->ResponseType) {
|
||||
if (isset($this->aTagTimeouts[$sEndTag])) {
|
||||
$this->writeLog((\microtime(true) - $this->aTagTimeouts[$sEndTag]).' ('.$sEndTag.')',
|
||||
|
|
@ -1166,583 +1152,6 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
return $oResult;
|
||||
}
|
||||
|
||||
private function skipBracketParse(?Response $oImapResponse) : bool
|
||||
{
|
||||
return $oImapResponse &&
|
||||
$oImapResponse->ResponseType === \MailSo\Imap\Enumerations\ResponseType::UNTAGGED &&
|
||||
(
|
||||
($oImapResponse->StatusOrIndex === 'STATUS' && 2 === \count($oImapResponse->ResponseList)) ||
|
||||
($oImapResponse->StatusOrIndex === 'LIST' && 4 === \count($oImapResponse->ResponseList)) ||
|
||||
($oImapResponse->StatusOrIndex === 'LSUB' && 4 === \count($oImapResponse->ResponseList))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string
|
||||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
*/
|
||||
private function partialParseResponseBranch(?Response $oImapResponse,
|
||||
bool $bTreatAsAtom = false, string $sParentToken = '', string $sOpenBracket = '')
|
||||
{
|
||||
if ($oImapResponse) {
|
||||
$this->iResponseBufParsedPos = 0;
|
||||
$this->bNeedNext = true;
|
||||
}
|
||||
|
||||
$iPos = $this->iResponseBufParsedPos;
|
||||
|
||||
$sPreviousAtomUpperCase = null;
|
||||
$sClosingBracket = ')';
|
||||
$iLiteralLen = 0;
|
||||
$iBufferEndIndex = 0;
|
||||
$iDebugCount = 0;
|
||||
|
||||
$bIsGotoDefault = false;
|
||||
$bIsGotoLiteral = false;
|
||||
$bIsGotoLiteralEnd = false;
|
||||
$bIsGotoAtomBracket = false;
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
|
||||
$bCountOneInited = false;
|
||||
$bCountTwoInited = false;
|
||||
|
||||
$sAtomBuilder = $bTreatAsAtom ? '' : null;
|
||||
$aList = array();
|
||||
if ($oImapResponse)
|
||||
{
|
||||
$aList =& $oImapResponse->ResponseList;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (100000 === ++$iDebugCount)
|
||||
{
|
||||
$this->Logger()->Write('PartialParseOver: '.$iDebugCount, \MailSo\Log\Enumerations\Type::ERROR);
|
||||
}
|
||||
|
||||
if ($this->bNeedNext)
|
||||
{
|
||||
$iPos = 0;
|
||||
$this->getNextBuffer();
|
||||
$this->iResponseBufParsedPos = $iPos;
|
||||
$this->bNeedNext = false;
|
||||
}
|
||||
|
||||
$sChar = null;
|
||||
if ($bIsGotoDefault)
|
||||
{
|
||||
$sChar = 'GOTO_DEFAULT';
|
||||
$bIsGotoDefault = false;
|
||||
}
|
||||
else if ($bIsGotoLiteral)
|
||||
{
|
||||
$bIsGotoLiteral = false;
|
||||
$bIsGotoLiteralEnd = true;
|
||||
|
||||
if ($this->partialResponseLiteralCallbackCallable(
|
||||
$sParentToken, null === $sPreviousAtomUpperCase ? '' : \strtoupper($sPreviousAtomUpperCase), $iLiteralLen))
|
||||
{
|
||||
if (!$bTreatAsAtom)
|
||||
{
|
||||
$aList[] = '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLiteral = '';
|
||||
$iRead = $iLiteralLen;
|
||||
|
||||
while (0 < $iRead)
|
||||
{
|
||||
$sAddRead = \fread($this->ConnectionResource(), $iRead);
|
||||
if (false === $sAddRead)
|
||||
{
|
||||
$sLiteral = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$sLiteral .= $sAddRead;
|
||||
$iRead -= \strlen($sAddRead);
|
||||
|
||||
\MailSo\Base\Utils::ResetTimeLimit();
|
||||
}
|
||||
|
||||
if (false !== $sLiteral)
|
||||
{
|
||||
$iLiteralSize = \strlen($sLiteral);
|
||||
if ($iLiteralLen !== $iLiteralSize)
|
||||
{
|
||||
$this->writeLog('Literal stream read warning "read '.$iLiteralSize.' of '.
|
||||
$iLiteralLen.'" bytes', \MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
|
||||
if (!$bTreatAsAtom)
|
||||
{
|
||||
$aList[] = $sLiteral;
|
||||
|
||||
if (\MailSo\Config::$LogSimpleLiterals)
|
||||
{
|
||||
$this->writeLog('{'.\strlen($sLiteral).'} '.$sLiteral, \MailSo\Log\Enumerations\Type::INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->writeLog('Can\'t read imap stream', \MailSo\Log\Enumerations\Type::NOTE);
|
||||
}
|
||||
|
||||
unset($sLiteral);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if ($bIsGotoLiteralEnd)
|
||||
{
|
||||
$sPreviousAtomUpperCase = null;
|
||||
$this->bNeedNext = true;
|
||||
$bIsGotoLiteralEnd = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if ($bIsGotoAtomBracket)
|
||||
{
|
||||
if ($bTreatAsAtom)
|
||||
{
|
||||
$sAtomBlock = $this->partialParseResponseBranch(null, true,
|
||||
null === $sPreviousAtomUpperCase ? '' : \strtoupper($sPreviousAtomUpperCase),
|
||||
$sOpenBracket);
|
||||
|
||||
$sAtomBuilder .= $sAtomBlock;
|
||||
$iPos = $this->iResponseBufParsedPos;
|
||||
$sAtomBuilder .= $sClosingBracket;
|
||||
}
|
||||
|
||||
$sPreviousAtomUpperCase = null;
|
||||
$bIsGotoAtomBracket = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if ($bIsGotoNotAtomBracket)
|
||||
{
|
||||
$aSubItems = $this->partialParseResponseBranch(null, false,
|
||||
null === $sPreviousAtomUpperCase ? '' : \strtoupper($sPreviousAtomUpperCase),
|
||||
$sOpenBracket);
|
||||
|
||||
$aList[] = $aSubItems;
|
||||
$iPos = $this->iResponseBufParsedPos;
|
||||
$sPreviousAtomUpperCase = null;
|
||||
if ($oImapResponse && $oImapResponse->IsStatusResponse)
|
||||
{
|
||||
$oImapResponse->OptionalResponse = $aSubItems;
|
||||
|
||||
$bIsGotoDefault = true;
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
continue;
|
||||
}
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iBufferEndIndex = \strlen($this->sResponseBuffer) - 3;
|
||||
|
||||
if ($iPos > $iBufferEndIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
$sChar = $this->sResponseBuffer[$iPos];
|
||||
}
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case ']' === $sChar:
|
||||
case ')' === $sChar:
|
||||
if ($this->skipBracketParse($oImapResponse))
|
||||
{
|
||||
$bIsGotoDefault = true;
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$iPos;
|
||||
$sPreviousAtomUpperCase = null;
|
||||
}
|
||||
break 2;
|
||||
case ' ' === $sChar:
|
||||
if ($bTreatAsAtom)
|
||||
{
|
||||
$sAtomBuilder .= ' ';
|
||||
}
|
||||
++$iPos;
|
||||
break;
|
||||
case '[' === $sChar:
|
||||
case '(' === $sChar:
|
||||
$sOpenBracket = $sChar;
|
||||
$sClosingBracket = '[' === $sChar ? ']' : ')';
|
||||
if ($bTreatAsAtom)
|
||||
{
|
||||
$sAtomBuilder .= $sChar;
|
||||
$bIsGotoAtomBracket = true;
|
||||
$this->iResponseBufParsedPos = ++$iPos;
|
||||
}
|
||||
else if ($this->skipBracketParse($oImapResponse))
|
||||
{
|
||||
$sOpenBracket = '';
|
||||
$sClosingBracket = '';
|
||||
$bIsGotoDefault = true;
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bIsGotoNotAtomBracket = true;
|
||||
$this->iResponseBufParsedPos = ++$iPos;
|
||||
}
|
||||
break;
|
||||
case '{' === $sChar:
|
||||
$bIsLiteralParsed = false;
|
||||
$mLiteralEndPos = \strpos($this->sResponseBuffer, '}', $iPos);
|
||||
if (false !== $mLiteralEndPos && $mLiteralEndPos > $iPos)
|
||||
{
|
||||
$sLiteralLenAsString = \substr($this->sResponseBuffer, $iPos + 1, $mLiteralEndPos - $iPos - 1);
|
||||
if (\is_numeric($sLiteralLenAsString))
|
||||
{
|
||||
$iLiteralLen = (int) $sLiteralLenAsString;
|
||||
$bIsLiteralParsed = true;
|
||||
$iPos = $mLiteralEndPos + 3;
|
||||
$bIsGotoLiteral = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$bIsLiteralParsed)
|
||||
{
|
||||
$iPos = $iBufferEndIndex;
|
||||
}
|
||||
$sPreviousAtomUpperCase = null;
|
||||
break;
|
||||
case '"' === $sChar:
|
||||
$bIsQuotedParsed = false;
|
||||
while (true)
|
||||
{
|
||||
$iClosingPos = $iPos + 1;
|
||||
if ($iClosingPos > $iBufferEndIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
$iClosingPos = \strpos($this->sResponseBuffer, '"', $iClosingPos);
|
||||
if (false === $iClosingPos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO
|
||||
$iClosingPosNext = $iClosingPos + 1;
|
||||
if (
|
||||
isset($this->sResponseBuffer[$iClosingPosNext]) &&
|
||||
' ' !== $this->sResponseBuffer[$iClosingPosNext] &&
|
||||
"\r" !== $this->sResponseBuffer[$iClosingPosNext] &&
|
||||
"\n" !== $this->sResponseBuffer[$iClosingPosNext] &&
|
||||
']' !== $this->sResponseBuffer[$iClosingPosNext] &&
|
||||
')' !== $this->sResponseBuffer[$iClosingPosNext]
|
||||
)
|
||||
{
|
||||
++$iClosingPos;
|
||||
continue;
|
||||
}
|
||||
|
||||
$iSlashCount = 0;
|
||||
while ('\\' === $this->sResponseBuffer[$iClosingPos - $iSlashCount - 1])
|
||||
{
|
||||
++$iSlashCount;
|
||||
}
|
||||
|
||||
if ($iSlashCount % 2 == 1)
|
||||
{
|
||||
++$iClosingPos;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $iClosingPos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bIsQuotedParsed = true;
|
||||
if ($bTreatAsAtom)
|
||||
{
|
||||
$sAtomBuilder .= \strtr(
|
||||
\substr($this->sResponseBuffer, $iPos, $iClosingPos - $iPos + 1),
|
||||
array('\\\\' => '\\', '\\"' => '"')
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aList[] = \strtr(
|
||||
\substr($this->sResponseBuffer, $iPos + 1, $iClosingPos - $iPos - 1),
|
||||
array('\\\\' => '\\', '\\"' => '"')
|
||||
);
|
||||
}
|
||||
|
||||
$iPos = $iClosingPos + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bIsQuotedParsed)
|
||||
{
|
||||
$iPos = $iBufferEndIndex;
|
||||
}
|
||||
|
||||
$sPreviousAtomUpperCase = null;
|
||||
break;
|
||||
|
||||
case 'GOTO_DEFAULT' === $sChar:
|
||||
default:
|
||||
$iCharBlockStartPos = $iPos;
|
||||
|
||||
if ($oImapResponse && $oImapResponse->IsStatusResponse)
|
||||
{
|
||||
$iPos = $iBufferEndIndex;
|
||||
|
||||
while ($iPos > $iCharBlockStartPos && $this->sResponseBuffer[$iCharBlockStartPos] === ' ')
|
||||
{
|
||||
++$iCharBlockStartPos;
|
||||
}
|
||||
}
|
||||
|
||||
$bIsAtomDone = false;
|
||||
while (!$bIsAtomDone && ($iPos <= $iBufferEndIndex))
|
||||
{
|
||||
$sCharDef = $this->sResponseBuffer[$iPos];
|
||||
switch (true)
|
||||
{
|
||||
case ('[' === $sCharDef || ']' === $sCharDef || '(' === $sCharDef || ')' === $sCharDef) &&
|
||||
$this->skipBracketParse($oImapResponse):
|
||||
++$iPos;
|
||||
break;
|
||||
case '[' === $sCharDef:
|
||||
if (null === $sAtomBuilder)
|
||||
{
|
||||
$sAtomBuilder = '';
|
||||
}
|
||||
|
||||
$sAtomBuilder .= \substr($this->sResponseBuffer, $iCharBlockStartPos, $iPos - $iCharBlockStartPos + 1);
|
||||
|
||||
++$iPos;
|
||||
$this->iResponseBufParsedPos = $iPos;
|
||||
|
||||
$sListBlock = $this->partialParseResponseBranch(null, true,
|
||||
null === $sPreviousAtomUpperCase ? '' : \strtoupper($sPreviousAtomUpperCase),
|
||||
'[');
|
||||
|
||||
if (null !== $sListBlock)
|
||||
{
|
||||
$sAtomBuilder .= $sListBlock.']';
|
||||
}
|
||||
|
||||
$iPos = $this->iResponseBufParsedPos;
|
||||
$iCharBlockStartPos = $iPos;
|
||||
break;
|
||||
case ' ' === $sCharDef:
|
||||
case ')' === $sCharDef && '(' === $sOpenBracket:
|
||||
case ']' === $sCharDef && '[' === $sOpenBracket:
|
||||
$bIsAtomDone = true;
|
||||
break;
|
||||
default:
|
||||
++$iPos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($iPos > $iCharBlockStartPos || null !== $sAtomBuilder)
|
||||
{
|
||||
$sLastCharBlock = \substr($this->sResponseBuffer, $iCharBlockStartPos, $iPos - $iCharBlockStartPos);
|
||||
if (null === $sAtomBuilder)
|
||||
{
|
||||
$aList[] = $sLastCharBlock;
|
||||
$sPreviousAtomUpperCase = $sLastCharBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sAtomBuilder .= $sLastCharBlock;
|
||||
|
||||
if (!$bTreatAsAtom)
|
||||
{
|
||||
$aList[] = $sAtomBuilder;
|
||||
$sPreviousAtomUpperCase = $sAtomBuilder;
|
||||
$sAtomBuilder = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($oImapResponse)
|
||||
{
|
||||
// if (1 === \count($aList))
|
||||
if (!$bCountOneInited && 1 === \count($aList))
|
||||
// if (isset($aList[0]) && !isset($aList[1])) // fast 1 === \count($aList)
|
||||
{
|
||||
$bCountOneInited = true;
|
||||
|
||||
$oImapResponse->Tag = $aList[0];
|
||||
if ('+' === $oImapResponse->Tag)
|
||||
{
|
||||
$oImapResponse->ResponseType = Enumerations\ResponseType::CONTINUATION;
|
||||
}
|
||||
else if ('*' === $oImapResponse->Tag)
|
||||
{
|
||||
$oImapResponse->ResponseType = Enumerations\ResponseType::UNTAGGED;
|
||||
}
|
||||
else if ($this->getCurrentTag() === $oImapResponse->Tag)
|
||||
{
|
||||
$oImapResponse->ResponseType = Enumerations\ResponseType::TAGGED;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oImapResponse->ResponseType = Enumerations\ResponseType::UNKNOWN;
|
||||
}
|
||||
}
|
||||
// else if (2 === \count($aList))
|
||||
else if (!$bCountTwoInited && 2 === \count($aList))
|
||||
// else if (isset($aList[1]) && !isset($aList[2])) // fast 2 === \count($aList)
|
||||
{
|
||||
$bCountTwoInited = true;
|
||||
|
||||
$oImapResponse->StatusOrIndex = strtoupper($aList[1]);
|
||||
|
||||
if ($oImapResponse->StatusOrIndex == Enumerations\ResponseStatus::OK ||
|
||||
$oImapResponse->StatusOrIndex == Enumerations\ResponseStatus::NO ||
|
||||
$oImapResponse->StatusOrIndex == Enumerations\ResponseStatus::BAD ||
|
||||
$oImapResponse->StatusOrIndex == Enumerations\ResponseStatus::BYE ||
|
||||
$oImapResponse->StatusOrIndex == Enumerations\ResponseStatus::PREAUTH)
|
||||
{
|
||||
$oImapResponse->IsStatusResponse = true;
|
||||
}
|
||||
}
|
||||
else if (Enumerations\ResponseType::CONTINUATION === $oImapResponse->ResponseType)
|
||||
{
|
||||
$oImapResponse->HumanReadable = $sLastCharBlock;
|
||||
}
|
||||
else if ($oImapResponse->IsStatusResponse)
|
||||
{
|
||||
$oImapResponse->HumanReadable = $sLastCharBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->iResponseBufParsedPos = $iPos;
|
||||
|
||||
if (100000 < $iDebugCount)
|
||||
{
|
||||
$this->Logger()->Write('PartialParseOverResult: '.$iDebugCount, \MailSo\Log\Enumerations\Type::ERROR);
|
||||
}
|
||||
|
||||
return $bTreatAsAtom ? $sAtomBuilder : $aList;
|
||||
}
|
||||
|
||||
private function partialResponseLiteralCallbackCallable(string $sParent, string $sLiteralAtomUpperCase, int $iLiteralLen) : bool
|
||||
{
|
||||
if (!$this->aFetchCallbacks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sLiteralAtomUpperCasePeek = '';
|
||||
if (0 === \strpos($sLiteralAtomUpperCase, 'BODY'))
|
||||
{
|
||||
$sLiteralAtomUpperCasePeek = \str_replace('BODY', 'BODY.PEEK', $sLiteralAtomUpperCase);
|
||||
}
|
||||
|
||||
$sFetchKey = '';
|
||||
if (\strlen($sLiteralAtomUpperCasePeek) && isset($this->aFetchCallbacks[$sLiteralAtomUpperCasePeek]))
|
||||
{
|
||||
$sFetchKey = $sLiteralAtomUpperCasePeek;
|
||||
}
|
||||
else if (\strlen($sLiteralAtomUpperCase) && isset($this->aFetchCallbacks[$sLiteralAtomUpperCase]))
|
||||
{
|
||||
$sFetchKey = $sLiteralAtomUpperCase;
|
||||
}
|
||||
|
||||
if (empty($this->aFetchCallbacks[$sFetchKey]) || !\is_callable($this->aFetchCallbacks[$sFetchKey])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rImapLiteralStream =
|
||||
\MailSo\Base\StreamWrappers\Literal::CreateStream($this->ConnectionResource(), $iLiteralLen);
|
||||
|
||||
$this->writeLog('Start Callback for '.$sParent.' / '.$sLiteralAtomUpperCase.
|
||||
' - try to read '.$iLiteralLen.' bytes.', \MailSo\Log\Enumerations\Type::NOTE);
|
||||
|
||||
$this->bRunningCallback = true;
|
||||
|
||||
try
|
||||
{
|
||||
\call_user_func($this->aFetchCallbacks[$sFetchKey],
|
||||
$sParent, $sLiteralAtomUpperCase, $rImapLiteralStream);
|
||||
}
|
||||
catch (\Throwable $oException)
|
||||
{
|
||||
$this->writeLog('Callback Exception', \MailSo\Log\Enumerations\Type::NOTICE);
|
||||
$this->writeLogException($oException);
|
||||
}
|
||||
|
||||
if ($rImapLiteralStream)
|
||||
{
|
||||
$iNotReadLiteralLen = 0;
|
||||
|
||||
$bFeof = \feof($rImapLiteralStream);
|
||||
$this->writeLog('End Callback for '.$sParent.' / '.$sLiteralAtomUpperCase.
|
||||
' - feof = '.($bFeof ? 'good' : 'BAD'), $bFeof ?
|
||||
\MailSo\Log\Enumerations\Type::NOTE : \MailSo\Log\Enumerations\Type::WARNING);
|
||||
|
||||
if (!$bFeof)
|
||||
{
|
||||
while (!\feof($rImapLiteralStream))
|
||||
{
|
||||
$sBuf = \fread($rImapLiteralStream, 1024 * 1024);
|
||||
if (false === $sBuf || 0 === \strlen($sBuf) || null === $sBuf)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
\MailSo\Base\Utils::ResetTimeLimit();
|
||||
$iNotReadLiteralLen += \strlen($sBuf);
|
||||
}
|
||||
|
||||
if (!\feof($rImapLiteralStream))
|
||||
{
|
||||
\stream_get_contents($rImapLiteralStream);
|
||||
}
|
||||
}
|
||||
|
||||
\fclose($rImapLiteralStream);
|
||||
|
||||
if ($iNotReadLiteralLen > 0)
|
||||
{
|
||||
$this->writeLog('Not read literal size is '.$iNotReadLiteralLen.' bytes.',
|
||||
\MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->writeLog('Literal stream is not resource after callback.',
|
||||
\MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
|
||||
$this->bRunningCallback = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function prepareParamLine(array $aParams = array()) : string
|
||||
{
|
||||
$sReturn = '';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,622 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of MailSo.
|
||||
*
|
||||
* (c) 2014 Usenko Timur
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MailSo\Imap\Traits;
|
||||
|
||||
use \MailSo\Imap\Response;
|
||||
use \MailSo\Imap\Enumerations\ResponseStatus;
|
||||
use \MailSo\Imap\Enumerations\ResponseType;
|
||||
use \MailSo\Imap\Exceptions\ResponseNotFoundException;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Imap
|
||||
*/
|
||||
trait ResponseParser
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $iResponseBufParsedPos;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bNeedNext = true;
|
||||
|
||||
protected function partialParseResponse() : Response
|
||||
{
|
||||
$oResponse = new Response;
|
||||
$this->partialParseResponseBranch($oResponse);
|
||||
if (ResponseType::UNKNOWN === $oResponse->ResponseType) {
|
||||
throw new ResponseNotFoundException;
|
||||
}
|
||||
return $oResponse;
|
||||
}
|
||||
|
||||
private function skipBracketParse(?Response $oImapResponse) : bool
|
||||
{
|
||||
return $oImapResponse &&
|
||||
$oImapResponse->ResponseType === ResponseType::UNTAGGED &&
|
||||
(
|
||||
($oImapResponse->StatusOrIndex === 'STATUS' && 2 === \count($oImapResponse->ResponseList)) ||
|
||||
($oImapResponse->StatusOrIndex === 'LIST' && 4 === \count($oImapResponse->ResponseList)) ||
|
||||
($oImapResponse->StatusOrIndex === 'LSUB' && 4 === \count($oImapResponse->ResponseList))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string
|
||||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
*/
|
||||
private function partialParseResponseBranch(?Response $oImapResponse,
|
||||
bool $bTreatAsAtom = false, string $sParentToken = '', string $sOpenBracket = '')
|
||||
{
|
||||
if ($oImapResponse) {
|
||||
$this->iResponseBufParsedPos = 0;
|
||||
$this->bNeedNext = true;
|
||||
}
|
||||
|
||||
$iPos = $this->iResponseBufParsedPos;
|
||||
|
||||
$sPreviousAtomUpperCase = null;
|
||||
$sClosingBracket = ')';
|
||||
$iLiteralLen = 0;
|
||||
$iBufferEndIndex = 0;
|
||||
$iDebugCount = 0;
|
||||
|
||||
$bIsGotoDefault = false;
|
||||
$bIsGotoLiteral = false;
|
||||
$bIsGotoLiteralEnd = false;
|
||||
$bIsGotoAtomBracket = false;
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
|
||||
$bCountOneInited = false;
|
||||
$bCountTwoInited = false;
|
||||
|
||||
$sAtomBuilder = $bTreatAsAtom ? '' : null;
|
||||
$aList = array();
|
||||
if ($oImapResponse)
|
||||
{
|
||||
$aList =& $oImapResponse->ResponseList;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (100000 === ++$iDebugCount)
|
||||
{
|
||||
$this->Logger()->Write('PartialParseOver: '.$iDebugCount, \MailSo\Log\Enumerations\Type::ERROR);
|
||||
}
|
||||
|
||||
if ($this->bNeedNext)
|
||||
{
|
||||
$iPos = 0;
|
||||
$this->getNextBuffer();
|
||||
$this->iResponseBufParsedPos = $iPos;
|
||||
$this->bNeedNext = false;
|
||||
}
|
||||
|
||||
$sChar = null;
|
||||
if ($bIsGotoDefault)
|
||||
{
|
||||
$sChar = 'GOTO_DEFAULT';
|
||||
$bIsGotoDefault = false;
|
||||
}
|
||||
else if ($bIsGotoLiteral)
|
||||
{
|
||||
$bIsGotoLiteral = false;
|
||||
$bIsGotoLiteralEnd = true;
|
||||
|
||||
if ($this->partialResponseLiteralCallbackCallable(
|
||||
$sParentToken, null === $sPreviousAtomUpperCase ? '' : \strtoupper($sPreviousAtomUpperCase), $iLiteralLen))
|
||||
{
|
||||
if (!$bTreatAsAtom)
|
||||
{
|
||||
$aList[] = '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLiteral = '';
|
||||
$iRead = $iLiteralLen;
|
||||
|
||||
while (0 < $iRead)
|
||||
{
|
||||
$sAddRead = \fread($this->ConnectionResource(), $iRead);
|
||||
if (false === $sAddRead)
|
||||
{
|
||||
$sLiteral = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$sLiteral .= $sAddRead;
|
||||
$iRead -= \strlen($sAddRead);
|
||||
|
||||
\MailSo\Base\Utils::ResetTimeLimit();
|
||||
}
|
||||
|
||||
if (false !== $sLiteral)
|
||||
{
|
||||
$iLiteralSize = \strlen($sLiteral);
|
||||
if ($iLiteralLen !== $iLiteralSize)
|
||||
{
|
||||
$this->writeLog('Literal stream read warning "read '.$iLiteralSize.' of '.
|
||||
$iLiteralLen.'" bytes', \MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
|
||||
if (!$bTreatAsAtom)
|
||||
{
|
||||
$aList[] = $sLiteral;
|
||||
|
||||
if (\MailSo\Config::$LogSimpleLiterals)
|
||||
{
|
||||
$this->writeLog('{'.\strlen($sLiteral).'} '.$sLiteral, \MailSo\Log\Enumerations\Type::INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->writeLog('Can\'t read imap stream', \MailSo\Log\Enumerations\Type::NOTE);
|
||||
}
|
||||
|
||||
unset($sLiteral);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if ($bIsGotoLiteralEnd)
|
||||
{
|
||||
$sPreviousAtomUpperCase = null;
|
||||
$this->bNeedNext = true;
|
||||
$bIsGotoLiteralEnd = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if ($bIsGotoAtomBracket)
|
||||
{
|
||||
if ($bTreatAsAtom)
|
||||
{
|
||||
$sAtomBlock = $this->partialParseResponseBranch(null, true,
|
||||
null === $sPreviousAtomUpperCase ? '' : \strtoupper($sPreviousAtomUpperCase),
|
||||
$sOpenBracket);
|
||||
|
||||
$sAtomBuilder .= $sAtomBlock;
|
||||
$iPos = $this->iResponseBufParsedPos;
|
||||
$sAtomBuilder .= $sClosingBracket;
|
||||
}
|
||||
|
||||
$sPreviousAtomUpperCase = null;
|
||||
$bIsGotoAtomBracket = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if ($bIsGotoNotAtomBracket)
|
||||
{
|
||||
$aSubItems = $this->partialParseResponseBranch(null, false,
|
||||
null === $sPreviousAtomUpperCase ? '' : \strtoupper($sPreviousAtomUpperCase),
|
||||
$sOpenBracket);
|
||||
|
||||
$aList[] = $aSubItems;
|
||||
$iPos = $this->iResponseBufParsedPos;
|
||||
$sPreviousAtomUpperCase = null;
|
||||
if ($oImapResponse && $oImapResponse->IsStatusResponse)
|
||||
{
|
||||
$oImapResponse->OptionalResponse = $aSubItems;
|
||||
|
||||
$bIsGotoDefault = true;
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
continue;
|
||||
}
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iBufferEndIndex = \strlen($this->sResponseBuffer) - 3;
|
||||
|
||||
if ($iPos > $iBufferEndIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
$sChar = $this->sResponseBuffer[$iPos];
|
||||
}
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case ']' === $sChar:
|
||||
case ')' === $sChar:
|
||||
if ($this->skipBracketParse($oImapResponse))
|
||||
{
|
||||
$bIsGotoDefault = true;
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$iPos;
|
||||
$sPreviousAtomUpperCase = null;
|
||||
}
|
||||
break 2;
|
||||
case ' ' === $sChar:
|
||||
if ($bTreatAsAtom)
|
||||
{
|
||||
$sAtomBuilder .= ' ';
|
||||
}
|
||||
++$iPos;
|
||||
break;
|
||||
case '[' === $sChar:
|
||||
case '(' === $sChar:
|
||||
$sOpenBracket = $sChar;
|
||||
$sClosingBracket = '[' === $sChar ? ']' : ')';
|
||||
if ($bTreatAsAtom)
|
||||
{
|
||||
$sAtomBuilder .= $sChar;
|
||||
$bIsGotoAtomBracket = true;
|
||||
$this->iResponseBufParsedPos = ++$iPos;
|
||||
}
|
||||
else if ($this->skipBracketParse($oImapResponse))
|
||||
{
|
||||
$sOpenBracket = '';
|
||||
$sClosingBracket = '';
|
||||
$bIsGotoDefault = true;
|
||||
$bIsGotoNotAtomBracket = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bIsGotoNotAtomBracket = true;
|
||||
$this->iResponseBufParsedPos = ++$iPos;
|
||||
}
|
||||
break;
|
||||
case '{' === $sChar:
|
||||
$bIsLiteralParsed = false;
|
||||
$mLiteralEndPos = \strpos($this->sResponseBuffer, '}', $iPos);
|
||||
if (false !== $mLiteralEndPos && $mLiteralEndPos > $iPos)
|
||||
{
|
||||
$sLiteralLenAsString = \substr($this->sResponseBuffer, $iPos + 1, $mLiteralEndPos - $iPos - 1);
|
||||
if (\is_numeric($sLiteralLenAsString))
|
||||
{
|
||||
$iLiteralLen = (int) $sLiteralLenAsString;
|
||||
$bIsLiteralParsed = true;
|
||||
$iPos = $mLiteralEndPos + 3;
|
||||
$bIsGotoLiteral = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$bIsLiteralParsed)
|
||||
{
|
||||
$iPos = $iBufferEndIndex;
|
||||
}
|
||||
$sPreviousAtomUpperCase = null;
|
||||
break;
|
||||
case '"' === $sChar:
|
||||
$bIsQuotedParsed = false;
|
||||
while (true)
|
||||
{
|
||||
$iClosingPos = $iPos + 1;
|
||||
if ($iClosingPos > $iBufferEndIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
$iClosingPos = \strpos($this->sResponseBuffer, '"', $iClosingPos);
|
||||
if (false === $iClosingPos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO
|
||||
$iClosingPosNext = $iClosingPos + 1;
|
||||
if (
|
||||
isset($this->sResponseBuffer[$iClosingPosNext]) &&
|
||||
' ' !== $this->sResponseBuffer[$iClosingPosNext] &&
|
||||
"\r" !== $this->sResponseBuffer[$iClosingPosNext] &&
|
||||
"\n" !== $this->sResponseBuffer[$iClosingPosNext] &&
|
||||
']' !== $this->sResponseBuffer[$iClosingPosNext] &&
|
||||
')' !== $this->sResponseBuffer[$iClosingPosNext]
|
||||
)
|
||||
{
|
||||
++$iClosingPos;
|
||||
continue;
|
||||
}
|
||||
|
||||
$iSlashCount = 0;
|
||||
while ('\\' === $this->sResponseBuffer[$iClosingPos - $iSlashCount - 1])
|
||||
{
|
||||
++$iSlashCount;
|
||||
}
|
||||
|
||||
if ($iSlashCount % 2 == 1)
|
||||
{
|
||||
++$iClosingPos;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $iClosingPos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bIsQuotedParsed = true;
|
||||
if ($bTreatAsAtom)
|
||||
{
|
||||
$sAtomBuilder .= \strtr(
|
||||
\substr($this->sResponseBuffer, $iPos, $iClosingPos - $iPos + 1),
|
||||
array('\\\\' => '\\', '\\"' => '"')
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aList[] = \strtr(
|
||||
\substr($this->sResponseBuffer, $iPos + 1, $iClosingPos - $iPos - 1),
|
||||
array('\\\\' => '\\', '\\"' => '"')
|
||||
);
|
||||
}
|
||||
|
||||
$iPos = $iClosingPos + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bIsQuotedParsed)
|
||||
{
|
||||
$iPos = $iBufferEndIndex;
|
||||
}
|
||||
|
||||
$sPreviousAtomUpperCase = null;
|
||||
break;
|
||||
|
||||
case 'GOTO_DEFAULT' === $sChar:
|
||||
default:
|
||||
$iCharBlockStartPos = $iPos;
|
||||
|
||||
if ($oImapResponse && $oImapResponse->IsStatusResponse)
|
||||
{
|
||||
$iPos = $iBufferEndIndex;
|
||||
|
||||
while ($iPos > $iCharBlockStartPos && $this->sResponseBuffer[$iCharBlockStartPos] === ' ')
|
||||
{
|
||||
++$iCharBlockStartPos;
|
||||
}
|
||||
}
|
||||
|
||||
$bIsAtomDone = false;
|
||||
while (!$bIsAtomDone && ($iPos <= $iBufferEndIndex))
|
||||
{
|
||||
$sCharDef = $this->sResponseBuffer[$iPos];
|
||||
switch (true)
|
||||
{
|
||||
case ('[' === $sCharDef || ']' === $sCharDef || '(' === $sCharDef || ')' === $sCharDef) &&
|
||||
$this->skipBracketParse($oImapResponse):
|
||||
++$iPos;
|
||||
break;
|
||||
case '[' === $sCharDef:
|
||||
if (null === $sAtomBuilder)
|
||||
{
|
||||
$sAtomBuilder = '';
|
||||
}
|
||||
|
||||
$sAtomBuilder .= \substr($this->sResponseBuffer, $iCharBlockStartPos, $iPos - $iCharBlockStartPos + 1);
|
||||
|
||||
++$iPos;
|
||||
$this->iResponseBufParsedPos = $iPos;
|
||||
|
||||
$sListBlock = $this->partialParseResponseBranch(null, true,
|
||||
null === $sPreviousAtomUpperCase ? '' : \strtoupper($sPreviousAtomUpperCase),
|
||||
'[');
|
||||
|
||||
if (null !== $sListBlock)
|
||||
{
|
||||
$sAtomBuilder .= $sListBlock.']';
|
||||
}
|
||||
|
||||
$iPos = $this->iResponseBufParsedPos;
|
||||
$iCharBlockStartPos = $iPos;
|
||||
break;
|
||||
case ' ' === $sCharDef:
|
||||
case ')' === $sCharDef && '(' === $sOpenBracket:
|
||||
case ']' === $sCharDef && '[' === $sOpenBracket:
|
||||
$bIsAtomDone = true;
|
||||
break;
|
||||
default:
|
||||
++$iPos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($iPos > $iCharBlockStartPos || null !== $sAtomBuilder)
|
||||
{
|
||||
$sLastCharBlock = \substr($this->sResponseBuffer, $iCharBlockStartPos, $iPos - $iCharBlockStartPos);
|
||||
if (null === $sAtomBuilder)
|
||||
{
|
||||
$aList[] = $sLastCharBlock;
|
||||
$sPreviousAtomUpperCase = $sLastCharBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sAtomBuilder .= $sLastCharBlock;
|
||||
|
||||
if (!$bTreatAsAtom)
|
||||
{
|
||||
$aList[] = $sAtomBuilder;
|
||||
$sPreviousAtomUpperCase = $sAtomBuilder;
|
||||
$sAtomBuilder = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($oImapResponse)
|
||||
{
|
||||
// if (1 === \count($aList))
|
||||
if (!$bCountOneInited && 1 === \count($aList))
|
||||
// if (isset($aList[0]) && !isset($aList[1])) // fast 1 === \count($aList)
|
||||
{
|
||||
$bCountOneInited = true;
|
||||
|
||||
$oImapResponse->Tag = $aList[0];
|
||||
if ('+' === $oImapResponse->Tag)
|
||||
{
|
||||
$oImapResponse->ResponseType = ResponseType::CONTINUATION;
|
||||
}
|
||||
else if ('*' === $oImapResponse->Tag)
|
||||
{
|
||||
$oImapResponse->ResponseType = ResponseType::UNTAGGED;
|
||||
}
|
||||
else if ($this->getCurrentTag() === $oImapResponse->Tag)
|
||||
{
|
||||
$oImapResponse->ResponseType = ResponseType::TAGGED;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oImapResponse->ResponseType = ResponseType::UNKNOWN;
|
||||
}
|
||||
}
|
||||
// else if (2 === \count($aList))
|
||||
else if (!$bCountTwoInited && 2 === \count($aList))
|
||||
// else if (isset($aList[1]) && !isset($aList[2])) // fast 2 === \count($aList)
|
||||
{
|
||||
$bCountTwoInited = true;
|
||||
|
||||
$oImapResponse->StatusOrIndex = strtoupper($aList[1]);
|
||||
|
||||
if ($oImapResponse->StatusOrIndex == ResponseStatus::OK ||
|
||||
$oImapResponse->StatusOrIndex == ResponseStatus::NO ||
|
||||
$oImapResponse->StatusOrIndex == ResponseStatus::BAD ||
|
||||
$oImapResponse->StatusOrIndex == ResponseStatus::BYE ||
|
||||
$oImapResponse->StatusOrIndex == ResponseStatus::PREAUTH)
|
||||
{
|
||||
$oImapResponse->IsStatusResponse = true;
|
||||
}
|
||||
}
|
||||
else if (ResponseType::CONTINUATION === $oImapResponse->ResponseType)
|
||||
{
|
||||
$oImapResponse->HumanReadable = $sLastCharBlock;
|
||||
}
|
||||
else if ($oImapResponse->IsStatusResponse)
|
||||
{
|
||||
$oImapResponse->HumanReadable = $sLastCharBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->iResponseBufParsedPos = $iPos;
|
||||
|
||||
if (100000 < $iDebugCount)
|
||||
{
|
||||
$this->Logger()->Write('PartialParseOverResult: '.$iDebugCount, \MailSo\Log\Enumerations\Type::ERROR);
|
||||
}
|
||||
|
||||
return $bTreatAsAtom ? $sAtomBuilder : $aList;
|
||||
}
|
||||
|
||||
private function partialResponseLiteralCallbackCallable(string $sParent, string $sLiteralAtomUpperCase, int $iLiteralLen) : bool
|
||||
{
|
||||
if (!$this->aFetchCallbacks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sLiteralAtomUpperCasePeek = '';
|
||||
if (0 === \strpos($sLiteralAtomUpperCase, 'BODY'))
|
||||
{
|
||||
$sLiteralAtomUpperCasePeek = \str_replace('BODY', 'BODY.PEEK', $sLiteralAtomUpperCase);
|
||||
}
|
||||
|
||||
$sFetchKey = '';
|
||||
if (\strlen($sLiteralAtomUpperCasePeek) && isset($this->aFetchCallbacks[$sLiteralAtomUpperCasePeek]))
|
||||
{
|
||||
$sFetchKey = $sLiteralAtomUpperCasePeek;
|
||||
}
|
||||
else if (\strlen($sLiteralAtomUpperCase) && isset($this->aFetchCallbacks[$sLiteralAtomUpperCase]))
|
||||
{
|
||||
$sFetchKey = $sLiteralAtomUpperCase;
|
||||
}
|
||||
|
||||
if (empty($this->aFetchCallbacks[$sFetchKey]) || !\is_callable($this->aFetchCallbacks[$sFetchKey])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rImapLiteralStream =
|
||||
\MailSo\Base\StreamWrappers\Literal::CreateStream($this->ConnectionResource(), $iLiteralLen);
|
||||
|
||||
$this->writeLog('Start Callback for '.$sParent.' / '.$sLiteralAtomUpperCase.
|
||||
' - try to read '.$iLiteralLen.' bytes.', \MailSo\Log\Enumerations\Type::NOTE);
|
||||
|
||||
$this->bRunningCallback = true;
|
||||
|
||||
try
|
||||
{
|
||||
\call_user_func($this->aFetchCallbacks[$sFetchKey],
|
||||
$sParent, $sLiteralAtomUpperCase, $rImapLiteralStream);
|
||||
}
|
||||
catch (\Throwable $oException)
|
||||
{
|
||||
$this->writeLog('Callback Exception', \MailSo\Log\Enumerations\Type::NOTICE);
|
||||
$this->writeLogException($oException);
|
||||
}
|
||||
|
||||
if ($rImapLiteralStream)
|
||||
{
|
||||
$iNotReadLiteralLen = 0;
|
||||
|
||||
$bFeof = \feof($rImapLiteralStream);
|
||||
$this->writeLog('End Callback for '.$sParent.' / '.$sLiteralAtomUpperCase.
|
||||
' - feof = '.($bFeof ? 'good' : 'BAD'), $bFeof ?
|
||||
\MailSo\Log\Enumerations\Type::NOTE : \MailSo\Log\Enumerations\Type::WARNING);
|
||||
|
||||
if (!$bFeof)
|
||||
{
|
||||
while (!\feof($rImapLiteralStream))
|
||||
{
|
||||
$sBuf = \fread($rImapLiteralStream, 1024 * 1024);
|
||||
if (false === $sBuf || 0 === \strlen($sBuf) || null === $sBuf)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
\MailSo\Base\Utils::ResetTimeLimit();
|
||||
$iNotReadLiteralLen += \strlen($sBuf);
|
||||
}
|
||||
|
||||
if (!\feof($rImapLiteralStream))
|
||||
{
|
||||
\stream_get_contents($rImapLiteralStream);
|
||||
}
|
||||
}
|
||||
|
||||
\fclose($rImapLiteralStream);
|
||||
|
||||
if ($iNotReadLiteralLen > 0)
|
||||
{
|
||||
$this->writeLog('Not read literal size is '.$iNotReadLiteralLen.' bytes.',
|
||||
\MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->writeLog('Literal stream is not resource after callback.',
|
||||
\MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
|
||||
$this->bRunningCallback = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue