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 31ec3dd47..01ce04d31 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 @@ -168,6 +168,19 @@ class BodyStructure return 'doc' === \MailSo\Base\Utils::ContentTypeType($this->sContentType, $this->sFileName); } + public function IsPgpEncrypted() : bool + { + // 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'])) + // The multipart/encrypted body MUST consist of exactly two parts. + && 2 === \count($this->aSubParts) + && 'application/pgp-encrypted' === $this->aSubParts[0]->ContentType() + && 'application/octet-stream' === $this->aSubParts[1]->ContentType(); +// && 'Version: 1' === $this->aSubParts[0]->Body() + } + public function IsPgpSigned() : bool { // https://datatracker.ietf.org/doc/html/rfc3156#section-5 @@ -187,44 +200,21 @@ class BodyStructure public function IsAttachBodyPart() : bool { - return 'attachment' === $this->sDisposition - || ( + return 'application/pgp-encrypted' !== $this->sContentType + && ( + 'attachment' === $this->sDisposition || ( !\str_starts_with($this->sContentType, 'multipart/') && 'text/html' !== $this->sContentType && 'text/plain' !== $this->sContentType - ); - - return $bResult; + ) + ); } public function IsFlowedFormat() : bool { - $bResult = !empty($this->aBodyParams['format']) && - 'flowed' === \strtolower(\trim($this->aBodyParams['format'])); - - if ($bResult && \in_array($this->sMailEncodingName, array('base64', 'quoted-printable'))) - { - $bResult = false; - } - - return $bResult; - } - - public function SearchInlineEncryptedPart() : ?self - { - if ('multipart/encrypted' === \strtolower($this->sContentType)) - { - $aSearchParts = \iterator_to_array($this->SearchByCallback(function ($oItem) { - return $oItem->IsInline(); - })); - - if (1 === \count($aSearchParts) && isset($aSearchParts[0])) - { - return $aSearchParts[0]; - } - } - - return null; + return !empty($this->aBodyParams['format']) + && 'flowed' === \strtolower(\trim($this->aBodyParams['format'])) + && !\in_array($this->sMailEncodingName, array('base64', 'quoted-printable')); } public function GetHtmlAndPlainParts() : array @@ -238,9 +228,15 @@ class BodyStructure return \array_merge([$aParts->current()], \iterator_to_array($aParts)); } - $oPart = $this->SearchInlineEncryptedPart(); - if ($oPart instanceof self) { - return array($oPart); + /** + * No text found, is it encrypted? + * If so, just return that. + */ + $gEncryptedParts = $this->SearchByContentType('multipart/encrypted'); + foreach ($gEncryptedParts as $oPart) { + if ($oPart->IsPgpEncrypted() && $oPart->SubParts()[1]->IsInline()) { + return array($oPart->SubParts()[1]); + } } return []; @@ -267,20 +263,21 @@ class BodyStructure * @param mixed $fCallback */ // public function SearchByCallback($fCallback) : \Generator - public function SearchByCallback($fCallback) : iterable + public function SearchByCallback($fCallback, $parent = null) : iterable { - if ($fCallback($this)) { + if ($fCallback($this, $parent)) { yield $this; } foreach ($this->aSubParts as /* @var $oSubPart \MailSo\Imap\BodyStructure */ $oSubPart) { - yield from $oSubPart->SearchByCallback($fCallback); + yield from $oSubPart->SearchByCallback($fCallback, $this); } } public function SearchAttachmentsParts() : iterable { - return $this->SearchByCallback(function ($oItem) { - return $oItem->IsAttachBodyPart(); + return $this->SearchByCallback(function ($oItem, $oParent) { +// return $oItem->IsAttachBodyPart(); + return $oItem->IsAttachBodyPart() && (!$oParent || !$oParent->IsPgpEncrypted()); }); } 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 e07557764..9effeaf44 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 @@ -490,9 +490,8 @@ class MailClient if (!\in_array(\strtolower(MessageFlag::SEEN), $aFlags)) { $iUid = (int) $oFetchResponse->GetFetchValue(FetchType::UID); - $sHeaders = $oFetchResponse->GetHeaderFieldsValue(); - $oHeaders = new \MailSo\Mime\HeaderCollection($sHeaders); + $oHeaders = new \MailSo\Mime\HeaderCollection($oFetchResponse->GetHeaderFieldsValue()); $sContentTypeCharset = $oHeaders->ParameterValue(MimeHeader::CONTENT_TYPE, MimeParameter::CHARSET); 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 b6eb063da..b26a6ac74 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 @@ -78,6 +78,7 @@ class Message implements \JsonSerializable $aThreads = array(), $aPgpSigned = null, + $aPgpEncrypted = null, $bPgpEncrypted = false; function __construct() @@ -100,9 +101,14 @@ class Message implements \JsonSerializable return $this->aPgpSigned; } + public function PgpEncrypted() : ?array + { + return $this->aPgpEncrypted; + } + public function isPgpEncrypted() : bool { - return $this->bPgpEncrypted; + return $this->bPgpEncrypted || $this->aPgpEncrypted; } public function Folder() : string @@ -491,16 +497,28 @@ class Message implements \JsonSerializable if ($oBodyStructure) { + $gEncryptedParts = $oBodyStructure->SearchByContentType('multipart/encrypted'); + foreach ($gEncryptedParts as $oPart) { + if ($oPart->IsPgpEncrypted()) { + if (!$oMessage->aPgpEncrypted) { + $oMessage->aPgpEncrypted = []; + } + $oMessage->aPgpEncrypted = [ + 'PartId' => $oPart->SubParts()[1]->PartID() + ]; + } + } + $gSignatureParts = $oBodyStructure->SearchByContentType('multipart/signed'); foreach ($gSignatureParts as $oPart) { if (!$oPart->IsPgpSigned()) { continue; } - $oPgpSignaturePart = $oBodyStructure->SubParts()[1]; + $oPgpSignaturePart = $oPart->SubParts()[1]; $oMessage->aPgpSigned = [ // /?/Raw/&q[]=/0/Download/&q[]=/... // /?/Raw/&q[]=/0/View/&q[]=/... - 'BodyPartId' => $oBodyStructure->SubParts()[0]->PartID(), + 'BodyPartId' => $oPart->SubParts()[0]->PartID(), 'SigPartId' => $oPgpSignaturePart->PartID(), 'MicAlg' => (string) $oHeaders->ParameterValue(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'micalg') ]; @@ -517,7 +535,7 @@ class Message implements \JsonSerializable } $sPgpSignatureText = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->aPgpSigned['SigPartId'].']'); if ($sPgpSignatureText && 0 < \strpos($sPgpSignatureText, 'BEGIN PGP SIGNATURE')) { - $oMessage->aPgpSigned['Signature'] = $oBodyStructure->SubParts()[0]->PartID(); + $oMessage->aPgpSigned['Signature'] = $oPart->SubParts()[0]->PartID(); } */ break; 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 9962f6244..468e298f3 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 @@ -257,6 +257,7 @@ trait Response // $this->GetCapa(false, Capa::OPEN_PGP) || $this->GetCapa(false, Capa::GNUPG) $mResult['isPgpEncrypted'] = $mResponse->isPgpEncrypted(); $mResult['PgpSigned'] = $mResponse->PgpSigned(); + $mResult['PgpEncrypted'] = $mResponse->PgpEncrypted(); $mResult['HasExternals'] = $bHasExternals; $mResult['HasInternals'] = \count($aFoundCIDs) || \count($aFoundContentLocationUrls);