From 8343e76d5fe908899acd7255262526391525d6c4 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Fri, 14 Jan 2022 12:51:32 +0100 Subject: [PATCH] Cleanup and improved handling of BODYSTRUCTURE --- .../0.0.0/app/libraries/MailSo/Base/Utils.php | 36 +- .../libraries/MailSo/Imap/BodyStructure.php | 451 ++++++------------ .../libraries/MailSo/Imap/FetchResponse.php | 25 +- .../app/libraries/MailSo/Mail/Attachment.php | 5 - .../app/libraries/MailSo/Mail/MailClient.php | 11 +- .../app/libraries/MailSo/Mail/Message.php | 84 ++-- .../0.0.0/app/libraries/MailSo/Mime/Part.php | 9 +- .../libraries/RainLoop/Actions/Response.php | 16 +- 8 files changed, 201 insertions(+), 436 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php index afc7808bb..bea06f027 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php @@ -20,11 +20,7 @@ abstract class Utils public static function NormalizeCharset(string $sEncoding, bool $bAsciAsUtf8 = false) : string { - $sEncoding = \strtolower($sEncoding); - - $sEncoding = \preg_replace('/^iso8/', 'iso-8', $sEncoding); - $sEncoding = \preg_replace('/^cp-([\d])/', 'cp$1', $sEncoding); - $sEncoding = \preg_replace('/^windows?12/', 'windows-12', $sEncoding); + $sEncoding = \preg_replace('/^iso8/', 'iso-8', \strtolower($sEncoding)); switch ($sEncoding) { @@ -32,38 +28,40 @@ abstract class Utils case 'ascii': case 'us-asci': case 'us-ascii': - $sEncoding = $bAsciAsUtf8 ? Enumerations\Charset::UTF_8 : + return $bAsciAsUtf8 ? Enumerations\Charset::UTF_8 : Enumerations\Charset::ISO_8859_1; - break; + case 'unicode-1-1-utf-7': case 'unicode-1-utf-7': case 'unicode-utf-7': - $sEncoding = 'utf-7'; - break; + return 'utf-7'; + case 'utf8': case 'utf-8': - $sEncoding = Enumerations\Charset::UTF_8; - break; + return Enumerations\Charset::UTF_8; + case 'utf7imap': case 'utf-7imap': case 'utf7-imap': case 'utf-7-imap': - $sEncoding = 'utf7-imap'; - break; + return 'utf7-imap'; + case 'ks-c-5601-1987': case 'ks_c_5601-1987': case 'ks_c_5601_1987': - $sEncoding = 'euc-kr'; - break; + return 'euc-kr'; + case 'x-gbk': - $sEncoding = 'gb2312'; - break; + return 'gb2312'; + case 'iso-8859-i': case 'iso-8859-8-i': - $sEncoding = 'iso-8859-8'; - break; + return 'iso-8859-8'; } + $sEncoding = \preg_replace('/^cp-([\d])/', 'cp$1', $sEncoding); + $sEncoding = \preg_replace('/^windows?12/', 'windows-12', $sEncoding); + return $sEncoding; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php index 1e07425eb..289b53e5b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php @@ -52,11 +52,6 @@ class BodyStructure */ private $sDisposition; - /** - * @var array - */ - private $aDispositionParams; - /** * @var string */ @@ -65,23 +60,18 @@ class BodyStructure /** * @var string */ - private $sLanguage; + private $sLanguage = ''; /** * @var string */ - private $sLocation; + private $sLocation = ''; /** * @var int */ private $iSize; - /** - * @var int - */ - private $iTextLineCount; - /** * @var string */ @@ -92,35 +82,14 @@ class BodyStructure */ private $aSubParts; - function __construct(string $sContentType, ?string $sCharset, array $aBodyParams, ?string $sContentID, - ?string $sDescription, ?string $sMailEncodingName, ?string $sDisposition, ?array $aDispositionParams, string $sFileName, - ?string $sLanguage, ?string $sLocation, int $iSize, int $iTextLineCount, string $sPartID, array $aSubParts) - { - $this->sContentType = $sContentType; - $this->sCharset = $sCharset; - $this->aBodyParams = $aBodyParams; - $this->sContentID = $sContentID; - $this->sDescription = $sDescription; - $this->sMailEncodingName = $sMailEncodingName; - $this->sDisposition = $sDisposition; - $this->aDispositionParams = $aDispositionParams; - $this->sFileName = $sFileName; - $this->sLanguage = $sLanguage; - $this->sLocation = $sLocation; - $this->iSize = $iSize; - $this->iTextLineCount = $iTextLineCount; - $this->sPartID = $sPartID; - $this->aSubParts = $aSubParts; - } - - public function MailEncodingName() : ?string + public function MailEncodingName() : string { return $this->sMailEncodingName; } public function PartID() : string { - return (string) $this->sPartID; + return $this->sPartID; } public function FileName() : string @@ -141,7 +110,7 @@ class BodyStructure public function EstimatedSize() : int { $fCoefficient = 1; - switch (\strtolower($this->MailEncodingName())) + switch ($this->sMailEncodingName) { case 'base64': $fCoefficient = 0.75; @@ -151,68 +120,63 @@ class BodyStructure break; } - return (int) ($this->Size() * $fCoefficient); + return (int) ($this->iSize * $fCoefficient); } - public function Charset() : ?string + public function Charset() : string { return $this->sCharset; } public function ContentID() : string { - return (null === $this->sContentID) ? '' : $this->sContentID; + return $this->sContentID; } public function ContentLocation() : string { - return (null === $this->sLocation) ? '' : $this->sLocation; + return $this->sLocation; } public function IsInline() : bool { - return (null === $this->sDisposition) ? - (\strlen($this->ContentID())) : ('inline' === strtolower($this->sDisposition)); + return 'inline' === $this->sDisposition || \strlen($this->sContentID); } public function IsImage() : bool { - return 'image' === \MailSo\Base\Utils::ContentTypeType($this->ContentType(), $this->FileName()); + return 'image' === \MailSo\Base\Utils::ContentTypeType($this->sContentType, $this->sFileName); } public function IsArchive() : bool { - return 'archive' === \MailSo\Base\Utils::ContentTypeType($this->ContentType(), $this->FileName()); + return 'archive' === \MailSo\Base\Utils::ContentTypeType($this->sContentType, $this->sFileName); } public function IsPdf() : bool { - return 'pdf' === \MailSo\Base\Utils::ContentTypeType($this->ContentType(), $this->FileName()); + return 'pdf' === \MailSo\Base\Utils::ContentTypeType($this->sContentType, $this->sFileName); } public function IsDoc() : bool { - return 'doc' === \MailSo\Base\Utils::ContentTypeType($this->ContentType(), $this->FileName()); + return 'doc' === \MailSo\Base\Utils::ContentTypeType($this->sContentType, $this->sFileName); } public function IsPgpSignature() : bool { - return \in_array(\strtolower($this->ContentType()), + return \in_array($this->sContentType, array('application/pgp-signature', 'application/pkcs7-signature')); } public function IsAttachBodyPart() : bool { - $bResult = ( - (null !== $this->sDisposition && 'attachment' === \strtolower($this->sDisposition)) - ); - - if (!$bResult && null !== $this->sContentType) - { - $sContentType = \strtolower($this->sContentType); - $bResult = false === \strpos($sContentType, 'multipart/') && - 'text/html' !== $sContentType && 'text/plain' !== $sContentType; - } + return 'attachment' === $this->sDisposition + || ( + !\str_starts_with($this->sContentType, 'multipart/') + && 'text/html' !== $this->sContentType + && 'text/plain' !== $this->sContentType + ); return $bResult; } @@ -222,7 +186,7 @@ class BodyStructure $bResult = !empty($this->aBodyParams['format']) && 'flowed' === \strtolower(\trim($this->aBodyParams['format'])); - if ($bResult && \in_array(\strtolower($this->MailEncodingName()), array('base64', 'quoted-printable'))) + if ($bResult && \in_array($this->sMailEncodingName, array('base64', 'quoted-printable'))) { $bResult = false; } @@ -230,43 +194,13 @@ class BodyStructure return $bResult; } - public function SearchPlainParts() : array - { - $aReturn = array(); - $aParts = $this->SearchByContentType('text/plain'); - foreach ($aParts as $oPart) - { - if (!$oPart->IsAttachBodyPart()) - { - $aReturn[] = $oPart; - } - } - return $aReturn; - } - - public function SearchHtmlParts() : array - { - $aReturn = array(); - $aParts = $this->SearchByContentType('text/html'); - - foreach ($aParts as $oPart) - { - if (!$oPart->IsAttachBodyPart()) - { - $aReturn[] = $oPart; - } - } - - return $aReturn; - } - public function SearchInlineEncryptedPart() : ?self { - if ('multipart/encrypted' === \strtolower($this->ContentType())) + if ('multipart/encrypted' === \strtolower($this->sContentType)) { - $aSearchParts = $this->SearchByCallback(function ($oItem) { + $aSearchParts = \iterator_to_array($this->SearchByCallback(function ($oItem) { return $oItem->IsInline(); - }); + })); if (1 === \count($aSearchParts) && isset($aSearchParts[0])) { @@ -277,82 +211,68 @@ class BodyStructure return null; } - public function SearchHtmlOrPlainParts() : array + public function GetHtmlAndPlainParts() : array { - $mResult = $this->SearchHtmlParts() ?: $this->SearchPlainParts(); - if (!$mResult) - { - $oPart = $this->SearchInlineEncryptedPart(); - if ($oPart instanceof self) - { - $mResult = array($oPart); - } + $aParts = $this->SearchByCallback(function ($oItem) { + return ('text/html' === $oItem->sContentType || 'text/plain' === $oItem->sContentType) + && !$oItem->IsAttachBodyPart(); + }); + + if ($aParts->valid()) { + return \iterator_to_array($aParts); } - return $mResult; + $oPart = $this->SearchInlineEncryptedPart(); + if ($oPart instanceof self) { + return array($oPart); + } + + return []; } public function SearchCharset() : string { - $sResult = ''; - $mParts = \array_merge($this->SearchHtmlParts(), $this->SearchPlainParts()); + $gParts = $this->SearchByCallback(function ($oPart) { + return $oPart->Charset() + && ('text/html' === $oPart->sContentType || 'text/plain' === $oPart->sContentType) + && !$oPart->IsAttachBodyPart(); + }); - foreach ($mParts as $oPart) - { - $sResult = $oPart ? $oPart->Charset() : ''; - if ($sResult) - { - break; - } + if (!$gParts->valid()) { + $gParts = $this->SearchByCallback(function ($oPart) { + return $oPart->Charset() && $oPart->IsAttachBodyPart(); + }); } - if (!$sResult) - { - $aParts = $this->SearchAttachmentsParts(); - foreach ($aParts as $oPart) - { - $sResult = $oPart ? $oPart->Charset() : ''; - if ($sResult) - { - break; - } - } - } - - return $sResult ?: ''; + return $gParts->valid() ? $gParts->current()->Charset() : ''; } /** * @param mixed $fCallback */ - public function SearchByCallback($fCallback) : array +// public function SearchByCallback($fCallback) : \Generator + public function SearchByCallback($fCallback) : iterable { - $aReturn = array(); - if ($fCallback($this)) - { - $aReturn[] = $this; + if ($fCallback($this)) { + yield $this; } - - foreach ($this->aSubParts as /* @var $oSubPart \MailSo\Imap\BodyStructure */ $oSubPart) - { - $aReturn = \array_merge($aReturn, $oSubPart->SearchByCallback($fCallback)); + foreach ($this->aSubParts as /* @var $oSubPart \MailSo\Imap\BodyStructure */ $oSubPart) { + yield from $oSubPart->SearchByCallback($fCallback); } - - return $aReturn; } - public function SearchAttachmentsParts() : array + public function SearchAttachmentsParts() : iterable { return $this->SearchByCallback(function ($oItem) { return $oItem->IsAttachBodyPart(); }); } - public function SearchByContentType(string $sContentType) : array + public function SearchByContentType(string $sContentType) : iterable { $sContentType = \strtolower($sContentType); return $this->SearchByCallback(function ($oItem) use ($sContentType) { - return $sContentType === $oItem->ContentType(); + return $sContentType === $oItem->sContentType; }); } @@ -382,7 +302,7 @@ class BodyStructure return $oPart; } - private static function decodeAttrParameter(array $aParams, string $sParamName, string $sCharset = \MailSo\Base\Enumerations\Charset::UTF_8) : string + private static function decodeAttrParameter(array $aParams, string $sParamName, string $sCharset) : string { $sResult = ''; if (isset($aParams[$sParamName])) @@ -455,34 +375,23 @@ class BodyStructure return null; } - $sBodyMainType = null; - if (\is_string($aBodyStructure[0])) - { - $sBodyMainType = $aBodyStructure[0]; - } - - $sBodySubType = null; - $sContentType = ''; + $sContentTypeMain = ''; + $sContentTypeSub = ''; $aSubParts = array(); $aBodyParams = array(); - $sName = null; - $sCharset = null; - $sContentID = null; - $sDescription = null; - $sMailEncodingName = null; + $sName = ''; + $sCharset = ''; // \MailSo\Base\Enumerations\Charset::UTF_8 ? + $sContentID = ''; + $sDescription = ''; + $sMailEncodingName = ''; $iSize = 0; - $iTextLineCount = 0; // valid for rfc822/message and text parts $iExtraItemPos = 0; // list index of items which have no well-established position (such as 0, 1, 5, etc). - if (null === $sBodyMainType) + if (\is_array($aBodyStructure[0])) { // Process multipart body structure - if (!\is_array($aBodyStructure[0])) - { - return null; - } - - $sBodyMainType = 'multipart'; + $sContentTypeMain = 'multipart'; + $sContentTypeSub = 'mixed'; // primary default $sSubPartIDPrefix = ''; if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) { @@ -498,17 +407,22 @@ class BodyStructure $iIndex = 1; + /** + * First process the subparts, like: + ("text" "plain" ("charset" "utf-8") …) + ("text" "html" …) + */ while ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) { $oPart = self::NewInstance($aBodyStructure[$iExtraItemPos], $sSubPartIDPrefix.$iIndex); - if (null === $oPart) + if (!$oPart) { return null; } // For multipart, we have no charset info in the part itself. Thus, // obtain charset from nested parts. - if ($sCharset == null) + if (!$sCharset) { $sCharset = $oPart->Charset(); } @@ -518,43 +432,35 @@ class BodyStructure ++$iIndex; } + /** + * Now process the subparts containter like: + "alternative" ("boundary" "--boundary_id") … + */ if ($iExtraItemPos < \count($aBodyStructure)) { if (!\is_string($aBodyStructure[$iExtraItemPos])) { return null; } + $sContentTypeSub = \strtolower($aBodyStructure[$iExtraItemPos]); - $sBodySubType = \strtolower($aBodyStructure[$iExtraItemPos]); ++$iExtraItemPos; - } - - if ($iExtraItemPos < \count($aBodyStructure)) - { - $sBodyParamList = $aBodyStructure[$iExtraItemPos]; - if (\is_array($sBodyParamList)) + if ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) { - $aBodyParams = self::getKeyValueListFromArrayList($sBodyParamList); + $aBodyParams = self::getKeyValueListFromArrayList($aBodyStructure[$iExtraItemPos]); } } - - ++$iExtraItemPos; } - else + else if (\is_string($aBodyStructure[0])) { // Process simple (singlepart) body structure - if (7 > \count($aBodyStructure)) + if (7 > \count($aBodyStructure) || !\is_string($aBodyStructure[1])) { return null; } - $sBodyMainType = \strtolower($sBodyMainType); - if (!\is_string($aBodyStructure[1])) - { - return null; - } - - $sBodySubType = \strtolower($aBodyStructure[1]); + $sContentTypeMain = \strtolower($aBodyStructure[0]); + $sContentTypeSub = \strtolower($aBodyStructure[1]); $aBodyParamList = $aBodyStructure[2]; if (\is_array($aBodyParamList)) @@ -565,7 +471,7 @@ class BodyStructure $sCharset = $aBodyParams['charset']; } - $sName = self::decodeAttrParameter($aBodyParams, 'name', $sContentType); + $sName = self::decodeAttrParameter($aBodyParams, 'name', $sCharset); } if (null !== $aBodyStructure[3]) @@ -574,7 +480,6 @@ class BodyStructure { return null; } - $sContentID = $aBodyStructure[3]; } @@ -584,7 +489,6 @@ class BodyStructure { return null; } - $sDescription = $aBodyStructure[4]; } @@ -597,14 +501,7 @@ class BodyStructure $sMailEncodingName = $aBodyStructure[5]; } - if (\is_numeric($aBodyStructure[6])) - { - $iSize = (int) $aBodyStructure[6]; - } - else - { - $iSize = -1; - } + $iSize = \is_numeric($aBodyStructure[6]) ? (int) $aBodyStructure[6] : -1; if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) { @@ -614,161 +511,85 @@ class BodyStructure } $iExtraItemPos = 7; - if ('text' === $sBodyMainType) + if ('text' === $sContentTypeMain) { - if ($iExtraItemPos < \count($aBodyStructure)) - { - if (\is_numeric($aBodyStructure[$iExtraItemPos])) - { - $iTextLineCount = (int) $aBodyStructure[$iExtraItemPos]; - } - else - { - $iTextLineCount = -1; - } - } - else - { - $iTextLineCount = -1; - } - + /** + * A body type of type TEXT contains, immediately after the basic + * fields, the size of the body in text lines. + */ ++$iExtraItemPos; } - else if ('message' === $sBodyMainType && 'rfc822' === $sBodySubType) + else if ('message' === $sContentTypeMain && 'rfc822' === $sContentTypeSub) { - if ($iExtraItemPos + 2 < \count($aBodyStructure)) - { - if (\is_numeric($aBodyStructure[$iExtraItemPos + 2])) - { - $iTextLineCount = (int) $aBodyStructure[$iExtraItemPos + 2]; - } - else - { - $iTextLineCount = -1; - } - } - else - { - $iTextLineCount = -1; - } - + /** + * A body type of type MESSAGE and subtype RFC822 contains, + * immediately after the basic fields, the envelope structure, + * body structure, and size in text lines of the encapsulated message. + */ $iExtraItemPos += 3; } - - ++$iExtraItemPos; // skip MD5 digest of the body because most mail servers leave it NIL anyway + } + else + { + return null; } - $sContentType = $sBodyMainType.'/'.$sBodySubType; + // Skip body MD5 because most mail servers leave it NIL anyway + ++$iExtraItemPos; - $sDisposition = null; - $aDispositionParams = null; - $sFileName = null; + $sDisposition = ''; + $sFileName = ''; if ($iExtraItemPos < \count($aBodyStructure)) { $aDispList = $aBodyStructure[$iExtraItemPos]; if (\is_array($aDispList) && 1 < \count($aDispList)) { - if (null !== $aDispList[0]) + if (!\is_string($aDispList[0])) { - if (\is_string($aDispList[0])) - { - $sDisposition = $aDispList[0]; - } - else - { - return null; - } + return null; } - - $aDispParamList = $aDispList[1]; - if (\is_array($aDispParamList)) + $sDisposition = $aDispList[0]; + if (\is_array($aDispList[1])) { - $aDispositionParams = self::getKeyValueListFromArrayList($aDispParamList); - $sFileName = self::decodeAttrParameter($aDispositionParams, 'filename', $sCharset ?: ''); + $aDispositionParams = self::getKeyValueListFromArrayList($aDispList[1]); + $sFileName = self::decodeAttrParameter($aDispositionParams, 'filename', $sCharset); } } } - ++$iExtraItemPos; + $oStructure = new self; + $oStructure->sContentType = \strtolower($sContentTypeMain.'/'.$sContentTypeSub); + $oStructure->sCharset = $sCharset; + $oStructure->aBodyParams = $aBodyParams; + $oStructure->sContentID = $sContentID; + $oStructure->sDescription = $sDescription; + $oStructure->sMailEncodingName = \strtolower($sMailEncodingName); + $oStructure->sDisposition = \strtolower($sDisposition); + $oStructure->sFileName = \MailSo\Base\Utils::Utf8Clear($sFileName ?: $sName); + $oStructure->iSize = $iSize; + $oStructure->sPartID = $sPartID; + $oStructure->aSubParts = $aSubParts; - $sLanguage = null; if ($iExtraItemPos < \count($aBodyStructure)) { - if (null !== $aBodyStructure[$iExtraItemPos]) + if (\is_array($aBodyStructure[$iExtraItemPos])) { - if (\is_array($aBodyStructure[$iExtraItemPos])) - { - $sLanguage = \implode(',', $aBodyStructure[$iExtraItemPos]); - } - else if (\is_string($aBodyStructure[$iExtraItemPos])) - { - $sLanguage = $aBodyStructure[$iExtraItemPos]; - } + $oStructure->sLanguage = \implode(',', $aBodyStructure[$iExtraItemPos]); + } + else if (\is_string($aBodyStructure[$iExtraItemPos])) + { + $oStructure->sLanguage = $aBodyStructure[$iExtraItemPos]; } ++$iExtraItemPos; } - $sLocation = null; - if ($iExtraItemPos < \count($aBodyStructure)) + if ($iExtraItemPos < \count($aBodyStructure) && \is_string($aBodyStructure[$iExtraItemPos])) { - if (null !== $aBodyStructure[$iExtraItemPos]) - { - if (\is_string($aBodyStructure[$iExtraItemPos])) - { - $sLocation = $aBodyStructure[$iExtraItemPos]; - } - } - ++$iExtraItemPos; + $oStructure->sLocation = $aBodyStructure[$iExtraItemPos]; } - return new self( - $sContentType, - $sCharset, - $aBodyParams, - $sContentID, - $sDescription, - $sMailEncodingName, - $sDisposition, - $aDispositionParams, - \MailSo\Base\Utils::Utf8Clear($sFileName ?: $sName), - $sLanguage, - $sLocation, - $iSize, - $iTextLineCount, - $sPartID, - $aSubParts - ); - } - - public static function NewInstanceFromRfc822SubPart(array $aBodyStructure, string $sSubPartID) : ?self - { - $aBodySubStructure = self::findPartByIndexInArray($aBodyStructure, $sSubPartID); - if ($aBodySubStructure && \is_array($aBodySubStructure) && isset($aBodySubStructure[8])) - { - return self::NewInstance($aBodySubStructure[8], $sSubPartID); - } - - return null; - } - - private static function findPartByIndexInArray(array $aList, string $sPartID) : ?array - { - $bFind = false; - $aPath = \explode('.', ''.$sPartID); - $aCurrentPart = $aList; - - foreach ($aPath as $iPos => $iNum) - { - $iIndex = \intval($iNum) - 1; - if (0 <= $iIndex && 0 < $iPos ? isset($aCurrentPart[8][$iIndex]) : isset($aCurrentPart[$iIndex])) - { - $aCurrentPart = 0 < $iPos ? $aCurrentPart[8][$iIndex] : $aCurrentPart[$iIndex]; - $bFind = true; - } - } - - return $bFind ? $aCurrentPart : null; + return $oStructure; } /** @@ -783,7 +604,7 @@ class BodyStructure { for ($iIndex = 0; $iIndex < $iLen; $iIndex += 2) { - if (\is_string($aList[$iIndex]) && isset($aList[$iIndex + 1]) && \is_string($aList[$iIndex + 1])) + if (\is_string($aList[$iIndex]) && \is_string($aList[$iIndex + 1])) { $aDict[\strtolower($aList[$iIndex])] = $aList[$iIndex + 1]; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/FetchResponse.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/FetchResponse.php index 9735bbaff..56c560d29 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/FetchResponse.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/FetchResponse.php @@ -83,20 +83,13 @@ class FetchResponse return $oResult; } - public function GetFetchBodyStructure(string $sRfc822SubMimeIndex = '') : ?BodyStructure + public function GetFetchBodyStructure() : ?BodyStructure { $aBodyStructureArray = $this->GetFetchValue(Enumerations\FetchType::BODYSTRUCTURE); - if (\is_array($aBodyStructureArray)) - { - if (\strlen($sRfc822SubMimeIndex)) - { - return BodyStructure::NewInstanceFromRfc822SubPart($aBodyStructureArray, $sRfc822SubMimeIndex); - } - return BodyStructure::NewInstance($aBodyStructureArray); - } - - return null; + return \is_array($aBodyStructureArray) + ? BodyStructure::NewInstance($aBodyStructureArray) + : null; } /** @@ -125,12 +118,10 @@ class FetchResponse * Like: BODY[HEADER.FIELDS (RETURN-PATH RECEIVED MIME-VERSION MESSAGE-ID CONTENT-TYPE FROM TO CC BCC SENDER REPLY-TO DELIVERED-TO IN-REPLY-TO REFERENCES DATE SUBJECT SENSITIVITY X-MSMAIL-PRIORITY IMPORTANCE X-PRIORITY X-DRAFT-INFO RETURN-RECEIPT-TO DISPOSITION-NOTIFICATION-TO X-CONFIRM-READING-TO AUTHENTICATION-RESULTS X-DKIM-AUTHENTICATION-RESULTS LIST-UNSUBSCRIBE X-SPAM-STATUS X-SPAMD-RESULT X-BOGOSITY X-VIRUS X-VIRUS-SCANNED X-VIRUS-STATUS)] * @return mixed */ - public function GetHeaderFieldsValue(string $sRfc822SubMimeIndex = '') : string + public function GetHeaderFieldsValue() : string { $bNextIsValue = false; - $sRfc822SubMimeIndex = \strlen($sRfc822SubMimeIndex) ? ''.$sRfc822SubMimeIndex.'.' : ''; - if (isset($this->oImapResponse->ResponseList[3]) && \is_array($this->oImapResponse->ResponseList[3])) { foreach ($this->oImapResponse->ResponseList[3] as $mItem) @@ -141,9 +132,9 @@ class FetchResponse } if (\is_string($mItem) && ( - $mItem === 'BODY['.$sRfc822SubMimeIndex.'HEADER]' || - 0 === \strpos($mItem, 'BODY['.$sRfc822SubMimeIndex.'HEADER.FIELDS') || - $mItem === 'BODY['.$sRfc822SubMimeIndex.'MIME]')) + $mItem === 'BODY[HEADER]' || + 0 === \strpos($mItem, 'BODY[HEADER.FIELDS') || + $mItem === 'BODY[MIME]')) { $bNextIsValue = true; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php index 741bfd4e4..93ccb71ae 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php @@ -100,11 +100,6 @@ class Attachment implements \JsonSerializable return $this->oBodyStructure ? $this->oBodyStructure->ContentType() : ''; } - public function ContentTransferEncoding() : string - { - return $this->oBodyStructure ? $this->oBodyStructure->MailEncodingName() : ''; - } - public function EncodedSize() : int { return $this->oBodyStructure ? $this->oBodyStructure->Size() : 0; diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index cf39ea9ae..1dd08dcb6 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -170,7 +170,7 @@ class MailClient $oBodyStructure = $aFetchResponse[0]->GetFetchBodyStructure(); if ($oBodyStructure) { - foreach ($oBodyStructure->SearchHtmlOrPlainParts() as $oPart) + foreach ($oBodyStructure->GetHtmlAndPlainParts() as $oPart) { $sLine = FetchType::BODY_PEEK.'['.$oPart->PartID().']'; if (0 < $iBodyTextLimit && $iBodyTextLimit < $oPart->Size()) @@ -181,13 +181,10 @@ class MailClient $aFetchItems[] = $sLine; } - $aSignatureParts = $oBodyStructure->SearchByContentType('application/pgp-signature'); - if (\is_array($aSignatureParts) && \count($aSignatureParts)) + $gSignatureParts = $oBodyStructure->SearchByContentType('application/pgp-signature'); + foreach ($gSignatureParts as $oPart) { - foreach ($aSignatureParts as $oPart) - { - $aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->PartID().']'; - } + $aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->PartID().']'; } } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php index 7bd95be6a..8c3c1aeb4 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php @@ -11,6 +11,9 @@ namespace MailSo\Mail; +use \MailSo\Base\Utils; +use \MailSo\Imap\Enumerations\FetchType; + /** * @category MailSo * @package Mail @@ -116,11 +119,6 @@ class Message implements \JsonSerializable return $this->bPgpEncrypted; } - public function SetHtml(string $sHtml) : void - { - $this->sHtml = $sHtml; - } - public function Folder() : string { return $this->sFolder; @@ -298,19 +296,18 @@ class Message implements \JsonSerializable $oBodyStructure = $oFetchResponse->GetFetchBodyStructure(); } - $sInternalDate = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::INTERNALDATE); - $aFlags = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::FLAGS); + $sInternalDate = $oFetchResponse->GetFetchValue(FetchType::INTERNALDATE); + $aFlags = $oFetchResponse->GetFetchValue(FetchType::FLAGS); $this->sFolder = $sFolder; - $this->iUid = (int) $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::UID); - $this->iSize = (int) $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::RFC822_SIZE); + $this->iUid = (int) $oFetchResponse->GetFetchValue(FetchType::UID); + $this->iSize = (int) $oFetchResponse->GetFetchValue(FetchType::RFC822_SIZE); $this->aFlagsLowerCase = \array_map('strtolower', $aFlags ?: []); $this->iInternalTimeStampInUTC = \MailSo\Base\DateTimeHelper::ParseInternalDateString($sInternalDate); - $sCharset = $oBodyStructure ? $oBodyStructure->SearchCharset() : ''; - $sCharset = \MailSo\Base\Utils::NormalizeCharset($sCharset); + $sCharset = $oBodyStructure ? Utils::NormalizeCharset($oBodyStructure->SearchCharset()) : ''; $sHeaders = $oFetchResponse->GetHeaderFieldsValue(); if (\strlen($sHeaders)) @@ -324,8 +321,7 @@ class Message implements \JsonSerializable if (\strlen($sContentTypeCharset)) { - $sCharset = $sContentTypeCharset; - $sCharset = \MailSo\Base\Utils::NormalizeCharset($sCharset); + $sCharset = Utils::NormalizeCharset($sContentTypeCharset); } if (\strlen($sCharset)) @@ -353,7 +349,7 @@ class Message implements \JsonSerializable $this->oDeliveredTo = $oHeaders->GetAsEmailCollection(\MailSo\Mime\Enumerations\Header::DELIVERED_TO, $bCharsetAutoDetect); $this->sInReplyTo = $oHeaders->ValueByName(\MailSo\Mime\Enumerations\Header::IN_REPLY_TO); - $this->sReferences = \MailSo\Base\Utils::StripSpaces( + $this->sReferences = Utils::StripSpaces( $oHeaders->ValueByName(\MailSo\Mime\Enumerations\Header::REFERENCES)); $sHeaderDate = $oHeaders->ValueByName(\MailSo\Mime\Enumerations\Header::DATE); @@ -491,20 +487,11 @@ class Message implements \JsonSerializable } else if ($oFetchResponse->GetEnvelope()) { - if (!\strlen($sCharset) && $oBodyStructure) - { - $sCharset = $oBodyStructure->SearchCharset(); - $sCharset = \MailSo\Base\Utils::NormalizeCharset($sCharset); - } - - if (!\strlen($sCharset)) - { - $sCharset = \MailSo\Base\Enumerations\Charset::ISO_8859_1; - } + $sCharset = $sCharset ?: \MailSo\Base\Enumerations\Charset::ISO_8859_1; // date, subject, from, sender, reply-to, to, cc, bcc, in-reply-to, message-id $this->sMessageId = $oFetchResponse->GetFetchEnvelopeValue(9, ''); - $this->sSubject = \MailSo\Base\Utils::DecodeHeaderValue($oFetchResponse->GetFetchEnvelopeValue(1, ''), $sCharset); + $this->sSubject = Utils::DecodeHeaderValue($oFetchResponse->GetFetchEnvelopeValue(1, ''), $sCharset); $this->oFrom = $oFetchResponse->GetFetchEnvelopeEmailCollection(2, $sCharset); $this->oSender = $oFetchResponse->GetFetchEnvelopeEmailCollection(3, $sCharset); @@ -519,9 +506,10 @@ class Message implements \JsonSerializable if ('multipart/signed' === \strtolower($this->sContentType) && 'application/pgp-signature' === \strtolower($oHeaders->ParameterValue(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, \MailSo\Mime\Enumerations\Parameter::PROTOCOL))) { - $aPgpSignatureParts = $oBodyStructure ? $oBodyStructure->SearchByContentType('application/pgp-signature') : null; - if ($this->bPgpSigned = !empty($aPgpSignatureParts)) { - $sPgpSignatureText = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::BODY.'['.$aPgpSignatureParts[0]->PartID().']'); + $gPgpSignatureParts = $oBodyStructure ? $oBodyStructure->SearchByContentType('application/pgp-signature') : null; + $this->bPgpSigned = $gPgpSignatureParts && $gPgpSignatureParts->valid(); + if ($this->bPgpSigned) { + $sPgpSignatureText = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$gPgpSignatureParts->current()->PartID().']'); if (\is_string($sPgpSignatureText) && \strlen($sPgpSignatureText) && 0 < \strpos($sPgpSignatureText, 'BEGIN PGP SIGNATURE')) { $this->sPgpSignature = \trim($sPgpSignatureText); $this->sPgpSignatureMicAlg = (string) $oHeaders->ParameterValue(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'micalg'); @@ -533,23 +521,21 @@ class Message implements \JsonSerializable $this->bPgpEncrypted = ('multipart/encrypted' === \strtolower($this->sContentType) && 'application/pgp-encrypted' === \strtolower($oHeaders->ParameterValue(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, \MailSo\Mime\Enumerations\Parameter::PROTOCOL))); - $aTextParts = $oBodyStructure ? $oBodyStructure->SearchHtmlOrPlainParts() : null; + $aTextParts = $oBodyStructure ? $oBodyStructure->GetHtmlAndPlainParts() : null; + if ($aTextParts) { - if (!\strlen($sCharset)) - { - $sCharset = \MailSo\Base\Enumerations\Charset::UTF_8; - } + $sCharset = $sCharset ?: \MailSo\Base\Enumerations\Charset::UTF_8; $aHtmlParts = array(); $aPlainParts = array(); foreach ($aTextParts as $oPart) { - $sText = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::BODY.'['.$oPart->PartID().']'); + $sText = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oPart->PartID().']'); if (null === $sText) { - $sText = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::BODY.'['.$oPart->PartID().']<0>'); + $sText = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oPart->PartID().']<0>'); if (\is_string($sText) && \strlen($sText)) { $this->bTextPartIsTrimmed = true; @@ -564,11 +550,11 @@ class Message implements \JsonSerializable $sTextCharset = $sCharset; } - $sTextCharset = \MailSo\Base\Utils::NormalizeCharset($sTextCharset, true); + $sTextCharset = Utils::NormalizeCharset($sTextCharset, true); - $sText = \MailSo\Base\Utils::DecodeEncodingValue($sText, $oPart->MailEncodingName()); - $sText = \MailSo\Base\Utils::ConvertEncoding($sText, $sTextCharset, \MailSo\Base\Enumerations\Charset::UTF_8); - $sText = \MailSo\Base\Utils::Utf8Clear($sText); + $sText = Utils::DecodeEncodingValue($sText, $oPart->MailEncodingName()); + $sText = Utils::ConvertEncoding($sText, $sTextCharset, \MailSo\Base\Enumerations\Charset::UTF_8); + $sText = Utils::Utf8Clear($sText); if ('text/html' === $oPart->ContentType()) { @@ -578,7 +564,7 @@ class Message implements \JsonSerializable { if ($oPart->IsFlowedFormat()) { - $sText = \MailSo\Base\Utils::DecodeFlowedFormat($sText); + $sText = Utils::DecodeFlowedFormat($sText); } $aPlainParts[] = $sText; @@ -586,14 +572,8 @@ class Message implements \JsonSerializable } } - if (\count($aHtmlParts)) - { - $this->sHtml = \implode('
', $aHtmlParts); - } - else - { - $this->sPlain = \trim(\implode("\n", $aPlainParts)); - } + $this->sHtml = \implode('
', $aHtmlParts); + $this->sPlain = \trim(\implode("\n", $aPlainParts)); $aMatch = array(); if (!$this->bPgpSigned && \preg_match('/-----BEGIN PGP SIGNATURE-----(.+)-----END PGP SIGNATURE-----/ism', $this->sPlain, $aMatch) && !empty($aMatch[0])) @@ -609,11 +589,11 @@ class Message implements \JsonSerializable if ($oBodyStructure) { - $aAttachmentsParts = $oBodyStructure->SearchAttachmentsParts(); - if ($aAttachmentsParts && \count($aAttachmentsParts)) + $gAttachmentsParts = $oBodyStructure->SearchAttachmentsParts(); + if ($gAttachmentsParts->valid()) { $this->oAttachments = new AttachmentCollection; - foreach ($aAttachmentsParts as /* @var $oAttachmentItem \MailSo\Imap\BodyStructure */ $oAttachmentItem) + foreach ($gAttachmentsParts as /* @var $oAttachmentItem \MailSo\Imap\BodyStructure */ $oAttachmentItem) { $this->oAttachments->append( Attachment::NewBodyStructureInstance($this->sFolder, $this->iUid, $oAttachmentItem) @@ -631,7 +611,7 @@ class Message implements \JsonSerializable '@Object' => 'Object/Message', 'Folder' => $this->sFolder, 'Uid' => $this->iUid, - 'Subject' => \trim(\MailSo\Base\Utils::Utf8Clear($this->sSubject)), + 'Subject' => \trim(Utils::Utf8Clear($this->sSubject)), 'MessageId' => $this->sMessageId, 'Size' => $this->iSize, 'SpamScore' => $this->iSpamScore, diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php index e19544a31..9c3133eef 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php @@ -141,13 +141,6 @@ class Part Enumerations\Header::CONTENT_TYPE))) : ''; } - public function ContentTransferEncoding() : string - { - return ($this->Headers) ? - \trim(\strtolower($this->Headers->ValueByName( - Enumerations\Header::CONTENT_TRANSFER_ENCODING))) : ''; - } - public function ContentID() : string { return ($this->Headers) ? \trim($this->Headers->ValueByName( @@ -169,7 +162,7 @@ class Part Enumerations\Header::CONTENT_TYPE, Enumerations\Parameter::FORMAT))); - if ($bResult && \in_array(\strtolower($this->MailEncodingName()), array('base64', 'quoted-printable'))) + if ($bResult && \in_array($this->MailEncodingName(), array('base64', 'quoted-printable'))) { $bResult = false; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php index 00e16e9fe..707400ac2 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php @@ -223,14 +223,6 @@ trait Response } } - $sPlain = ''; - $sHtml = \trim($mResponse->Html()); - - if (!\strlen($sHtml)) - { - $sPlain = \trim($mResponse->Plain()); - } - $mResult['DraftInfo'] = $mResponse->DraftInfo(); $mResult['InReplyTo'] = $mResponse->InReplyTo(); $mResult['UnsubsribeLinks'] = $mResponse->UnsubsribeLinks(); @@ -248,27 +240,25 @@ trait Response }; } + $sHtml = $mResponse->Html(); $sHtml = \preg_replace_callback('/(]*>)([\s\S\r\n\t]*?)(<\/pre>)/mi', function ($aMatches) { return \preg_replace('/[\r\n]+/', '
', $aMatches[1].\trim($aMatches[2]).$aMatches[3]); }, $sHtml); - $mResult['Html'] = \strlen($sHtml) ? \MailSo\Base\HtmlUtils::ClearHtml( $sHtml, $bHasExternals, $aFoundCIDs, $aContentLocationUrls, $aFoundContentLocationUrls, $fAdditionalExternalFilter, !!$this->Config()->Get('labs', 'try_to_detect_hidden_images', false) ) : ''; + unset($sHtml); $mResult['ExternalProxy'] = null !== $fAdditionalExternalFilter; - $mResult['Plain'] = $sPlain; -// $mResult['Plain'] = \strlen($sPlain) ? \MailSo\Base\HtmlUtils::ConvertPlainToHtml($sPlain) : ''; + $mResult['Plain'] = $mResponse->Plain(); $mResult['isPgpSigned'] = $mResponse->isPgpSigned(); $mResult['isPgpEncrypted'] = $mResponse->isPgpEncrypted(); // $mResult['PgpSignature'] = $mResponse->PgpSignature(); // $mResult['PgpSignatureMicAlg'] = $mResponse->PgpSignatureMicAlg(); - unset($sHtml, $sPlain); - $mResult['HasExternals'] = $bHasExternals; $mResult['HasInternals'] = \count($aFoundCIDs) || \count($aFoundContentLocationUrls);