mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-07 08:27:03 +03:00
Added isSMimeEncrypted() and isSMimeSigned() #259
This commit is contained in:
parent
3648b3eb60
commit
7b3102a6b3
5 changed files with 77 additions and 27 deletions
|
|
@ -127,9 +127,9 @@ class BodyStructure implements \JsonSerializable
|
||||||
return 'text/html' === $this->sContentType || 'text/plain' === $this->sContentType;
|
return 'text/html' === $this->sContentType || 'text/plain' === $this->sContentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsPgpEncrypted() : bool
|
// https://datatracker.ietf.org/doc/html/rfc3156#section-4
|
||||||
|
public function isPgpEncrypted() : bool
|
||||||
{
|
{
|
||||||
// https://datatracker.ietf.org/doc/html/rfc3156#section-4
|
|
||||||
return 'multipart/encrypted' === $this->sContentType
|
return 'multipart/encrypted' === $this->sContentType
|
||||||
&& !empty($this->aContentTypeParams['protocol'])
|
&& !empty($this->aContentTypeParams['protocol'])
|
||||||
&& 'application/pgp-encrypted' === \strtolower(\trim($this->aContentTypeParams['protocol']))
|
&& 'application/pgp-encrypted' === \strtolower(\trim($this->aContentTypeParams['protocol']))
|
||||||
|
|
@ -140,20 +140,39 @@ class BodyStructure implements \JsonSerializable
|
||||||
// && 'Version: 1' === $this->aSubParts[0]->Body()
|
// && 'Version: 1' === $this->aSubParts[0]->Body()
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsPgpSigned() : bool
|
// https://datatracker.ietf.org/doc/html/rfc3156#section-5
|
||||||
|
public function isPgpSigned() : bool
|
||||||
{
|
{
|
||||||
// https://datatracker.ietf.org/doc/html/rfc3156#section-5
|
|
||||||
return 'multipart/signed' === $this->sContentType
|
return 'multipart/signed' === $this->sContentType
|
||||||
&& !empty($this->aContentTypeParams['protocol'])
|
&& !empty($this->aContentTypeParams['protocol'])
|
||||||
&& 'application/pgp-signature' === \strtolower(\trim($this->aContentTypeParams['protocol']))
|
&& 'application/pgp-signature' === \strtolower(\trim($this->aContentTypeParams['protocol']))
|
||||||
// The multipart/signed body MUST consist of exactly two parts.
|
// The multipart/signed body MUST consist of exactly two parts.
|
||||||
&& 2 === \count($this->aSubParts)
|
&& 2 === \count($this->aSubParts)
|
||||||
&& $this->aSubParts[1]->IsPgpSignature();
|
&& 'application/pgp-signature' === $this->aSubParts[1]->ContentType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsPgpSignature() : bool
|
// https://datatracker.ietf.org/doc/html/rfc2633#section-3.3
|
||||||
|
public function isSMimeEncrypted() : bool
|
||||||
{
|
{
|
||||||
return \in_array($this->sContentType, ['application/pgp-signature', 'application/pkcs7-signature']);
|
$type = \strtolower(\trim($this->aContentTypeParams['smime-type'] ?? ''));
|
||||||
|
return 'application/pkcs7-mime' === $this->sContentType
|
||||||
|
&& !empty($this->aContentTypeParams['smime-type'])
|
||||||
|
&& ('enveloped-data' === $type || 'authenveloped-data' === $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc8551.html#section-3.5
|
||||||
|
public function isSMimeSigned() : bool
|
||||||
|
{
|
||||||
|
return ('multipart/signed' === $this->sContentType
|
||||||
|
&& !empty($this->aContentTypeParams['protocol'])
|
||||||
|
&& 'application/pkcs7-signature' === \strtolower(\trim($this->aContentTypeParams['protocol']))
|
||||||
|
// The multipart/signed body MUST consist of exactly two parts.
|
||||||
|
&& 2 === \count($this->aSubParts)
|
||||||
|
&& 'application/pkcs7-signature' === $this->aSubParts[1]->ContentType()
|
||||||
|
) || ('application/pkcs7-mime' === $this->sContentType
|
||||||
|
&& !empty($this->aContentTypeParams['smime-type'])
|
||||||
|
&& 'signed-data' === \strtolower(\trim($this->aContentTypeParams['smime-type']))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsAttachment() : bool
|
public function IsAttachment() : bool
|
||||||
|
|
@ -192,7 +211,7 @@ class BodyStructure implements \JsonSerializable
|
||||||
if (!$aParts) {
|
if (!$aParts) {
|
||||||
$gEncryptedParts = $this->SearchByContentType('multipart/encrypted');
|
$gEncryptedParts = $this->SearchByContentType('multipart/encrypted');
|
||||||
foreach ($gEncryptedParts as $oPart) {
|
foreach ($gEncryptedParts as $oPart) {
|
||||||
if ($oPart->IsPgpEncrypted() && $oPart->SubParts()[1]->isInline()) {
|
if ($oPart->isPgpEncrypted() && $oPart->SubParts()[1]->isInline()) {
|
||||||
return array($oPart->SubParts()[1]);
|
return array($oPart->SubParts()[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -242,7 +261,7 @@ class BodyStructure implements \JsonSerializable
|
||||||
{
|
{
|
||||||
return $this->SearchByCallback(function ($oItem, $oParent) {
|
return $this->SearchByCallback(function ($oItem, $oParent) {
|
||||||
// return $oItem->IsAttachment();
|
// return $oItem->IsAttachment();
|
||||||
return $oItem->IsAttachment() && (!$oParent || !$oParent->IsPgpEncrypted());
|
return $oItem->IsAttachment() && (!$oParent || !$oParent->isPgpEncrypted());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ class MailClient
|
||||||
/*
|
/*
|
||||||
$gSignatureParts = $oBodyStructure->SearchByContentType('multipart/signed');
|
$gSignatureParts = $oBodyStructure->SearchByContentType('multipart/signed');
|
||||||
foreach ($gSignatureParts as $oPart) {
|
foreach ($gSignatureParts as $oPart) {
|
||||||
if ($oPart->IsPgpSigned()) {
|
if ($oPart->isPgpSigned()) {
|
||||||
// An empty section specification refers to the entire message, including the header.
|
// 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].
|
// But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME].
|
||||||
$aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->SubParts()[0]->PartID().'.MIME]';
|
$aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->SubParts()[0]->PartID().'.MIME]';
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,13 @@ class Message implements \JsonSerializable
|
||||||
$aThreadUnseenUIDs = [];
|
$aThreadUnseenUIDs = [];
|
||||||
|
|
||||||
private ?array $DraftInfo = null;
|
private ?array $DraftInfo = null;
|
||||||
|
|
||||||
private ?array $pgpSigned = null;
|
private ?array $pgpSigned = null;
|
||||||
private ?array $pgpEncrypted = null;
|
private ?array $pgpEncrypted = null;
|
||||||
|
|
||||||
|
private ?array $smimeSigned = null;
|
||||||
|
private ?array $smimeEncrypted = null;
|
||||||
|
|
||||||
private ?\MailSo\Mime\EmailCollection
|
private ?\MailSo\Mime\EmailCollection
|
||||||
$oFrom = null,
|
$oFrom = null,
|
||||||
$oSender = null,
|
$oSender = null,
|
||||||
|
|
@ -309,26 +313,44 @@ class Message implements \JsonSerializable
|
||||||
if ($oBodyStructure) {
|
if ($oBodyStructure) {
|
||||||
$gEncryptedParts = $oBodyStructure->SearchByContentType('multipart/encrypted');
|
$gEncryptedParts = $oBodyStructure->SearchByContentType('multipart/encrypted');
|
||||||
foreach ($gEncryptedParts as $oPart) {
|
foreach ($gEncryptedParts as $oPart) {
|
||||||
if ($oPart->IsPgpEncrypted()) {
|
if ($oPart->isPgpEncrypted()) {
|
||||||
$oMessage->pgpEncrypted = [
|
$oMessage->pgpEncrypted = [
|
||||||
'partId' => $oPart->SubParts()[1]->PartID()
|
'partId' => $oPart->SubParts()[1]->PartID()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$gEncryptedParts = $oBodyStructure->SearchByContentType('application/pkcs7-mime');
|
||||||
|
foreach ($gEncryptedParts as $oPart) {
|
||||||
|
if ($oPart->isSMimeEncrypted()) {
|
||||||
|
$oMessage->smimeEncrypted = [
|
||||||
|
'partId' => $oPart->PartID()
|
||||||
|
];
|
||||||
|
} else if ($oPart->isSMimeSigned()) {
|
||||||
|
$oMessage->smimeSigned = [
|
||||||
|
'sigPartId' => $oPart->PartID(),
|
||||||
|
'micAlg' => $oHeaders ? (string) $oHeaders->ParameterValue(MimeHeader::CONTENT_TYPE, 'micalg') : ''
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$gSignatureParts = $oBodyStructure->SearchByContentType('multipart/signed');
|
$gSignatureParts = $oBodyStructure->SearchByContentType('multipart/signed');
|
||||||
foreach ($gSignatureParts as $oPart) {
|
foreach ($gSignatureParts as $oPart) {
|
||||||
if (!$oPart->IsPgpSigned()) {
|
if ($oPart->isPgpSigned()) {
|
||||||
continue;
|
$oMessage->pgpSigned = [
|
||||||
|
// /?/Raw/&q[]=/0/Download/&q[]=/...
|
||||||
|
// /?/Raw/&q[]=/0/View/&q[]=/...
|
||||||
|
'bodyPartId' => $oPart->SubParts()[0]->PartID(),
|
||||||
|
'sigPartId' => $oPart->SubParts()[1]->PartID(),
|
||||||
|
'micAlg' => $oHeaders ? (string) $oHeaders->ParameterValue(MimeHeader::CONTENT_TYPE, 'micalg') : ''
|
||||||
|
];
|
||||||
|
} else if ($oPart->isSMimeSigned()) {
|
||||||
|
$oMessage->smimeSigned = [
|
||||||
|
'bodyPartId' => $oPart->SubParts()[0]->PartID(),
|
||||||
|
'sigPartId' => $oPart->SubParts()[1]->PartID(),
|
||||||
|
'micAlg' => $oHeaders ? (string) $oHeaders->ParameterValue(MimeHeader::CONTENT_TYPE, 'micalg') : ''
|
||||||
|
];
|
||||||
}
|
}
|
||||||
$oPgpSignaturePart = $oPart->SubParts()[1];
|
|
||||||
$oMessage->pgpSigned = [
|
|
||||||
// /?/Raw/&q[]=/0/Download/&q[]=/...
|
|
||||||
// /?/Raw/&q[]=/0/View/&q[]=/...
|
|
||||||
'bodyPartId' => $oPart->SubParts()[0]->PartID(),
|
|
||||||
'sigPartId' => $oPgpSignaturePart->PartID(),
|
|
||||||
'micAlg' => $oHeaders ? (string) $oHeaders->ParameterValue(MimeHeader::CONTENT_TYPE, 'micalg') : ''
|
|
||||||
];
|
|
||||||
/*
|
/*
|
||||||
// An empty section specification refers to the entire message, including the header.
|
// 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].
|
// But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME].
|
||||||
|
|
|
||||||
|
|
@ -80,20 +80,29 @@ class Part
|
||||||
return $sResult;
|
return $sResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsPgpSigned() : bool
|
// https://datatracker.ietf.org/doc/html/rfc3156#section-5
|
||||||
|
public function isPgpSigned() : bool
|
||||||
{
|
{
|
||||||
// https://datatracker.ietf.org/doc/html/rfc3156#section-5
|
|
||||||
$header = $this->Headers->GetByName(Enumerations\Header::CONTENT_TYPE);
|
$header = $this->Headers->GetByName(Enumerations\Header::CONTENT_TYPE);
|
||||||
return $header
|
return $header
|
||||||
&& \preg_match('#multipart/signed.+protocol=["\']?application/pgp-signature#si', $header->FullValue())
|
&& \preg_match('#multipart/signed.+protocol=["\']?application/pgp-signature#si', $header->FullValue())
|
||||||
// The multipart/signed body MUST consist of exactly two parts.
|
// The multipart/signed body MUST consist of exactly two parts.
|
||||||
&& 2 === \count($this->SubParts)
|
&& 2 === \count($this->SubParts)
|
||||||
&& $this->SubParts[1]->IsPgpSignature();
|
&& 'application/pgp-signature' === $this->SubParts[1]->ContentType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsPgpSignature() : bool
|
// https://www.rfc-editor.org/rfc/rfc8551.html#section-3.5
|
||||||
|
public function isSMimeSigned() : bool
|
||||||
{
|
{
|
||||||
return \in_array($this->ContentType(), array('application/pgp-signature', 'application/pkcs7-signature'));
|
$header = $this->Headers->GetByName(Enumerations\Header::CONTENT_TYPE);
|
||||||
|
return ($header
|
||||||
|
&& \preg_match('#multipart/signed.+protocol=["\']?application/pkcs7-signature#si', $header->FullValue())
|
||||||
|
// The multipart/signed body MUST consist of exactly two parts.
|
||||||
|
&& 2 === \count($this->SubParts)
|
||||||
|
&& 'application/pkcs7-signature' === $this->SubParts[1]->ContentType()
|
||||||
|
) || ($header
|
||||||
|
&& \preg_match('#application/pkcs7-mime.+smime-type=["\']?signed-data#si', $header->FullValue())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function FromFile(string $sFileName) : ?self
|
public static function FromFile(string $sFileName) : ?self
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ trait Pgp
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($oPart && $oPart->IsPgpSigned()) {
|
if ($oPart && $oPart->isPgpSigned()) {
|
||||||
// $GPG->verifyStream($oPart->SubParts[0]->Body, \stream_get_contents($oPart->SubParts[1]->Body));
|
// $GPG->verifyStream($oPart->SubParts[0]->Body, \stream_get_contents($oPart->SubParts[1]->Body));
|
||||||
// $result['signatures'] = $oPart->SubParts[0];
|
// $result['signatures'] = $oPart->SubParts[0];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue