Revamp MailSo\Mime\Message for sending proper multipart/encrypted

This commit is contained in:
the-djmaze 2022-02-03 21:41:59 +01:00
parent 5514264914
commit fb1c0dd606
6 changed files with 174 additions and 149 deletions

View file

@ -454,8 +454,8 @@ class ComposePopupView extends AbstractViewPopup {
if (this.mailvelope && 'mailvelope' === this.viewArea()) {
this.mailvelope.encrypt(this.allRecipients()).then(armored => {
params.Html = '';
params.Text = armored;
params.Text = params.Html = '';
params.Encrypted = armored;
send();
});
} else if (encrypt) {
@ -474,7 +474,7 @@ class ComposePopupView extends AbstractViewPopup {
send();
} else {
this.sendError(true);
this.sendErrorDesc(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { ERROR: 'Signing failed' }));
this.sendErrorDesc(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { ERROR: 'Encryption failed' }));
this.sending(false);
}
});
@ -489,8 +489,8 @@ class ComposePopupView extends AbstractViewPopup {
if (detached) {
// Append headers
params.Text = [
'Content-Transfer-Encoding: base64',
'Content-Type: text/plain; charset="utf-8"; protected-headers="v1"',
'Content-Transfer-Encoding: base64',
// 'From: Demo <demo@snappymail.eu>',
// 'To: Demo <demo@snappymail.eu>',
// 'Subject: text detached signed'

View file

@ -19,7 +19,6 @@ namespace MailSo\Mime\Enumerations;
abstract class MimeType
{
const TEXT_PLAIN = 'text/plain';
const TEXT_HTML = 'text/html';
const MULTIPART_ALTERNATIVE = 'multipart/alternative';
const MULTIPART_RELATED = 'multipart/related';

View file

@ -15,18 +15,13 @@ namespace MailSo\Mime;
* @category MailSo
* @package Mime
*/
class Message
class Message extends Part
{
/**
* @var array
*/
private $aHeadersValue = array();
/**
* @var array
*/
private $aAlternativeParts = array();
/**
* @var AttachmentCollection
*/
@ -44,6 +39,7 @@ class Message
function __construct()
{
parent::__construct();
$this->oAttachmentCollection = new AttachmentCollection;
}
@ -293,28 +289,7 @@ class Message
return $this;
}
public function AddPlain(string $sPlain) : self
{
return $this->AddAlternative(Enumerations\MimeType::TEXT_PLAIN, $sPlain);
}
public function AddHtml(string $sHtml) : self
{
return $this->AddAlternative(Enumerations\MimeType::TEXT_HTML, $sHtml);
}
public function AddAlternative(string $sContentType, string $sData) : self
{
$this->aAlternativeParts[] = array(
$sContentType,
\preg_replace('/\\r?\\n/', "\r\n", \trim($sData)),
\MailSo\Base\Enumerations\Encoding::QUOTED_PRINTABLE_LOWER,
array()
);
return $this;
}
private function generateNewBoundary() : string
public function generateNewBoundary() : string
{
return '--='.\MailSo\Config::$BoundaryPrefix.
\rand(100, 999).'_'.\rand(100000000, 999999999).'.'.\time();
@ -447,7 +422,6 @@ class Message
$aAlternativeData[0].'; '.$oParameters->ToString())
);
$oAlternativePart->Body = null;
if (isset($aAlternativeData[1]))
{
if (\is_resource($aAlternativeData[1]))
@ -493,60 +467,6 @@ class Message
return $oAlternativePart;
}
private function createNewMessageRelatedBody(Part $oIncPart) : Part
{
$oResultPart = null;
foreach ($this->oAttachmentCollection as $oAttachment) {
if ($oAttachment->IsLinked()) {
if (!$oResultPart) {
$oResultPart = new Part;
$oResultPart->Headers->append(
new Header(Enumerations\Header::CONTENT_TYPE,
Enumerations\MimeType::MULTIPART_RELATED.'; '.
(new ParameterCollection)->Add(
new Parameter(
Enumerations\Parameter::BOUNDARY,
$this->generateNewBoundary())
)->ToString()
)
);
$oResultPart->SubParts->append($oIncPart);
}
$oResultPart->SubParts->append($this->createNewMessageAttachmentBody($oAttachment));
}
}
return $oResultPart ?: $oIncPart;
}
private function createNewMessageMixedBody(Part $oIncPart) : Part
{
$oResultPart = null;
foreach ($this->oAttachmentCollection as $oAttachment) {
if (!$oAttachment->IsLinked()) {
if (!$oResultPart) {
$oResultPart = new Part;
$oResultPart->Headers->AddByName(Enumerations\Header::CONTENT_TYPE,
Enumerations\MimeType::MULTIPART_MIXED.'; '.
(new ParameterCollection)->Add(
new Parameter(
Enumerations\Parameter::BOUNDARY,
$this->generateNewBoundary())
)->ToString()
);
$oResultPart->SubParts->append($oIncPart);
}
$oResultPart->SubParts->append($this->createNewMessageAttachmentBody($oAttachment));
}
}
return $oResultPart ?: $oIncPart;
}
private function setDefaultHeaders(Part $oIncPart, bool $bWithoutBcc = false) : Part
{
if (!isset($this->aHeadersValue[Enumerations\Header::DATE]))
@ -590,54 +510,48 @@ class Message
}
/**
* @return resource
* @return resource|bool
*/
public function ToStream(bool $bWithoutBcc = false)
{
$oPart = null;
if (1 < \count($this->aAlternativeParts))
$oResultPart = null;
if (\count($this->SubParts))
{
$oPart = new Part;
if (1 !== \count($this->SubParts)) {
throw new \Exception('Invalid part structure');
}
// createNewMessageRelatedBody
foreach ($this->oAttachmentCollection as $oAttachment) {
if ($oAttachment->IsLinked()) {
if (!$oResultPart) {
$oResultPart = new Part;
$oResultPart->Headers->append(
new Header(Enumerations\Header::CONTENT_TYPE,
Enumerations\MimeType::MULTIPART_RELATED.'; '.
(new ParameterCollection)->Add(
new Parameter(
Enumerations\Parameter::BOUNDARY,
$this->generateNewBoundary())
)->ToString()
)
);
$oResultPart->SubParts = $this->SubParts;
$this->SubParts = new PartCollection();
$this->SubParts->append($oResultPart);
}
$oPart->Headers->append(
new Header(Enumerations\Header::CONTENT_TYPE,
Enumerations\MimeType::MULTIPART_ALTERNATIVE.'; '.
(new ParameterCollection)->Add(
new Parameter(
Enumerations\Parameter::BOUNDARY,
$this->generateNewBoundary())
)->ToString()
)
);
foreach ($this->aAlternativeParts as $aAlternativeData)
{
$oAlternativePart = $this->createNewMessageAlternativePartBody($aAlternativeData);
if ($oAlternativePart)
{
$oPart->SubParts->append($oAlternativePart);
$oResultPart->SubParts->append($this->createNewMessageAttachmentBody($oAttachment));
}
unset($oAlternativePart);
}
}
else if (1 === \count($this->aAlternativeParts))
{
$oAlternativePart = $this->createNewMessageAlternativePartBody($this->aAlternativeParts[0]);
if ($oAlternativePart)
{
$oPart = $oAlternativePart;
}
}
if (!$oPart)
else
{
if ($this->bAddEmptyTextPart)
{
$oPart = $this->createNewMessageAlternativePartBody(array(
Enumerations\MimeType::TEXT_PLAIN, null
));
$oPart = new Part;
$oPart->Headers->AddByName(Enumerations\Header::CONTENT_TYPE, 'text/plain; charset="utf-8"');
$oPart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString('');
$this->SubParts->append($oPart);
}
else
{
@ -645,18 +559,40 @@ class Message
if (1 === \count($aAttachments) && isset($aAttachments[0]))
{
$this->oAttachmentCollection->Clear();
$oPart = $this->createNewMessageAlternativePartBody(array(
$aAttachments[0]->ContentType(), $aAttachments[0]->Resource(),
'', $aAttachments[0]->CustomContentTypeParams()
$aAttachments[0]->ContentType(),
$aAttachments[0]->Resource(),
'',
$aAttachments[0]->CustomContentTypeParams()
));
$this->SubParts->append($oPart);
}
}
}
$oPart = $this->createNewMessageRelatedBody($oPart);
$oPart = $this->createNewMessageMixedBody($oPart);
$oPart = $this->setDefaultHeaders($oPart, $bWithoutBcc);
// createNewMessageMixedBody
foreach ($this->oAttachmentCollection as $oAttachment) {
if (!$oAttachment->IsLinked()) {
if (!$oResultPart) {
$oResultPart = new Part;
$oResultPart->Headers->AddByName(Enumerations\Header::CONTENT_TYPE,
Enumerations\MimeType::MULTIPART_MIXED.'; '.
(new ParameterCollection)->Add(
new Parameter(
Enumerations\Parameter::BOUNDARY,
$this->generateNewBoundary())
)->ToString()
);
$oResultPart->SubParts = $this->SubParts;
$this->SubParts = new PartCollection();
$this->SubParts->append($oResultPart);
}
$oResultPart->SubParts->append($this->createNewMessageAttachmentBody($oAttachment));
}
}
$oPart = $this->setDefaultHeaders($this->SubParts[0], $bWithoutBcc);
return $oPart->ToStream();
}
/*

View file

@ -520,7 +520,7 @@ class Part
}
/**
* @return resource
* @return resource|bool
*/
public function ToStream()
{
@ -542,7 +542,7 @@ class Part
"\r\n"
);
if (0 < $this->SubParts->Count())
if ($this->SubParts->Count())
{
$sBoundary = $this->HeaderBoundary();
if (\strlen($sBoundary))

View file

@ -24,7 +24,7 @@ class PartCollection extends \MailSo\Base\Collection
}
/**
* @return resource
* @return resource|bool|null
*/
public function ToStream(string $sBoundary)
{

View file

@ -9,6 +9,7 @@ use RainLoop\Notifications;
use MailSo\Imap\SequenceSet;
use MailSo\Imap\Enumerations\FetchType;
use MailSo\Imap\Enumerations\MessageFlag;
use MailSo\Mime\Part as MimePart;
trait Messages
{
@ -1010,7 +1011,14 @@ trait Messages
$this->Plugins()->RunHook('filter.read-receipt-message-plain', array($oAccount, $oMessage, &$sText));
$oMessage->AddPlain($sText);
$oPart = new MimePart;
$oPart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/plain; charset="utf-8"');
$oPart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'quoted-printable');
$oPart->Body = \MailSo\Base\StreamWrappers\Binary::CreateStream(
\MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\R/', "\r\n", \trim($sText))),
'convert.quoted-printable-encode'
);
$this->SubParts->append($oPart);
$this->Plugins()->RunHook('filter.build-read-receipt-message', array($oMessage, $oAccount));
@ -1097,25 +1105,107 @@ trait Messages
$aFoundDataURL = array();
$aFoundContentLocationUrls = array();
$sHtml = $this->GetActionParam('Html', '');
$sText = $this->GetActionParam('Text', '');
if ($sHtml) {
if ($sHtml = $this->GetActionParam('Html', '')) {
$oPart = new MimePart;
$oPart->Headers->AddByName(
\MailSo\Mime\Enumerations\Header::CONTENT_TYPE,
\MailSo\Mime\Enumerations\MimeType::MULTIPART_ALTERNATIVE
. '; ' . (new \MailSo\Mime\ParameterCollection)->Add(
new \MailSo\Mime\Parameter(\MailSo\Mime\Enumerations\Parameter::BOUNDARY, $oMessage->generateNewBoundary())
)->ToString()
);
$oMessage->SubParts->append($oPart);
$sHtml = \MailSo\Base\HtmlUtils::BuildHtml($sHtml, $aFoundCids, $aFoundDataURL, $aFoundContentLocationUrls);
$this->Plugins()->RunHook('filter.message-html', array($oAccount, $oMessage, &$sHtml));
$sTextConverted = \MailSo\Base\HtmlUtils::ConvertHtmlToPlain($sText);
$this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sTextConverted));
$oMessage->AddPlain($sTextConverted);
$oMessage->AddHtml($sText);
$oAlternativePart = new MimePart;
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/html; charset=utf-8');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'quoted-printable');
$oAlternativePart->Body = \MailSo\Base\StreamWrappers\Binary::CreateStream(
\MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\R/', "\r\n", \trim($sHtml))),
'convert.quoted-printable-encode'
);
$oPart->SubParts->append($oAlternativePart);
$sPlain = \MailSo\Base\HtmlUtils::ConvertHtmlToPlain($sHtml);
$this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sPlain));
$oAlternativePart = new MimePart;
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/plain; charset=utf-8');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'quoted-printable');
$oAlternativePart->Body = \MailSo\Base\StreamWrappers\Binary::CreateStream(
\MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\R/', "\r\n", \trim($sPlain))),
'convert.quoted-printable-encode'
);
$oPart->SubParts->append($oAlternativePart);
unset($oAlternativePart);
unset($sHtml);
unset($sPlain);
} else if ($sEncrypted = $this->GetActionParam('Encrypted', '')) {
$oPart = new MimePart;
$oPart->Headers->AddByName(
\MailSo\Mime\Enumerations\Header::CONTENT_TYPE,
'multipart/encrypted; ' . (new \MailSo\Mime\ParameterCollection)->Add(
new \MailSo\Mime\Parameter(\MailSo\Mime\Enumerations\Parameter::BOUNDARY, $oMessage->generateNewBoundary())
)->ToString() . '; protocol="application/pgp-encrypted"'
);
$oMessage->SubParts->append($oPart);
$oAlternativePart = new MimePart;
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/pgp-encrypted');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_DISPOSITION, 'attachment');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit');
$oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString('Version: 1');
$oPart->SubParts->append($oAlternativePart);
$oAlternativePart = new MimePart;
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/octet-stream');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_DISPOSITION, 'inline; filename="msg.asc"');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit');
$oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\R/', "\r\n", \trim($sEncrypted)));
$oPart->SubParts->append($oAlternativePart);
unset($oAlternativePart);
unset($sEncrypted);
} else {
$sSignature = $this->GetActionParam('Signature', null);
if ($sSignature) {
// MimeType::MULTIPART_SIGNED
// MimeType::APPLICATION_PGP_SIGNATURE
$oMessage->AddPlain($sText);
$sPlain = $this->GetActionParam('Text', '');
if ($sSignature = $this->GetActionParam('Signature', null)) {
$oPart = new MimePart;
$oPart->Headers->AddByName(
\MailSo\Mime\Enumerations\Header::CONTENT_TYPE,
'multipart/signed; ' . (new ParameterCollection)->Add(
new \MailSo\Mime\Parameter(\MailSo\Mime\Enumerations\Parameter::BOUNDARY, $oMessage->generateNewBoundary())
)->ToString() . '; micalg="pgp-sha256"; protocol="application/pgp-signature"'
);
$oMessage->SubParts->append($oPart);
$oAlternativePart = new MimePart;
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/plain; charset="utf-8"; protected-headers="v1"');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'base64');
$oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\R/', "\r\n", \trim($sPlain)));
$oPart->SubParts->append($oAlternativePart);
$oAlternativePart = new MimePart;
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/pgp-signature; name="signature.asc"');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit');
$oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\R/', "\r\n", \trim($sSignature)));
$oPart->SubParts->append($oAlternativePart);
} else {
$this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sText));
$oMessage->AddPlain($sText);
$this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sPlain));
$oAlternativePart = new MimePart;
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/plain; charset="utf-8"');
$oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'quoted-printable');
$oAlternativePart->Body = \MailSo\Base\StreamWrappers\Binary::CreateStream(
\MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\R/', "\r\n", \trim($sPlain))),
'convert.quoted-printable-encode'
);
$oMessage->SubParts->append($oAlternativePart);
}
unset($oAlternativePart);
unset($sSignature);
unset($sPlain);
}
$aAttachments = $this->GetActionParam('Attachments', null);