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 11c77e330..070a27521 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 @@ -11,6 +11,8 @@ namespace MailSo\Imap; +use MailSo\Mime\ParameterCollection; + /** * @category MailSo * @package Imap @@ -19,9 +21,9 @@ class BodyStructure { private string $sContentType; - private string $sCharset; + private array $aContentTypeParams; - private array $aBodyParams; + private string $sCharset; private string $sContentID; @@ -63,6 +65,11 @@ class BodyStructure return $this->sContentType; } + public function ContentTypeParameters() : array + { + return $this->aContentTypeParams; + } + public function Size() : int { return $this->iSize; @@ -133,8 +140,8 @@ class BodyStructure { // https://datatracker.ietf.org/doc/html/rfc3156#section-4 return 'multipart/encrypted' === $this->sContentType - && !empty($this->aBodyParams['protocol']) - && 'application/pgp-encrypted' === \strtolower(\trim($this->aBodyParams['protocol'])) + && !empty($this->aContentTypeParams['protocol']) + && 'application/pgp-encrypted' === \strtolower(\trim($this->aContentTypeParams['protocol'])) // The multipart/encrypted body MUST consist of exactly two parts. && 2 === \count($this->aSubParts) && 'application/pgp-encrypted' === $this->aSubParts[0]->ContentType() @@ -146,8 +153,8 @@ class BodyStructure { // https://datatracker.ietf.org/doc/html/rfc3156#section-5 return 'multipart/signed' === $this->sContentType - && !empty($this->aBodyParams['protocol']) - && 'application/pgp-signature' === \strtolower(\trim($this->aBodyParams['protocol'])) + && !empty($this->aContentTypeParams['protocol']) + && 'application/pgp-signature' === \strtolower(\trim($this->aContentTypeParams['protocol'])) // The multipart/signed body MUST consist of exactly two parts. && 2 === \count($this->aSubParts) && $this->aSubParts[1]->IsPgpSignature(); @@ -172,8 +179,8 @@ class BodyStructure public function IsFlowedFormat() : bool { - return !empty($this->aBodyParams['format']) - && 'flowed' === \strtolower(\trim($this->aBodyParams['format'])) + return !empty($this->aContentTypeParams['format']) + && 'flowed' === \strtolower(\trim($this->aContentTypeParams['format'])) && !\in_array($this->sMailEncodingName, array('base64', 'quoted-printable')); } @@ -255,20 +262,15 @@ class BodyStructure public function GetPartByMimeIndex(string $sMimeIndex) : self { $oPart = null; - if (\strlen($sMimeIndex)) - { - if ($sMimeIndex === $this->sPartID) - { + if (\strlen($sMimeIndex)) { + if ($sMimeIndex === $this->sPartID) { $oPart = $this; } - if (null === $oPart) - { - foreach ($this->aSubParts as /* @var $oSubPart \MailSo\Imap\BodyStructure */ $oSubPart) - { + if (null === $oPart) { + foreach ($this->aSubParts as /* @var $oSubPart \MailSo\Imap\BodyStructure */ $oSubPart) { $oPart = $oSubPart->GetPartByMimeIndex($sMimeIndex); - if (null !== $oPart) - { + if (null !== $oPart) { break; } } @@ -281,60 +283,43 @@ class BodyStructure private static function decodeAttrParameter(array $aParams, string $sParamName, string $sCharset) : string { $sResult = ''; - if (isset($aParams[$sParamName])) - { + if (isset($aParams[$sParamName])) { $sResult = \MailSo\Base\Utils::DecodeHeaderValue($aParams[$sParamName], $sCharset); - } - else if (isset($aParams[$sParamName.'*'])) - { + } else if (isset($aParams[$sParamName.'*'])) { $aValueParts = \explode("''", $aParams[$sParamName.'*'], 2); - if (2 === \count($aValueParts)) - { + if (2 === \count($aValueParts)) { $sCharset = isset($aValueParts[0]) ? $aValueParts[0] : \MailSo\Base\Enumerations\Charset::UTF_8; - $sResult = \MailSo\Base\Utils::ConvertEncoding( \urldecode($aValueParts[1]), $sCharset, \MailSo\Base\Enumerations\Charset::UTF_8); - } - else - { + } else { $sResult = \urldecode($aParams[$sParamName.'*']); } - } - else - { + } else { $sCharset = ''; $sCharsetIndex = -1; $aFileNames = array(); - foreach ($aParams as $sName => $sValue) - { + foreach ($aParams as $sName => $sValue) { $aMatches = array(); - if (\preg_match('/^'.\preg_quote($sParamName, '/').'\*([0-9]+)\*$/i', $sName, $aMatches)) - { + if (\preg_match('/^'.\preg_quote($sParamName, '/').'\*([0-9]+)\*$/i', $sName, $aMatches)) { $iIndex = (int) $aMatches[1]; - if ($sCharsetIndex < $iIndex && false !== \strpos($sValue, "''")) - { + if ($sCharsetIndex < $iIndex && false !== \strpos($sValue, "''")) { $aValueParts = \explode("''", $sValue, 2); - if (2 === \count($aValueParts) && \strlen($aValueParts[0])) - { + if (2 === \count($aValueParts) && \strlen($aValueParts[0])) { $sCharsetIndex = $iIndex; $sCharset = $aValueParts[0]; $sValue = $aValueParts[1]; } } - $aFileNames[$iIndex] = $sValue; } } - if (\count($aFileNames)) - { + if (\count($aFileNames)) { \ksort($aFileNames, SORT_NUMERIC); $sResult = \implode(\array_values($aFileNames)); $sResult = \urldecode($sResult); - - if (\strlen($sCharset)) - { + if (\strlen($sCharset)) { $sResult = \MailSo\Base\Utils::ConvertEncoding($sResult, $sCharset, \MailSo\Base\Enumerations\Charset::UTF_8); } @@ -353,8 +338,8 @@ class BodyStructure $sContentTypeMain = ''; $sContentTypeSub = ''; $aSubParts = array(); - $aBodyParams = array(); - $sName = ''; + $aContentTypeParams = array(); + $sFileName = ''; $sCharset = ''; // \MailSo\Base\Enumerations\Charset::UTF_8 ? $sContentID = ''; $sDescription = ''; @@ -362,20 +347,16 @@ class BodyStructure $iSize = 0; $iExtraItemPos = 0; // list index of items which have no well-established position (such as 0, 1, 5, etc). - if (\is_array($aBodyStructure[0])) - { + if (\is_array($aBodyStructure[0])) { // Process multipart body structure $sContentTypeMain = 'multipart'; $sContentTypeSub = 'mixed'; // primary default $sSubPartIDPrefix = ''; - if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) - { + if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) { // This multi-part is root part of message. $sSubPartIDPrefix = $sPartID; $sPartID .= 'TEXT'; - } - else if (\strlen($sPartID)) - { + } else if (\strlen($sPartID)) { // This multi-part is a part of another multi-part. $sSubPartIDPrefix = $sPartID.'.'; } @@ -387,18 +368,15 @@ class BodyStructure ("text" "plain" ("charset" "utf-8") …) ("text" "html" …) */ - while ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) - { + while ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) { $oPart = self::NewInstance($aBodyStructure[$iExtraItemPos], $sSubPartIDPrefix.$iIndex); - if (!$oPart) - { + if (!$oPart) { return null; } // For multipart, we have no charset info in the part itself. Thus, // obtain charset from nested parts. - if (!$sCharset) - { + if (!$sCharset) { $sCharset = $oPart->Charset(); } @@ -411,26 +389,20 @@ class BodyStructure * Now process the subparts containter like: "alternative" ("boundary" "--boundary_id") … */ - if ($iExtraItemPos < \count($aBodyStructure)) - { - if (!\is_string($aBodyStructure[$iExtraItemPos])) - { + if ($iExtraItemPos < \count($aBodyStructure)) { + if (!\is_string($aBodyStructure[$iExtraItemPos])) { return null; } $sContentTypeSub = \strtolower($aBodyStructure[$iExtraItemPos]); ++$iExtraItemPos; - if ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) - { - $aBodyParams = self::getKeyValueListFromArrayList($aBodyStructure[$iExtraItemPos]); + if ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) { + $aContentTypeParams = self::getKeyValueListFromArrayList($aBodyStructure[$iExtraItemPos]); } } - } - else if (\is_string($aBodyStructure[0])) - { + } else if (\is_string($aBodyStructure[0])) { // Process simple (singlepart) body structure - if (7 > \count($aBodyStructure) || !\is_string($aBodyStructure[1])) - { + if (7 > \count($aBodyStructure) || !\is_string($aBodyStructure[1])) { return null; } @@ -438,39 +410,33 @@ class BodyStructure $sContentTypeSub = \strtolower($aBodyStructure[1]); $aBodyParamList = $aBodyStructure[2]; - if (\is_array($aBodyParamList)) - { - $aBodyParams = self::getKeyValueListFromArrayList($aBodyParamList); - if (isset($aBodyParams['charset'])) - { - $sCharset = $aBodyParams['charset']; + if (\is_array($aBodyParamList)) { + $aContentTypeParams = self::getKeyValueListFromArrayList($aBodyParamList); + if (isset($aContentTypeParams['charset'])) { + $sCharset = $aContentTypeParams['charset']; + } + $sFileName = self::decodeAttrParameter($aContentTypeParams, 'name', $sCharset); + if ($sFileName) { + $aContentTypeParams['name'] = $sFileName; } - - $sName = self::decodeAttrParameter($aBodyParams, 'name', $sCharset); } - if (null !== $aBodyStructure[3]) - { - if (!\is_string($aBodyStructure[3])) - { + if (null !== $aBodyStructure[3]) { + if (!\is_string($aBodyStructure[3])) { return null; } $sContentID = $aBodyStructure[3]; } - if (null !== $aBodyStructure[4]) - { - if (!\is_string($aBodyStructure[4])) - { + if (null !== $aBodyStructure[4]) { + if (!\is_string($aBodyStructure[4])) { return null; } $sDescription = $aBodyStructure[4]; } - if (null !== $aBodyStructure[5]) - { - if (!\is_string($aBodyStructure[5])) - { + if (null !== $aBodyStructure[5]) { + if (!\is_string($aBodyStructure[5])) { return null; } $sMailEncodingName = $aBodyStructure[5]; @@ -478,24 +444,20 @@ class BodyStructure $iSize = \is_numeric($aBodyStructure[6]) ? (int) $aBodyStructure[6] : -1; - if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) - { + if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) { // This is the only sub-part of the message (otherwise, it would be // one of sub-parts of a multi-part, and partID would already be fully set up). $sPartID .= '1'; } $iExtraItemPos = 7; - if ('text' === $sContentTypeMain) - { + if ('text' === $sContentTypeMain) { /** * A body type of type TEXT contains, immediately after the basic * fields, the size of the body in text lines. */ ++$iExtraItemPos; - } - else if ('message' === $sContentTypeMain && 'rfc822' === $sContentTypeSub) - { + } else if ('message' === $sContentTypeMain && 'rfc822' === $sContentTypeSub) { /** * A body type of type MESSAGE and subtype RFC822 contains, * immediately after the basic fields, the envelope structure, @@ -503,9 +465,7 @@ class BodyStructure */ $iExtraItemPos += 3; } - } - else - { + } else { return null; } @@ -515,18 +475,14 @@ class BodyStructure $sDisposition = ''; $sFileName = ''; - if ($iExtraItemPos < \count($aBodyStructure)) - { + if ($iExtraItemPos < \count($aBodyStructure)) { $aDispList = $aBodyStructure[$iExtraItemPos]; - if (\is_array($aDispList) && 1 < \count($aDispList)) - { - if (!\is_string($aDispList[0])) - { + if (\is_array($aDispList) && 1 < \count($aDispList)) { + if (!\is_string($aDispList[0])) { return null; } $sDisposition = $aDispList[0]; - if (\is_array($aDispList[1])) - { + if (\is_array($aDispList[1])) { $aDispositionParams = self::getKeyValueListFromArrayList($aDispList[1]); $sFileName = self::decodeAttrParameter($aDispositionParams, 'filename', $sCharset); } @@ -536,13 +492,13 @@ class BodyStructure $oStructure = new self; $oStructure->sContentType = \strtolower($sContentTypeMain.'/'.$sContentTypeSub); + $oStructure->aContentTypeParams = $aContentTypeParams; $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->sFileName = \MailSo\Base\Utils::Utf8Clear($sFileName); $oStructure->iSize = $iSize; $oStructure->sPartID = $sPartID; $oStructure->aSubParts = $aSubParts; @@ -571,12 +527,9 @@ class BodyStructure { $aDict = array(); $iLen = \count($aList); - if (0 === ($iLen % 2)) - { - for ($iIndex = 0; $iIndex < $iLen; $iIndex += 2) - { - if (\is_string($aList[$iIndex]) && \is_string($aList[$iIndex + 1])) - { + if (0 === ($iLen % 2)) { + for ($iIndex = 0; $iIndex < $iLen; $iIndex += 2) { + 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/Mail/Attachment.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php index 2f3f5635a..b4ac54835 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 @@ -99,6 +99,7 @@ class Attachment implements \JsonSerializable 'Uid' => (string) $this->iUid, 'MimeIndex' => (string) $this->oBodyStructure->PartID(), 'MimeType' => $this->oBodyStructure->ContentType(), + 'MimeTypeParams' => $this->oBodyStructure->ContentTypeParameters(), 'FileName' => \MailSo\Base\Utils::SecureFileName($this->oBodyStructure->FileName(true)), 'EstimatedSize' => $this->oBodyStructure->EstimatedSize(), 'Cid' => $this->oBodyStructure->ContentID(), 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 70e7c740b..6cc00245a 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,8 +11,8 @@ namespace MailSo\Mail; -use \MailSo\Base\Utils; -use \MailSo\Imap\Enumerations\FetchType; +use MailSo\Base\Utils; +use MailSo\Imap\Enumerations\FetchType; /** * @category MailSo diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Attachment.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Attachment.php index 10ef808da..b03a7e10b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Attachment.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Attachment.php @@ -151,15 +151,15 @@ class Attachment $oAttachmentPart->Headers->append( new Header(Enumerations\Header::CONTENT_TYPE, - $this->ContentType().';'. - (($oContentTypeParameters) ? ' '.$oContentTypeParameters->ToString() : '') + $this->ContentType(). + ($oContentTypeParameters ? '; '.$oContentTypeParameters : '') ) ); $oAttachmentPart->Headers->append( new Header(Enumerations\Header::CONTENT_DISPOSITION, - ($this->IsInline() ? 'inline' : 'attachment').';'. - (($oContentDispositionParameters) ? ' '.$oContentDispositionParameters->ToString() : '') + ($this->IsInline() ? 'inline' : 'attachment'). + ($oContentDispositionParameters ? '; '.$oContentDispositionParameters : '') ) ); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php index 8a9c84152..ba41125f7 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php @@ -200,6 +200,11 @@ class Email implements \JsonSerializable return \trim($sReturn); } + public function __toString() : string + { + return $this->ToString(); + } + #[\ReturnTypeWillChange] public function jsonSerialize() { diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/EmailCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/EmailCollection.php index b909f318f..adda686d7 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/EmailCollection.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/EmailCollection.php @@ -66,6 +66,11 @@ class EmailCollection extends \MailSo\Base\Collection return \implode(', ', $aReturn); } + public function __toString() : string + { + return $this->ToString(); + } + private function parseEmailAddresses(string $sRawEmails) : void { // $sRawEmails = \MailSo\Base\Utils::Trim($sRawEmails); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php index 8f361b586..8681b266a 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php @@ -126,13 +126,6 @@ class Header $this->oParameters->setParameter($sName, $sValue); } - private function wordWrapHelper(string $sValue, string $sGlue = "\r\n ") : string - { - return \trim(\substr(\wordwrap($this->NameWithDelimitrom().$sValue, - 74, $sGlue - ), \strlen($this->NameWithDelimitrom()))); - } - public function __toString() : string { $sResult = $this->sFullValue; @@ -159,11 +152,12 @@ class Header { $oEmailCollection = new EmailCollection($this->sFullValue); if ($oEmailCollection && $oEmailCollection->count()) { - $sResult = $oEmailCollection->ToString(true, false); + $sResult = $oEmailCollection->ToString(true); } } - return $this->NameWithDelimitrom().$this->wordWrapHelper($sResult); + // https://www.rfc-editor.org/rfc/rfc2822#section-2.1.1 + return \wordwrap($this->NameWithDelimitrom() . $sValue, 78, "\r\n "); } public function IsSubject() : bool diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php index 94f4e25a1..ab84d32d8 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php @@ -342,7 +342,7 @@ class Message extends Part } $oPart->Headers->append( new Header(Enumerations\Header::CONTENT_TYPE, - $aAttachments[0]->ContentType().'; '.$oParameters->ToString()) + $aAttachments[0]->ContentType().'; '.$oParameters) ); if ($resource = $aAttachments[0]->Resource()) { @@ -427,11 +427,6 @@ class Message extends Part foreach ($this->aHeadersValue as $sName => $mValue) { if (!($bWithoutBcc && \strtolower(Enumerations\Header::BCC) === \strtolower($sName))) { - if (\is_object($mValue)) { - if ($mValue instanceof EmailCollection || $mValue instanceof Email || $mValue instanceof ParameterCollection) { - $mValue = $mValue->ToString(); - } - } $oRootPart->Headers->SetByName($sName, (string) $mValue); } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Parameter.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Parameter.php index ba4f8dc85..2e20ffe09 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Parameter.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Parameter.php @@ -15,7 +15,7 @@ namespace MailSo\Mime; * @category MailSo * @package Mime */ -class Parameter +class Parameter implements \JsonSerializable { private string $sName; @@ -88,4 +88,18 @@ class Parameter return $this->sName . '="' . $this->sValue . '"'; } + + public function __toString() : string + { + return $this->ToString(); + } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array( + 'name' => $this->sName, + 'value' => $this->sValue + ); + } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/ParameterCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/ParameterCollection.php index 9028be20e..af34acb40 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/ParameterCollection.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/ParameterCollection.php @@ -81,10 +81,29 @@ class ParameterCollection extends \MailSo\Base\Collection $aResult[] = $sLine; } } - return \count($aResult) ? \implode('; ', $aResult) : ''; } + public function __toString() : string + { + return $this->ToString(); + } + +/* + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + $aResult = array(); + foreach ($this as $oParam) { + $aResult[$oParam->Name()] = $oParam->Value(); + } + return array( + '@Object' => 'Collection/ParameterCollection', + '@Collection' => $aResult + ); + } +*/ + private function reParseParameters() : void { $aDataToReParse = $this->getArrayCopy(); 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 b15cc24c8..4ecd1c137 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 @@ -51,7 +51,12 @@ class Part { return \trim(\strtolower($this->Headers->ValueByName(Enumerations\Header::CONTENT_TYPE))); } - +/* + public function ContentTypeParameters() : ParameterCollection + { + return $this->Headers->ParametersByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE); + } +*/ public function ContentID() : string { return \trim($this->Headers->ValueByName(Enumerations\Header::CONTENT_ID));