mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 15:45:55 +08:00
Improved detection of PGP/MIME encrypted messages instead of showing them as attachments
This commit is contained in:
parent
48febdb414
commit
0ed7f000d6
|
@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue