mirror of
https://github.com/itflow-org/itflow
synced 2026-06-26 03:31:05 +00:00
Mail Parser: Completely remove Webklex IMAP and all dependcies
This commit is contained in:
@@ -79,6 +79,14 @@ abstract class ErrorBag implements IErrorBag
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the source bag's own errors into this one without re-logging them.
|
||||
*/
|
||||
protected function copyErrorsFrom(ErrorBag $source) : void
|
||||
{
|
||||
$this->errors = \array_merge($this->errors, $source->getErrors(false, LogLevel::DEBUG));
|
||||
}
|
||||
|
||||
public function getErrors(bool $validate = false, string $minPsrLevel = LogLevel::ERROR) : array
|
||||
{
|
||||
if ($validate && !$this->validated) {
|
||||
|
||||
@@ -43,7 +43,8 @@ class MimeToken extends Token
|
||||
public function __construct(LoggerInterface $logger, MbWrapper $charsetConverter, string $value)
|
||||
{
|
||||
parent::__construct($logger, $charsetConverter, $value);
|
||||
$this->value = $this->decodeMime(\preg_replace('/\r|\n/', '', $this->value));
|
||||
$decoded = $this->decodeMime(\preg_replace('/\r|\n/', '', $this->value));
|
||||
$this->value = \preg_replace('/[\r\n]+/', '', $decoded) ?? '';
|
||||
$pattern = self::MIME_PART_PATTERN;
|
||||
$this->canIgnoreSpacesBefore = (bool) \preg_match("/^\s*{$pattern}|\s+/", $this->rawValue);
|
||||
$this->canIgnoreSpacesAfter = (bool) \preg_match("/{$pattern}\s*|\s+\$/", $this->rawValue);
|
||||
|
||||
@@ -64,10 +64,10 @@ class ParameterPart extends NameValuePart
|
||||
|
||||
protected function decodePartValue(string $value, ?string $charset = null) : string
|
||||
{
|
||||
if ($charset !== null) {
|
||||
return $this->convertEncoding(\rawurldecode($value), $charset, true);
|
||||
}
|
||||
return $this->convertEncoding(\rawurldecode($value));
|
||||
$decoded = ($charset !== null)
|
||||
? $this->convertEncoding(\rawurldecode($value), $charset, true)
|
||||
: $this->convertEncoding(\rawurldecode($value));
|
||||
return \preg_replace('/[\r\n]+/', '', $decoded) ?? $decoded;
|
||||
}
|
||||
|
||||
protected function getValueFromParts(array $parts) : string
|
||||
|
||||
@@ -48,7 +48,7 @@ class MultipartHelper extends AbstractHelper
|
||||
public function getUniqueBoundary(string $mimeType) : string
|
||||
{
|
||||
$type = \ltrim(\strtoupper(\preg_replace('/^(multipart\/(.{3}).*|.*)$/i', '$2-', $mimeType)), '-');
|
||||
return \uniqid('----=MMP-' . $type . '-', true);
|
||||
return '----=MMP-' . $type . '-' . \bin2hex(\random_bytes(16));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -311,7 +311,8 @@ class MultipartHelper extends AbstractHelper
|
||||
$filename = 'file' . \uniqid();
|
||||
}
|
||||
|
||||
$safe = \iconv('UTF-8', 'US-ASCII//translit//ignore', $filename);
|
||||
$converted = \iconv('UTF-8', 'US-ASCII//translit//ignore', $filename);
|
||||
$safe = \preg_replace('/[\x00-\x1F\x7F]+/', ' ', ($converted !== false) ? $converted : '') ?? '';
|
||||
if ($message->isMime()) {
|
||||
$part = $this->mimePartFactory->newInstance();
|
||||
$part->setRawHeader(HeaderConsts::CONTENT_TRANSFER_ENCODING, $encoding);
|
||||
|
||||
@@ -100,13 +100,16 @@ class PartChildrenContainer implements ArrayAccess, RecursiveIterator
|
||||
*/
|
||||
public function add(IMessagePart $part, $position = null) : static
|
||||
{
|
||||
$index = $position ?? \count($this->children);
|
||||
\array_splice(
|
||||
$this->children,
|
||||
$index,
|
||||
0,
|
||||
[$part]
|
||||
);
|
||||
if ($position === null || $position >= \count($this->children)) {
|
||||
$this->children[] = $part;
|
||||
} else {
|
||||
\array_splice(
|
||||
$this->children,
|
||||
$position,
|
||||
0,
|
||||
[$part]
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ class PartHeaderContainer extends ErrorBag implements IteratorAggregate
|
||||
$this->headerObjects = $cloneSource->headerObjects;
|
||||
$this->headerMap = $cloneSource->headerMap;
|
||||
$this->nextIndex = $cloneSource->nextIndex;
|
||||
$this->copyErrorsFrom($cloneSource);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,16 @@ use ZBateson\MailMimeParser\Message\PartHeaderContainer;
|
||||
*/
|
||||
class HeaderParserService
|
||||
{
|
||||
private int $maxHeaderCount;
|
||||
|
||||
private int $maxHeaderSizeBytes;
|
||||
|
||||
public function __construct(int $maxHeaderCount = 1000, int $maxHeaderSizeBytes = 1048576)
|
||||
{
|
||||
$this->maxHeaderCount = $maxHeaderCount;
|
||||
$this->maxHeaderSizeBytes = $maxHeaderSizeBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the header isn't empty and contains a colon separator character,
|
||||
* then splits it and adds it to the passed PartHeaderContainer.
|
||||
@@ -51,16 +61,28 @@ class HeaderParserService
|
||||
public function parse($handle, PartHeaderContainer $container) : static
|
||||
{
|
||||
$header = '';
|
||||
$count = 0;
|
||||
$start = \ftell($handle);
|
||||
do {
|
||||
$offset = \ftell($handle);
|
||||
$line = MessageParserService::readLine($handle);
|
||||
if ($line === false || $line === '' || $line[0] !== "\t" && $line[0] !== ' ') {
|
||||
if ($header !== '') {
|
||||
++$count;
|
||||
}
|
||||
$this->addRawHeaderToPart($offset, $header, $container);
|
||||
$header = '';
|
||||
} else {
|
||||
$line = "\r\n" . $line;
|
||||
}
|
||||
$header .= \rtrim($line, "\r\n");
|
||||
if ($count >= $this->maxHeaderCount || \ftell($handle) - $start >= $this->maxHeaderSizeBytes) {
|
||||
$container->addError(
|
||||
'Header count or total size limit reached while parsing headers',
|
||||
LogLevel::ERROR
|
||||
);
|
||||
break;
|
||||
}
|
||||
} while ($header !== '');
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
namespace ZBateson\MailMimeParser\Parser;
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use ZBateson\MailMimeParser\Message\Factory\PartHeaderContainerFactory;
|
||||
use ZBateson\MailMimeParser\Message\PartHeaderContainer;
|
||||
use ZBateson\MailMimeParser\Parser\Proxy\ParserMessageProxyFactory;
|
||||
@@ -32,16 +33,23 @@ class MimeParserService extends AbstractParserService
|
||||
*/
|
||||
protected HeaderParserService $headerParser;
|
||||
|
||||
/**
|
||||
* @var int Maximum multipart nesting depth.
|
||||
*/
|
||||
protected int $maxMimePartDepth;
|
||||
|
||||
public function __construct(
|
||||
ParserMessageProxyFactory $parserMessageProxyFactory,
|
||||
ParserMimePartProxyFactory $parserMimePartProxyFactory,
|
||||
PartBuilderFactory $partBuilderFactory,
|
||||
PartHeaderContainerFactory $partHeaderContainerFactory,
|
||||
HeaderParserService $headerParser
|
||||
HeaderParserService $headerParser,
|
||||
int $maxMimePartDepth = 256
|
||||
) {
|
||||
parent::__construct($parserMessageProxyFactory, $parserMimePartProxyFactory, $partBuilderFactory);
|
||||
$this->partHeaderContainerFactory = $partHeaderContainerFactory;
|
||||
$this->headerParser = $headerParser;
|
||||
$this->maxMimePartDepth = $maxMimePartDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,8 +168,29 @@ class MimeParserService extends AbstractParserService
|
||||
if ($proxy->isParentBoundaryFound()) {
|
||||
return null;
|
||||
}
|
||||
if ($this->exceedsMaxDepth($proxy)) {
|
||||
$proxy->addError(
|
||||
'Maximum MIME part nesting depth of ' . $this->maxMimePartDepth . ' reached',
|
||||
LogLevel::ERROR
|
||||
);
|
||||
return null;
|
||||
}
|
||||
$headerContainer = $this->partHeaderContainerFactory->newInstance();
|
||||
$child = $this->partBuilderFactory->newChildPartBuilder($headerContainer, $proxy);
|
||||
return $this->createPart($proxy, $headerContainer, $child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if adding a child to $proxy would exceed $maxMimePartDepth.
|
||||
*/
|
||||
private function exceedsMaxDepth(ParserMimePartProxy $proxy) : bool
|
||||
{
|
||||
$depth = 1;
|
||||
for ($p = $proxy->getParent(); $p !== null; $p = $p->getParent()) {
|
||||
if (++$depth >= $this->maxMimePartDepth) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ use ZBateson\MailMimeParser\Header\Consumer\Received\GenericReceivedConsumerServ
|
||||
use ZBateson\MailMimeParser\Header\Consumer\ReceivedConsumerService;
|
||||
use ZBateson\MailMimeParser\Message\Factory\PartStreamContainerFactory;
|
||||
use ZBateson\MailMimeParser\Message\PartStreamContainer;
|
||||
use ZBateson\MailMimeParser\Parser\HeaderParserService;
|
||||
use ZBateson\MailMimeParser\Parser\MimeParserService;
|
||||
use ZBateson\MailMimeParser\Parser\Part\ParserPartStreamContainerFactory;
|
||||
use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
|
||||
@@ -24,6 +26,13 @@ return [
|
||||
// header parts
|
||||
'throwExceptionReadingPartContentFromUnsupportedCharsets' => false,
|
||||
|
||||
// Maximum multipart nesting depth before parsing stops with a recorded error.
|
||||
'maxMimePartDepth' => 256,
|
||||
|
||||
// Maximum header count and total header bytes before parsing stops.
|
||||
'maxHeaderCount' => 1000,
|
||||
'maxHeaderSizeBytes' => 1048576,
|
||||
|
||||
'fromDomainConsumerService' => (new AutowireDefinitionHelper(DomainConsumerService::class))
|
||||
->constructorParameter('partName', 'from'),
|
||||
'byDomainConsumerService' => (new AutowireDefinitionHelper(DomainConsumerService::class))
|
||||
@@ -61,4 +70,13 @@ return [
|
||||
->constructor(
|
||||
throwExceptionReadingPartContentFromUnsupportedCharsets: new Reference('throwExceptionReadingPartContentFromUnsupportedCharsets')
|
||||
),
|
||||
HeaderParserService::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
maxHeaderCount: new Reference('maxHeaderCount'),
|
||||
maxHeaderSizeBytes: new Reference('maxHeaderSizeBytes')
|
||||
),
|
||||
MimeParserService::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
maxMimePartDepth: new Reference('maxMimePartDepth')
|
||||
),
|
||||
];
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.0.4
|
||||
3.0.6
|
||||
|
||||
Reference in New Issue
Block a user