feat(smime): decrypt incoming messages
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
This commit is contained in:
Родитель
3a5aa49ff8
Коммит
6fcb90796f
|
@ -12,7 +12,7 @@
|
|||
- **🙈 We’re not reinventing the wheel!** Based on the great [Horde](https://horde.org) libraries.
|
||||
- **📬 Want to host your own mail server?** We do not have to reimplement this as you could set up [Mail-in-a-Box](https://mailinabox.email)!
|
||||
]]></description>
|
||||
<version>2.3.0-alpha.4</version>
|
||||
<version>2.3.0-alpha.5</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Greta Doçi</author>
|
||||
<author homepage="https://github.com/nextcloud/groupware">Nextcloud Groupware Team</author>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* @author Thomas I <thomas@oatr.be>
|
||||
* @author Thomas Mueller <thomas.mueller@tmit.eu>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -34,7 +35,6 @@
|
|||
namespace OCA\Mail;
|
||||
|
||||
use Horde_Imap_Client_Exception;
|
||||
use Horde_Imap_Client_Mailbox;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use Horde_Mail_Transport;
|
||||
use Horde_Mail_Transport_Smtphorde;
|
||||
|
@ -174,20 +174,6 @@ class Account implements JsonSerializable {
|
|||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param string $folderId
|
||||
* @return Mailbox
|
||||
*
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function getMailbox($folderId) {
|
||||
return new Mailbox(
|
||||
$this->getImapConnection(),
|
||||
new Horde_Imap_Client_Mailbox($folderId)
|
||||
);
|
||||
}
|
||||
|
||||
#[ReturnTypeWillChange]
|
||||
public function jsonSerialize() {
|
||||
return $this->account->toJson();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* @author Christoph Wurst <wurst.christoph@gmail.com>
|
||||
* @author Lukas Reschke <lukas@statuscode.ch>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -24,113 +25,24 @@
|
|||
|
||||
namespace OCA\Mail;
|
||||
|
||||
use Horde_Imap_Client_Data_Fetch;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use Horde_Mime_Part;
|
||||
|
||||
class Attachment {
|
||||
/**
|
||||
* @param \Horde_Imap_Client_Socket $conn
|
||||
* @param \Horde_Imap_Client_Mailbox $mailBox
|
||||
* @param int $messageUid
|
||||
* @param string $attachmentId
|
||||
*/
|
||||
public function __construct($conn, $mailBox, $messageUid, $attachmentId) {
|
||||
$this->conn = $conn;
|
||||
$this->mailBox = $mailBox;
|
||||
$this->messageUid = $messageUid;
|
||||
$this->attachmentId = $attachmentId;
|
||||
private Horde_Mime_Part $mimePart;
|
||||
|
||||
$this->load();
|
||||
public function __construct(Horde_Mime_Part $mimePart) {
|
||||
$this->mimePart = $mimePart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var \Horde_Imap_Client_Socket
|
||||
*/
|
||||
private $conn;
|
||||
|
||||
/**
|
||||
* @var \Horde_Imap_Client_Mailbox
|
||||
*/
|
||||
private $mailBox;
|
||||
private $messageUid;
|
||||
private $attachmentId;
|
||||
|
||||
/**
|
||||
* @var \Horde_Mime_Part
|
||||
*/
|
||||
private $mimePart;
|
||||
|
||||
private function load(): void {
|
||||
$fetch_query = new \Horde_Imap_Client_Fetch_Query();
|
||||
$fetch_query->bodyPart($this->attachmentId);
|
||||
$fetch_query->mimeHeader($this->attachmentId);
|
||||
|
||||
// $list is an array of Horde_Imap_Client_Data_Fetch objects.
|
||||
$ids = new \Horde_Imap_Client_Ids($this->messageUid);
|
||||
$headers = $this->conn->fetch($this->mailBox, $fetch_query, ['ids' => $ids]);
|
||||
if (!isset($headers[$this->messageUid])) {
|
||||
throw new DoesNotExistException('Unable to load the attachment.');
|
||||
}
|
||||
/** @var Horde_Imap_Client_Data_Fetch $fetch */
|
||||
$fetch = $headers[$this->messageUid];
|
||||
/** @var \Horde_Mime_Headers $mimeHeaders */
|
||||
$mimeHeaders = $fetch->getMimeHeader($this->attachmentId, Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
|
||||
|
||||
$this->mimePart = new \Horde_Mime_Part();
|
||||
|
||||
// Serve all files with a content-disposition of "attachment" to prevent Cross-Site Scripting
|
||||
$this->mimePart->setDisposition('attachment');
|
||||
|
||||
// Extract headers from part
|
||||
$contentDisposition = $mimeHeaders->getValue('content-disposition', \Horde_Mime_Headers::VALUE_PARAMS);
|
||||
if (!is_null($contentDisposition) && isset($contentDisposition['filename'])) {
|
||||
$this->mimePart->setDispositionParameter('filename', $contentDisposition['filename']);
|
||||
} else {
|
||||
$contentDisposition = $mimeHeaders->getValue('content-type', \Horde_Mime_Headers::VALUE_PARAMS);
|
||||
if (isset($contentDisposition['name'])) {
|
||||
$this->mimePart->setContentTypeParameter('name', $contentDisposition['name']);
|
||||
}
|
||||
}
|
||||
|
||||
/* Content transfer encoding. */
|
||||
if ($tmp = $mimeHeaders->getValue('content-transfer-encoding')) {
|
||||
$this->mimePart->setTransferEncoding($tmp);
|
||||
}
|
||||
|
||||
/* Content type */
|
||||
if (strstr($mimeHeaders->getValue('content-type'), 'text/calendar')) {
|
||||
$this->mimePart->setType('text/calendar');
|
||||
if ($this->mimePart->getContentTypeParameter('name') === null) {
|
||||
$this->mimePart->setContentTypeParameter('name', 'calendar.ics');
|
||||
}
|
||||
} else {
|
||||
// To prevent potential problems with the SOP we serve all files but calendar entries with the
|
||||
// MIME type "application/octet-stream"
|
||||
$this->mimePart->setType('application/octet-stream');
|
||||
}
|
||||
|
||||
$body = $fetch->getBodyPart($this->attachmentId);
|
||||
$this->mimePart->setContents($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getContents() {
|
||||
public function getContents(): string {
|
||||
return $this->mimePart->getContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName() {
|
||||
public function getName(): ?string {
|
||||
return $this->mimePart->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType() {
|
||||
public function getType(): string {
|
||||
return $this->mimePart->getType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace OCA\Mail\Contracts;
|
|||
use Horde_Imap_Client;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Attachment;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Db\Tag;
|
||||
|
@ -249,6 +250,15 @@ interface IMailManager {
|
|||
*/
|
||||
public function getMailAttachments(Account $account, Mailbox $mailbox, Message $message) : array;
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Mailbox $mailbox
|
||||
* @param Message $message
|
||||
* @param string $attachmentId
|
||||
* @return Attachment
|
||||
*/
|
||||
public function getMailAttachment(Account $account, Mailbox $mailbox, Message $message, string $attachmentId): Attachment;
|
||||
|
||||
/**
|
||||
* @param string $imapLabel
|
||||
* @param string $userId
|
||||
|
|
|
@ -211,12 +211,13 @@ class MessagesController extends Controller {
|
|||
|
||||
$client = $this->clientFactory->getClient($account);
|
||||
try {
|
||||
$json = $this->mailManager->getImapMessage(
|
||||
$imapMessage = $this->mailManager->getImapMessage(
|
||||
$client,
|
||||
$account,
|
||||
$mailbox,
|
||||
$message->getUid(), true
|
||||
)->getFullMessage($id);
|
||||
);
|
||||
$json = $imapMessage->getFullMessage($id);
|
||||
$rawMessage = $this->mailManager->getSource(
|
||||
$client,
|
||||
$account,
|
||||
|
@ -243,7 +244,13 @@ class MessagesController extends Controller {
|
|||
$json['isSenderTrusted'] = $this->isSenderTrusted($message);
|
||||
|
||||
$smimeData = new SmimeData();
|
||||
$smimeData->setIsEncrypted($message->isEncrypted() || $imapMessage->isEncrypted());
|
||||
if ($imapMessage->isSigned()) {
|
||||
$smimeData->setIsSigned(true);
|
||||
$smimeData->setSignatureIsValid($imapMessage->isSignatureValid());
|
||||
}
|
||||
try {
|
||||
// TODO: possibly merge with code in ImapMessageFetcher to handle verification early
|
||||
$parsedMessage = Horde_Mime_Part::parseMessage($rawMessage, ['no_body' => true]);
|
||||
if ($parsedMessage->getType() === 'multipart/signed') {
|
||||
$smimeData->setIsSigned(true);
|
||||
|
@ -556,15 +563,12 @@ class MessagesController extends Controller {
|
|||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param int $accountId
|
||||
* @param string $folderId
|
||||
* @param int $id
|
||||
* @param string $attachmentId
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws ClientException
|
||||
* @throws ServiceException
|
||||
*/
|
||||
#[TrapError]
|
||||
public function downloadAttachment(int $id,
|
||||
|
@ -576,8 +580,13 @@ class MessagesController extends Controller {
|
|||
} catch (DoesNotExistException $e) {
|
||||
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
$folder = $account->getMailbox($mailbox->getName());
|
||||
$attachment = $folder->getAttachment($message->getUid(), $attachmentId);
|
||||
|
||||
$attachment = $this->mailManager->getMailAttachment(
|
||||
$account,
|
||||
$mailbox,
|
||||
$message,
|
||||
$attachmentId,
|
||||
);
|
||||
|
||||
// Body party and embedded messages do not have a name
|
||||
if ($attachment->getName() === null) {
|
||||
|
@ -656,12 +665,22 @@ class MessagesController extends Controller {
|
|||
} catch (DoesNotExistException $e) {
|
||||
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
$folder = $account->getMailbox($mailbox->getName());
|
||||
|
||||
if ($attachmentId === '0') {
|
||||
$client = $this->clientFactory->getClient($account);
|
||||
try {
|
||||
$m = $this->mailManager->getImapMessage(
|
||||
$client,
|
||||
$account,
|
||||
$mailbox,
|
||||
$message->getUid(),
|
||||
true // Body is required for attachments
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
||||
// Save all attachments
|
||||
/* @var $m IMAPMessage */
|
||||
$m = $folder->getMessage($message->getUid());
|
||||
$attachmentIds = array_map(static function ($a) {
|
||||
return $a['id'];
|
||||
}, $m->attachments);
|
||||
|
@ -670,7 +689,12 @@ class MessagesController extends Controller {
|
|||
}
|
||||
|
||||
foreach ($attachmentIds as $aid) {
|
||||
$attachment = $folder->getAttachment($message->getUid(), $aid);
|
||||
$attachment = $this->mailManager->getMailAttachment(
|
||||
$account,
|
||||
$mailbox,
|
||||
$message,
|
||||
$aid,
|
||||
);
|
||||
|
||||
$fileName = $attachment->getName() ?? $this->l10n->t('Embedded message %s', [
|
||||
$aid,
|
||||
|
|
|
@ -6,6 +6,7 @@ declare(strict_types=1);
|
|||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
|
@ -82,6 +83,8 @@ use function json_encode;
|
|||
* @method void setImipProcessed(bool $imipProcessed)
|
||||
* @method bool isImipError()
|
||||
* @method void setImipError(bool $imipError)
|
||||
* @method bool|null isEncrypted()
|
||||
* @method void setEncrypted(bool|null $encrypted)
|
||||
*/
|
||||
class Message extends Entity implements JsonSerializable {
|
||||
private const MUTABLE_FLAGS = [
|
||||
|
@ -124,6 +127,11 @@ class Message extends Entity implements JsonSerializable {
|
|||
protected $imipProcessed = false;
|
||||
protected $imipError = false;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $encrypted;
|
||||
|
||||
/** @var AddressList */
|
||||
private $from;
|
||||
|
||||
|
@ -164,6 +172,7 @@ class Message extends Entity implements JsonSerializable {
|
|||
$this->addType('imipMessage', 'boolean');
|
||||
$this->addType('imipProcessed', 'boolean');
|
||||
$this->addType('imipError', 'boolean');
|
||||
$this->addType('encrypted', 'boolean');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -330,6 +339,7 @@ class Message extends Entity implements JsonSerializable {
|
|||
'threadRootId' => $this->getThreadRootId(),
|
||||
'imipMessage' => $this->isImipMessage(),
|
||||
'previewText' => $this->getPreviewText(),
|
||||
'encrypted' => ($this->isEncrypted() === true),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -490,6 +490,7 @@ class MessageMapper extends QBMapper {
|
|||
->set('structure_analyzed', $query->createNamedParameter(true, IQueryBuilder::PARAM_BOOL))
|
||||
->set('updated_at', $query->createNamedParameter($this->timeFactory->getTime(), IQueryBuilder::PARAM_INT))
|
||||
->set('imip_message', $query->createParameter('imip_message'))
|
||||
->set('encrypted', $query->createParameter('encrypted'))
|
||||
->where($query->expr()->andX(
|
||||
$query->expr()->eq('uid', $query->createParameter('uid')),
|
||||
$query->expr()->eq('mailbox_id', $query->createParameter('mailbox_id'))
|
||||
|
@ -516,6 +517,7 @@ class MessageMapper extends QBMapper {
|
|||
$previewText === null ? IQueryBuilder::PARAM_NULL : IQueryBuilder::PARAM_STR
|
||||
);
|
||||
$query->setParameter('imip_message', $message->isImipMessage(), IQueryBuilder::PARAM_BOOL);
|
||||
$query->setParameter('encrypted', $message->isEncrypted(), IQueryBuilder::PARAM_BOOL);
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
|
|
|
@ -81,24 +81,23 @@ class SmimeCertificateMapper extends QBMapper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Find all S/MIME certificates by email address.
|
||||
*
|
||||
* @param string $userId
|
||||
* @param string $emailAddress
|
||||
* @return SmimeCertificate
|
||||
* @return SmimeCertificate[]
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function findByEmailAddress(string $userId, string $emailAddress): SmimeCertificate {
|
||||
public function findAllByEmailAddress(string $userId, string $emailAddress): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where(
|
||||
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->eq('email_address', $qb->createNamedParameter($emailAddress, IQueryBuilder::PARAM_STR))
|
||||
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId)),
|
||||
$qb->expr()->eq('email_address', $qb->createNamedParameter($emailAddress)),
|
||||
);
|
||||
|
||||
return $this->findEntity($qb);
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SmimeDecryptException extends Exception {
|
||||
}
|
|
@ -0,0 +1,513 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\IMAP;
|
||||
|
||||
use Horde_Imap_Client_Base;
|
||||
use Horde_Imap_Client_Data_Envelope;
|
||||
use Horde_Imap_Client_Data_Fetch;
|
||||
use Horde_Imap_Client_Exception;
|
||||
use Horde_Imap_Client_Exception_NoSupportExtension;
|
||||
use Horde_Imap_Client_Fetch_Query;
|
||||
use Horde_Imap_Client_Ids;
|
||||
use Horde_Mime_Exception;
|
||||
use Horde_Mime_Headers;
|
||||
use Horde_Mime_Part;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\Model\IMAPMessage;
|
||||
use OCA\Mail\Service\Html;
|
||||
use OCA\Mail\Service\SmimeService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
|
||||
class ImapMessageFetcher {
|
||||
/** @var string[] */
|
||||
private array $attachmentsToIgnore = ['signature.asc', 'smime.p7s'];
|
||||
|
||||
private Html $htmlService;
|
||||
private SmimeService $smimeService;
|
||||
private string $userId;
|
||||
|
||||
// Conditional fetching/parsing
|
||||
private bool $loadBody = false;
|
||||
|
||||
private int $uid;
|
||||
private Horde_Imap_Client_Base $client;
|
||||
private string $htmlMessage = '';
|
||||
private string $plainMessage = '';
|
||||
private array $attachments = [];
|
||||
private array $inlineAttachments = [];
|
||||
private bool $hasAnyAttachment = false;
|
||||
private array $scheduling = [];
|
||||
private bool $hasHtmlMessage = false;
|
||||
private string $mailbox;
|
||||
private string $rawReferences = '';
|
||||
private string $dispositionNotificationTo = '';
|
||||
|
||||
public function __construct(int $uid,
|
||||
string $mailbox,
|
||||
Horde_Imap_Client_Base $client,
|
||||
string $userId,
|
||||
Html $htmlService,
|
||||
SmimeService $smimeService) {
|
||||
$this->uid = $uid;
|
||||
$this->mailbox = $mailbox;
|
||||
$this->client = $client;
|
||||
$this->userId = $userId;
|
||||
$this->htmlService = $htmlService;
|
||||
$this->smimeService = $smimeService;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure the fetcher to fetch the body of the message.
|
||||
*
|
||||
* @param bool $value
|
||||
* @return $this
|
||||
*/
|
||||
public function withBody(bool $value): ImapMessageFetcher {
|
||||
$this->loadBody = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Imap_Client_Data_Fetch|null $fetch
|
||||
* Will be reused if no body is requested.
|
||||
* It should at least contain envelope, flags, imapDate and headerText.
|
||||
* Otherwise, some data might not be parsed correctly.
|
||||
* @return IMAPMessage
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
* @throws Horde_Mime_Exception
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function fetchMessage(?Horde_Imap_Client_Data_Fetch $fetch = null): IMAPMessage {
|
||||
$ids = new Horde_Imap_Client_Ids($this->uid);
|
||||
|
||||
$isSigned = false;
|
||||
$signatureIsValid = false;
|
||||
$isEncrypted = false;
|
||||
|
||||
if ($this->loadBody) {
|
||||
// Ignore given query because lots of data needs to be fetched anyway
|
||||
// TODO: reuse given query if beneficial for performance and worth the refactoring effort
|
||||
$query = new Horde_Imap_Client_Fetch_Query();
|
||||
$query->envelope();
|
||||
$query->structure();
|
||||
$query->flags();
|
||||
$query->imapDate();
|
||||
$query->headerText([
|
||||
'peek' => true,
|
||||
]);
|
||||
$this->smimeService->addEncryptionCheckQueries($query);
|
||||
|
||||
$headers = $this->client->fetch($this->mailbox, $query, ['ids' => $ids]);
|
||||
/** @var Horde_Imap_Client_Data_Fetch $fetch */
|
||||
$fetch = $headers[$this->uid];
|
||||
if (is_null($fetch)) {
|
||||
throw new DoesNotExistException("This email ($this->uid) can't be found. Probably it was deleted from the server recently. Please reload.");
|
||||
}
|
||||
|
||||
// analyse the body part
|
||||
$structure = $fetch->getStructure();
|
||||
|
||||
$this->hasAnyAttachment = $this->hasAttachments($structure);
|
||||
|
||||
$isEncrypted = $this->smimeService->isEncrypted($fetch);
|
||||
if ($isEncrypted) {
|
||||
// Fetch and parse full text if message is encrypted in order to analyze the
|
||||
// structure. Conditional fetching doesn't work for encrypted messages.
|
||||
|
||||
$query = new Horde_Imap_Client_Fetch_Query();
|
||||
$query->envelope();
|
||||
$query->flags();
|
||||
$query->imapDate();
|
||||
$query->headerText([
|
||||
'peek' => true,
|
||||
]);
|
||||
$this->smimeService->addDecryptQueries($query, true);
|
||||
|
||||
$headers = $this->client->fetch($this->mailbox, $query, ['ids' => $ids]);
|
||||
/** @var Horde_Imap_Client_Data_Fetch $fullTextFetch */
|
||||
$fullTextFetch = $headers[$this->uid];
|
||||
if (is_null($fullTextFetch)) {
|
||||
throw new DoesNotExistException("This email ($this->uid) can't be found. Probably it was deleted from the server recently. Please reload.");
|
||||
}
|
||||
|
||||
$decryptedText = $this->smimeService->decryptDataFetch($fullTextFetch, $this->userId);
|
||||
$structure = Horde_Mime_Part::parseMessage($decryptedText, [
|
||||
'forcemime' => true,
|
||||
]);
|
||||
} elseif ($structure->getType() === 'application/pkcs7-mime'
|
||||
&& $structure->getContentTypeParameter('smime-type') === 'signed-data') {
|
||||
// Handle smime-type="signed-data" as the content is opaque until verified
|
||||
// TODO: Idea: also handle regular signed message verification here
|
||||
|
||||
$isSigned = true;
|
||||
|
||||
$query = new Horde_Imap_Client_Fetch_Query();
|
||||
$query->envelope();
|
||||
$query->flags();
|
||||
$query->imapDate();
|
||||
$query->headerText([
|
||||
'peek' => true,
|
||||
]);
|
||||
$query->fullText([
|
||||
'peek' => true,
|
||||
]);
|
||||
|
||||
$headers = $this->client->fetch($this->mailbox, $query, ['ids' => $ids]);
|
||||
/** @var Horde_Imap_Client_Data_Fetch $fullTextFetch */
|
||||
$fullTextFetch = $headers[$this->uid];
|
||||
if (is_null($fullTextFetch)) {
|
||||
throw new DoesNotExistException("This email ($this->uid) can't be found. Probably it was deleted from the server recently. Please reload.");
|
||||
}
|
||||
|
||||
$signedText = $fullTextFetch->getFullMsg();
|
||||
$signatureIsValid = $this->smimeService->verifyMessage($signedText);
|
||||
$signedContent = $this->smimeService->extractSignedContent($signedText);
|
||||
|
||||
$structure = Horde_Mime_Part::parseMessage($signedContent, [
|
||||
'forcemime' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
// debugging below
|
||||
$structure_type = $structure->getPrimaryType();
|
||||
if ($structure_type === 'multipart') {
|
||||
$i = 1;
|
||||
foreach ($structure->getParts() as $p) {
|
||||
$this->getPart($p, (string)$i++, $isEncrypted || $isSigned);
|
||||
}
|
||||
} else {
|
||||
$bodyPartId = $structure->findBody();
|
||||
if (!is_null($bodyPartId)) {
|
||||
$this->getPart($structure[$bodyPartId], $bodyPartId, $isEncrypted || $isSigned);
|
||||
}
|
||||
}
|
||||
} elseif (is_null($fetch)) {
|
||||
// Reuse given query or construct a new minimal one
|
||||
$query = new Horde_Imap_Client_Fetch_Query();
|
||||
$query->envelope();
|
||||
$query->flags();
|
||||
$query->imapDate();
|
||||
$query->headerText([
|
||||
'peek' => true,
|
||||
]);
|
||||
|
||||
$result = $this->client->fetch($this->mailbox, $query, ['ids' => $ids]);
|
||||
$fetch = $result[$this->uid];
|
||||
if (is_null($fetch)) {
|
||||
throw new DoesNotExistException("This email ($this->uid) can't be found. Probably it was deleted from the server recently. Please reload.");
|
||||
}
|
||||
}
|
||||
|
||||
$this->parseHeaders($fetch);
|
||||
|
||||
$envelope = $fetch->getEnvelope();
|
||||
return new IMAPMessage(
|
||||
$this->uid,
|
||||
$envelope->message_id,
|
||||
$fetch->getFlags(),
|
||||
AddressList::fromHorde($envelope->from),
|
||||
AddressList::fromHorde($envelope->to),
|
||||
AddressList::fromHorde($envelope->cc),
|
||||
AddressList::fromHorde($envelope->bcc),
|
||||
AddressList::fromHorde($envelope->reply_to),
|
||||
$this->decodeSubject($envelope),
|
||||
$this->plainMessage,
|
||||
$this->htmlMessage,
|
||||
$this->hasHtmlMessage,
|
||||
$this->attachments,
|
||||
$this->inlineAttachments,
|
||||
$this->hasAnyAttachment,
|
||||
$this->scheduling,
|
||||
$fetch->getImapDate(),
|
||||
$this->rawReferences,
|
||||
$this->dispositionNotificationTo,
|
||||
$envelope->in_reply_to,
|
||||
$isEncrypted,
|
||||
$isSigned,
|
||||
$signatureIsValid,
|
||||
$this->htmlService, // TODO: drop the html service dependency
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $p
|
||||
* @param string $partNo
|
||||
* @param bool $isEncrypted
|
||||
* @return void
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
*/
|
||||
private function getPart(Horde_Mime_Part $p, string $partNo, bool $isEncrypted): void {
|
||||
// iMIP messages
|
||||
// Handle text/calendar parts first because they might be attachments at the same time.
|
||||
// Otherwise, some of the following if-conditions might break the handling and treat iMIP
|
||||
// data like regular attachments.
|
||||
$allContentTypeParameters = $p->getAllContentTypeParameters();
|
||||
if ($p->getType() === 'text/calendar') {
|
||||
// Handle event data like a regular attachment
|
||||
// Outlook doesn't set a content disposition
|
||||
// We work around this by checking for the name only
|
||||
if ($p->getName() !== null) {
|
||||
$this->attachments[] = [
|
||||
'id' => $p->getMimeId(),
|
||||
'messageId' => $this->uid,
|
||||
'fileName' => $p->getName(),
|
||||
'mime' => $p->getType(),
|
||||
'size' => $p->getBytes(),
|
||||
'cid' => $p->getContentId(),
|
||||
'disposition' => $p->getDisposition()
|
||||
];
|
||||
}
|
||||
|
||||
// return if this is an event attachment only
|
||||
// the method parameter determines if this is a iMIP message
|
||||
if (!isset($allContentTypeParameters['method'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_array(strtoupper($allContentTypeParameters['method']), ['REQUEST', 'REPLY', 'CANCEL'])) {
|
||||
$this->scheduling[] = [
|
||||
'id' => $p->getMimeId(),
|
||||
'messageId' => $this->uid,
|
||||
'method' => strtoupper($allContentTypeParameters['method']),
|
||||
'contents' => $this->loadBodyData($p, $partNo, $isEncrypted),
|
||||
];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Regular attachments
|
||||
if ($p->isAttachment() || $p->getType() === 'message/rfc822') {
|
||||
$this->attachments[] = [
|
||||
'id' => $p->getMimeId(),
|
||||
'messageId' => $this->uid,
|
||||
'fileName' => $p->getName(),
|
||||
'mime' => $p->getType(),
|
||||
'size' => $p->getBytes(),
|
||||
'cid' => $p->getContentId(),
|
||||
'disposition' => $p->getDisposition()
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
// Inline attachments
|
||||
// Horde doesn't consider parts with content-disposition set to inline as
|
||||
// attachment so we need to use another way to get them.
|
||||
// We use these inline attachments to render a message's html body in $this->getHtmlBody()
|
||||
$filename = $p->getName();
|
||||
if ($p->getType() === 'message/rfc822' || isset($filename)) {
|
||||
if (in_array($filename, $this->attachmentsToIgnore)) {
|
||||
return;
|
||||
}
|
||||
$this->inlineAttachments[] = [
|
||||
'id' => $p->getMimeId(),
|
||||
'messageId' => $this->uid,
|
||||
'fileName' => $filename,
|
||||
'mime' => $p->getType(),
|
||||
'size' => $p->getBytes(),
|
||||
'cid' => $p->getContentId()
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
if ($p->getPrimaryType() === 'multipart') {
|
||||
$this->handleMultiPartMessage($p, $partNo, $isEncrypted);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($p->getType() === 'text/plain') {
|
||||
$this->handleTextMessage($p, $partNo, $isEncrypted);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($p->getType() === 'text/html') {
|
||||
$this->handleHtmlMessage($p, $partNo, $isEncrypted);
|
||||
return;
|
||||
}
|
||||
|
||||
// EMBEDDED MESSAGE
|
||||
// Many bounce notifications embed the original message as type 2,
|
||||
// but AOL uses type 1 (multipart), which is not handled here.
|
||||
// There are no PHP functions to parse embedded messages,
|
||||
// so this just appends the raw source to the main message.
|
||||
if ($p[0] === 'message') {
|
||||
$data = $this->loadBodyData($p, $partNo, $isEncrypted);
|
||||
$this->plainMessage .= trim($data) . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $part
|
||||
* @param string $partNo
|
||||
* @param bool $isFetched Body is already fetched and contained within the mime part object
|
||||
* @return void
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
*/
|
||||
private function handleMultiPartMessage(Horde_Mime_Part $part, string $partNo, bool $isFetched): void {
|
||||
$i = 1;
|
||||
foreach ($part->getParts() as $p) {
|
||||
$this->getPart($p, "$partNo.$i", $isFetched);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $p
|
||||
* @param string $partNo
|
||||
* @param bool $isFetched Body is already fetched and contained within the mime part object
|
||||
* @return void
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
*/
|
||||
private function handleTextMessage(Horde_Mime_Part $p, string $partNo, bool $isFetched): void {
|
||||
$data = $this->loadBodyData($p, $partNo, $isFetched);
|
||||
$this->plainMessage .= trim($data) . "\n\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $p
|
||||
* @param string $partNo
|
||||
* @param bool $isFetched Body is already fetched and contained within the mime part object
|
||||
* @return void
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
*/
|
||||
private function handleHtmlMessage(Horde_Mime_Part $p, string $partNo, bool $isFetched): void {
|
||||
$this->hasHtmlMessage = true;
|
||||
$data = $this->loadBodyData($p, $partNo, $isFetched);
|
||||
$this->htmlMessage .= $data . "<br><br>";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $p
|
||||
* @param string $partNo
|
||||
* @param bool $isFetched Body is already fetched and contained within the mime part object
|
||||
* @return string
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
*/
|
||||
private function loadBodyData(Horde_Mime_Part $p, string $partNo, bool $isFetched): string {
|
||||
if (!$isFetched) {
|
||||
$fetch_query = new Horde_Imap_Client_Fetch_Query();
|
||||
$ids = new Horde_Imap_Client_Ids($this->uid);
|
||||
|
||||
$fetch_query->bodyPart($partNo, [
|
||||
'peek' => true
|
||||
]);
|
||||
$fetch_query->bodyPartSize($partNo);
|
||||
$fetch_query->mimeHeader($partNo, [
|
||||
'peek' => true
|
||||
]);
|
||||
|
||||
$headers = $this->client->fetch($this->mailbox, $fetch_query, ['ids' => $ids]);
|
||||
/* @var $fetch Horde_Imap_Client_Data_Fetch */
|
||||
$fetch = $headers[$this->uid];
|
||||
if (is_null($fetch)) {
|
||||
throw new DoesNotExistException("Mail body for this mail($this->uid) could not be loaded");
|
||||
}
|
||||
|
||||
$mimeHeaders = $fetch->getMimeHeader($partNo, Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
|
||||
if ($enc = $mimeHeaders->getValue('content-transfer-encoding')) {
|
||||
$p->setTransferEncoding($enc);
|
||||
}
|
||||
|
||||
$data = $fetch->getBodyPart($partNo);
|
||||
|
||||
$p->setContents($data);
|
||||
}
|
||||
|
||||
$data = $p->getContents();
|
||||
if ($data === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Only convert encoding if it is explicitly specified in the header because text/calendar
|
||||
// data is utf-8 by default.
|
||||
$charset = $p->getContentTypeParameter('charset');
|
||||
if ($charset !== null && strtoupper($charset) !== 'UTF-8') {
|
||||
$data = mb_convert_encoding($data, 'UTF-8', $charset);
|
||||
}
|
||||
return (string)$data;
|
||||
}
|
||||
|
||||
private function hasAttachments(Horde_Mime_Part $part): bool {
|
||||
foreach ($part->getParts() as $p) {
|
||||
if ($p->isAttachment() || $p->getType() === 'message/rfc822') {
|
||||
return true;
|
||||
}
|
||||
if ($this->hasAttachments($p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function decodeSubject(Horde_Imap_Client_Data_Envelope $envelope): string {
|
||||
// Try a soft conversion first (some installations, eg: Alpine linux,
|
||||
// have issues with the '//IGNORE' option)
|
||||
$subject = $envelope->subject;
|
||||
$utf8 = iconv('UTF-8', 'UTF-8', $subject);
|
||||
if ($utf8 !== false) {
|
||||
return $utf8;
|
||||
}
|
||||
return iconv("UTF-8", "UTF-8//IGNORE", $subject);
|
||||
}
|
||||
|
||||
private function parseHeaders(Horde_Imap_Client_Data_Fetch $fetch): void {
|
||||
/** @var resource $headersStream */
|
||||
$headersStream = $fetch->getHeaderText('0', Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
|
||||
$parsedHeaders = Horde_Mime_Headers::parseHeaders($headersStream);
|
||||
fclose($headersStream);
|
||||
|
||||
$references = $parsedHeaders->getHeader('references');
|
||||
if ($references !== null) {
|
||||
$this->rawReferences = $references->value_single;
|
||||
}
|
||||
|
||||
$dispositionNotificationTo = $parsedHeaders->getHeader('disposition-notification-to');
|
||||
if ($dispositionNotificationTo !== null) {
|
||||
$this->dispositionNotificationTo = $dispositionNotificationTo->value_single;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\IMAP;
|
||||
|
||||
use Horde_Imap_Client_Base;
|
||||
use OCA\Mail\Service\Html;
|
||||
use OCA\Mail\Service\SmimeService;
|
||||
|
||||
class ImapMessageFetcherFactory {
|
||||
private Html $htmlService;
|
||||
private SmimeService $smimeService;
|
||||
|
||||
public function __construct(Html $htmlService,
|
||||
SmimeService $smimeService) {
|
||||
$this->htmlService = $htmlService;
|
||||
$this->smimeService = $smimeService;
|
||||
}
|
||||
|
||||
public function build(int $uid,
|
||||
string $mailbox,
|
||||
Horde_Imap_Client_Base $client,
|
||||
string $userId): ImapMessageFetcher {
|
||||
return new ImapMessageFetcher(
|
||||
$uid,
|
||||
$mailbox,
|
||||
$client,
|
||||
$userId,
|
||||
$this->htmlService,
|
||||
$this->smimeService,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -27,16 +28,21 @@ use Horde_Imap_Client;
|
|||
use Horde_Imap_Client_Base;
|
||||
use Horde_Imap_Client_Data_Fetch;
|
||||
use Horde_Imap_Client_Exception;
|
||||
use Horde_Imap_Client_Exception_NoSupportExtension;
|
||||
use Horde_Imap_Client_Fetch_Query;
|
||||
use Horde_Imap_Client_Search_Query;
|
||||
use Horde_Imap_Client_Ids;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use Horde_Mime_Exception;
|
||||
use Horde_Mime_Headers;
|
||||
use Horde_Mime_Mail;
|
||||
use Horde_Mime_Part;
|
||||
use Html2Text\Html2Text;
|
||||
use OCA\Mail\Attachment;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\Model\IMAPMessage;
|
||||
use OCA\Mail\Service\SmimeService;
|
||||
use OCA\Mail\Support\PerformanceLoggerTask;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -48,15 +54,21 @@ use function in_array;
|
|||
use function iterator_to_array;
|
||||
use function max;
|
||||
use function min;
|
||||
use function reset;
|
||||
use function sprintf;
|
||||
|
||||
class MessageMapper {
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
|
||||
public function __construct(LoggerInterface $logger) {
|
||||
private SMimeService $smimeService;
|
||||
private ImapMessageFetcherFactory $imapMessageFactory;
|
||||
|
||||
public function __construct(LoggerInterface $logger,
|
||||
SmimeService $smimeService,
|
||||
ImapMessageFetcherFactory $imapMessageFactory) {
|
||||
$this->logger = $logger;
|
||||
$this->smimeService = $smimeService;
|
||||
$this->imapMessageFactory = $imapMessageFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,8 +79,9 @@ class MessageMapper {
|
|||
public function find(Horde_Imap_Client_Base $client,
|
||||
string $mailbox,
|
||||
int $id,
|
||||
string $userId,
|
||||
bool $loadBody = false): IMAPMessage {
|
||||
$result = $this->findByIds($client, $mailbox, new Horde_Imap_Client_Ids([$id]), $loadBody);
|
||||
$result = $this->findByIds($client, $mailbox, new Horde_Imap_Client_Ids([$id]), $userId, $loadBody);
|
||||
|
||||
if (count($result) === 0) {
|
||||
throw new DoesNotExistException("Message does not exist");
|
||||
|
@ -93,7 +106,8 @@ class MessageMapper {
|
|||
int $maxResults,
|
||||
int $highestKnownUid,
|
||||
LoggerInterface $logger,
|
||||
PerformanceLoggerTask $perf): array {
|
||||
PerformanceLoggerTask $perf,
|
||||
string $userId): array {
|
||||
/**
|
||||
* To prevent memory exhaustion, we don't want to just ask for a list of
|
||||
* all UIDs and limit them client-side. Instead, we can (hopefully
|
||||
|
@ -179,7 +193,7 @@ class MessageMapper {
|
|||
* there is nothing to fetch in $highestKnownUid:$upper
|
||||
*/
|
||||
$logger->debug("Range for findAll did not find any messages. Trying again with a succeeding range");
|
||||
return $this->findAll($client, $mailbox, $maxResults, $upper, $logger, $perf);
|
||||
return $this->findAll($client, $mailbox, $maxResults, $upper, $logger, $perf, $userId);
|
||||
}
|
||||
$uidCandidates = array_filter(
|
||||
array_map(
|
||||
|
@ -211,7 +225,8 @@ class MessageMapper {
|
|||
$messages = $this->findByIds(
|
||||
$client,
|
||||
$mailbox,
|
||||
new Horde_Imap_Client_Ids($fetchRange)
|
||||
new Horde_Imap_Client_Ids($fetchRange),
|
||||
$userId,
|
||||
);
|
||||
$perf->step('find IMAP messages by UID');
|
||||
return [
|
||||
|
@ -222,12 +237,23 @@ class MessageMapper {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Imap_Client_Base $client
|
||||
* @param string $mailbox
|
||||
* @param Horde_Imap_Client_Ids $ids
|
||||
* @param string $userId
|
||||
* @param bool $loadBody
|
||||
* @return IMAPMessage[]
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
* @throws Horde_Mime_Exception
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function findByIds(Horde_Imap_Client_Base $client,
|
||||
string $mailbox,
|
||||
Horde_Imap_Client_Ids $ids,
|
||||
string $userId,
|
||||
bool $loadBody = false): array {
|
||||
$query = new Horde_Imap_Client_Fetch_Query();
|
||||
$query->envelope();
|
||||
|
@ -259,23 +285,16 @@ class MessageMapper {
|
|||
$this->logger->debug("findByIds in $mailbox got " . count($ids) . " UIDs ($range) and found " . count($fetchResults) . ". minFetched=$minFetched maxFetched=$maxFetched");
|
||||
}
|
||||
|
||||
return array_map(static function (Horde_Imap_Client_Data_Fetch $fetchResult) use ($client, $mailbox, $loadBody) {
|
||||
if ($loadBody) {
|
||||
return new IMAPMessage(
|
||||
$client,
|
||||
$mailbox,
|
||||
return array_map(function (Horde_Imap_Client_Data_Fetch $fetchResult) use ($client, $mailbox, $loadBody, $userId) {
|
||||
return $this->imapMessageFactory
|
||||
->build(
|
||||
$fetchResult->getUid(),
|
||||
null,
|
||||
$loadBody
|
||||
);
|
||||
} else {
|
||||
return new IMAPMessage(
|
||||
$client,
|
||||
$mailbox,
|
||||
$fetchResult->getUid(),
|
||||
$fetchResult
|
||||
);
|
||||
}
|
||||
$client,
|
||||
$userId,
|
||||
)
|
||||
->withBody($loadBody)
|
||||
->fetchMessage($fetchResult);
|
||||
}, $fetchResults);
|
||||
}
|
||||
|
||||
|
@ -422,23 +441,29 @@ class MessageMapper {
|
|||
* @param Horde_Imap_Client_Socket $client
|
||||
* @param string $mailbox
|
||||
* @param int $uid
|
||||
*
|
||||
* @param string $userId
|
||||
* @param bool $decrypt
|
||||
* @return string|null
|
||||
*
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function getFullText(Horde_Imap_Client_Socket $client,
|
||||
string $mailbox,
|
||||
int $uid): ?string {
|
||||
int $uid,
|
||||
string $userId,
|
||||
bool $decrypt = true): ?string {
|
||||
$query = new Horde_Imap_Client_Fetch_Query();
|
||||
$query->uid();
|
||||
$query->fullText([
|
||||
'peek' => true,
|
||||
]);
|
||||
|
||||
if ($decrypt) {
|
||||
$this->smimeService->addDecryptQueries($query);
|
||||
} else {
|
||||
$query->fullText([ 'peek' => true ]);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = iterator_to_array($client->fetch($mailbox, $query, [
|
||||
$result = $client->fetch($mailbox, $query, [
|
||||
'ids' => new Horde_Imap_Client_Ids($uid),
|
||||
]), false);
|
||||
]);
|
||||
} catch (Horde_Imap_Client_Exception $e) {
|
||||
throw new ServiceException(
|
||||
"Could not fetch message source: " . $e->getMessage(),
|
||||
|
@ -447,23 +472,38 @@ class MessageMapper {
|
|||
);
|
||||
}
|
||||
|
||||
$msg = array_map(static function (Horde_Imap_Client_Data_Fetch $result) {
|
||||
return $result->getFullMsg();
|
||||
}, $result);
|
||||
|
||||
if (empty($msg)) {
|
||||
if (($message = $result->first()) === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return reset($msg);
|
||||
if ($decrypt) {
|
||||
return $this->smimeService->decryptDataFetch($message, $userId);
|
||||
}
|
||||
|
||||
return $message->getFullMsg();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Imap_Client_Socket $client
|
||||
* @param string $mailbox
|
||||
* @param int $uid
|
||||
* @param string $userId
|
||||
* @return string|null
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
* @throws Horde_Mime_Exception
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function getHtmlBody(Horde_Imap_Client_Socket $client,
|
||||
string $mailbox,
|
||||
int $uid): ?string {
|
||||
string $mailbox,
|
||||
int $uid,
|
||||
string $userId): ?string {
|
||||
$messageQuery = new Horde_Imap_Client_Fetch_Query();
|
||||
$messageQuery->envelope();
|
||||
$messageQuery->structure();
|
||||
$this->smimeService->addEncryptionCheckQueries($messageQuery, true);
|
||||
|
||||
$result = $client->fetch($mailbox, $messageQuery, [
|
||||
'ids' => new Horde_Imap_Client_Ids([$uid]),
|
||||
|
@ -474,6 +514,23 @@ class MessageMapper {
|
|||
}
|
||||
|
||||
$structure = $message->getStructure();
|
||||
|
||||
// Handle S/MIME encrypted message
|
||||
if ($this->smimeService->isEncrypted($message)) {
|
||||
// Encrypted messages have to be fully fetched in order to analyze the structure because
|
||||
// it is hidden (obviously).
|
||||
$fullText = $this->getFullText($client, $mailbox, $uid, $userId);
|
||||
|
||||
// Force mime parsing as decrypted S/MIME payload doesn't have to contain a MIME header
|
||||
$mimePart = Horde_Mime_Part::parseMessage($fullText, ['forcemime' => true ]);
|
||||
$htmlPartId = $mimePart->findBody('html');
|
||||
if (!isset($mimePart[$htmlPartId])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $mimePart[$htmlPartId];
|
||||
}
|
||||
|
||||
$htmlPartId = $structure->findBody('html');
|
||||
if ($htmlPartId === null) {
|
||||
// No HTML part
|
||||
|
@ -501,58 +558,31 @@ class MessageMapper {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use getAttachments() instead
|
||||
*
|
||||
* @param Horde_Imap_Client_Socket $client
|
||||
* @param string $mailbox
|
||||
* @param int $uid
|
||||
* @param string $userId
|
||||
* @param array|null $attachmentIds
|
||||
* @return array
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
* @throws Horde_Mime_Exception
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function getRawAttachments(Horde_Imap_Client_Socket $client,
|
||||
string $mailbox,
|
||||
int $uid,
|
||||
?array $attachmentIds = []): array {
|
||||
$messageQuery = new Horde_Imap_Client_Fetch_Query();
|
||||
$messageQuery->structure();
|
||||
|
||||
$result = $client->fetch($mailbox, $messageQuery, [
|
||||
'ids' => new Horde_Imap_Client_Ids([$uid]),
|
||||
]);
|
||||
|
||||
if (($structureResult = $result->first()) === null) {
|
||||
throw new DoesNotExistException('Message does not exist');
|
||||
}
|
||||
|
||||
$structure = $structureResult->getStructure();
|
||||
$partsQuery = $this->buildAttachmentsPartsQuery($structure, $attachmentIds);
|
||||
|
||||
$parts = $client->fetch($mailbox, $partsQuery, [
|
||||
'ids' => new Horde_Imap_Client_Ids([$uid]),
|
||||
]);
|
||||
if (($messageData = $parts->first()) === null) {
|
||||
throw new DoesNotExistException('Message does not exist');
|
||||
}
|
||||
|
||||
$attachments = [];
|
||||
foreach ($structure->partIterator() as $key => $part) {
|
||||
/** @var Horde_Mime_Part $part */
|
||||
|
||||
if (!$part->isAttachment()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($attachmentIds) && !in_array($part->getMimeId(), $attachmentIds, true)) {
|
||||
// We are looking for specific parts only and this is not one of them
|
||||
continue;
|
||||
}
|
||||
|
||||
$stream = $messageData->getBodyPart($key, true);
|
||||
$mimeHeaders = $messageData->getMimeHeader($key, Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
|
||||
if ($enc = $mimeHeaders->getValue('content-transfer-encoding')) {
|
||||
$part->setTransferEncoding($enc);
|
||||
}
|
||||
$part->setContents($stream, [
|
||||
'usestream' => true,
|
||||
]);
|
||||
$decoded = $part->getContents();
|
||||
fclose($stream);
|
||||
|
||||
$attachments[] = $decoded;
|
||||
}
|
||||
return $attachments;
|
||||
string $mailbox,
|
||||
int $uid,
|
||||
string $userId,
|
||||
?array $attachmentIds = []): array {
|
||||
$attachments = $this->getAttachments($client, $mailbox, $uid, $userId, $attachmentIds);
|
||||
return array_map(static function (array $attachment) {
|
||||
return $attachment['content'];
|
||||
}, $attachments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -561,32 +591,55 @@ class MessageMapper {
|
|||
* @param Horde_Imap_Client_Socket $client
|
||||
* @param string $mailbox
|
||||
* @param integer $uid
|
||||
* @param string $userId
|
||||
* @param array|null $attachmentIds
|
||||
* @return array[]
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
* @throws ServiceException
|
||||
* @throws Horde_Mime_Exception
|
||||
*/
|
||||
public function getAttachments(Horde_Imap_Client_Socket $client,
|
||||
string $mailbox,
|
||||
int $uid,
|
||||
?array $attachmentIds = []): array {
|
||||
string $mailbox,
|
||||
int $uid,
|
||||
string $userId,
|
||||
?array $attachmentIds = []): array {
|
||||
$uids = new Horde_Imap_Client_Ids([$uid]);
|
||||
|
||||
$messageQuery = new Horde_Imap_Client_Fetch_Query();
|
||||
$messageQuery->structure();
|
||||
$this->smimeService->addEncryptionCheckQueries($messageQuery);
|
||||
|
||||
$result = $client->fetch($mailbox, $messageQuery, [
|
||||
'ids' => new Horde_Imap_Client_Ids([$uid]),
|
||||
]);
|
||||
$result = $client->fetch($mailbox, $messageQuery, ['ids' => $uids ]);
|
||||
|
||||
if (($structureResult = $result->first()) === null) {
|
||||
throw new DoesNotExistException('Message does not exist');
|
||||
}
|
||||
|
||||
$structure = $structureResult->getStructure();
|
||||
$partsQuery = $this->buildAttachmentsPartsQuery($structure, $attachmentIds);
|
||||
$messageData = null;
|
||||
|
||||
$parts = $client->fetch($mailbox, $partsQuery, [
|
||||
'ids' => new Horde_Imap_Client_Ids([$uid]),
|
||||
]);
|
||||
if (($messageData = $parts->first()) === null) {
|
||||
throw new DoesNotExistException('Message does not exist');
|
||||
$isEncrypted = $this->smimeService->isEncrypted($structureResult);
|
||||
if ($isEncrypted) {
|
||||
$fullTextQuery = new Horde_Imap_Client_Fetch_Query();
|
||||
$this->smimeService->addDecryptQueries($fullTextQuery);
|
||||
|
||||
$fullTextParts = $client->fetch($mailbox, $fullTextQuery, ['ids' => $uids ]);
|
||||
if (($fullTextResult = $fullTextParts->first()) === null) {
|
||||
throw new DoesNotExistException('Message does not exist');
|
||||
}
|
||||
$decryptedText = $this->smimeService->decryptDataFetch($fullTextResult, $userId);
|
||||
|
||||
// Replace opaque structure with decrypted structure
|
||||
$structure = Horde_Mime_Part::parseMessage($decryptedText, [ 'forcemime' => true ]);
|
||||
} else {
|
||||
$partsQuery = $this->buildAttachmentsPartsQuery($structure, $attachmentIds);
|
||||
$parts = $client->fetch($mailbox, $partsQuery, ['ids' => $uids ]);
|
||||
if (($messageData = $parts->first()) === null) {
|
||||
throw new DoesNotExistException('Message does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
$attachments = [];
|
||||
|
@ -602,28 +655,136 @@ class MessageMapper {
|
|||
continue;
|
||||
}
|
||||
|
||||
$stream = $messageData->getBodyPart($key, true);
|
||||
$mimeHeaders = $messageData->getMimeHeader($key, Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
|
||||
if ($enc = $mimeHeaders->getValue('content-transfer-encoding')) {
|
||||
$part->setTransferEncoding($enc);
|
||||
if ($isEncrypted) {
|
||||
// Decrypted message already contains all data
|
||||
$content = $part->getContents();
|
||||
} else {
|
||||
$stream = $messageData->getBodyPart($key, true);
|
||||
$mimeHeaders = $messageData->getMimeHeader($key, Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
|
||||
if ($enc = $mimeHeaders->getValue('content-transfer-encoding')) {
|
||||
$part->setTransferEncoding($enc);
|
||||
}
|
||||
$part->setContents($stream, [
|
||||
'usestream' => true,
|
||||
]);
|
||||
$content = $part->getContents();
|
||||
fclose($stream);
|
||||
}
|
||||
$part->setContents($stream, [
|
||||
'usestream' => true,
|
||||
]);
|
||||
|
||||
$attachments[] = [
|
||||
'content' => $part->getContents(),
|
||||
'content' => $content,
|
||||
'name' => $part->getName(),
|
||||
'size' => $part->getBytes()
|
||||
];
|
||||
fclose($stream);
|
||||
}
|
||||
return $attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Imap_Client_Base $client
|
||||
* @param string $mailbox
|
||||
* @param int $messageUid
|
||||
* @param string $attachmentId
|
||||
* @param string $userId
|
||||
* @return Attachment
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws ServiceException
|
||||
* @throws Horde_Mime_Exception
|
||||
*/
|
||||
public function getAttachment(Horde_Imap_Client_Base $client,
|
||||
string $mailbox,
|
||||
int $messageUid,
|
||||
string $attachmentId,
|
||||
string $userId): Attachment {
|
||||
// TODO: compare logic and merge with getAttachments()
|
||||
|
||||
$query = new Horde_Imap_Client_Fetch_Query();
|
||||
$query->bodyPart($attachmentId);
|
||||
$query->mimeHeader($attachmentId);
|
||||
$this->smimeService->addEncryptionCheckQueries($query);
|
||||
|
||||
$uids = new Horde_Imap_Client_Ids($messageUid);
|
||||
$headers = $client->fetch($mailbox, $query, ['ids' => $uids]);
|
||||
if (!isset($headers[$messageUid])) {
|
||||
throw new DoesNotExistException('Unable to load the attachment.');
|
||||
}
|
||||
|
||||
/** @var Horde_Imap_Client_Data_Fetch $fetch */
|
||||
$fetch = $headers[$messageUid];
|
||||
|
||||
/** @var Horde_Mime_Headers $mimeHeaders */
|
||||
$mimeHeaders = $fetch->getMimeHeader($attachmentId, Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
|
||||
|
||||
$body = $fetch->getBodyPart($attachmentId);
|
||||
|
||||
$isEncrypted = $this->smimeService->isEncrypted($fetch);
|
||||
if ($isEncrypted) {
|
||||
$fullTextQuery = new Horde_Imap_Client_Fetch_Query();
|
||||
$this->smimeService->addDecryptQueries($fullTextQuery);
|
||||
|
||||
$result = $client->fetch($mailbox, $fullTextQuery, ['ids' => $uids]);
|
||||
if (!isset($result[$messageUid])) {
|
||||
throw new DoesNotExistException('Unable to load the attachment.');
|
||||
}
|
||||
/** @var Horde_Imap_Client_Data_Fetch $fetch */
|
||||
$fullTextResult = $result[$messageUid];
|
||||
|
||||
$decryptedText = $this->smimeService->decryptDataFetch($fullTextResult, $userId);
|
||||
$decryptedPart = Horde_Mime_Part::parseMessage($decryptedText, [ 'forcemime' => true ]);
|
||||
if (!isset($decryptedPart[$attachmentId])) {
|
||||
throw new DoesNotExistException('Unable to load the attachment.');
|
||||
}
|
||||
|
||||
$attachmentPart = $decryptedPart[$attachmentId];
|
||||
$body = $attachmentPart->getContents();
|
||||
$mimeHeaders = $attachmentPart->addMimeHeaders();
|
||||
}
|
||||
|
||||
$mimePart = new Horde_Mime_Part();
|
||||
|
||||
// Serve all files with a content-disposition of "attachment" to prevent Cross-Site Scripting
|
||||
$mimePart->setDisposition('attachment');
|
||||
|
||||
// Extract headers from part
|
||||
$contentDisposition = $mimeHeaders->getValue('content-disposition', Horde_Mime_Headers::VALUE_PARAMS);
|
||||
if (!is_null($contentDisposition) && isset($contentDisposition['filename'])) {
|
||||
$mimePart->setDispositionParameter('filename', $contentDisposition['filename']);
|
||||
} else {
|
||||
$contentDisposition = $mimeHeaders->getValue('content-type', Horde_Mime_Headers::VALUE_PARAMS);
|
||||
if (isset($contentDisposition['name'])) {
|
||||
$mimePart->setContentTypeParameter('name', $contentDisposition['name']);
|
||||
}
|
||||
}
|
||||
|
||||
// Content transfer encoding
|
||||
// Decrypted parts are already decoded because they went through the MIME parser
|
||||
if (!$isEncrypted && $tmp = $mimeHeaders->getValue('content-transfer-encoding')) {
|
||||
$mimePart->setTransferEncoding($tmp);
|
||||
}
|
||||
|
||||
/* Content type */
|
||||
$contentType = $mimeHeaders->getValue('content-type');
|
||||
if (!is_null($contentType) && str_contains($contentType, 'text/calendar')) {
|
||||
$mimePart->setType('text/calendar');
|
||||
if ($mimePart->getContentTypeParameter('name') === null) {
|
||||
$mimePart->setContentTypeParameter('name', 'calendar.ics');
|
||||
}
|
||||
} else {
|
||||
// To prevent potential problems with the SOP we serve all files but calendar entries with the
|
||||
// MIME type "application/octet-stream"
|
||||
$mimePart->setType('application/octet-stream');
|
||||
}
|
||||
|
||||
$mimePart->setContents($body);
|
||||
return new Attachment($mimePart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the parts query for attachments
|
||||
*
|
||||
* @param $structure
|
||||
* @param Horde_Mime_Part $structure
|
||||
* @param array $attachmentIds
|
||||
* @return Horde_Imap_Client_Fetch_Query
|
||||
*/
|
||||
|
@ -669,17 +830,24 @@ class MessageMapper {
|
|||
'cache' => true,
|
||||
'peek' => true,
|
||||
]);
|
||||
$this->smimeService->addEncryptionCheckQueries($structureQuery);
|
||||
|
||||
$structures = $client->fetch($mailbox, $structureQuery, [
|
||||
'ids' => new Horde_Imap_Client_Ids($uids),
|
||||
]);
|
||||
|
||||
return array_map(static function (Horde_Imap_Client_Data_Fetch $fetchData) use ($mailbox, $client) {
|
||||
return array_map(function (Horde_Imap_Client_Data_Fetch $fetchData) use ($mailbox, $client) {
|
||||
$hasAttachments = false;
|
||||
$text = '';
|
||||
$isImipMessage = false;
|
||||
$isEncrypted = false;
|
||||
|
||||
if ($this->smimeService->isEncrypted($fetchData)) {
|
||||
$isEncrypted = true;
|
||||
}
|
||||
|
||||
$structure = $fetchData->getStructure();
|
||||
|
||||
/** @var Horde_Mime_Part $part */
|
||||
foreach ($structure->getParts() as $part) {
|
||||
if ($part->isAttachment()) {
|
||||
|
@ -698,7 +866,7 @@ class MessageMapper {
|
|||
$textBodyId = $structure->findBody() ?? $structure->findBody('text');
|
||||
$htmlBodyId = $structure->findBody('html');
|
||||
if ($textBodyId === null && $htmlBodyId === null) {
|
||||
return new MessageStructureData($hasAttachments, $text, $isImipMessage);
|
||||
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted);
|
||||
}
|
||||
$partsQuery = new Horde_Imap_Client_Fetch_Query();
|
||||
if ($htmlBodyId !== null) {
|
||||
|
@ -724,7 +892,7 @@ class MessageMapper {
|
|||
$part = $parts[$fetchData->getUid()];
|
||||
// This is sus - why does this even happen? A delete / move in the middle of this processing?
|
||||
if ($part === null) {
|
||||
return new MessageStructureData($hasAttachments, $text, $isImipMessage);
|
||||
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted);
|
||||
}
|
||||
$htmlBody = ($htmlBodyId !== null) ? $part->getBodyPart($htmlBodyId) : null;
|
||||
if (!empty($htmlBody)) {
|
||||
|
@ -735,7 +903,12 @@ class MessageMapper {
|
|||
$htmlBody = $structure->getContents();
|
||||
}
|
||||
$html = new Html2Text($htmlBody);
|
||||
return new MessageStructureData($hasAttachments, trim($html->getText()), $isImipMessage);
|
||||
return new MessageStructureData(
|
||||
$hasAttachments,
|
||||
trim($html->getText()),
|
||||
$isImipMessage,
|
||||
$isEncrypted,
|
||||
);
|
||||
}
|
||||
$textBody = $part->getBodyPart($textBodyId);
|
||||
if (!empty($textBody)) {
|
||||
|
@ -745,10 +918,15 @@ class MessageMapper {
|
|||
$structure->setContents($textBody);
|
||||
$textBody = $structure->getContents();
|
||||
}
|
||||
return new MessageStructureData($hasAttachments, $textBody, $isImipMessage);
|
||||
return new MessageStructureData(
|
||||
$hasAttachments,
|
||||
$textBody,
|
||||
$isImipMessage,
|
||||
$isEncrypted,
|
||||
);
|
||||
}
|
||||
|
||||
return new MessageStructureData($hasAttachments, $text, $isImipMessage);
|
||||
return new MessageStructureData($hasAttachments, $text, $isImipMessage, $isEncrypted);
|
||||
}, iterator_to_array($structures->getIterator()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ declare(strict_types=1);
|
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
|
@ -35,12 +36,16 @@ class MessageStructureData {
|
|||
/** @var bool */
|
||||
private $isImipMessage;
|
||||
|
||||
private bool $isEncrypted;
|
||||
|
||||
public function __construct(bool $hasAttachments,
|
||||
string $previewText,
|
||||
bool $isImipMessage) {
|
||||
bool $isImipMessage,
|
||||
bool $isEncrypted) {
|
||||
$this->hasAttachments = $hasAttachments;
|
||||
$this->previewText = $previewText;
|
||||
$this->isImipMessage = $isImipMessage;
|
||||
$this->isEncrypted = $isEncrypted;
|
||||
}
|
||||
|
||||
public function hasAttachments(): bool {
|
||||
|
@ -54,4 +59,8 @@ class MessageStructureData {
|
|||
public function isImipMessage(): bool {
|
||||
return $this->isImipMessage;
|
||||
}
|
||||
|
||||
public function isEncrypted(): bool {
|
||||
return $this->isEncrypted;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ declare(strict_types=1);
|
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
|
@ -110,6 +111,7 @@ class PreviewEnhancer {
|
|||
$message->setPreviewText($structureData->getPreviewText());
|
||||
$message->setStructureAnalyzed(true);
|
||||
$message->setImipMessage($structureData->isImipMessage());
|
||||
$message->setEncrypted($structureData->isEncrypted());
|
||||
|
||||
return $message;
|
||||
}, $messages));
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -65,6 +66,7 @@ class Synchronizer {
|
|||
*/
|
||||
public function sync(Horde_Imap_Client_Base $imapClient,
|
||||
Request $request,
|
||||
string $userId,
|
||||
int $criteria = Horde_Imap_Client::SYNC_NEWMSGSUIDS | Horde_Imap_Client::SYNC_FLAGSUIDS | Horde_Imap_Client::SYNC_VANISHEDUIDS): Response {
|
||||
$mailbox = new Horde_Imap_Client_Mailbox($request->getMailbox());
|
||||
try {
|
||||
|
@ -95,8 +97,8 @@ class Synchronizer {
|
|||
throw $e;
|
||||
}
|
||||
|
||||
$newMessages = $this->messageMapper->findByIds($imapClient, $request->getMailbox(), new Horde_Imap_Client_Ids($newUids));
|
||||
$changedMessages = $this->messageMapper->findByIds($imapClient, $request->getMailbox(), new Horde_Imap_Client_Ids($changedUids));
|
||||
$newMessages = $this->messageMapper->findByIds($imapClient, $request->getMailbox(), new Horde_Imap_Client_Ids($newUids), $userId);
|
||||
$changedMessages = $this->messageMapper->findByIds($imapClient, $request->getMailbox(), new Horde_Imap_Client_Ids($changedUids), $userId);
|
||||
$vanishedMessageUids = $vanishedUids;
|
||||
|
||||
return new Response($newMessages, $changedMessages, $vanishedMessageUids);
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Clement Wong <mail@clement.hk>
|
||||
* @author Jan-Christoph Borchardt <hey@jancborchardt.net>
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
* @author matiasdelellis <mati86dl@gmail.com>
|
||||
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
|
||||
* @author Thomas Imbreckx <zinks@iozero.be>
|
||||
* @author Thomas I <thomas@oatr.be>
|
||||
* @author Thomas Mueller <thomas.mueller@tmit.eu>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* @author Maximilian Zellhofer <max.zellhofer@gmail.com>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Mail;
|
||||
|
||||
use Horde_Imap_Client;
|
||||
use Horde_Imap_Client_Mailbox;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use OCA\Mail\Model\IMAPMessage;
|
||||
|
||||
class Mailbox {
|
||||
/**
|
||||
* @var Horde_Imap_Client_Socket
|
||||
*/
|
||||
protected $conn;
|
||||
|
||||
/**
|
||||
* @var Horde_Imap_Client_Mailbox
|
||||
*/
|
||||
protected $mailBox;
|
||||
|
||||
/**
|
||||
* @param Horde_Imap_Client_Socket $conn
|
||||
* @param Horde_Imap_Client_Mailbox $mailBox
|
||||
*/
|
||||
public function __construct($conn, $mailBox) {
|
||||
$this->conn = $conn;
|
||||
$this->mailBox = $mailBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $uid
|
||||
* @param bool $loadHtmlMessageBody
|
||||
*
|
||||
* @return IMAPMessage
|
||||
*/
|
||||
public function getMessage(int $uid, bool $loadHtmlMessageBody = false) {
|
||||
return new IMAPMessage($this->conn, $this->mailBox, $uid, null, $loadHtmlMessageBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $messageUid
|
||||
* @param string $attachmentId
|
||||
*
|
||||
* @return Attachment
|
||||
*/
|
||||
public function getAttachment(int $messageUid, string $attachmentId): Attachment {
|
||||
return new Attachment($this->conn, $this->mailBox, $messageUid, $attachmentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rawBody
|
||||
* @param array $flags
|
||||
* @return array<int> UIDs
|
||||
*
|
||||
* @deprecated only used for testing
|
||||
*/
|
||||
public function saveMessage($rawBody, $flags = []) {
|
||||
$uids = $this->conn->append($this->mailBox, [
|
||||
[
|
||||
'data' => $rawBody,
|
||||
'flags' => $flags
|
||||
]
|
||||
])->ids;
|
||||
|
||||
return reset($uids);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Migration;
|
||||
|
||||
use Closure;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
class Version2300Date20230214104736 extends SimpleMigrationStep {
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
$messageTable = $schema->getTable('mail_messages');
|
||||
if (!$messageTable->hasColumn('encrypted')) {
|
||||
$messageTable->addColumn('encrypted', 'boolean', [
|
||||
'notnull' => false,
|
||||
'default' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
|
@ -32,29 +32,19 @@ namespace OCA\Mail\Model;
|
|||
|
||||
use Exception;
|
||||
use Horde_Imap_Client;
|
||||
use Horde_Imap_Client_Data_Envelope;
|
||||
use Horde_Imap_Client_Data_Fetch;
|
||||
use Horde_Imap_Client_DateTime;
|
||||
use Horde_Imap_Client_Fetch_Query;
|
||||
use Horde_Imap_Client_Ids;
|
||||
use Horde_Imap_Client_Mailbox;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use Horde_Mime_Headers;
|
||||
use Horde_Mime_Headers_MessageId;
|
||||
use Horde_Mime_Part;
|
||||
use JsonSerializable;
|
||||
use OC;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Db\LocalAttachment;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Db\Tag;
|
||||
use OCA\Mail\Service\Html;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use ReturnTypeWillChange;
|
||||
use function fclose;
|
||||
use function in_array;
|
||||
use function mb_convert_encoding;
|
||||
use function mb_strcut;
|
||||
|
@ -63,75 +53,84 @@ use function trim;
|
|||
class IMAPMessage implements IMessage, JsonSerializable {
|
||||
use ConvertAddresses;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $attachmentsToIgnore = ['signature.asc', 'smime.p7s'];
|
||||
private Html $htmlService;
|
||||
|
||||
/** @var Html|null */
|
||||
private $htmlService;
|
||||
/** @var string[] */
|
||||
private array $flags;
|
||||
|
||||
/**
|
||||
* @param Horde_Imap_Client_Socket|null $conn
|
||||
* @param Horde_Imap_Client_Mailbox|string $mailBox
|
||||
* @param int $messageId
|
||||
* @param Horde_Imap_Client_Data_Fetch|null $fetch
|
||||
* @param bool $loadHtmlMessage
|
||||
* @param Html|null $htmlService
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function __construct($conn,
|
||||
$mailBox,
|
||||
int $messageId,
|
||||
Horde_Imap_Client_Data_Fetch $fetch = null,
|
||||
bool $loadHtmlMessage = false,
|
||||
Html $htmlService = null) {
|
||||
$this->conn = $conn;
|
||||
$this->mailBox = $mailBox;
|
||||
$this->messageId = $messageId;
|
||||
$this->loadHtmlMessage = $loadHtmlMessage;
|
||||
private int $messageId;
|
||||
private string $realMessageId;
|
||||
private AddressList $from;
|
||||
private AddressList $to;
|
||||
private AddressList $cc;
|
||||
private AddressList $bcc;
|
||||
private AddressList $replyTo;
|
||||
private string $subject;
|
||||
public string $plainMessage;
|
||||
public string $htmlMessage;
|
||||
public array $attachments;
|
||||
public array $inlineAttachments;
|
||||
private bool $hasAttachments;
|
||||
public array $scheduling;
|
||||
private bool $hasHtmlMessage;
|
||||
private Horde_Imap_Client_DateTime $imapDate;
|
||||
private string $rawReferences;
|
||||
private string $dispositionNotificationTo;
|
||||
private string $rawInReplyTo;
|
||||
private bool $isEncrypted;
|
||||
private bool $isSigned;
|
||||
private bool $signatureIsValid;
|
||||
|
||||
public function __construct(int $uid,
|
||||
string $messageId,
|
||||
array $flags,
|
||||
AddressList $from,
|
||||
AddressList $to,
|
||||
AddressList $cc,
|
||||
AddressList $bcc,
|
||||
AddressList $replyTo,
|
||||
string $subject,
|
||||
string $plainMessage,
|
||||
string $htmlMessage,
|
||||
bool $hasHtmlMessage,
|
||||
array $attachments,
|
||||
array $inlineAttachments,
|
||||
bool $hasAttachments,
|
||||
array $scheduling,
|
||||
Horde_Imap_Client_DateTime $imapDate,
|
||||
string $rawReferences,
|
||||
string $dispositionNotificationTo,
|
||||
string $rawInReplyTo,
|
||||
bool $isEncrypted,
|
||||
bool $isSigned,
|
||||
bool $signatureIsValid,
|
||||
Html $htmlService) {
|
||||
$this->messageId = $uid;
|
||||
$this->realMessageId = $messageId;
|
||||
$this->flags = $flags;
|
||||
$this->from = $from;
|
||||
$this->to = $to;
|
||||
$this->cc = $cc;
|
||||
$this->bcc = $bcc;
|
||||
$this->replyTo = $replyTo;
|
||||
$this->subject = $subject;
|
||||
$this->plainMessage = $plainMessage;
|
||||
$this->htmlMessage = $htmlMessage;
|
||||
$this->hasHtmlMessage = $hasHtmlMessage;
|
||||
$this->attachments = $attachments;
|
||||
$this->inlineAttachments = $inlineAttachments;
|
||||
$this->hasAttachments = $hasAttachments;
|
||||
$this->scheduling = $scheduling;
|
||||
$this->imapDate = $imapDate;
|
||||
$this->rawReferences = $rawReferences;
|
||||
$this->dispositionNotificationTo = $dispositionNotificationTo;
|
||||
$this->rawInReplyTo = $rawInReplyTo;
|
||||
$this->isEncrypted = $isEncrypted;
|
||||
$this->isSigned = $isSigned;
|
||||
$this->signatureIsValid = $signatureIsValid;
|
||||
$this->htmlService = $htmlService;
|
||||
if (is_null($htmlService)) {
|
||||
$urlGenerator = OC::$server->getURLGenerator();
|
||||
$request = OC::$server->getRequest();
|
||||
$this->htmlService = new Html($urlGenerator, $request);
|
||||
}
|
||||
|
||||
if ($fetch === null) {
|
||||
$this->loadMessageBodies();
|
||||
} else {
|
||||
$this->fetch = $fetch;
|
||||
}
|
||||
}
|
||||
|
||||
// output all the following:
|
||||
public $header = null;
|
||||
public $htmlMessage = '';
|
||||
public $plainMessage = '';
|
||||
public $attachments = [];
|
||||
public $inlineAttachments = [];
|
||||
public $scheduling = [];
|
||||
private $loadHtmlMessage = false;
|
||||
private $hasHtmlMessage = false;
|
||||
|
||||
/**
|
||||
* @var Horde_Imap_Client_Socket
|
||||
*/
|
||||
private $conn;
|
||||
|
||||
/**
|
||||
* @var Horde_Imap_Client_Mailbox
|
||||
*/
|
||||
private $mailBox;
|
||||
private $messageId;
|
||||
|
||||
/**
|
||||
* @var Horde_Imap_Client_Data_Fetch
|
||||
*/
|
||||
private $fetch;
|
||||
|
||||
public static function generateMessageId(): string {
|
||||
return Horde_Mime_Headers_MessageId::create('nextcloud-mail-generated')->value;
|
||||
}
|
||||
|
@ -140,7 +139,7 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
* @return int
|
||||
*/
|
||||
public function getUid(): int {
|
||||
return $this->fetch->getUid();
|
||||
return $this->messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,17 +147,16 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
* @return array
|
||||
*/
|
||||
public function getFlags(): array {
|
||||
$flags = $this->fetch->getFlags();
|
||||
return [
|
||||
'seen' => in_array(Horde_Imap_Client::FLAG_SEEN, $flags),
|
||||
'flagged' => in_array(Horde_Imap_Client::FLAG_FLAGGED, $flags),
|
||||
'answered' => in_array(Horde_Imap_Client::FLAG_ANSWERED, $flags),
|
||||
'deleted' => in_array(Horde_Imap_Client::FLAG_DELETED, $flags),
|
||||
'draft' => in_array(Horde_Imap_Client::FLAG_DRAFT, $flags),
|
||||
'forwarded' => in_array(Horde_Imap_Client::FLAG_FORWARDED, $flags),
|
||||
'hasAttachments' => $this->hasAttachments($this->fetch->getStructure()),
|
||||
'mdnsent' => in_array(Horde_Imap_Client::FLAG_MDNSENT, $flags, true),
|
||||
'important' => in_array(Tag::LABEL_IMPORTANT, $flags, true)
|
||||
'seen' => in_array(Horde_Imap_Client::FLAG_SEEN, $this->flags),
|
||||
'flagged' => in_array(Horde_Imap_Client::FLAG_FLAGGED, $this->flags),
|
||||
'answered' => in_array(Horde_Imap_Client::FLAG_ANSWERED, $this->flags),
|
||||
'deleted' => in_array(Horde_Imap_Client::FLAG_DELETED, $this->flags),
|
||||
'draft' => in_array(Horde_Imap_Client::FLAG_DRAFT, $this->flags),
|
||||
'forwarded' => in_array(Horde_Imap_Client::FLAG_FORWARDED, $this->flags),
|
||||
'hasAttachments' => $this->hasAttachments,
|
||||
'mdnsent' => in_array(Horde_Imap_Client::FLAG_MDNSENT, $this->flags, true),
|
||||
'important' => in_array(Tag::LABEL_IMPORTANT, $this->flags, true)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -175,46 +173,20 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
throw new Exception('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Horde_Imap_Client_Data_Envelope
|
||||
*/
|
||||
public function getEnvelope() {
|
||||
return $this->fetch->getEnvelope();
|
||||
}
|
||||
|
||||
private function getRawReferences(): string {
|
||||
/** @var resource $headersStream */
|
||||
$headersStream = $this->fetch->getHeaderText('0', Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
|
||||
$parsedHeaders = Horde_Mime_Headers::parseHeaders($headersStream);
|
||||
fclose($headersStream);
|
||||
$references = $parsedHeaders->getHeader('references');
|
||||
if ($references === null) {
|
||||
return '';
|
||||
}
|
||||
return $references->value_single;
|
||||
return $this->rawReferences;
|
||||
}
|
||||
|
||||
private function getRawInReplyTo(): string {
|
||||
return $this->fetch->getEnvelope()->in_reply_to;
|
||||
return $this->rawInReplyTo;
|
||||
}
|
||||
|
||||
public function getDispositionNotificationTo(): string {
|
||||
/** @var resource $headersStream */
|
||||
$headersStream = $this->fetch->getHeaderText('0', Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
|
||||
$parsedHeaders = Horde_Mime_Headers::parseHeaders($headersStream);
|
||||
fclose($headersStream);
|
||||
$header = $parsedHeaders->getHeader('disposition-notification-to');
|
||||
if ($header === null) {
|
||||
return '';
|
||||
}
|
||||
return $header->value_single;
|
||||
return $this->dispositionNotificationTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getFrom(): AddressList {
|
||||
return AddressList::fromHorde($this->getEnvelope()->from);
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,11 +200,8 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
throw new Exception('IMAP message is immutable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getTo(): AddressList {
|
||||
return AddressList::fromHorde($this->getEnvelope()->to);
|
||||
return $this->to;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -246,11 +215,8 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
throw new Exception('IMAP message is immutable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getCC(): AddressList {
|
||||
return AddressList::fromHorde($this->getEnvelope()->cc);
|
||||
return $this->cc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -264,11 +230,8 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
throw new Exception('IMAP message is immutable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getBCC(): AddressList {
|
||||
return AddressList::fromHorde($this->getEnvelope()->bcc);
|
||||
return $this->bcc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -282,27 +245,12 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
throw new Exception('IMAP message is immutable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID if available
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessageId(): string {
|
||||
return $this->getEnvelope()->message_id;
|
||||
return $this->realMessageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject(): string {
|
||||
// Try a soft conversion first (some installations, eg: Alpine linux,
|
||||
// have issues with the '//IGNORE' option)
|
||||
$subject = $this->getEnvelope()->subject;
|
||||
$utf8 = iconv('UTF-8', 'UTF-8', $subject);
|
||||
if ($utf8 !== false) {
|
||||
return $utf8;
|
||||
}
|
||||
return iconv("UTF-8", "UTF-8//IGNORE", $subject);
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -316,186 +264,8 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
throw new Exception('IMAP message is immutable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Horde_Imap_Client_DateTime
|
||||
*/
|
||||
public function getSentDate(): Horde_Imap_Client_DateTime {
|
||||
return $this->fetch->getImapDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSize(): int {
|
||||
return $this->fetch->getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasAttachments($part) {
|
||||
foreach ($part->getParts() as $p) {
|
||||
if ($p->isAttachment() || $p->getType() === 'message/rfc822') {
|
||||
return true;
|
||||
}
|
||||
if ($this->hasAttachments($p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function loadMessageBodies(): void {
|
||||
$fetch_query = new Horde_Imap_Client_Fetch_Query();
|
||||
$fetch_query->envelope();
|
||||
$fetch_query->structure();
|
||||
$fetch_query->flags();
|
||||
$fetch_query->size();
|
||||
$fetch_query->imapDate();
|
||||
$fetch_query->headerText([
|
||||
'cache' => true,
|
||||
'peek' => true,
|
||||
]);
|
||||
|
||||
// $list is an array of Horde_Imap_Client_Data_Fetch objects.
|
||||
$ids = new Horde_Imap_Client_Ids($this->messageId);
|
||||
$headers = $this->conn->fetch($this->mailBox, $fetch_query, ['ids' => $ids]);
|
||||
/** @var Horde_Imap_Client_Data_Fetch $fetch */
|
||||
$fetch = $headers[$this->messageId];
|
||||
if (is_null($fetch)) {
|
||||
throw new DoesNotExistException("This email ($this->messageId) can't be found. Probably it was deleted from the server recently. Please reload.");
|
||||
}
|
||||
|
||||
// set $this->fetch to get to, from ...
|
||||
$this->fetch = $fetch;
|
||||
|
||||
// analyse the body part
|
||||
$structure = $fetch->getStructure();
|
||||
|
||||
// debugging below
|
||||
$structure_type = $structure->getPrimaryType();
|
||||
if ($structure_type === 'multipart') {
|
||||
$i = 1;
|
||||
foreach ($structure->getParts() as $p) {
|
||||
$this->getPart($p, $i++);
|
||||
}
|
||||
} else {
|
||||
if (!is_null($structure->findBody())) {
|
||||
// get the body from the server
|
||||
$partId = (int)$structure->findBody();
|
||||
$this->getPart($structure->getPart($partId), $partId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $p
|
||||
* @param mixed $partNo
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function getPart(Horde_Mime_Part $p, $partNo): void {
|
||||
// iMIP messages
|
||||
// Handle text/calendar parts first because they might be attachments at the same time.
|
||||
// Otherwise, some of the following if-conditions might break the handling and treat iMIP
|
||||
// data like regular attachments.
|
||||
$allContentTypeParameters = $p->getAllContentTypeParameters();
|
||||
if ($p->getType() === 'text/calendar') {
|
||||
// Handle event data like a regular attachment
|
||||
// Outlook doesn't set a content disposition
|
||||
// We work around this by checking for the name only
|
||||
if ($p->getName() !== null) {
|
||||
$this->attachments[] = [
|
||||
'id' => $p->getMimeId(),
|
||||
'messageId' => $this->messageId,
|
||||
'fileName' => $p->getName(),
|
||||
'mime' => $p->getType(),
|
||||
'size' => $p->getBytes(),
|
||||
'cid' => $p->getContentId(),
|
||||
'disposition' => $p->getDisposition()
|
||||
];
|
||||
}
|
||||
|
||||
// return if this is an event attachment only
|
||||
// the method parameter determines if this is a iMIP message
|
||||
if (!isset($allContentTypeParameters['method'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_array(strtoupper($allContentTypeParameters['method']), ['REQUEST', 'REPLY', 'CANCEL'])) {
|
||||
$this->scheduling[] = [
|
||||
'id' => $p->getMimeId(),
|
||||
'messageId' => $this->messageId,
|
||||
'method' => strtoupper($allContentTypeParameters['method']),
|
||||
'contents' => $this->loadBodyData($p, $partNo),
|
||||
];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Regular attachments
|
||||
if ($p->isAttachment() || $p->getType() === 'message/rfc822') {
|
||||
$this->attachments[] = [
|
||||
'id' => $p->getMimeId(),
|
||||
'messageId' => $this->messageId,
|
||||
'fileName' => $p->getName(),
|
||||
'mime' => $p->getType(),
|
||||
'size' => $p->getBytes(),
|
||||
'cid' => $p->getContentId(),
|
||||
'disposition' => $p->getDisposition()
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
// Inline attachments
|
||||
// Horde doesn't consider parts with content-disposition set to inline as
|
||||
// attachment so we need to use another way to get them.
|
||||
// We use these inline attachments to render a message's html body in $this->getHtmlBody()
|
||||
$filename = $p->getName();
|
||||
if ($p->getType() === 'message/rfc822' || isset($filename)) {
|
||||
if (in_array($filename, $this->attachmentsToIgnore)) {
|
||||
return;
|
||||
}
|
||||
$this->inlineAttachments[] = [
|
||||
'id' => $p->getMimeId(),
|
||||
'messageId' => $this->messageId,
|
||||
'fileName' => $filename,
|
||||
'mime' => $p->getType(),
|
||||
'size' => $p->getBytes(),
|
||||
'cid' => $p->getContentId()
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
if ($p->getPrimaryType() === 'multipart') {
|
||||
$this->handleMultiPartMessage($p, $partNo);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($p->getType() === 'text/plain') {
|
||||
$this->handleTextMessage($p, $partNo);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($p->getType() === 'text/html') {
|
||||
$this->handleHtmlMessage($p, $partNo);
|
||||
return;
|
||||
}
|
||||
|
||||
// EMBEDDED MESSAGE
|
||||
// Many bounce notifications embed the original message as type 2,
|
||||
// but AOL uses type 1 (multipart), which is not handled here.
|
||||
// There are no PHP functions to parse embedded messages,
|
||||
// so this just appends the raw source to the main message.
|
||||
if ($p[0] === 'message') {
|
||||
$data = $this->loadBodyData($p, $partNo);
|
||||
$this->plainMessage .= trim($data) . "\n\n";
|
||||
}
|
||||
return $this->imapDate;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -567,102 +337,6 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
return $this->plainMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $part
|
||||
* @param mixed $partNo
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handleMultiPartMessage(Horde_Mime_Part $part, $partNo): void {
|
||||
$i = 1;
|
||||
foreach ($part->getParts() as $p) {
|
||||
$this->getPart($p, "$partNo.$i");
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $p
|
||||
* @param mixed $partNo
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handleTextMessage(Horde_Mime_Part $p, $partNo): void {
|
||||
$data = $this->loadBodyData($p, $partNo);
|
||||
$this->plainMessage .= trim($data) . "\n\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $p
|
||||
* @param mixed $partNo
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handleHtmlMessage(Horde_Mime_Part $p, $partNo): void {
|
||||
$this->hasHtmlMessage = true;
|
||||
if ($this->loadHtmlMessage) {
|
||||
$data = $this->loadBodyData($p, $partNo);
|
||||
$this->htmlMessage .= $data . "<br><br>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Horde_Mime_Part $p
|
||||
* @param mixed $partNo
|
||||
*
|
||||
* @return string
|
||||
* @throws DoesNotExistException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function loadBodyData(Horde_Mime_Part $p, $partNo): string {
|
||||
// DECODE DATA
|
||||
$fetch_query = new Horde_Imap_Client_Fetch_Query();
|
||||
$ids = new Horde_Imap_Client_Ids($this->messageId);
|
||||
|
||||
$fetch_query->bodyPart($partNo, [
|
||||
'peek' => true
|
||||
]);
|
||||
$fetch_query->bodyPartSize($partNo);
|
||||
$fetch_query->mimeHeader($partNo, [
|
||||
'peek' => true
|
||||
]);
|
||||
|
||||
$headers = $this->conn->fetch($this->mailBox, $fetch_query, ['ids' => $ids]);
|
||||
/* @var $fetch Horde_Imap_Client_Data_Fetch */
|
||||
$fetch = $headers[$this->messageId];
|
||||
if (is_null($fetch)) {
|
||||
throw new DoesNotExistException("Mail body for this mail($this->messageId) could not be loaded");
|
||||
}
|
||||
|
||||
$mimeHeaders = $fetch->getMimeHeader($partNo, Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
|
||||
if ($enc = $mimeHeaders->getValue('content-transfer-encoding')) {
|
||||
$p->setTransferEncoding($enc);
|
||||
}
|
||||
|
||||
$data = $fetch->getBodyPart($partNo);
|
||||
|
||||
$p->setContents($data);
|
||||
$data = $p->getContents();
|
||||
|
||||
if ($data === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Only convert encoding if it is explicitly specified in the header because text/calendar
|
||||
// data is utf-8 by default.
|
||||
$charset = $p->getContentTypeParameter('charset');
|
||||
if ($charset !== null && strtoupper($charset) !== 'UTF-8') {
|
||||
$data = mb_convert_encoding($data, 'UTF-8', $charset);
|
||||
}
|
||||
return (string)$data;
|
||||
}
|
||||
|
||||
public function getContent(): string {
|
||||
return $this->getPlainBody();
|
||||
}
|
||||
|
@ -736,11 +410,8 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
throw new Exception('not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getReplyTo() {
|
||||
return AddressList::fromHorde($this->getEnvelope()->reply_to);
|
||||
public function getReplyTo(): AddressList {
|
||||
return $this->replyTo;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -752,6 +423,18 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
throw new Exception('not implemented');
|
||||
}
|
||||
|
||||
public function isEncrypted(): bool {
|
||||
return $this->isEncrypted;
|
||||
}
|
||||
|
||||
public function isSigned(): bool {
|
||||
return $this->isSigned;
|
||||
}
|
||||
|
||||
public function isSignatureValid(): bool {
|
||||
return $this->signatureIsValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast all values from an IMAP message into the correct DB format
|
||||
*
|
||||
|
@ -783,7 +466,7 @@ class IMAPMessage implements IMessage, JsonSerializable {
|
|||
$msg->setSubject(mb_strcut($this->getSubject(), 0, 255));
|
||||
$msg->setSentAt($this->getSentDate()->getTimestamp());
|
||||
|
||||
$flags = $this->fetch->getFlags();
|
||||
$flags = $this->flags;
|
||||
$msg->setFlagAnswered(in_array(Horde_Imap_Client::FLAG_ANSWERED, $flags, true));
|
||||
$msg->setFlagDeleted(in_array(Horde_Imap_Client::FLAG_DELETED, $flags, true));
|
||||
$msg->setFlagDraft(in_array(Horde_Imap_Client::FLAG_DRAFT, $flags, true));
|
||||
|
|
|
@ -30,10 +30,12 @@ use JsonSerializable;
|
|||
class SmimeData implements JsonSerializable {
|
||||
private bool $isSigned;
|
||||
private ?bool $signatureIsValid;
|
||||
private bool $isEncrypted;
|
||||
|
||||
public function __construct() {
|
||||
$this->isSigned = false;
|
||||
$this->signatureIsValid = null;
|
||||
$this->isEncrypted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,11 +66,27 @@ class SmimeData implements JsonSerializable {
|
|||
$this->signatureIsValid = $signatureIsValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEncrypted(): bool {
|
||||
return $this->isEncrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isEncrypted
|
||||
* @return void
|
||||
*/
|
||||
public function setIsEncrypted(bool $isEncrypted): void {
|
||||
$this->isEncrypted = $isEncrypted;
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'isSigned' => $this->isSigned,
|
||||
'signatureIsValid' => $this->signatureIsValid,
|
||||
'isEncrypted' => $this->isEncrypted,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Luc Calaresu <dev@calaresu.com>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -279,7 +280,8 @@ class AttachmentService implements IAttachmentService {
|
|||
$fullText = $this->messageMapper->getFullText(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$attachmentMessage->getUid()
|
||||
$attachmentMessage->getUid(),
|
||||
$account->getUserId()
|
||||
);
|
||||
|
||||
// detect mime type
|
||||
|
@ -317,6 +319,7 @@ class AttachmentService implements IAttachmentService {
|
|||
$client,
|
||||
$mailbox->getName(),
|
||||
(int)$attachment['uid'],
|
||||
$account->getUserId(),
|
||||
[
|
||||
$attachment['id'] ?? []
|
||||
]
|
||||
|
|
|
@ -87,7 +87,7 @@ class ItineraryService {
|
|||
$client = $this->clientFactory->getClient($account);
|
||||
try {
|
||||
$itinerary = new Itinerary();
|
||||
$htmlBody = $this->messageMapper->getHtmlBody($client, $mailbox->getName(), $id);
|
||||
$htmlBody = $this->messageMapper->getHtmlBody($client, $mailbox->getName(), $id, $account->getUserId());
|
||||
if ($htmlBody !== null) {
|
||||
$itinerary = $itinerary->merge(
|
||||
$this->extractor->extract($htmlBody)
|
||||
|
@ -96,7 +96,7 @@ class ItineraryService {
|
|||
} else {
|
||||
$this->logger->debug('Message does not have an HTML body, can\'t extract itinerary info');
|
||||
}
|
||||
$attachments = $this->messageMapper->getRawAttachments($client, $mailbox->getName(), $id);
|
||||
$attachments = $this->messageMapper->getRawAttachments($client, $mailbox->getName(), $id, $account->getUserId());
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@ use Horde_Imap_Client_Exception;
|
|||
use Horde_Imap_Client_Exception_NoSupportExtension;
|
||||
use Horde_Imap_Client_Ids;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use Horde_Mime_Exception;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Attachment;
|
||||
use OCA\Mail\Contracts\IMailManager;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
|
@ -192,6 +194,7 @@ class MailManager implements IMailManager {
|
|||
$client,
|
||||
$mailbox->getName(),
|
||||
$uid,
|
||||
$account->getUserId(),
|
||||
$loadBody
|
||||
);
|
||||
} catch (Horde_Imap_Client_Exception | DoesNotExistException $e) {
|
||||
|
@ -219,6 +222,7 @@ class MailManager implements IMailManager {
|
|||
$client,
|
||||
$mailbox->getName(),
|
||||
new Horde_Imap_Client_Ids($uids),
|
||||
$account->getUserId(),
|
||||
true
|
||||
);
|
||||
} catch (Horde_Imap_Client_Exception $e) {
|
||||
|
@ -262,7 +266,9 @@ class MailManager implements IMailManager {
|
|||
return $this->imapMessageMapper->getFullText(
|
||||
$client,
|
||||
$mailbox,
|
||||
$uid
|
||||
$uid,
|
||||
$account->getUserId(),
|
||||
false,
|
||||
);
|
||||
} catch (Horde_Imap_Client_Exception | DoesNotExistException $e) {
|
||||
throw new ServiceException("Could not load message", 0, $e);
|
||||
|
@ -664,7 +670,43 @@ class MailManager implements IMailManager {
|
|||
public function getMailAttachments(Account $account, Mailbox $mailbox, Message $message): array {
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
try {
|
||||
return $this->imapMessageMapper->getAttachments($client, $mailbox->getName(), $message->getUid());
|
||||
return $this->imapMessageMapper->getAttachments(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$message->getUid(),
|
||||
$account->getUserId(),
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Mailbox $mailbox
|
||||
* @param Message $message
|
||||
* @param string $attachmentId
|
||||
* @return Attachment
|
||||
*
|
||||
* @throws DoesNotExistException
|
||||
* @throws Horde_Imap_Client_Exception
|
||||
* @throws Horde_Imap_Client_Exception_NoSupportExtension
|
||||
* @throws ServiceException
|
||||
* @throws Horde_Mime_Exception
|
||||
*/
|
||||
public function getMailAttachment(Account $account,
|
||||
Mailbox $mailbox,
|
||||
Message $message,
|
||||
string $attachmentId): Attachment {
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
try {
|
||||
return $this->imapMessageMapper->getAttachment(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$message->getUid(),
|
||||
$attachmentId,
|
||||
$account->getUserId(),
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
|
|
@ -497,7 +497,8 @@ class MailTransmission implements IMailTransmission {
|
|||
$fullText = $this->messageMapper->getFullText(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$attachmentMessage->getUid()
|
||||
$attachmentMessage->getUid(),
|
||||
$userId
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
|
@ -527,7 +528,8 @@ class MailTransmission implements IMailTransmission {
|
|||
$fullText = $this->messageMapper->getFullText(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$attachmentMessage->getUid()
|
||||
$attachmentMessage->getUid(),
|
||||
$userId
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
|
@ -558,6 +560,7 @@ class MailTransmission implements IMailTransmission {
|
|||
$client,
|
||||
$mailbox->getName(),
|
||||
$attachmentMessage->getUid(),
|
||||
$userId,
|
||||
[
|
||||
$attachment['id']
|
||||
]
|
||||
|
|
|
@ -26,12 +26,19 @@ declare(strict_types=1);
|
|||
namespace OCA\Mail\Service;
|
||||
|
||||
use Exception;
|
||||
use Horde_Imap_Client;
|
||||
use Horde_Imap_Client_Data_Fetch;
|
||||
use Horde_Imap_Client_Fetch_Query;
|
||||
use Horde_Mail_Rfc822_Address;
|
||||
use Horde_Mime_Exception;
|
||||
use Horde_Mime_Headers;
|
||||
use Horde_Mime_Headers_ContentParam_ContentType;
|
||||
use Horde_Mime_Part;
|
||||
use OCA\Mail\Db\SmimeCertificate;
|
||||
use OCA\Mail\Db\SmimeCertificateMapper;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\Exception\SmimeCertificateParserException;
|
||||
use OCA\Mail\Exception\SmimeDecryptException;
|
||||
use OCA\Mail\Exception\SmimeSignException;
|
||||
use OCA\Mail\Model\EnrichedSmimeCertificate;
|
||||
use OCA\Mail\Model\SmimeCertificateInfo;
|
||||
|
@ -63,6 +70,7 @@ class SmimeService {
|
|||
|
||||
/**
|
||||
* Attempt to verify a message signed with S/MIME.
|
||||
*
|
||||
* Requires the openssl extension.
|
||||
*
|
||||
* @param string $message Whole message including all headers and parts as stored on IMAP
|
||||
|
@ -78,9 +86,13 @@ class SmimeService {
|
|||
fwrite($messageTempHandle, $message);
|
||||
fclose($messageTempHandle);
|
||||
/** @psalm-suppress NullArgument */
|
||||
$valid = openssl_pkcs7_verify($messageTemp, 0, null, [
|
||||
$this->certificateManager->getAbsoluteBundlePath(),
|
||||
]);
|
||||
$valid = openssl_pkcs7_verify(
|
||||
$messageTemp,
|
||||
0,
|
||||
null,
|
||||
[$this->certificateManager->getAbsoluteBundlePath()],
|
||||
);
|
||||
|
||||
if (is_int($valid)) {
|
||||
// OpenSSL error
|
||||
return false;
|
||||
|
@ -89,6 +101,52 @@ class SmimeService {
|
|||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to extract the signed content from a signed S/MIME message.
|
||||
* Can be used to extract opaque signed content even if the signature itself can't be verified.
|
||||
*
|
||||
* Warning: This method does not attempt to verify the signature.
|
||||
*
|
||||
* Requires the openssl extension.
|
||||
*
|
||||
* @param string $message Whole message including all headers and parts as stored on IMAP
|
||||
* @return string Signed content
|
||||
*
|
||||
* @throws ServiceException If no signed content can be extracted
|
||||
*/
|
||||
public function extractSignedContent(string $message): string {
|
||||
// Ideally, we should use the more modern openssl cms module as it is a superset of the
|
||||
// smime/pkcs7 module. Unfortunately, it is only supported since php 8.
|
||||
// Ref https://www.php.net/manual/en/function.openssl-cms-verify.php
|
||||
|
||||
$verifiedContentTemp = $this->tempManager->getTemporaryFile();
|
||||
$messageTemp = $this->tempManager->getTemporaryFile();
|
||||
$messageTempHandle = fopen($messageTemp, 'wb');
|
||||
fwrite($messageTempHandle, $message);
|
||||
fclose($messageTempHandle);
|
||||
/** @psalm-suppress NullArgument */
|
||||
$valid = openssl_pkcs7_verify(
|
||||
$messageTemp,
|
||||
PKCS7_NOSIGS | PKCS7_NOVERIFY,
|
||||
null,
|
||||
[$this->certificateManager->getAbsoluteBundlePath()],
|
||||
null,
|
||||
$verifiedContentTemp,
|
||||
);
|
||||
|
||||
if (is_int($valid)) {
|
||||
// OpenSSL error
|
||||
throw new ServiceException('Failed to extract signed content');
|
||||
}
|
||||
|
||||
$verifiedContent = file_get_contents($verifiedContentTemp);
|
||||
if ($verifiedContent === false) {
|
||||
throw new ServiceException('Could not read back verified content');
|
||||
}
|
||||
|
||||
return $verifiedContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a X509 certificate.
|
||||
*
|
||||
|
@ -182,6 +240,28 @@ class SmimeService {
|
|||
return $this->certificateMapper->find($certificateId, $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all S/MIME certificates belonging to an email address.
|
||||
*
|
||||
* @param string $emailAddress
|
||||
* @param string $userId
|
||||
* @return SmimeCertificate[]
|
||||
*
|
||||
* @throws ServiceException If the database query fails
|
||||
*/
|
||||
public function findCertificatesByEmailAddress(string $emailAddress,
|
||||
string $userId): array {
|
||||
try {
|
||||
return $this->certificateMapper->findAllByEmailAddress($userId, $emailAddress);
|
||||
} catch (\OCP\DB\Exception $e) {
|
||||
throw new ServiceException(
|
||||
'Failed to fetch certificates by email address: ' . $e->getMessage(),
|
||||
0,
|
||||
$e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all S/MIME certificates of the given user.
|
||||
*
|
||||
|
@ -293,4 +373,152 @@ class SmimeService {
|
|||
|
||||
return $parsedPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt full text of a MIME message.
|
||||
* This method assumes the given mime part text to be encrypted without checking.
|
||||
*
|
||||
* @param string $mimePartText
|
||||
* @param SmimeCertificate $certificate The certificate needs to contain a private key.
|
||||
* @return string Full text of decrypted MIME message. It will probably contain multiple parts.
|
||||
*
|
||||
* @throws ServiceException If the given certificate does not have a private key or can't be decrypted
|
||||
* @throws SmimeDecryptException If openssl reports an error during decryption
|
||||
*/
|
||||
public function decryptMimePartText(string $mimePartText,
|
||||
SmimeCertificate $certificate): string {
|
||||
if ($certificate->getPrivateKey() === null) {
|
||||
throw new ServiceException('Certificate does not have a private key');
|
||||
}
|
||||
|
||||
try {
|
||||
$decryptedCertificate = $this->crypto->decrypt($certificate->getCertificate());
|
||||
$decryptedKey = $this->crypto->decrypt($certificate->getPrivateKey());
|
||||
} catch (Exception $e) {
|
||||
throw new ServiceException(
|
||||
'Failed to decrypt certificate or private key: ' . $e->getMessage(),
|
||||
0,
|
||||
$e,
|
||||
);
|
||||
}
|
||||
|
||||
$inPath = $this->tempManager->getTemporaryFile();
|
||||
$outPath = $this->tempManager->getTemporaryFile();
|
||||
file_put_contents($inPath, $mimePartText);
|
||||
if (!openssl_pkcs7_decrypt($inPath, $outPath, $decryptedCertificate, $decryptedKey)) {
|
||||
throw new SmimeDecryptException('Failed to decrypt MIME part text');
|
||||
}
|
||||
|
||||
$decryptedMessage = file_get_contents($outPath);
|
||||
|
||||
// Handle smime-type="signed-data" as the content is opaque until verified
|
||||
$headers = Horde_Mime_Headers::parseHeaders($decryptedMessage);
|
||||
if (!isset($headers['content-type'])) {
|
||||
return $decryptedMessage;
|
||||
}
|
||||
/** @var Horde_Mime_Headers_ContentParam_ContentType $contentType */
|
||||
$contentType = $headers['content-type'];
|
||||
|
||||
if ($contentType->ptype !== 'application'
|
||||
|| $contentType->stype !== 'pkcs7-mime'
|
||||
|| !isset($contentType['smime-type'])
|
||||
|| $contentType['smime-type'] !== 'signed-data') {
|
||||
return $decryptedMessage;
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: propagate signature verification status
|
||||
$decryptedMessage = $this->extractSignedContent($decryptedMessage);
|
||||
} catch (ServiceException $e) {
|
||||
throw new ServiceException(
|
||||
'Failed to extract nested signed data: ' . $e->getMessage(),
|
||||
0,
|
||||
$e,
|
||||
);
|
||||
}
|
||||
|
||||
return $decryptedMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to decrypt a raw data fetch from horde.
|
||||
* The fetch needs to contain at least envelope, headerText and fullText.
|
||||
* See the addDecryptQueries() method.
|
||||
*
|
||||
* This method will do nothing to the full text if the message is not encrypted.
|
||||
*
|
||||
* @param Horde_Imap_Client_Data_Fetch $message
|
||||
* @param string $userId
|
||||
* @return string
|
||||
*
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function decryptDataFetch(Horde_Imap_Client_Data_Fetch $message, string $userId): string {
|
||||
$encryptedText = $message->getFullMsg();
|
||||
if (!$this->isEncrypted($message)) {
|
||||
return $encryptedText;
|
||||
}
|
||||
|
||||
$decryptedText = null;
|
||||
$envelope = $message->getEnvelope();
|
||||
foreach ($envelope->to as $recipient) {
|
||||
/** @var Horde_Mail_Rfc822_Address $recipient */
|
||||
$recipientAddress = $recipient->bare_address;
|
||||
$certs = $this->findCertificatesByEmailAddress(
|
||||
$recipientAddress,
|
||||
$userId,
|
||||
);
|
||||
|
||||
foreach ($certs as $cert) {
|
||||
try {
|
||||
$decryptedText = $this->decryptMimePartText($encryptedText, $cert);
|
||||
} catch (ServiceException | SmimeDecryptException $e) {
|
||||
// Certificate probably didn't match -> continue
|
||||
// TODO: filter a real decryption error
|
||||
// (is hard because openssl doesn't return a proper error code)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($decryptedText === null) {
|
||||
throw new ServiceException('Failed to find a suitable S/MIME certificate for decryption');
|
||||
}
|
||||
|
||||
return $decryptedText;
|
||||
}
|
||||
|
||||
public function addEncryptionCheckQueries(Horde_Imap_Client_Fetch_Query $query,
|
||||
bool $peek = true): void {
|
||||
if (!$query->contains(Horde_Imap_Client::FETCH_HEADERTEXT)) {
|
||||
$query->headerText([
|
||||
'peek' => $peek,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function addDecryptQueries(Horde_Imap_Client_Fetch_Query $query,
|
||||
bool $peek = true): void {
|
||||
$this->addEncryptionCheckQueries($query, $peek);
|
||||
$query->envelope();
|
||||
if (!$query->contains(Horde_Imap_Client::FETCH_FULLMSG)) {
|
||||
$query->fullText([
|
||||
'peek' => $peek,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function isEncrypted(Horde_Imap_Client_Data_Fetch $message): bool {
|
||||
$headers = $message->getHeaderText('0', Horde_Imap_Client_Data_Fetch::HEADER_PARSE);
|
||||
if (!isset($headers['content-type'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var Horde_Mime_Headers_ContentParam_ContentType $contentType */
|
||||
$contentType = $headers['content-type'];
|
||||
return $contentType->ptype === 'application'
|
||||
&& $contentType->stype === 'pkcs7-mime'
|
||||
&& isset($contentType['smime-type'])
|
||||
&& $contentType['smime-type'] === 'enveloped-data';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ declare(strict_types=1);
|
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
|
@ -302,7 +303,8 @@ class ImapToDbSynchronizer {
|
|||
self::MAX_NEW_MESSAGES,
|
||||
$highestKnownUid ?? 0,
|
||||
$logger,
|
||||
$perf
|
||||
$perf,
|
||||
$account->getUserId(),
|
||||
);
|
||||
$perf->step(sprintf('fetch %d messages from IMAP', count($imapMessages)));
|
||||
} catch (Horde_Imap_Client_Exception $e) {
|
||||
|
@ -372,6 +374,7 @@ class ImapToDbSynchronizer {
|
|||
$mailbox->getSyncNewToken(),
|
||||
$uids
|
||||
),
|
||||
$account->getUserId(),
|
||||
Horde_Imap_Client::SYNC_NEWMSGSUIDS
|
||||
);
|
||||
$perf->step('get new messages via Horde');
|
||||
|
@ -415,6 +418,7 @@ class ImapToDbSynchronizer {
|
|||
$mailbox->getSyncChangedToken(),
|
||||
$uids
|
||||
),
|
||||
$account->getUserId(),
|
||||
Horde_Imap_Client::SYNC_FLAGSUIDS
|
||||
);
|
||||
$perf->step('get changed messages via Horde');
|
||||
|
@ -444,6 +448,7 @@ class ImapToDbSynchronizer {
|
|||
$mailbox->getSyncVanishedToken(),
|
||||
$uids
|
||||
),
|
||||
$account->getUserId(),
|
||||
Horde_Imap_Client::SYNC_VANISHEDUIDS
|
||||
);
|
||||
$perf->step('get vanished messages via Horde');
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
{{ subjectForSubtitle }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="data.previewText"
|
||||
<div v-if="data.encrypted || data.previewText"
|
||||
class="envelope__preview-text">
|
||||
{{ isEncrypted ? t('mail', 'Encrypted message') : data.previewText }}
|
||||
</div>
|
||||
|
@ -435,8 +435,8 @@ export default {
|
|||
return this.data.flags.seen
|
||||
},
|
||||
isEncrypted() {
|
||||
return this.data.previewText
|
||||
&& isPgpText(this.data.previewText)
|
||||
return this.data.encrypted // S/MIME
|
||||
|| (this.data.previewText && isPgpText(this.data.previewText)) // PGP/Mailvelope
|
||||
},
|
||||
isImportant() {
|
||||
return this.$store.getters
|
||||
|
|
|
@ -77,9 +77,12 @@
|
|||
<div class="right">
|
||||
<Moment class="timestamp" :timestamp="envelope.dateInt" />
|
||||
<template v-if="expanded">
|
||||
<NcActions v-if="smimeData.isSigned">
|
||||
<NcActions v-if="smimeData.isSigned || smimeData.isEncrypted">
|
||||
<template #icon>
|
||||
<LockIcon v-if="smimeData.signatureIsValid"
|
||||
<LockIcon v-if="smimeData.isEncrypted"
|
||||
:size="20"
|
||||
fill-color="#008000" />
|
||||
<LockIcon v-else-if="smimeData.signatureIsValid"
|
||||
:size="20"
|
||||
fill-color="#ffcc00" />
|
||||
<LockOffIcon v-else
|
||||
|
@ -87,7 +90,7 @@
|
|||
fill-color="red" />
|
||||
</template>
|
||||
<NcActionText>
|
||||
{{ smimeSignMessage }}
|
||||
{{ smimeMessage }}
|
||||
</NcActionText>
|
||||
<!-- TODO: display information about signer and/or CA certificate -->
|
||||
</NcActions>
|
||||
|
@ -380,12 +383,16 @@ export default {
|
|||
smimeData() {
|
||||
return this.message?.smime ?? {}
|
||||
},
|
||||
smimeSignMessage() {
|
||||
smimeMessage() {
|
||||
if (this.smimeData.isEncrypted) {
|
||||
return t('mail', 'This message was encrypted by the sender before it was sent.')
|
||||
}
|
||||
|
||||
if (this.smimeData.signatureIsValid) {
|
||||
return t('mail', 'This message contains a verified digital S/MIME signature. The message wasn\'t changed since it was sent.')
|
||||
} else {
|
||||
return t('mail', 'This message contains an unverified digital S/MIME signature. The message might have been changed since it was sent or the certificate of the signer is untrusted.')
|
||||
}
|
||||
|
||||
return t('mail', 'This message contains an unverified digital S/MIME signature. The message might have been changed since it was sent or the certificate of the signer is untrusted.')
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
|
|
@ -27,6 +27,7 @@ use Horde_Imap_Client_Fetch_Query;
|
|||
use Horde_Imap_Client_Ids;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use Horde_Mail_Rfc822_Address;
|
||||
use Horde_Mime_Headers;
|
||||
use Horde_Mime_Headers_MessageId;
|
||||
use Horde_Mime_Mail;
|
||||
use Horde_Mime_Part;
|
||||
|
@ -170,6 +171,33 @@ trait ImapTest {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mailbox
|
||||
* @param string $mimeText
|
||||
* @param MailAccount|null $account
|
||||
* @return int Uid of the new message
|
||||
*/
|
||||
public function saveMimeMessage(string $mailbox, string $mimeText, ?MailAccount $account = null): int {
|
||||
$headers = Horde_Mime_Headers::parseHeaders($mimeText);
|
||||
$mimePart = Horde_Mime_Part::parseMessage($mimeText);
|
||||
|
||||
$mail = new Horde_Mime_Mail();
|
||||
$mail->addHeaders($headers);
|
||||
$mail->setBasePart($mimePart);
|
||||
|
||||
$data = $mail->getRaw(false);
|
||||
$client = $this->getClient($account);
|
||||
try {
|
||||
return $client->append($mailbox, [
|
||||
[
|
||||
'data' => $mimeText,
|
||||
]
|
||||
])->ids[0];
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
}
|
||||
|
||||
public function flagMessage($mailbox, $id, MailAccount $account = null) {
|
||||
$client = $this->getClient($account);
|
||||
try {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Christoph Wurst <wurst.christoph@gmail.com>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -29,7 +30,6 @@ use OC;
|
|||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\Mailbox;
|
||||
|
||||
/**
|
||||
* @group IMAP
|
||||
|
@ -108,23 +108,4 @@ abstract class AbstractTest extends TestCase {
|
|||
protected function assertMailBoxNotExists($name) {
|
||||
$this->assertFalse($this->existsMailBox($name));
|
||||
}
|
||||
|
||||
protected function createTestMessage(
|
||||
Mailbox $mailbox, $subject = 'Don\'t panic!',
|
||||
$contents = 'Don\'t forget your towel', $from = 'someone@there.com',
|
||||
$to = 'me@here.com'
|
||||
) {
|
||||
$message = "From: $from
|
||||
Subject: $subject
|
||||
To: $to
|
||||
Message-ID: <20150415133206.Horde.M8uzSs0lxFX6uUE2sc6_rw5@localhost>
|
||||
User-Agent: Horde Application Framework 5
|
||||
Date: Wed, 15 Apr 2015 13:32:06 +0000
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed; DelSp=Yes
|
||||
MIME-Version: 1.0
|
||||
|
||||
|
||||
$contents";
|
||||
$mailbox->saveMessage($message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Tests\Integration\IMAP;
|
||||
|
||||
use OC;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\SmimeCertificate;
|
||||
use OCA\Mail\Db\SmimeCertificateMapper;
|
||||
use OCA\Mail\IMAP\ImapMessageFetcherFactory;
|
||||
use OCA\Mail\Tests\Integration\Framework\ImapTest;
|
||||
use OCA\Mail\Tests\Integration\Framework\ImapTestAccount;
|
||||
use OCA\Mail\Tests\Integration\TestCase;
|
||||
use OCP\ICertificateManager;
|
||||
use OCP\Security\ICrypto;
|
||||
|
||||
class ImapMessageFetcherIntegrationTest extends TestCase {
|
||||
use ImapTest,
|
||||
ImapTestAccount;
|
||||
|
||||
private MailAccount $account;
|
||||
private ImapMessageFetcherFactory $fetcherFactory;
|
||||
private SmimeCertificateMapper $certificateMapper;
|
||||
private ICrypto $crypto;
|
||||
private ICertificateManager $certificateManager;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
|
||||
$this->account = $this->createTestAccount();
|
||||
$this->fetcherFactory = OC::$server->get(ImapMessageFetcherFactory::class);
|
||||
$this->certificateMapper = OC::$server->get(SmimeCertificateMapper::class);
|
||||
$this->crypto = OC::$server->get(ICrypto::class);
|
||||
$this->certificateManager = OC::$server->get(ICertificateManager::class);
|
||||
|
||||
$this->certificateManager->addCertificate(
|
||||
file_get_contents(__DIR__ . '/../../data/smime-certs/domain.tld.ca.crt'),
|
||||
'domain.tld.ca.crt'
|
||||
);
|
||||
|
||||
$this->importCertificate('user@imap.localhost');
|
||||
$this->importCertificate('user@domain.tld');
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
parent::tearDown();
|
||||
|
||||
$this->certificateManager->removeCertificate('domain.tld.ca.crt');
|
||||
$this->clearCertificates();
|
||||
}
|
||||
|
||||
private function importCertificate(string $emailAddress): SmimeCertificate {
|
||||
// TODO: convert to a trait?!
|
||||
|
||||
$certificateData = file_get_contents(__DIR__ . "/../../data/smime-certs/$emailAddress.crt");
|
||||
$privateKeyData = file_get_contents(__DIR__ . "/../../data/smime-certs/$emailAddress.key");
|
||||
|
||||
$certificate = new SmimeCertificate();
|
||||
$certificate->setUserId($this->account->getUserId());
|
||||
$certificate->setEmailAddress($emailAddress);
|
||||
$certificate->setCertificate($this->crypto->encrypt($certificateData));
|
||||
$certificate->setPrivateKey($this->crypto->encrypt($privateKeyData));
|
||||
$this->certificateMapper->insert($certificate);
|
||||
|
||||
return $certificate;
|
||||
}
|
||||
|
||||
private function clearCertificates(): void {
|
||||
// TODO: convert to a trait?!
|
||||
|
||||
$certificates = $this->certificateMapper->findAll($this->account->getUserId());
|
||||
foreach ($certificates as $certificate) {
|
||||
$this->certificateMapper->delete($certificate);
|
||||
}
|
||||
}
|
||||
|
||||
public function testFetchMessageWithEncryptedMessage(): void {
|
||||
$encryptedMessage = file_get_contents(__DIR__ . '/../../data/encrypted-message.txt');
|
||||
$uid = $this->saveMimeMessage('INBOX', $encryptedMessage);
|
||||
$fetcher = $this->fetcherFactory
|
||||
->build(
|
||||
$uid,
|
||||
'INBOX',
|
||||
$this->getTestClient(),
|
||||
$this->account->getUserId()
|
||||
)
|
||||
->withBody(true);
|
||||
|
||||
$message = $fetcher->fetchMessage();
|
||||
|
||||
$this->assertEquals("Just some encrypted test images.\n\n", $message->getPlainBody());
|
||||
$this->assertCount(3, $message->attachments);
|
||||
}
|
||||
|
||||
public function testFetchMessageWithEncryptedSignedOpaqueMessage(): void {
|
||||
$encryptedMessage = file_get_contents(__DIR__ . '/../../data/encrypted-signed-opaque-message.txt');
|
||||
$uid = $this->saveMimeMessage('INBOX', $encryptedMessage);
|
||||
$fetcher = $this->fetcherFactory
|
||||
->build(
|
||||
$uid,
|
||||
'INBOX',
|
||||
$this->getTestClient(),
|
||||
$this->account->getUserId()
|
||||
)
|
||||
->withBody(true);
|
||||
|
||||
$message = $fetcher->fetchMessage();
|
||||
|
||||
$this->assertEquals("hoi\n\n", $message->getPlainBody());
|
||||
}
|
||||
}
|
|
@ -32,7 +32,6 @@ use OCP\IL10N;
|
|||
use OCP\IRequest;
|
||||
use OCA\Mail\Db\Tag;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Mailbox;
|
||||
use OCP\Files\Folder;
|
||||
use ReflectionObject;
|
||||
use OCP\IURLGenerator;
|
||||
|
@ -101,9 +100,6 @@ class MessagesControllerTest extends TestCase {
|
|||
/** @var MockObject|Account */
|
||||
private $account;
|
||||
|
||||
/** @var MockObject|Mailbox */
|
||||
private $mailbox;
|
||||
|
||||
/** @var MockObject|Message */
|
||||
private $message;
|
||||
|
||||
|
@ -186,7 +182,6 @@ class MessagesControllerTest extends TestCase {
|
|||
);
|
||||
|
||||
$this->account = $this->createMock(Account::class);
|
||||
$this->mailbox = $this->createMock(Mailbox::class);
|
||||
$this->message = $this->createMock(IMAPMessage::class);
|
||||
$this->attachment = $this->createMock(Attachment::class);
|
||||
}
|
||||
|
@ -298,17 +293,14 @@ class MessagesControllerTest extends TestCase {
|
|||
->method('getMailbox')
|
||||
->with($this->userId, $mailboxId)
|
||||
->willReturn($mailbox);
|
||||
$this->mailManager->expects($this->once())
|
||||
->method('getMailAttachment')
|
||||
->with($this->account, $mailbox, $message, $attachmentId)
|
||||
->will($this->returnValue($this->attachment));
|
||||
$this->accountService->expects($this->once())
|
||||
->method('find')
|
||||
->with($this->equalTo($this->userId), $this->equalTo($accountId))
|
||||
->will($this->returnValue($this->account));
|
||||
$this->account->expects($this->once())
|
||||
->method('getMailbox')
|
||||
->willReturn($this->mailbox);
|
||||
$this->mailbox->expects($this->once())
|
||||
->method('getAttachment')
|
||||
->with($uid, $attachmentId)
|
||||
->will($this->returnValue($this->attachment));
|
||||
$this->attachment->expects($this->once())
|
||||
->method('getContents')
|
||||
->will($this->returnValue($contents));
|
||||
|
@ -353,13 +345,9 @@ class MessagesControllerTest extends TestCase {
|
|||
->method('find')
|
||||
->with($this->equalTo($this->userId), $this->equalTo($accountId))
|
||||
->will($this->returnValue($this->account));
|
||||
$this->account->expects($this->once())
|
||||
->method('getMailbox')
|
||||
->with('INBOX')
|
||||
->will($this->returnValue($this->mailbox));
|
||||
$this->mailbox->expects($this->once())
|
||||
->method('getAttachment')
|
||||
->with($uid, $attachmentId)
|
||||
$this->mailManager->expects($this->once())
|
||||
->method('getMailAttachment')
|
||||
->with($this->account, $mailbox, $message, $attachmentId)
|
||||
->will($this->returnValue($this->attachment));
|
||||
$this->attachment->expects($this->once())
|
||||
->method('getName')
|
||||
|
@ -406,6 +394,7 @@ class MessagesControllerTest extends TestCase {
|
|||
$mailbox = new \OCA\Mail\Db\Mailbox();
|
||||
$mailbox->setName('INBOX');
|
||||
$mailbox->setAccountId($accountId);
|
||||
$client = $this->createMock(Horde_Imap_Client_Socket::class);
|
||||
$this->mailManager->expects($this->once())
|
||||
->method('getMessage')
|
||||
->with($this->userId, $id)
|
||||
|
@ -418,23 +407,23 @@ class MessagesControllerTest extends TestCase {
|
|||
->method('find')
|
||||
->with($this->equalTo($this->userId), $this->equalTo($accountId))
|
||||
->will($this->returnValue($this->account));
|
||||
$this->account->expects($this->once())
|
||||
->method('getMailbox')
|
||||
->with('INBOX')
|
||||
->will($this->returnValue($this->mailbox));
|
||||
$this->mailbox->expects($this->once())
|
||||
->method('getMessage')
|
||||
->with($uid)
|
||||
->will($this->returnValue($this->message));
|
||||
$this->clientFactory->expects($this->once())
|
||||
->method('getClient')
|
||||
->with($this->account)
|
||||
->willReturn($client);
|
||||
$this->mailManager->expects($this->once())
|
||||
->method('getImapMessage')
|
||||
->with($client, $this->account, $mailbox, $message->getUid(), true)
|
||||
->willReturn($this->message);
|
||||
$this->message->attachments = [
|
||||
[
|
||||
'id' => $attachmentId
|
||||
]
|
||||
];
|
||||
|
||||
$this->mailbox->expects($this->once())
|
||||
->method('getAttachment')
|
||||
->with($uid, $attachmentId)
|
||||
$this->mailManager->expects($this->once())
|
||||
->method('getMailAttachment')
|
||||
->with($this->account, $mailbox, $message, $attachmentId)
|
||||
->will($this->returnValue($this->attachment));
|
||||
$this->attachment->expects($this->once())
|
||||
->method('getName')
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -31,8 +32,11 @@ use Horde_Imap_Client_Fetch_Results;
|
|||
use Horde_Imap_Client_Ids;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\IMAP\ImapMessageFetcher;
|
||||
use OCA\Mail\IMAP\ImapMessageFetcherFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCA\Mail\Model\IMAPMessage;
|
||||
use OCA\Mail\Service\SmimeService;
|
||||
use OCA\Mail\Support\PerformanceLoggerTask;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -45,13 +49,23 @@ class MessageMapperTest extends TestCase {
|
|||
/** @var MessageMapper */
|
||||
private $mapper;
|
||||
|
||||
/** @var SmimeService|MockObject */
|
||||
private $sMimeService;
|
||||
|
||||
/** @var ImapMessageFetcherFactory|MockObject */
|
||||
private $imapMessageFactory;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->sMimeService = $this->createMock(SmimeService::class);
|
||||
$this->imapMessageFactory = $this->createMock(ImapMessageFetcherFactory::class);
|
||||
|
||||
$this->mapper = new MessageMapper(
|
||||
$this->logger
|
||||
$this->logger,
|
||||
$this->sMimeService,
|
||||
$this->imapMessageFactory,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -60,6 +74,13 @@ class MessageMapperTest extends TestCase {
|
|||
$imapClient = $this->createMock(Horde_Imap_Client_Socket::class);
|
||||
$mailbox = 'inbox';
|
||||
$ids = [1, 3];
|
||||
$userId = 'user';
|
||||
$loadBody = false;
|
||||
|
||||
$imapMessageFetcher1 = $this->createMock(ImapMessageFetcher::class);
|
||||
$imapMessageFetcher2 = $this->createMock(ImapMessageFetcher::class);
|
||||
$message1 = $this->createMock(IMAPMessage::class);
|
||||
$message2 = $this->createMock(IMAPMessage::class);
|
||||
|
||||
$fetchResults = new Horde_Imap_Client_Fetch_Results();
|
||||
$fetchResult1 = $this->createMock(Horde_Imap_Client_Data_Fetch::class);
|
||||
|
@ -81,15 +102,42 @@ class MessageMapperTest extends TestCase {
|
|||
->willReturn(1);
|
||||
$fetchResult2->method('getUid')
|
||||
->willReturn(3);
|
||||
$this->imapMessageFactory->expects(self::exactly(2))
|
||||
->method('build')
|
||||
->willReturnMap([
|
||||
[1, $mailbox, $imapClient, $userId, $imapMessageFetcher1],
|
||||
[3, $mailbox, $imapClient, $userId, $imapMessageFetcher2],
|
||||
]);
|
||||
|
||||
$imapMessageFetcher1->expects(self::once())
|
||||
->method('withBody')
|
||||
->with($loadBody)
|
||||
->willReturnSelf();
|
||||
$imapMessageFetcher1->expects(self::once())
|
||||
->method('fetchMessage')
|
||||
->with($fetchResult1)
|
||||
->willReturn($message1);
|
||||
$imapMessageFetcher2->expects(self::once())
|
||||
->method('withBody')
|
||||
->with($loadBody)
|
||||
->willReturnSelf();
|
||||
$imapMessageFetcher2->expects(self::once())
|
||||
->method('fetchMessage')
|
||||
->with($fetchResult2)
|
||||
->willReturn($message2);
|
||||
|
||||
$message1 = new IMAPMessage($imapClient, $mailbox, 1, $fetchResult1);
|
||||
$message2 = new IMAPMessage($imapClient, $mailbox, 3, $fetchResult2);
|
||||
$expected = [
|
||||
$message1,
|
||||
$message2,
|
||||
];
|
||||
|
||||
$result = $this->mapper->findByIds($imapClient, $mailbox, new Horde_Imap_Client_Ids($ids));
|
||||
$result = $this->mapper->findByIds(
|
||||
$imapClient,
|
||||
$mailbox,
|
||||
new Horde_Imap_Client_Ids($ids),
|
||||
$userId,
|
||||
$loadBody
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
@ -99,6 +147,11 @@ class MessageMapperTest extends TestCase {
|
|||
$imapClient = $this->createMock(Horde_Imap_Client_Socket::class);
|
||||
$mailbox = 'inbox';
|
||||
$ids = [1, 3];
|
||||
$userId = 'user';
|
||||
$loadBody = false;
|
||||
|
||||
$imapMessageFetcher1 = $this->createMock(ImapMessageFetcher::class);
|
||||
$message1 = $this->createMock(IMAPMessage::class);
|
||||
|
||||
$fetchResults = new Horde_Imap_Client_Fetch_Results();
|
||||
$fetchResult1 = $this->createMock(Horde_Imap_Client_Data_Fetch::class);
|
||||
|
@ -120,13 +173,32 @@ class MessageMapperTest extends TestCase {
|
|||
->willReturn(1);
|
||||
$fetchResult2->expects(self::never())
|
||||
->method('getUid');
|
||||
$this->imapMessageFactory->expects(self::once())
|
||||
->method('build')
|
||||
->willReturnMap([
|
||||
[1, $mailbox, $imapClient, $userId, $imapMessageFetcher1],
|
||||
]);
|
||||
|
||||
$imapMessageFetcher1->expects(self::once())
|
||||
->method('withBody')
|
||||
->with($loadBody)
|
||||
->willReturnSelf();
|
||||
$imapMessageFetcher1->expects(self::once())
|
||||
->method('fetchMessage')
|
||||
->with($fetchResult1)
|
||||
->willReturn($message1);
|
||||
|
||||
$message1 = new IMAPMessage($imapClient, $mailbox, 1, $fetchResult1);
|
||||
$expected = [
|
||||
$message1
|
||||
];
|
||||
|
||||
$result = $this->mapper->findByIds($imapClient, $mailbox, new Horde_Imap_Client_Ids($ids));
|
||||
$result = $this->mapper->findByIds(
|
||||
$imapClient,
|
||||
$mailbox,
|
||||
new Horde_Imap_Client_Ids($ids),
|
||||
$userId,
|
||||
$loadBody
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
@ -162,7 +234,8 @@ class MessageMapperTest extends TestCase {
|
|||
5000,
|
||||
0,
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(PerformanceLoggerTask::class)
|
||||
$this->createMock(PerformanceLoggerTask::class),
|
||||
'user'
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
|
@ -233,7 +306,8 @@ class MessageMapperTest extends TestCase {
|
|||
5000,
|
||||
0,
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(PerformanceLoggerTask::class)
|
||||
$this->createMock(PerformanceLoggerTask::class),
|
||||
'user'
|
||||
);
|
||||
|
||||
self::assertTrue($result['all']);
|
||||
|
@ -297,7 +371,8 @@ class MessageMapperTest extends TestCase {
|
|||
5000,
|
||||
300,
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(PerformanceLoggerTask::class)
|
||||
$this->createMock(PerformanceLoggerTask::class),
|
||||
'user'
|
||||
);
|
||||
|
||||
self::assertTrue($result['all']);
|
||||
|
@ -368,7 +443,8 @@ class MessageMapperTest extends TestCase {
|
|||
5000,
|
||||
92000,
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(PerformanceLoggerTask::class)
|
||||
$this->createMock(PerformanceLoggerTask::class),
|
||||
'user'
|
||||
);
|
||||
|
||||
// This chunk returns 8k messages, when we only expected 5k. So the process
|
||||
|
@ -409,7 +485,8 @@ class MessageMapperTest extends TestCase {
|
|||
5000,
|
||||
99999,
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(PerformanceLoggerTask::class)
|
||||
$this->createMock(PerformanceLoggerTask::class),
|
||||
'user'
|
||||
);
|
||||
|
||||
self::assertTrue($result['all']);
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -87,6 +88,7 @@ class SynchronizerTest extends TestCase {
|
|||
$response = $this->synchronizer->sync(
|
||||
$imapClient,
|
||||
$request,
|
||||
'user',
|
||||
Horde_Imap_Client::SYNC_VANISHEDUIDS
|
||||
);
|
||||
|
||||
|
@ -125,6 +127,7 @@ class SynchronizerTest extends TestCase {
|
|||
$response = $this->synchronizer->sync(
|
||||
$imapClient,
|
||||
$request,
|
||||
'user',
|
||||
Horde_Imap_Client::SYNC_VANISHEDUIDS
|
||||
);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -23,71 +24,136 @@
|
|||
namespace OCA\Mail\Tests\Unit\Model;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use Horde_Imap_Client;
|
||||
use Horde_Imap_Client_Data_Fetch;
|
||||
use Horde_Imap_Client_Fetch_Results;
|
||||
use Horde_Imap_Client_DateTime;
|
||||
use Horde_Mime_Part;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Db\Tag;
|
||||
use OCA\Mail\Model\IMAPMessage;
|
||||
use OCA\Mail\Service\Html;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IRequest;
|
||||
|
||||
class IMAPMessageTest extends TestCase {
|
||||
public function testNoFrom() {
|
||||
$data = new Horde_Imap_Client_Data_Fetch();
|
||||
$m = new IMAPMessage(null, 'INBOX', 123, $data);
|
||||
/** @var Html|MockObject */
|
||||
private $htmlService;
|
||||
|
||||
$this->assertEquals(new AddressList(), $m->getFrom());
|
||||
protected function setUp(): void {
|
||||
$this->htmlService = $this->createMock(Html::class);
|
||||
}
|
||||
|
||||
public function testIconvHtmlMessage() {
|
||||
$conn = $this->getMockBuilder('Horde_Imap_Client_Socket')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['fetch'])
|
||||
->getMock();
|
||||
|
||||
$urlGenerator = $this->getMockBuilder('\OCP\IURLGenerator')
|
||||
$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$request = $this->getMockBuilder('\OCP\IRequest')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
//linkToRoute 'mail.proxy.proxy'
|
||||
$urlGenerator->expects($this->any())
|
||||
->method('linkToRoute')
|
||||
->will($this->returnCallback(function ($url) {
|
||||
return "https://docs.example.com/server/go.php?to=$url";
|
||||
}));
|
||||
$request = $this->getMockBuilder(IRequest::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$htmlService = new Html($urlGenerator, $request);
|
||||
|
||||
// mock first fetch
|
||||
$firstFetch = new Horde_Imap_Client_Data_Fetch();
|
||||
$firstPart = Horde_Mime_Part::parseMessage(file_get_contents(__DIR__ . '/../../data/mail-message-123.txt'),
|
||||
$part = Horde_Mime_Part::parseMessage(file_get_contents(__DIR__ . '/../../data/mail-message-123.txt'),
|
||||
['level' => 1]);
|
||||
$firstFetch->setStructure($firstPart);
|
||||
$firstFetch->setBodyPart(1, $firstPart->getPart(1)->getContents());
|
||||
$firstFetch->setBodyPart(2, $firstPart->getPart(2)->getContents());
|
||||
$firstResult = new Horde_Imap_Client_Fetch_Results();
|
||||
$firstResult[123] = $firstFetch;
|
||||
$conn->expects($this->any())
|
||||
->method('fetch')
|
||||
->willReturn($firstResult);
|
||||
$plainTextBody = $part[$part->findBody('plain')]->getContents();
|
||||
$htmlBody = $part[$part->findBody('html')]->getContents();
|
||||
|
||||
$inlineAttachmentPart = $part->getPart(2);
|
||||
$message = new IMAPMessage(
|
||||
1234,
|
||||
'<1576747741.9.1432038946316.JavaMail.root@ip-172-32-11-10>',
|
||||
[],
|
||||
AddressList::parse('from@mail.com'),
|
||||
AddressList::parse('to@mail.com'),
|
||||
AddressList::parse('cc@mail.com'),
|
||||
AddressList::parse('bcc@mail.com'),
|
||||
AddressList::parse('reply-to@mail.com'),
|
||||
'core/master has new results',
|
||||
$plainTextBody,
|
||||
$htmlBody,
|
||||
true,
|
||||
[],
|
||||
[],
|
||||
false,
|
||||
[],
|
||||
new Horde_Imap_Client_DateTime('2016-01-01 00:00:00'),
|
||||
'',
|
||||
'disposition',
|
||||
'',
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
$htmlService,
|
||||
);
|
||||
|
||||
$message = new IMAPMessage($conn, 'INBOX', 123, null, true, $htmlService);
|
||||
$htmlBody = $message->getHtmlBody(123);
|
||||
$this->assertTrue(strlen($htmlBody) > 1000);
|
||||
$actualHtmlBody = $message->getHtmlBody(123);
|
||||
$this->assertTrue(strlen($actualHtmlBody) > 1000);
|
||||
|
||||
$plainTextBody = $message->getPlainBody();
|
||||
$this->assertTrue(strlen($plainTextBody) > 1000);
|
||||
$actualPlainTextBody = $message->getPlainBody();
|
||||
$this->assertEquals($plainTextBody, $actualPlainTextBody);
|
||||
}
|
||||
|
||||
public function testSerialize() {
|
||||
$data = new Horde_Imap_Client_Data_Fetch();
|
||||
$data->setUid(1234);
|
||||
$m = new IMAPMessage(null, 'INBOX', 123, $data);
|
||||
$m = new IMAPMessage(
|
||||
1234,
|
||||
'foo',
|
||||
[ Horde_Imap_Client::FLAG_SEEN, Tag::LABEL_IMPORTANT ],
|
||||
AddressList::parse('from@mail.com'),
|
||||
AddressList::parse('to@mail.com'),
|
||||
AddressList::parse('cc@mail.com'),
|
||||
AddressList::parse('bcc@mail.com'),
|
||||
AddressList::parse('reply-to@mail.com'),
|
||||
'subject',
|
||||
'',
|
||||
'',
|
||||
true,
|
||||
[],
|
||||
[],
|
||||
false,
|
||||
[],
|
||||
new Horde_Imap_Client_DateTime('2016-01-01 00:00:00'),
|
||||
'',
|
||||
'disposition',
|
||||
'',
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
$this->htmlService,
|
||||
);
|
||||
|
||||
$json = $m->jsonSerialize();
|
||||
|
||||
$this->assertEquals([
|
||||
'uid' => 1234,
|
||||
'messageId' => 'foo',
|
||||
'from' => [ [ 'label' => 'from@mail.com', 'email' => 'from@mail.com' ] ],
|
||||
'to' => [ [ 'label' => 'to@mail.com', 'email' => 'to@mail.com' ] ],
|
||||
'cc' => [ [ 'label' => 'cc@mail.com', 'email' => 'cc@mail.com' ] ],
|
||||
'bcc' => [ [ 'label' => 'bcc@mail.com', 'email' => 'bcc@mail.com' ] ],
|
||||
'subject' => 'subject',
|
||||
'dateInt' => 1451606400,
|
||||
'flags' => [
|
||||
'seen' => true,
|
||||
'flagged' => false,
|
||||
'answered' => false,
|
||||
'deleted' => false,
|
||||
'draft' => false,
|
||||
'forwarded' => false,
|
||||
'hasAttachments' => false,
|
||||
'mdnsent' => false,
|
||||
'important' => true,
|
||||
],
|
||||
'hasHtmlBody' => true,
|
||||
'dispositionNotificationTo' => 'disposition',
|
||||
'scheduling' => [],
|
||||
], $json);
|
||||
$this->assertEquals(1234, $json['uid']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -327,7 +328,7 @@ class AttachmentServiceTest extends TestCase {
|
|||
->willReturn($mailbox);
|
||||
$this->messageMapper->expects(self::once())
|
||||
->method('getFullText')
|
||||
->with($client, $mailbox->getName(), $message->getUid())
|
||||
->with($client, $mailbox->getName(), $message->getUid(), $userId)
|
||||
->willReturn('sjdhfkjsdhfkjsdhfkjdshfjhdskfjhds');
|
||||
$this->mapper->expects($this->once())
|
||||
->method('insert')
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* Mail
|
||||
*
|
||||
|
@ -517,6 +518,9 @@ class MailManagerTest extends TestCase {
|
|||
|
||||
public function testGetMailAttachments(): void {
|
||||
$account = $this->createMock(Account::class);
|
||||
$account->expects($this->once())
|
||||
->method('getUserId')
|
||||
->willReturn('user');
|
||||
$attachments = [
|
||||
[
|
||||
'content' => 'abcdefg',
|
||||
|
|
|
@ -184,8 +184,10 @@ class MailTransmissionTest extends TestCase {
|
|||
}
|
||||
|
||||
public function testSendNewMessageWithMessageAsAttachment() {
|
||||
$userId = 'testuser';
|
||||
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('testuser');
|
||||
$mailAccount->setUserId($userId);
|
||||
$mailAccount->setSentMailboxId(123);
|
||||
|
||||
/** @var Account|MockObject $account */
|
||||
|
@ -193,6 +195,7 @@ class MailTransmissionTest extends TestCase {
|
|||
$account->method('getMailAccount')->willReturn($mailAccount);
|
||||
$account->method('getName')->willReturn('Test User');
|
||||
$account->method('getEMailAddress')->willReturn('test@user');
|
||||
$account->method('getUserId')->willReturn($userId);
|
||||
|
||||
$originalAttachment = [
|
||||
[
|
||||
|
@ -244,7 +247,8 @@ class MailTransmissionTest extends TestCase {
|
|||
->with(
|
||||
$this->imapClientFactory->getClient($account),
|
||||
$mailbox->getName(),
|
||||
11
|
||||
11,
|
||||
$userId,
|
||||
)
|
||||
->willReturn($source);
|
||||
|
||||
|
@ -309,7 +313,7 @@ class MailTransmissionTest extends TestCase {
|
|||
$content = ['blablabla'];
|
||||
$this->messageMapper->expects($this->once())
|
||||
->method('getRawAttachments')
|
||||
->with($this->imapClientFactory->getClient($account), $mailbox->getName(), $attachmentMessage->getUid(),
|
||||
->with($this->imapClientFactory->getClient($account), $mailbox->getName(), $attachmentMessage->getUid(), 'testuser',
|
||||
[$originalAttachment[0]['id']])
|
||||
->willReturn($content);
|
||||
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Tests\Service;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use Horde_Imap_Client_Data_Envelope;
|
||||
use Horde_Imap_Client_Data_Fetch;
|
||||
use Horde_Mime_Headers;
|
||||
use Horde_Mime_Headers_ContentParam_ContentType;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Db\SmimeCertificate;
|
||||
use OCA\Mail\Db\SmimeCertificateMapper;
|
||||
use OCA\Mail\Service\SmimeService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\ICertificateManager;
|
||||
use OCP\ITempManager;
|
||||
use OCP\Security\ICrypto;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class SmimeServiceTest extends TestCase {
|
||||
private $tempFiles = [];
|
||||
|
||||
/** @var ITempManager|MockObject */
|
||||
private $tempManager;
|
||||
|
||||
/** @var ICertificateManager|MockObject */
|
||||
private $certificateManager;
|
||||
|
||||
/** @var ICrypto|MockObject */
|
||||
private $crypto;
|
||||
|
||||
/** @var SmimeCertificateMapper|MockObject */
|
||||
private $certificateMapper;
|
||||
|
||||
/** @var ITimeFactory|MockObject */
|
||||
private $timeFactory;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->tempManager = $this->createMock(ITempManager::class);
|
||||
$this->certificateManager = $this->createMock(ICertificateManager::class);
|
||||
$this->crypto = $this->createMock(ICrypto::class);
|
||||
$this->certificateMapper = $this->createMock(SmimeCertificateMapper::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
|
||||
$this->smimeService = new SmimeService(
|
||||
$this->tempManager,
|
||||
$this->certificateManager,
|
||||
$this->crypto,
|
||||
$this->certificateMapper,
|
||||
$this->timeFactory
|
||||
);
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
parent::tearDown();
|
||||
|
||||
foreach ($this->tempFiles as $tempFile) {
|
||||
unlink($tempFile);
|
||||
}
|
||||
$this->tempFiles = [];
|
||||
}
|
||||
|
||||
private function getTestCertificate(string $emailAddress, string $userId = 'user'): SmimeCertificate {
|
||||
$rawCert = file_get_contents(__DIR__ . "/../../data/smime-certs/{$emailAddress}.crt");
|
||||
$rawKey = file_get_contents(__DIR__ . "/../../data/smime-certs/{$emailAddress}.key");
|
||||
|
||||
$certificate = new SmimeCertificate();
|
||||
$certificate->setId(42);
|
||||
$certificate->setUserId($userId);
|
||||
$certificate->setEmailAddress($emailAddress);
|
||||
$certificate->setCertificate($rawCert);
|
||||
$certificate->setPrivateKey($rawKey);
|
||||
return $certificate;
|
||||
}
|
||||
|
||||
private function createTempFile(): string {
|
||||
$n = count($this->tempFiles);
|
||||
$tempFile = "/tmp/mail-smime-service-temp-{$n}";
|
||||
touch($tempFile);
|
||||
$this->tempFiles[] = $tempFile;
|
||||
return $tempFile;
|
||||
}
|
||||
|
||||
public function testDecryptMimePartText() {
|
||||
$encryptedMessage = file_get_contents(__DIR__ . '/../../data/encrypted-message.txt');
|
||||
$decryptedBody = file_get_contents(__DIR__ . '/../../data/decrypted-message-body.txt');
|
||||
|
||||
$certificate = $this->getTestCertificate('user@imap.localhost');
|
||||
$this->crypto->expects(self::exactly(2))
|
||||
->method('decrypt')
|
||||
->willReturnMap([
|
||||
[$certificate->getCertificate(), '', $certificate->getCertificate()],
|
||||
[$certificate->getPrivateKey(), '', $certificate->getPrivateKey()],
|
||||
]);
|
||||
$this->tempManager->expects(self::exactly(2))
|
||||
->method('getTemporaryFile')
|
||||
->willReturnOnConsecutiveCalls($this->createTempFile(), $this->createTempFile());
|
||||
|
||||
$this->assertEquals(
|
||||
$decryptedBody,
|
||||
$this->smimeService->decryptMimePartText($encryptedMessage, $certificate),
|
||||
);
|
||||
}
|
||||
|
||||
public function testDecryptDataFetch(): void {
|
||||
$encryptedMessage = file_get_contents(__DIR__ . '/../../data/encrypted-message.txt');
|
||||
$decryptedBody = file_get_contents(__DIR__ . '/../../data/decrypted-message-body.txt');
|
||||
|
||||
$message = $this->createMock(Horde_Imap_Client_Data_Fetch::class);
|
||||
$message->expects(self::once())
|
||||
->method('getFullMsg')
|
||||
->willReturn($encryptedMessage);
|
||||
$headers = new Horde_Mime_Headers();
|
||||
$contentType = new Horde_Mime_Headers_ContentParam_ContentType('', 'application/pkcs7-mime');
|
||||
$contentType['smime-type'] = 'enveloped-data';
|
||||
$headers['content-type'] = $contentType;
|
||||
$message->expects(self::once())
|
||||
->method('getHeaderText')
|
||||
->with('0', Horde_Imap_Client_Data_Fetch::HEADER_PARSE)
|
||||
->willReturn($headers);
|
||||
$envelope = new Horde_Imap_Client_Data_Envelope();
|
||||
$envelope->to = AddressList::parse('user@imap.localhost')->toHorde();
|
||||
$message->expects(self::once())
|
||||
->method('getEnvelope')
|
||||
->willReturn($envelope);
|
||||
$certificate = $this->getTestCertificate('user@imap.localhost');
|
||||
$this->certificateMapper->expects(self::once())
|
||||
->method('findAllByEmailAddress')
|
||||
->with('user', 'user@imap.localhost')
|
||||
->willReturn([$certificate]);
|
||||
$this->crypto->expects(self::exactly(2))
|
||||
->method('decrypt')
|
||||
->willReturnMap([
|
||||
[$certificate->getCertificate(), '', $certificate->getCertificate()],
|
||||
[$certificate->getPrivateKey(), '', $certificate->getPrivateKey()],
|
||||
]);
|
||||
$this->tempManager->expects(self::exactly(2))
|
||||
->method('getTemporaryFile')
|
||||
->willReturnOnConsecutiveCalls($this->createTempFile(), $this->createTempFile());
|
||||
|
||||
$this->assertEquals(
|
||||
$decryptedBody,
|
||||
$this->smimeService->decryptDataFetch($message, 'user'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testDecryptDataFetchWithOpaqueSignedData(): void {
|
||||
$encryptedMessage = file_get_contents(__DIR__ . '/../../data/encrypted-signed-opaque-message.txt');
|
||||
$decryptedBody = file_get_contents(__DIR__ . '/../../data/decrypted-signed-opaque-message-body.txt');
|
||||
|
||||
$message = $this->createMock(Horde_Imap_Client_Data_Fetch::class);
|
||||
$message->expects(self::once())
|
||||
->method('getFullMsg')
|
||||
->willReturn($encryptedMessage);
|
||||
$headers = new Horde_Mime_Headers();
|
||||
$contentType = new Horde_Mime_Headers_ContentParam_ContentType('', 'application/pkcs7-mime');
|
||||
$contentType['smime-type'] = 'enveloped-data';
|
||||
$headers['content-type'] = $contentType;
|
||||
$message->expects(self::once())
|
||||
->method('getHeaderText')
|
||||
->with('0', Horde_Imap_Client_Data_Fetch::HEADER_PARSE)
|
||||
->willReturn($headers);
|
||||
$envelope = new Horde_Imap_Client_Data_Envelope();
|
||||
$envelope->to = AddressList::parse('user@imap.localhost')->toHorde();
|
||||
$message->expects(self::once())
|
||||
->method('getEnvelope')
|
||||
->willReturn($envelope);
|
||||
$certificate = $this->getTestCertificate('user@domain.tld');
|
||||
$this->certificateMapper->expects(self::once())
|
||||
->method('findAllByEmailAddress')
|
||||
->with('user', 'user@imap.localhost')
|
||||
->willReturn([$certificate]);
|
||||
$this->crypto->expects(self::exactly(2))
|
||||
->method('decrypt')
|
||||
->willReturnMap([
|
||||
[$certificate->getCertificate(), '', $certificate->getCertificate()],
|
||||
[$certificate->getPrivateKey(), '', $certificate->getPrivateKey()],
|
||||
]);
|
||||
$this->tempManager->expects(self::exactly(4))
|
||||
->method('getTemporaryFile')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
$this->createTempFile(),
|
||||
$this->createTempFile(),
|
||||
$this->createTempFile(),
|
||||
$this->createTempFile(),
|
||||
);
|
||||
$this->certificateManager->expects(self::once())
|
||||
->method('getAbsoluteBundlePath')
|
||||
->willReturn(__DIR__ . '/../../data/smime-certs/domain.tld.ca.crt');
|
||||
|
||||
$this->assertEquals(
|
||||
$decryptedBody,
|
||||
$this->smimeService->decryptDataFetch($message, 'user'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testDecryptDataFetchWithRegularMessage(): void {
|
||||
$messageText = file_get_contents(__DIR__ . '/../../data/mail-message-123.txt');
|
||||
|
||||
$message = $this->createMock(Horde_Imap_Client_Data_Fetch::class);
|
||||
$message->expects(self::once())
|
||||
->method('getFullMsg')
|
||||
->willReturn($messageText);
|
||||
$headers = new Horde_Mime_Headers();
|
||||
$headers['content-type'] = new Horde_Mime_Headers_ContentParam_ContentType('', 'multipart/alternative');
|
||||
$message->expects(self::once())
|
||||
->method('getHeaderText')
|
||||
->with('0', Horde_Imap_Client_Data_Fetch::HEADER_PARSE)
|
||||
->willReturn($headers);
|
||||
|
||||
$this->assertEquals(
|
||||
$messageText,
|
||||
$this->smimeService->decryptDataFetch($message, 'user'),
|
||||
);
|
||||
}
|
||||
|
||||
public function provideIsEncryptedData(): array {
|
||||
return [
|
||||
['application/pkcs7-mime', ['smime-type' => 'enveloped-data'], true],
|
||||
['application/pkcs7-mime', ['smime-type' => 'signed-data'], false],
|
||||
['application/pkcs7-mime', [], false], // Should not happen in real life but who knows
|
||||
['multipart/alternative', [], false],
|
||||
['', [], false],
|
||||
[null, [], false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIsEncryptedData
|
||||
*/
|
||||
public function testIsEncrypted(?string $contentType,
|
||||
array $contentTypeParams,
|
||||
bool $expectedResult): void {
|
||||
$message = $this->createMock(Horde_Imap_Client_Data_Fetch::class);
|
||||
$headers = new Horde_Mime_Headers();
|
||||
$contentType = new Horde_Mime_Headers_ContentParam_ContentType('', $contentType);
|
||||
foreach ($contentTypeParams as $key => $value) {
|
||||
$contentType[$key] = $value;
|
||||
}
|
||||
$headers['content-type'] = $contentType;
|
||||
$message->expects(self::once())
|
||||
->method('getHeaderText')
|
||||
->with('0', Horde_Imap_Client_Data_Fetch::HEADER_PARSE)
|
||||
->willReturn($headers);
|
||||
|
||||
$this->assertEquals($expectedResult, $this->smimeService->isEncrypted($message));
|
||||
}
|
||||
|
||||
public function testIsEncryptedWhenHeaderIsMissing(): void {
|
||||
$message = $this->createMock(Horde_Imap_Client_Data_Fetch::class);
|
||||
$headers = new Horde_Mime_Headers();
|
||||
$message->expects(self::once())
|
||||
->method('getHeaderText')
|
||||
->with('0', Horde_Imap_Client_Data_Fetch::HEADER_PARSE)
|
||||
->willReturn($headers);
|
||||
$this->assertFalse($this->smimeService->isEncrypted($message));
|
||||
}
|
||||
|
||||
public function testExtractSignedContent(): void {
|
||||
$signedMessage = file_get_contents(__DIR__ . '/../../data/signed-opaque-message.txt');
|
||||
$verifiedContent = file_get_contents(__DIR__ . '/../../data/decrypted-signed-opaque-message-body.txt');
|
||||
|
||||
$this->tempManager->expects(self::exactly(2))
|
||||
->method('getTemporaryFile')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
$this->createTempFile(),
|
||||
$this->createTempFile(),
|
||||
);
|
||||
$this->certificateManager->expects(self::once())
|
||||
->method('getAbsoluteBundlePath')
|
||||
->willReturn(__DIR__ . '/../../data/smime-certs/domain.tld.ca.crt');
|
||||
|
||||
$this->assertEquals(
|
||||
$verifiedContent,
|
||||
$this->smimeService->extractSignedContent($signedMessage),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-512; boundary="------------ms070102030302080409090706"
|
||||
|
||||
--------------ms070102030302080409090706
|
||||
Content-Type: multipart/mixed; boundary="------------WSu0joeUl4a7h0IHpwEDRFqQ"
|
||||
|
||||
--------------WSu0joeUl4a7h0IHpwEDRFqQ
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Just some encrypted test images.
|
||||
|
||||
--------------WSu0joeUl4a7h0IHpwEDRFqQ
|
||||
Content-Type: image/png;
|
||||
name="img_800x600_3x8bit_RGB_grid_triangle_a_0200x0150_0.png"
|
||||
Content-Disposition: attachment;
|
||||
filename="img_800x600_3x8bit_RGB_grid_triangle_a_0200x0150_0.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
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
|
||||
--------------WSu0joeUl4a7h0IHpwEDRFqQ
|
||||
Content-Type: image/png;
|
||||
name="img_800x600_3x8bit_RGB_grid_rectangle_0200x0150_0.png"
|
||||
Content-Disposition: attachment;
|
||||
filename="img_800x600_3x8bit_RGB_grid_rectangle_0200x0150_0.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAARSklEQVR42u3VwQkAAAgDse6/
|
||||
tO4g9CPJCELPDBQECiwLvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsECwUKv0CsQ
|
||||
LAQLvQK9QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9AstCsBAs
|
||||
9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKwQK/Q
|
||||
KwQLBAu9Qq9AsBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQKvQLBQrDQK9ArBAvBAr1CrxAs
|
||||
ECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQ
|
||||
K9ArBAvBAr1CrxAsECz0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoEC8FCr0Cv
|
||||
ECwEC/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrAQ
|
||||
LNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsECwUKv
|
||||
0CsQLAQLvQK9QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECz0CvQKwUKwQK/QKwQLBAu9Qq9A
|
||||
sBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKw
|
||||
QK/QKwQLBAu9Qq9AsBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQK9ArBAvBAr1C
|
||||
rxAsECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvUKw
|
||||
QLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoEC8FC
|
||||
r0CvECwEC/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsBAs0Cv0CsECwUKv0CsQLAQLvQK9
|
||||
QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsEC
|
||||
wUKv0CsQLAQLvQK9QrAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKwQK/QKwQLBAu9
|
||||
Qq9AsBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0Cr0C
|
||||
wUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQK9ArBAvB
|
||||
Ar1CrxAsECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9QrBAsNAr9AoEC8FCr0CvECwEC/QK
|
||||
vUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoE
|
||||
C8FCr0CvECwEC/QKvUKwECzQK/QKwQLBQq/QKwCPEI8Q9Aq9QrBAsNAr9AoEC8FCr0CvECwE
|
||||
C/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrAQLNAr
|
||||
9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsECwUKv0CsQ
|
||||
LAQLvQK9QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECz0CvQKwUKwQK/QKwQLBAu9Qq9AsBAs
|
||||
9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKwQK/Q
|
||||
KwQLBAu9Qq9AsBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQK9ArBAvBAr1CrxAs
|
||||
ECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvUKwQLDQ
|
||||
K/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoEC8FCr0Cv
|
||||
ECwEC/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsBAs0Cv0CsECwUKv0CsQLAQLvQK9QrAQ
|
||||
LNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsECwUKv
|
||||
0CsQLAQLvQK9QrAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKwQK/QKwQLBAu9Qq9A
|
||||
sBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0Cr0CwUKw
|
||||
0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQK9ArBAvBAr1C
|
||||
rxAsECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AtNCsNAr9AoEC8FCr0CvECwEC/QKvUKw
|
||||
QLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoEC8FC
|
||||
r0CvECwEC/QKvUKwwLLQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsECwUKv0CsQLAQLvQK9
|
||||
QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAssCz0CsFC
|
||||
sECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKwQK/QKwQLBAu9
|
||||
Qq9AsBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQKLAvBQrDQK9ArBAvBAr1CrxAsECz0Cr0C
|
||||
wUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQK9ArBAvB
|
||||
Ar1CrxAsECz0Cr0CwUKw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoEC8FCr+DWKyfAI8Qj
|
||||
RK9AsBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQK
|
||||
wUKwQK/QKwQLBAu9Qq9AsBAs9Ar0CsFCsECv0CsECywLvUKvECwQLPQKvQLBQrDQK9ArBAvB
|
||||
Ar1CrxAsECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQK
|
||||
vQLBQrDQK9ArBAvBAstCrxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoE
|
||||
C8FCr0CvECwEC/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvwLIQLAQL
|
||||
vQK9QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0
|
||||
CsECwUKv0CsQLAQLvQK9QrAQLNAr9ArBAsFCr9ArECwEC71Cr0CwECz0CvQKwUKwQK/QKwQL
|
||||
BAu9Qq9AsBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0
|
||||
CvQKwUKwQK/QKwQLBAu9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQK9Ar
|
||||
BAvBAr1CrxAsECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwE
|
||||
C/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr
|
||||
9AoEC8FCr0CvECwEC/QKvUKwQLDQK/QKBAvBQq9ArxAsBAu9Ar1CsBAs0Cv0CsECwUKv0CsQ
|
||||
LAQLvQK9QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs
|
||||
0Cv0CsECwUKv0CsQLAQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKwQK/Q
|
||||
KwQLBAu9Qq9AsBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71CrxAs
|
||||
ECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQ
|
||||
K9ArBAvBAr1CrxAsECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLAQL9Aq9QrBAsNAr9AoEC8FCr0Cv
|
||||
ECwEC/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBA
|
||||
sNAr9AoEC8FCr0CvECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsECwUKv
|
||||
0CsQLAQLvQK9QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Qq9A
|
||||
sBAs9Ar0CsFCsECv0CsAjxCPEL0CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsEC
|
||||
wUKv0CsQLAQLvQK9QrAQLNAr9ArBAsFCr9ArECwEC71Cr0CwECz0CvQKwUKwQK/QKwQLBAu9
|
||||
Qq9AsBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQK
|
||||
wUKwQK/QKwQLBAu9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQK9ArBAvB
|
||||
Ar1CrxAsECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwEC/QK
|
||||
vUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoE
|
||||
C8FCr0CvECwEC/QKvUKwQLDQK/QKBAvBQq9ArxAsBAu9Ar1CsBAs0Cv0CsECwUKv0CsQLAQL
|
||||
vQK9QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0
|
||||
CsECwUKv0CsQLAQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKwQK/QKwQL
|
||||
BAu9Qq9AsBAs9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71CrxAsECz0
|
||||
Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQK9Ar
|
||||
BAvBAr1CrxAsECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLAQL9Aq9QrBAsNAr9AoEC8FCr0CvECwE
|
||||
C/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr
|
||||
9AoEC8FCr0CvECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Ar1CsBAs0Cv0CsECwUKv0CsQ
|
||||
LAQLvQK9QrAQLNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq/QKxAsBAu9Qq9AsBAs
|
||||
9Ar0CsFCsECv0CsECwQLvUKvQLAQLPQK9ArBQrBAr9ArBAsEC71Cr0CwECz0CvQKwUKwQK/Q
|
||||
KwQLBAu9Qq9AsBAs9Ar0CsFCsECv0CsECwQLvUKvwLQQLPQKvQLBQrDQK9ArBAvBAr1CrxAs
|
||||
ECz0Cr0CwUKw0CvQKwQLwQK9Qq8QLBAs9Aq9AsFCsNAr0CsEC8ECvUKvECwQLPQKvQLBQrDQ
|
||||
K9ArBAvBAr1CrxAssCz0Cr1CsECw0Cv0CgQLwUKvQK8QLAQL9Aq9QrBAsNAr9AoEC8FCr0Cv
|
||||
ECwEC/QKvUKwQLDQK/QKBAvBQq9ArxAsBAv0Cr1CsECw0Cv0CgQLwUKvQK8QLAQLLAu9QrAQ
|
||||
LNAr9ArBAsFCr9ArECwEC70CvUKwECzQK/QKwQLBQq94bQEqwyevAF3wnwAAAABJRU5ErkJg
|
||||
gg==
|
||||
--------------WSu0joeUl4a7h0IHpwEDRFqQ
|
||||
Content-Type: image/png;
|
||||
name="img_800x600_3x8bit_RGB_rectangles_concentric_0256_1.png"
|
||||
Content-Disposition: attachment;
|
||||
filename="img_800x600_3x8bit_RGB_rectangles_concentric_0256_1.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAKNElEQVR42u3WQQ0AAAwCMfyb
|
||||
3lTwIGlFwCUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYw4ACjwsAgsABBYILAAEFggs
|
||||
AAQWCCwAEFggsAAQWCCwABBYILAAQGCBwAJAYIHAAkBggcACAIEFAgsAgQUCCwCBBQILAIEF
|
||||
AgsABBYILAAEFggsAAQWCCwAEFggsAAQWCCwABBYILAAQGCBwAJAYIHAAkBggcACAIEFAgsA
|
||||
gQUCCwCBBQILAIEFAgsABBYILAAEFggsAAQWCCwAEFggsAAQWCCwABBYILAAQGCBwAJAYIHA
|
||||
AkBggcACAIEFAgsAgQUCCwCBBQILAIEFAgsABBYILAAEFggsAAQWCCwAEFggsAAQWCCwABBY
|
||||
ILAAQGCBwAJAYIHAAkBggcACAIGFwAIAgQUCCwCBBQILAIEFAgsABBYILAAEFggsAAQWCCwA
|
||||
EFggsAAQWCCwABBYILAAQGCBwAJAYIHAAkBggcACQGCBwAIAgQUCCwCBBQILAIEFAgsABBYI
|
||||
LAAEFggsAAQWCCwAEFggsAAQWCCwABBYILAAQGCBwAJAYIHAAkBggcACQGCBwAIAgQUCCwCB
|
||||
BQILAIEFAgsABBYILAAEFggsAAQWCCwAEFggsAAQWCCwABBYILAAQGCBwAJAYIHAAkBggcAC
|
||||
QGCBwAIAgQUCCwCBBQILAIEFAgsABBYILAAEFggsAAQWCCwAEFggsAAQWCCwABBYILAAQGAh
|
||||
sABAYIHAAkBggcACQGCBwAIAgQUCCwCBBQILAIEFAgsABBYILAAEFggsAAQWCCwAEFggsAAQ
|
||||
WCCwABBYILAAEFggsABAYIHAAkBggcACQGCBwAIAgQUCCwCBBQILAIEFAgsABBYILAAEFggs
|
||||
AAQWCCwAEFggsAAQWCCwABBYILAAEFggsABAYIHAAkBggcACQGCBwMIAgX3DvoEBMkCAfbNv
|
||||
YIAMEGDf7BsYIAwQ2DfsGxggAwT2DfsGBsgAAfbNvoEBwgCBfcO+gQEyQGDfsG9ggAwQYN/s
|
||||
GxggDBDYN/sGBggDBPYN+wYGyAAB9s2+gQHCAIF9s29ggDBAYN+wb2CADBDYN+wbGCADBNg3
|
||||
+wYGCAME9g37BgbIAIF9w76BATJAgH2zb2CAMEBg37BvGCAMENg37BsYIAME2Df7BgYIAwT2
|
||||
zb6BAcIAgX3DvoEBMkCAfbNvYIAwQGDf7BsYIAwQ2DfsGxggAwT2DfsGBsgAAfbNvoEBwgCB
|
||||
fcO+gQEyQGDfsG9ggAwQYN/sGxggDBDYN/sGBggDBPYN+wYGyAAB9s2+gQHCAIF9s29ggDBA
|
||||
YN+wb2CADBBg3+wbGCADBNg3+wYGCAME9g37BgbIAIF9w76BATJAgH2zb2CAMEBg37BvYIAM
|
||||
ENg37BsYIAME2Df7BgYIAwT2zb6BAcIAgX3DvoEBMkCAfbNvYIAwQGDf7BsYIAwQ2DfsGxgg
|
||||
AwTYN/sGBsgAAfbNvoEBwgCBfcO+gQEyQGDfsG9ggAwQYN/sGxggDBDYN+wbGCADBPYN+wYG
|
||||
yAAB9s2+gQHCAIF9s29ggDBAYN+wb2CADBBg3+wbGCAMENg3+wYGCAME9g37BgbIAIF9w76B
|
||||
ATJAgH2zb2CAMEBg37BvYIAMENg37BsYIAME2Df7BgYIAwT2DfuGAcIAgX3DvoEBMkCAfbNv
|
||||
YIAwQGDf7BsYIAwQ2DfsGxggAwTYN/sGBggDBPbNvoEBwgCBfcO+gQEyQGDfsG9ggAwQYN/s
|
||||
GxggDBDYN+wbGCADBPYN+wYGyAAB9s2+gQHCAIF9s29ggDBAYN+wb2CADBBg3+wbGCAMENg3
|
||||
+wYGCAME9g37BgbIAAH2zb6BATJAgH2zb2CAAEBggcACQGCBwAJAYIHAAgCBBQILAIEFAgsA
|
||||
gQUCCwCBBQILAAQWCCwABBYILAAEFggsABBYILAAEFggsAAQWCCwAEBggcACQGCBwAJAYIHA
|
||||
AgCBBQILAIEFAgsAgQUCCwCBBQILAAQWCCwABBYILAAEFggsABBYILAAEFggsAAQWCCwAEBg
|
||||
gcACQGCBwAJAYIHAAgCBBSYAAIEFAgsAgQUCCwCBBQILAAQWCCwABBYILAAEFggsABBYILAA
|
||||
EFggsAAQWCCwAEBggcACQGCBwAJAYIHAAgAPi8ACAIEFAgsAgQUCCwCBBQILAAQWCCwABBYI
|
||||
LAAEFggsABBYILAAEFggsAAQWCCwAEBggcACQGCBwAJAYIHAAkBggcACAIEFAgsAgQUCCwCB
|
||||
BQILAAQWCCwABBYILAAEFggsABBYILAAEFggsAAQWCCwAEBggcACQGCBwAJAYIHAAkBggcAC
|
||||
AIEFAgsAgQUCCwCBBQILAAQWCCwABBYILAAEFggsABBYILAAEFggsAAQWCCwAEBggcACQGCB
|
||||
wAJAYIHAAkBggcACAIEFAgsAgQUCCwCBBQILAAQWCCwABBYILAAEFggsABBYILAAEFggsAAQ
|
||||
WCCwAEBgIbAAQGCBwAJAYIHAAkBggcACAIEFAgsAgQUCCwCBBQILAAQWCCwABBYILAAEFggs
|
||||
ABBYILAAEFggsAAQWCCwABBYILAAQGCBwAJAYIHAAkBggcACAIEFAgsAgQUCCwCBBQILAAQW
|
||||
CCwABBYILAAEFggsABBYILAAEFggsAAQWCCwABBYILAAQGCBwAJAYIHAAkBggcACAIEFAgsA
|
||||
gQUCCwCBBQILAAQWCCwABBYILAAEFggsABBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
wKgHu00GsAtO32UAAAAASUVORK5CYII=
|
||||
|
||||
--------------WSu0joeUl4a7h0IHpwEDRFqQ--
|
||||
|
||||
--------------ms070102030302080409090706
|
||||
Content-Type: application/pkcs7-signature; name="smime.p7s"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment; filename="smime.p7s"
|
||||
Content-Description: S/MIME Cryptographic Signature
|
||||
|
||||
MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgMFADCABgkqhkiG9w0BBwEAAKCC
|
||||
BVkwggVVMIIDPaADAgECAhAnWBaXAG5ziJ/SgfDJiEDnMA0GCSqGSIb3DQEBCwUAMBQxEjAQ
|
||||
BgNVBAMMCVMvTUlNRSBDQTAeFw0yMzAxMjYxMDEyMjdaFw0yNDAxMjYxMDEyMjdaMDUxDjAM
|
||||
BgNVBAMMBWRlYnVnMSMwIQYJKoZIhvcNAQkBFhRkZWJ1Z0BpbWFwLmxvY2FsaG9zdDCCAiIw
|
||||
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALyeuKDdPScwDI4XhjXa1ujyzycyS3wOjPRk
|
||||
qg6K+oKpFMOcs9e5ZthPvrarC5jmoVvWCu+GvL05hzpZdi4G0V05rp4P2ZAjGpqh+wffjlpX
|
||||
RoHoAsxQwbwNcB7a1EZzQxNM233aTD9+45Q8jQcW9pMhvMbD9cb1w8hGj3L+h+JGIdq3XToN
|
||||
8omCn0Q7+3TPawhnQIeQfnuvL+w0w8wsWcAcjIqU4ZJmzRAXCyIVnRYbibONL8ln2XJSXTGM
|
||||
vZXrfwZY01aFOWKX71p0rYXfivaqfGcDZbk4n3u9N8M9saTKZov+Lt5B0hFloqzzmZDg/0R/
|
||||
LktF8OM0lTfTm3+FFa3t5wv4/BwaBsQ4eTMdnGbOndlB8TjjZGmYCQz6zq+B27so8kx2NJEV
|
||||
xBGX3OG2Yhk0Ro5vYarwxxxSlKWVMvRrA5ylOnk7jDZwWiXqqirdba29tyZkiWXYxoqC48Qv
|
||||
dESdgy2D4oCK834A4G9ZrXLHdCAfxZAYzjI/ZhX95aEfKeCbJq6u6mhao3hP039jrfcgbchp
|
||||
xIK9S8VfDa8VsxjRH/fsouN7BPz/ibCz3+K6pw1G+gs1Dxf/MA5I+mfVrcjkC3/wBTL4BfF4
|
||||
UIGCKJ2bWUCHyiD1ryNSNVgq2oGaisf8KoaeSX2EHqVnUSSqaBQPVCBNGVvwl2r/BKkKkNqN
|
||||
AgMBAAGjgYEwfzAdBgNVHQ4EFgQU0+cRvGVC1exYzK7ImfV3FPHfmtowHwYDVR0jBBgwFoAU
|
||||
k/Pw7XZTk9cfVRb1KbSKBx2HbhUwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwHwYD
|
||||
VR0RBBgwFoEUZGVidWdAaW1hcC5sb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggIBAJxefJDY
|
||||
xWKyUN+uxpigIG25zLykT76Kc/0az262OhqftzYriJoJcMZSmJMHxdr/qWoJ/DzfItsUWLEu
|
||||
FQL0+Ml+9zjPLavHD5xn8iSaRvixlS9XYog2Wy9zdFbQfPmA22bnFBP9/S0LI2FE7NBbTdo8
|
||||
oVkPi189XwZF2uKw7zJWqNCZc5pShbGOtebmKHWYT+T0lirvfcmaHbaT4zl44Qc/jJJJ75BR
|
||||
aEF8BoyWQS5PE8XwfT9Kj5VNRFWqk6KUry/lbaIacKwy4ajZoMXslLnGUOFwLVSwHTIr3ewS
|
||||
tMKZmnplL2Uq3KoaIdCinHwhX4O38gqW/4jiE2xURM0SR1a2jHwPMkSmo8ZyYAplMSmav0D/
|
||||
ndwDiqZDWRdUdohbU46SEhcMC/popWXBs/DMk640j1thu+CsYbV3q73HxBK/ZIxA02RTZT6R
|
||||
mU6IJq2M8Qrrm+rONj1zlfEplZh8lxadUig+U+kRlis43cELDVP30+aJQVYLcTvsCRhc+Ij4
|
||||
+xz1I0LuF2xBWxu496sXyJs9AqLa4AuUUGxB1rq6XQYKNqtr+aT4UfBKFBEZmWMCnYpMVWl1
|
||||
Vzz5ULMtwmrSbOON8+bmyAB3Y871ZFhB8h491tlNPTRpJia7fKlI4/sLfCbH40WNhevDYorg
|
||||
KobkYspiMPPCcBri5eGhXIHcBY0lMYIDwjCCA74CAQEwKDAUMRIwEAYDVQQDDAlTL01JTUUg
|
||||
Q0ECECdYFpcAbnOIn9KB8MmIQOcwDQYJYIZIAWUDBAIDBQCgggFrMBgGCSqGSIb3DQEJAzEL
|
||||
BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIzMDIxNTExMDgwMlowNwYJKwYBBAGCNxAE
|
||||
MSowKDAUMRIwEAYDVQQDDAlTL01JTUUgQ0ECECdYFpcAbnOIn9KB8MmIQOcwOQYLKoZIhvcN
|
||||
AQkQAgsxKqAoMBQxEjAQBgNVBAMMCVMvTUlNRSBDQQIQJ1gWlwBuc4if0oHwyYhA5zBPBgkq
|
||||
hkiG9w0BCQQxQgRA64LtvhK/nhX+/lLiSVbTg183UGS28vfN+Ze8ECy+299/WJr+Bjn0PfvV
|
||||
8tLH4nrUGqNwNZwEe8d4qxhyAnYgZjBsBgkqhkiG9w0BCQ8xXzBdMAsGCWCGSAFlAwQBKjAL
|
||||
BglghkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3DQMCAgFA
|
||||
MAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIICACpG3Gk4ANFb/8Kr
|
||||
tCNCwF7ZTwiWP0j0wKY9EYBaOlFvB7ajHCFQ8HFqTRMrrb4OE3d6b11vmsea3opmzINp2hgP
|
||||
4mwIIQne4BNH30IgKm6sHZ5SVp94p+fXJjPD2sMhXv+cU106YE+wByWAbH4DCuWZPNPWtXKf
|
||||
YwjSW9BfbI8Uxn/o1ivwLDw0/VFdu9fG2mY1GO4urMeBQP1UwvR3RGlb4O3MDZ7SNoYrfe5n
|
||||
oAsn0qudxWboIIml/F4Q3oH6HZ6GEXLnRDWWd5g/IGSIJXLe56Oyjhle1QjFPGkfgIHnUM7p
|
||||
fjrEkW89O8flXCh35dVPeLP2jPFbhzscchZj/eD/duLgas9GWnKpfDPEXOdRfxWRk1IYDwPq
|
||||
IJ7bULPmuziBjKmIbLNAIwqnuXJFLO5w4h1ElHvX/Y38VFtLuGXf+03KIpRGGvN/OPfWt91j
|
||||
GdtpW3yiPIuh6OOT96p5Q6DrUoqQLv3idCSOF5lp0ahwupWhYq0NPEZx91XdqfNqshV+cdj5
|
||||
PIuKE6JX1lrs7u0YenSm7/BgqV9esn8oxx4o+oME25BOHRjq2UO5KxTwI/jJgZQNb5ubgScd
|
||||
PVJd9wXiIfpU9qhbtU3i8wwR/8q82aueTZk8Un3fg0JCzfuiLkGyiLV/dUNR1de8IgLH2XS2
|
||||
GZAXp353gKS28x5OI/2RAAAAAAAA
|
||||
--------------ms070102030302080409090706--
|
|
@ -0,0 +1,4 @@
|
|||
Content-Type: text/plain
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
hoi
|
|
@ -0,0 +1,489 @@
|
|||
Return-Path: <debug@imap.localhost>
|
||||
Delivered-To: user@imap.localhost
|
||||
Received: from 887788ce658a
|
||||
by 887788ce658a (Dovecot) with LMTP id A5UpDBK97GOHCQAAq9CxcQ
|
||||
for <user@imap.localhost>; Wed, 15 Feb 2023 11:08:02 +0000
|
||||
Received: from [127.0.0.1] (887788ce658a [10.0.2.100])
|
||||
by 887788ce658a (Postfix) with ESMTP id 2C9691D33
|
||||
for <user@imap.localhost>; Wed, 15 Feb 2023 11:08:02 +0000 (UTC)
|
||||
Message-ID: <98eec0af-2074-685b-7bd8-ac0a06e8996f@imap.localhost>
|
||||
Date: Wed, 15 Feb 2023 12:08:02 +0100
|
||||
MIME-Version: 1.0
|
||||
User-Agent: redacted
|
||||
Content-Language: en-US
|
||||
To: user@imap.localhost
|
||||
From: admin <debug@imap.localhost>
|
||||
Subject: Encrypted test images
|
||||
Content-Type: application/pkcs7-mime; smime-type="enveloped-data"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: attachment; filename="smime.p7m"
|
||||
Content-Description: S/MIME Encrypted Message
|
||||
|
||||
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
|
|
@ -0,0 +1,124 @@
|
|||
From user@domain.tld Mon Feb 20 15:20:02 2023
|
||||
Message-ID: <3c1d3b003fd229f19aa3524039bd1a1c635bda7d.camel@domain.tld>
|
||||
Subject: Enc+Sig
|
||||
From: Christoph Wurst <user@domain.tld>
|
||||
To: user@domain.tld
|
||||
Date: Mon, 20 Feb 2023 15:20:02 +0100
|
||||
Content-Disposition: attachment; filename="smime.p7m"
|
||||
Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data"
|
||||
Content-Description: S/MIME Encrypted Message
|
||||
Content-Transfer-Encoding: base64
|
||||
User-Agent: Evolution 3.46.4
|
||||
MIME-Version: 1.0
|
||||
X-Evolution-Identity: 0b63ba807310263f00438acbc97aa98fa31618c8
|
||||
X-Evolution-Fcc: folder://c37eb41737e0eb558bb184bb47887967188025fd/Sent
|
||||
X-Evolution-Transport: c3b5e0a74d4ca20a717b63ebba6e41b6900a2969
|
||||
X-Evolution-Source:
|
||||
|
||||
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
|
||||
lURQV5QejJEAAAAAAAAAAAAA
|
||||
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="signed-data"
|
||||
Content-Description: S/MIME Signed Message
|
||||
Content-Disposition: attachment; filename="smime.p7m"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFCTCCAvGgAwIBAgIUUD02m8Fv/R2sAiuSziqI+FOb9EIwDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJUy9NSU1FIENBMB4XDTIzMDIyMDE0MDIxM1oXDTMzMDIx
|
||||
NzE0MDIxM1owFDESMBAGA1UEAwwJUy9NSU1FIENBMIICIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAg8AMIICCgKCAgEAuSjHnnWfu73pQmX+Rl9nx2K7h2ubK5dJJYHSBmU58ndX
|
||||
OZ4ntlmfWtMEWmiTf79DeLCDnBlnLGaxDxPxx75V0z3atnSuWqfokoH9RvIRtGm+
|
||||
3U4vk5SS7YIl6kX6EIr+Qmw3a7GrX4MNtBOLxTcPaJNRbV9BEffrAV2Ql017kFXq
|
||||
nldVVxb9uGm2MoLYokuWMShqx+WLIDESwNjKeL2qHG/M2TteGuHhm9t0ibRXOn5R
|
||||
qRE8BRoImK0Rs0wJw4Iw8wrmRDgabNsH3WxbaT9Kb/UqmHa7mtE4f4Qq2RduAZN3
|
||||
JCqLsTLL+jzRdRe3+AZNdjsbTZrIcwf3FqCbJYj1ALWIiiCwrVgSMK30qqp8ilDZ
|
||||
Zepiq6zks521PHPT9PmGxypzRyNCRyKM6pKvN9WGlkqZ+KEyFon/a0MLEb/BDmwl
|
||||
ds6AmFoqd7y2/07A4n/dj0fHjDtZRTw6k0YFQx2k4knETNKHT+z8BNkmT/lvtEwy
|
||||
RLz8yeA8hahIWVdeN926o8R/8bC39qZQ8Uh7ApUjTKSTGovAqoy4ggpETtjhvsTx
|
||||
DP1UppqMIaCLWEFxYBgI3FjQeEpeFf/eMepr+jjJ3X++fRaRDrDp7rlLBQEiGUOX
|
||||
HelQAe9vOSvvGb49LYmKHDWYeNF0K/QadhCeAOH+ucg2INLYl8xheUT6sXW3SFUC
|
||||
AwEAAaNTMFEwHQYDVR0OBBYEFNnBosNBMYiysP0qIj4tAClVowZ8MB8GA1UdIwQY
|
||||
MBaAFNnBosNBMYiysP0qIj4tAClVowZ8MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
|
||||
hvcNAQELBQADggIBAJODaBeTSUknv6TKH3YTp98RWXljEdgeu5ARm6zL+QFJxe/4
|
||||
UnAtNAQD36Ku14g5BV3gGdnkizEfNjuldAgAYAIy25mYrirPML53o57GmqTEEI3o
|
||||
0LxE7hnN8zKU0M5tFd3wH0u/z/QsG7x8/geF9wd1Up16n74RY+jcFFO1BrrBaUfJ
|
||||
JNGKKmk8HY7piL/6V6nt/KTMp0etV+rrvF8BH62supADq6hQcYcqUFiMHNIYNFRq
|
||||
OvMY2bRU3caN7blhrdrNYk+chX/En2gbjSPMde1V3p7sfuM7zKubVkaMxkdcrGTj
|
||||
Q8T6MlGJYt3u17ah4f/pkb2ncCjoC/LJDxbbWECxlAYmN3XS6ucJwzp73DMvDK1E
|
||||
Hoq7nKJEs1e1OGOOUXNFnC5d/z6lZDQcvgSgB5OujoHi/nQ9fPOGjXymxKTWgbBe
|
||||
Y7cn2PmbmDW7DJNFco9gc77i9pmGx4NH0WasFoD4QWiw3ba83s1BqaZv0GAe/uZ5
|
||||
NCKTh4epsiWEpfOa8u1c5UIwBhu0pRFB28U/sacS4ZVoOJYEOTCnyQ9XLCnxRgNP
|
||||
N76/bGzAcGtujaUn1TEmDYd3kyt7YXUWUEC17zWjfStERUaisM0A9+hrMu7coVO1
|
||||
H5Fd6iftq0NGKjMNg/ai/6b5B7ZGlfXX90C1+fER5G++G+OVd8AuEZ5pVHaC
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,31 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
fjFuKNTne6M+3hcNvg==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
8BnhmaIWSAKR7YlRKwPLWEFBOXJ30A==
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,31 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
aYAHcRXlsEqTNzseEXHIYWHosJLHRQ==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
z3DzrkpG8RTO/QaTJc8FV3bAm40B
|
||||
-----END PRIVATE KEY-----
|
Загрузка…
Ссылка в новой задаче