mirror of
https://github.com/the-djmaze/snappymail.git
synced 2025-01-09 08:17:53 +08:00
#89 Detect and verify PGP cleartext/clearsigned messages
This commit is contained in:
parent
d35473841f
commit
b8d898bc8a
2 changed files with 46 additions and 42 deletions
|
@ -77,8 +77,6 @@ class Message implements \JsonSerializable
|
|||
|
||||
$aThreads = array(),
|
||||
|
||||
$bTextPartIsTrimmed = false,
|
||||
|
||||
$aPgpSigned = null,
|
||||
$bPgpEncrypted = false;
|
||||
|
||||
|
@ -538,27 +536,28 @@ class Message implements \JsonSerializable
|
|||
$sText = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oPart->PartID().']');
|
||||
if (null === $sText)
|
||||
{
|
||||
// TextPartIsTrimmed ?
|
||||
$sText = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oPart->PartID().']<0>');
|
||||
if (\is_string($sText) && \strlen($sText))
|
||||
{
|
||||
$oMessage->bTextPartIsTrimmed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_string($sText) && \strlen($sText))
|
||||
{
|
||||
$sTextCharset = $oPart->Charset();
|
||||
if (empty($sTextCharset))
|
||||
{
|
||||
$sTextCharset = $sCharset;
|
||||
}
|
||||
|
||||
$sTextCharset = Utils::NormalizeCharset($sTextCharset, true);
|
||||
|
||||
$sText = Utils::DecodeEncodingValue($sText, $oPart->MailEncodingName());
|
||||
$sText = Utils::ConvertEncoding($sText, $sTextCharset, \MailSo\Base\Enumerations\Charset::UTF_8);
|
||||
$sText = Utils::ConvertEncoding($sText,
|
||||
Utils::NormalizeCharset($oPart->Charset() ?: $sCharset, true),
|
||||
\MailSo\Base\Enumerations\Charset::UTF_8
|
||||
);
|
||||
$sText = Utils::Utf8Clear($sText);
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc4880#section-7
|
||||
// Cleartext Signature
|
||||
if (!$oMessage->aPgpSigned && \str_contains($sText, '-----BEGIN PGP SIGNED MESSAGE-----'))
|
||||
{
|
||||
$oMessage->aPgpSigned = [
|
||||
'BodyPartId' => $oPart->PartID()
|
||||
];
|
||||
}
|
||||
|
||||
if ('text/html' === $oPart->ContentType())
|
||||
{
|
||||
$aHtmlParts[] = $sText;
|
||||
|
@ -578,19 +577,6 @@ class Message implements \JsonSerializable
|
|||
$oMessage->sHtml = \implode('<br />', $aHtmlParts);
|
||||
$oMessage->sPlain = \trim(\implode("\n", $aPlainParts));
|
||||
|
||||
$aMatch = array();
|
||||
if (!$oMessage->aPgpSigned && \preg_match('/-----BEGIN PGP SIGNATURE-----.+?-----END PGP SIGNATURE-----/ism', $oMessage->sPlain, $aMatch))
|
||||
{
|
||||
$oMessage->aPgpSigned = [
|
||||
// /?/Raw/&q[]=/0/Download/&q[]=/...
|
||||
// /?/Raw/&q[]=/0/View/&q[]=/...
|
||||
'BodyPartId' => 0,
|
||||
'SigPartId' => 0,
|
||||
'MicAlg' => '',
|
||||
'Signature' => \trim($aMatch[0])
|
||||
];
|
||||
}
|
||||
|
||||
$oMessage->bPgpEncrypted = !$oMessage->bPgpEncrypted && false !== \stripos($oMessage->sPlain, '-----BEGIN PGP MESSAGE-----');
|
||||
|
||||
unset($aHtmlParts, $aPlainParts, $aMatch);
|
||||
|
@ -602,6 +588,7 @@ class Message implements \JsonSerializable
|
|||
$oMessage->oAttachments = new AttachmentCollection;
|
||||
foreach ($gAttachmentsParts as /* @var $oAttachmentItem \MailSo\Imap\BodyStructure */ $oAttachmentItem)
|
||||
{
|
||||
// if ('application/pgp-keys' === $oAttachmentItem->ContentType()) import ???
|
||||
$oMessage->oAttachments->append(
|
||||
Attachment::NewBodyStructureInstance($oMessage->sFolder, $oMessage->iUid, $oAttachmentItem)
|
||||
);
|
||||
|
|
|
@ -652,6 +652,9 @@ trait Messages
|
|||
return $this->DefaultResponse(__FUNCTION__, $mResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://datatracker.ietf.org/doc/html/rfc3156#section-5
|
||||
*/
|
||||
public function DoMessagePgpVerify() : array
|
||||
{
|
||||
$sFolderName = $this->GetActionParam('Folder', '');
|
||||
|
@ -665,23 +668,37 @@ trait Messages
|
|||
$oImapClient = $this->MailClient()->ImapClient();
|
||||
$oImapClient->FolderExamine($sFolderName);
|
||||
|
||||
$oFetchResponse = $oImapClient->Fetch([
|
||||
$aParts = [
|
||||
FetchType::BODY_PEEK.'['.$sBodyPartId.']'
|
||||
];
|
||||
if ($sSigPartId) {
|
||||
// An empty section specification refers to the entire message, including the header.
|
||||
// But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME].
|
||||
FetchType::BODY_PEEK.'['.$sBodyPartId.'.MIME]',
|
||||
FetchType::BODY_PEEK.'['.$sBodyPartId.']',
|
||||
FetchType::BODY_PEEK.'['.$sSigPartId.']'
|
||||
], $iUid, true)[0];
|
||||
$aParts[] = FetchType::BODY_PEEK.'['.$sBodyPartId.'.MIME]';
|
||||
$aParts[] = FetchType::BODY_PEEK.'['.$sSigPartId.']';
|
||||
}
|
||||
|
||||
$result = [
|
||||
'Text' => \preg_replace('/\\R/s', "\r\n",
|
||||
$oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.'.MIME]')
|
||||
. $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']')
|
||||
),
|
||||
'Signature' => preg_replace('/[^\x00-\x7F]/', '',
|
||||
$oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sSigPartId.']')
|
||||
)
|
||||
];
|
||||
$oFetchResponse = $oImapClient->Fetch($aParts, $iUid, true)[0];
|
||||
|
||||
if ($sSigPartId) {
|
||||
$result = [
|
||||
'Text' => \preg_replace('/\\R/s', "\r\n",
|
||||
$oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.'.MIME]')
|
||||
. $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']')
|
||||
),
|
||||
'Signature' => preg_replace('/[^\x00-\x7F]/', '',
|
||||
$oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sSigPartId.']')
|
||||
)
|
||||
];
|
||||
} else {
|
||||
// clearsigned text
|
||||
$result = [
|
||||
'Text' => \preg_replace('/\\R/s', "\r\n",
|
||||
$oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']')
|
||||
),
|
||||
'Signature' => false
|
||||
];
|
||||
}
|
||||
|
||||
if (\class_exists('gnupg')) {
|
||||
$info = $this->GnuPG()->verify($result['Text'], $result['Signature'])[0];
|
||||
|
|
Loading…
Reference in a new issue