<?php
namespace ZBateson\MailMimeParser\Stream;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\AppendStream;
use Psr\Http\Message\StreamInterface;
use SplObserver;
use SplSubject;
use ZBateson\MailMimeParser\Header\HeaderConsts;
use ZBateson\MailMimeParser\MailMimeParser;
use ZBateson\MailMimeParser\Message\IMessagePart;
use ZBateson\MailMimeParser\Message\IMimePart;
use ZBateson\MbWrapper\UnsupportedCharsetException;
class MessagePartStream extends MessagePartStreamDecorator implements SplObserver, StreamInterface
{
protected StreamFactory $streamFactory;
protected IMessagePart $part;
protected bool $throwExceptionReadingPartContentFromUnsupportedCharsets;
protected ?AppendStream $appendStream = null;
public function __construct(StreamFactory $sdf, IMessagePart $part, bool $throwExceptionReadingPartContentFromUnsupportedCharsets)
{
parent::__construct($part);
$this->streamFactory = $sdf;
$this->part = $part;
$this->throwExceptionReadingPartContentFromUnsupportedCharsets = $throwExceptionReadingPartContentFromUnsupportedCharsets;
$part->attach($this);
unset($this->stream);
}
public function __destruct()
{
$this->part->detach($this);
}
public function update(SplSubject $subject) : void
{
if ($this->appendStream !== null) {
unset($this->stream);
$this->appendStream = null;
}
}
private function getCharsetDecoratorForStream(StreamInterface $stream) : StreamInterface
{
$charset = $this->part->getCharset();
if (!empty($charset)) {
if (!$this->throwExceptionReadingPartContentFromUnsupportedCharsets) {
$test = $this->streamFactory->newCharsetStream(
Psr7\Utils::streamFor(),
$charset,
MailMimeParser::DEFAULT_CHARSET
);
try {
$test->write('t');
} catch (UnsupportedCharsetException $e) {
return $stream;
} finally {
$test->close();
}
}
$stream = $this->streamFactory->newCharsetStream(
$stream,
$charset,
MailMimeParser::DEFAULT_CHARSET
);
}
return $stream;
}
protected function getBoundaryAndChildStreams(IMimePart $part) : array
{
$boundary = $part->getHeaderParameter(HeaderConsts::CONTENT_TYPE, 'boundary');
if ($boundary === null) {
return \array_map(
function($child) {
return $child->getStream();
},
$part->getChildParts()
);
}
$streams = [];
foreach ($part->getChildParts() as $i => $child) {
if ($i !== 0 || $part->hasContent()) {
$streams[] = Psr7\Utils::streamFor("\r\n");
}
$streams[] = Psr7\Utils::streamFor("--$boundary\r\n");
$streams[] = $child->getStream();
}
$streams[] = Psr7\Utils::streamFor("\r\n--$boundary--\r\n");
return $streams;
}
protected function getStreamsArray() : array
{
$contentStream = $this->part->getContentStream();
if ($contentStream !== null) {
$contentStream = $this->streamFactory->newDecoratedCachingStream(
$this->streamFactory->newSeekingStream($contentStream),
function($stream) {
$es = $this->streamFactory->getTransferEncodingDecoratedStream(
$stream,
$this->part->getContentTransferEncoding(),
$this->part->getFilename()
);
$cs = $this->getCharsetDecoratorForStream($es);
return $cs;
}
);
}
$streams = [$this->streamFactory->newHeaderStream($this->part), $contentStream ?: Psr7\Utils::streamFor()];
if ($this->part instanceof IMimePart && $this->part->getChildCount() > 0) {
$streams = \array_merge($streams, $this->getBoundaryAndChildStreams($this->part));
}
return $streams;
}
protected function createStream() : StreamInterface
{
if ($this->appendStream === null) {
$this->appendStream = new AppendStream($this->getStreamsArray());
}
return $this->appendStream;
}
}