fix(outbox): add status for messages
and try resending failed messages at POF Signed-off-by: Anna Larch <anna@nextcloud.com>
This commit is contained in:
Родитель
95e6ba62a5
Коммит
7c59040785
|
@ -40,7 +40,6 @@ use OCA\Mail\Dashboard\ImportantMailWidgetV2;
|
|||
use OCA\Mail\Dashboard\UnreadMailWidget;
|
||||
use OCA\Mail\Dashboard\UnreadMailWidgetV2;
|
||||
use OCA\Mail\Events\BeforeImapClientCreated;
|
||||
use OCA\Mail\Events\BeforeMessageSentEvent;
|
||||
use OCA\Mail\Events\DraftMessageCreatedEvent;
|
||||
use OCA\Mail\Events\DraftSavedEvent;
|
||||
use OCA\Mail\Events\MailboxesSynchronizedEvent;
|
||||
|
@ -55,9 +54,7 @@ use OCA\Mail\Http\Middleware\ErrorMiddleware;
|
|||
use OCA\Mail\Http\Middleware\ProvisioningMiddleware;
|
||||
use OCA\Mail\Listener\AccountSynchronizedThreadUpdaterListener;
|
||||
use OCA\Mail\Listener\AddressCollectionListener;
|
||||
use OCA\Mail\Listener\AntiAbuseListener;
|
||||
use OCA\Mail\Listener\DeleteDraftListener;
|
||||
use OCA\Mail\Listener\FlagRepliedMessageListener;
|
||||
use OCA\Mail\Listener\HamReportListener;
|
||||
use OCA\Mail\Listener\InteractionListener;
|
||||
use OCA\Mail\Listener\MailboxesSynchronizedSpecialMailboxesUpdater;
|
||||
|
@ -68,7 +65,6 @@ use OCA\Mail\Listener\NewMessageClassificationListener;
|
|||
use OCA\Mail\Listener\OauthTokenRefreshListener;
|
||||
use OCA\Mail\Listener\OptionalIndicesListener;
|
||||
use OCA\Mail\Listener\OutOfOfficeListener;
|
||||
use OCA\Mail\Listener\SaveSentMessageListener;
|
||||
use OCA\Mail\Listener\SpamReportListener;
|
||||
use OCA\Mail\Listener\UserDeletedListener;
|
||||
use OCA\Mail\Notification\Notifier;
|
||||
|
@ -132,7 +128,6 @@ class Application extends App implements IBootstrap {
|
|||
|
||||
$context->registerEventListener(AddMissingIndicesEvent::class, OptionalIndicesListener::class);
|
||||
$context->registerEventListener(BeforeImapClientCreated::class, OauthTokenRefreshListener::class);
|
||||
$context->registerEventListener(BeforeMessageSentEvent::class, AntiAbuseListener::class);
|
||||
$context->registerEventListener(DraftSavedEvent::class, DeleteDraftListener::class);
|
||||
$context->registerEventListener(DraftMessageCreatedEvent::class, DeleteDraftListener::class);
|
||||
$context->registerEventListener(OutboxMessageCreatedEvent::class, DeleteDraftListener::class);
|
||||
|
@ -143,9 +138,7 @@ class Application extends App implements IBootstrap {
|
|||
$context->registerEventListener(MessageFlaggedEvent::class, MoveJunkListener::class);
|
||||
$context->registerEventListener(MessageDeletedEvent::class, MessageCacheUpdaterListener::class);
|
||||
$context->registerEventListener(MessageSentEvent::class, AddressCollectionListener::class);
|
||||
$context->registerEventListener(MessageSentEvent::class, FlagRepliedMessageListener::class);
|
||||
$context->registerEventListener(MessageSentEvent::class, InteractionListener::class);
|
||||
$context->registerEventListener(MessageSentEvent::class, SaveSentMessageListener::class);
|
||||
$context->registerEventListener(NewMessagesSynchronized::class, NewMessageClassificationListener::class);
|
||||
$context->registerEventListener(NewMessagesSynchronized::class, MessageKnownSinceListener::class);
|
||||
$context->registerEventListener(SynchronizationEvent::class, AccountSynchronizedThreadUpdaterListener::class);
|
||||
|
|
|
@ -95,7 +95,7 @@ class QuotaJob extends TimedJob {
|
|||
}
|
||||
|
||||
$quota = $this->mailManager->getQuota($account);
|
||||
if($quota === null) {
|
||||
if ($quota === null) {
|
||||
$this->logger->debug('Could not get quota information for account <' . $account->getEmail() . '>', ['app' => 'mail']);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
|||
namespace OCA\Mail\Contracts;
|
||||
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\Alias;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\Message;
|
||||
|
@ -37,27 +36,12 @@ interface IMailTransmission {
|
|||
/**
|
||||
* Send a new message or reply to an existing one
|
||||
*
|
||||
* @param NewMessageData $messageData
|
||||
* @param string|null $repliedToMessageId
|
||||
* @param Alias|null $alias
|
||||
* @param Message|null $draft
|
||||
*
|
||||
* @param Account $account
|
||||
* @param LocalMessage $localMessage
|
||||
* @throws SentMailboxNotSetException
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function sendMessage(NewMessageData $messageData,
|
||||
?string $repliedToMessageId = null,
|
||||
?Alias $alias = null,
|
||||
?Message $draft = null): void;
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param LocalMessage $message
|
||||
* @throws ClientException
|
||||
* @throws ServiceException
|
||||
* @return void
|
||||
*/
|
||||
public function sendLocalMessage(Account $account, LocalMessage $message): void;
|
||||
public function sendMessage(Account $account, LocalMessage $localMessage): void;
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
|
|
|
@ -172,7 +172,6 @@ class DraftsController extends Controller {
|
|||
$message = $this->service->getMessage($id, $this->userId);
|
||||
$account = $this->accountService->find($this->userId, $accountId);
|
||||
|
||||
|
||||
$message->setType(LocalMessage::TYPE_DRAFT);
|
||||
$message->setAccountId($accountId);
|
||||
$message->setAliasId($aliasId);
|
||||
|
|
|
@ -163,7 +163,7 @@ class OutboxController extends Controller {
|
|||
|
||||
$outboxMessage = $this->service->convertDraft($draftMessage, $sendAt);
|
||||
|
||||
return JsonResponse::success(
|
||||
return JsonResponse::success(
|
||||
$outboxMessage,
|
||||
Http::STATUS_CREATED,
|
||||
);
|
||||
|
@ -209,6 +209,9 @@ class OutboxController extends Controller {
|
|||
?int $sendAt = null
|
||||
): JsonResponse {
|
||||
$message = $this->service->getMessage($id, $this->userId);
|
||||
if ($message->getStatus() === LocalMessage::STATUS_PROCESSED) {
|
||||
return JsonResponse::error('Cannot modify already sent message', Http::STATUS_FORBIDDEN, [$message]);
|
||||
}
|
||||
$account = $this->accountService->find($this->userId, $accountId);
|
||||
|
||||
$message->setAccountId($accountId);
|
||||
|
@ -217,7 +220,6 @@ class OutboxController extends Controller {
|
|||
$message->setBody($body);
|
||||
$message->setEditorBody($editorBody);
|
||||
$message->setHtml($isHtml);
|
||||
$message->setFailed($failed);
|
||||
$message->setInReplyToMessageId($inReplyToMessageId);
|
||||
$message->setSendAt($sendAt);
|
||||
$message->setSmimeSign($smimeSign);
|
||||
|
@ -244,8 +246,12 @@ class OutboxController extends Controller {
|
|||
$message = $this->service->getMessage($id, $this->userId);
|
||||
$account = $this->accountService->find($this->userId, $message->getAccountId());
|
||||
|
||||
$this->service->sendMessage($message, $account);
|
||||
return JsonResponse::success(
|
||||
$message = $this->service->sendMessage($message, $account);
|
||||
|
||||
if($message->getStatus() !== LocalMessage::STATUS_PROCESSED) {
|
||||
return JsonResponse::error('Could not send message', Http::STATUS_INTERNAL_SERVER_ERROR, [$message]);
|
||||
}
|
||||
return JsonResponse::success(
|
||||
'Message sent', Http::STATUS_ACCEPTED
|
||||
);
|
||||
}
|
||||
|
|
|
@ -60,13 +60,30 @@ use function array_filter;
|
|||
* @method setSmimeCertificateId(?int $smimeCertificateId)
|
||||
* @method bool|null getSmimeEncrypt()
|
||||
* @method setSmimeEncrypt (bool $smimeEncryt)
|
||||
* @method int|null getStatus();
|
||||
* @method setStatus(?int $status);
|
||||
* @method string|null getRaw()
|
||||
* @method setRaw(string|null $raw)
|
||||
*/
|
||||
class LocalMessage extends Entity implements JsonSerializable {
|
||||
public const TYPE_OUTGOING = 0;
|
||||
public const TYPE_DRAFT = 1;
|
||||
|
||||
public const STATUS_RAW = 0;
|
||||
public const STATUS_NO_SENT_MAILBOX = 1;
|
||||
public const STATUS_SMIME_SIGN_NO_CERT_ID = 2;
|
||||
public const STATUS_SMIME_SIGN_CERT = 3;
|
||||
public const STATUS_SMIME_SIGN_FAIL = 4;
|
||||
public const STATUS_SMIME_ENCRYPT_NO_CERT_ID = 5;
|
||||
public const STATUS_SMIME_ENCRYPT_CERT = 6;
|
||||
public const STATUS_SMIME_ENCRYT_FAIL = 7;
|
||||
public const STATUS_TOO_MANY_RECIPIENTS = 8;
|
||||
public const STATUS_RATELIMIT = 9;
|
||||
public const STATUS_SMPT_SEND_FAIL = 10;
|
||||
public const STATUS_IMAP_SENT_MAILBOX_FAIL = 11;
|
||||
public const STATUS_PROCESSED = 12;
|
||||
/**
|
||||
* @var int
|
||||
* @var int<1,12>
|
||||
* @psalm-var self::TYPE_*
|
||||
*/
|
||||
protected $type;
|
||||
|
@ -116,6 +133,15 @@ class LocalMessage extends Entity implements JsonSerializable {
|
|||
/** @var bool|null */
|
||||
protected $smimeEncrypt;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
* @psalm-var int-mask-of<self::STATUS_*>
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
/** @var string|null */
|
||||
protected $raw;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('type', 'integer');
|
||||
$this->addType('accountId', 'integer');
|
||||
|
@ -127,6 +153,7 @@ class LocalMessage extends Entity implements JsonSerializable {
|
|||
$this->addType('smimeSign', 'boolean');
|
||||
$this->addType('smimeCertificateId', 'integer');
|
||||
$this->addType('smimeEncrypt', 'boolean');
|
||||
$this->addType('status', 'integer');
|
||||
}
|
||||
|
||||
#[ReturnTypeWillChange]
|
||||
|
@ -168,6 +195,8 @@ class LocalMessage extends Entity implements JsonSerializable {
|
|||
'smimeCertificateId' => $this->getSmimeCertificateId(),
|
||||
'smimeSign' => $this->getSmimeSign() === true,
|
||||
'smimeEncrypt' => $this->getSmimeEncrypt() === true,
|
||||
'status' => $this->getStatus(),
|
||||
'raw' => $this->getRaw(),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,8 @@ class LocalMessageMapper extends QBMapper {
|
|||
->join('a', $this->getTableName(), 'm', $qb->expr()->eq('m.account_id', 'a.id'))
|
||||
->where(
|
||||
$qb->expr()->eq('a.user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR),
|
||||
$qb->expr()->eq('m.type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)
|
||||
$qb->expr()->eq('m.type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT),
|
||||
$qb->expr()->neq('m.status', $qb->createNamedParameter(LocalMessage::STATUS_PROCESSED, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)
|
||||
);
|
||||
$rows = $qb->executeQuery();
|
||||
|
||||
|
@ -134,10 +135,6 @@ class LocalMessageMapper extends QBMapper {
|
|||
$qb->expr()->isNotNull('send_at'),
|
||||
$qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT),
|
||||
$qb->expr()->lte('send_at', $qb->createNamedParameter($time, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('failed'),
|
||||
$qb->expr()->eq('failed', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL), IQueryBuilder::PARAM_BOOL),
|
||||
)
|
||||
)
|
||||
->orderBy('send_at', 'asc');
|
||||
$messages = $this->findEntities($select);
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @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\Events;
|
||||
|
||||
use Horde_Mime_Mail;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Model\IMessage;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCP\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class BeforeMessageSentEvent extends Event {
|
||||
/** @var Account */
|
||||
private $account;
|
||||
|
||||
/** @var NewMessageData */
|
||||
private $newMessageData;
|
||||
|
||||
/** @var Message|null */
|
||||
private $draft;
|
||||
|
||||
/** @var IMessage */
|
||||
private $message;
|
||||
|
||||
/** @var Horde_Mime_Mail */
|
||||
private $mail;
|
||||
|
||||
/** @var string|null */
|
||||
private $repliedToMessageId;
|
||||
|
||||
public function __construct(Account $account,
|
||||
NewMessageData $newMessageData,
|
||||
?string $repliedToMessageId,
|
||||
?Message $draft,
|
||||
IMessage $message,
|
||||
Horde_Mime_Mail $mail) {
|
||||
parent::__construct();
|
||||
$this->account = $account;
|
||||
$this->newMessageData = $newMessageData;
|
||||
$this->repliedToMessageId = $repliedToMessageId;
|
||||
$this->draft = $draft;
|
||||
$this->message = $message;
|
||||
$this->mail = $mail;
|
||||
}
|
||||
|
||||
public function getAccount(): Account {
|
||||
return $this->account;
|
||||
}
|
||||
|
||||
public function getNewMessageData(): NewMessageData {
|
||||
return $this->newMessageData;
|
||||
}
|
||||
|
||||
public function getRepliedToMessageId(): ?string {
|
||||
return $this->repliedToMessageId;
|
||||
}
|
||||
|
||||
public function getDraft(): ?Message {
|
||||
return $this->draft;
|
||||
}
|
||||
|
||||
public function getMessage(): IMessage {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function getMail(): Horde_Mime_Mail {
|
||||
return $this->mail;
|
||||
}
|
||||
}
|
|
@ -34,15 +34,15 @@ class DraftSavedEvent extends Event {
|
|||
/** @var Account */
|
||||
private $account;
|
||||
|
||||
/** @var NewMessageData */
|
||||
/** @var NewMessageData|null */
|
||||
private $newMessageData;
|
||||
|
||||
/** @var Message|null */
|
||||
private $draft;
|
||||
|
||||
public function __construct(Account $account,
|
||||
NewMessageData $newMessageData,
|
||||
?Message $draft) {
|
||||
?NewMessageData $newMessageData = null,
|
||||
?Message $draft = null) {
|
||||
parent::__construct();
|
||||
$this->account = $account;
|
||||
$this->newMessageData = $newMessageData;
|
||||
|
@ -53,7 +53,7 @@ class DraftSavedEvent extends Event {
|
|||
return $this->account;
|
||||
}
|
||||
|
||||
public function getNewMessageData(): NewMessageData {
|
||||
public function getNewMessageData(): ?NewMessageData {
|
||||
return $this->newMessageData;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Mail\Events;
|
||||
|
||||
use Horde_Mime_Mail;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Model\IMessage;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCP\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
|
@ -39,57 +36,17 @@ class MessageSentEvent extends Event {
|
|||
/** @var Account */
|
||||
private $account;
|
||||
|
||||
/** @var NewMessageData */
|
||||
private $newMessageData;
|
||||
|
||||
/** @var null|string */
|
||||
private $repliedToMessageId;
|
||||
|
||||
/** @var Message|null */
|
||||
private $draft;
|
||||
|
||||
/** @var IMessage */
|
||||
private $message;
|
||||
|
||||
/** @var Horde_Mime_Mail */
|
||||
private $mail;
|
||||
|
||||
public function __construct(Account $account,
|
||||
NewMessageData $newMessageData,
|
||||
?string $repliedToMessageId,
|
||||
?Message $draft,
|
||||
IMessage $message,
|
||||
Horde_Mime_Mail $mail) {
|
||||
private LocalMessage $localMessage) {
|
||||
parent::__construct();
|
||||
$this->account = $account;
|
||||
$this->newMessageData = $newMessageData;
|
||||
$this->repliedToMessageId = $repliedToMessageId;
|
||||
$this->draft = $draft;
|
||||
$this->message = $message;
|
||||
$this->mail = $mail;
|
||||
}
|
||||
|
||||
public function getAccount(): Account {
|
||||
return $this->account;
|
||||
}
|
||||
|
||||
public function getNewMessageData(): NewMessageData {
|
||||
return $this->newMessageData;
|
||||
}
|
||||
|
||||
public function getRepliedToMessageId(): ?string {
|
||||
return $this->repliedToMessageId;
|
||||
}
|
||||
|
||||
public function getDraft(): ?Message {
|
||||
return $this->draft;
|
||||
}
|
||||
|
||||
public function getMessage(): IMessage {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function getMail(): Horde_Mime_Mail {
|
||||
return $this->mail;
|
||||
public function getLocalMessage(): LocalMessage {
|
||||
return $this->localMessage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ use Horde_Imap_Client_Search_Query;
|
|||
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;
|
||||
|
@ -398,7 +397,7 @@ class MessageMapper {
|
|||
*/
|
||||
public function save(Horde_Imap_Client_Socket $client,
|
||||
Mailbox $mailbox,
|
||||
Horde_Mime_Mail $mail,
|
||||
string $mail,
|
||||
array $flags = []): int {
|
||||
$flags = array_merge([
|
||||
Horde_Imap_Client::FLAG_SEEN,
|
||||
|
@ -408,7 +407,7 @@ class MessageMapper {
|
|||
$mailbox->getName(),
|
||||
[
|
||||
[
|
||||
'data' => $mail->getRaw(),
|
||||
'data' => $mail,
|
||||
'flags' => $flags,
|
||||
]
|
||||
]
|
||||
|
|
|
@ -26,8 +26,10 @@ declare(strict_types=1);
|
|||
namespace OCA\Mail\Listener;
|
||||
|
||||
use OCA\Mail\Contracts\IUserPreferences;
|
||||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Events\MessageSentEvent;
|
||||
use OCA\Mail\Service\AutoCompletion\AddressCollector;
|
||||
use OCA\Mail\Service\TransmissionService;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -48,7 +50,8 @@ class AddressCollectionListener implements IEventListener {
|
|||
|
||||
public function __construct(IUserPreferences $preferences,
|
||||
AddressCollector $collector,
|
||||
LoggerInterface $logger) {
|
||||
LoggerInterface $logger,
|
||||
private TransmissionService $transmissionService) {
|
||||
$this->collector = $collector;
|
||||
$this->logger = $logger;
|
||||
$this->preferences = $preferences;
|
||||
|
@ -65,10 +68,12 @@ class AddressCollectionListener implements IEventListener {
|
|||
|
||||
// Non-essential feature, hence we catch all possible errors
|
||||
try {
|
||||
$message = $event->getMessage();
|
||||
$addresses = $message->getTo()
|
||||
->merge($message->getCC())
|
||||
->merge($message->getBCC());
|
||||
$message = $event->getLocalMessage();
|
||||
$to = $this->transmissionService->getAddressList($message, Recipient::TYPE_TO);
|
||||
$cc = $this->transmissionService->getAddressList($message, Recipient::TYPE_CC);
|
||||
$bcc = $this->transmissionService->getAddressList($message, Recipient::TYPE_BCC);
|
||||
|
||||
$addresses = $to->merge($cc)->merge($bcc);
|
||||
|
||||
$this->collector->addAddresses($event->getAccount()->getUserId(), $addresses);
|
||||
} catch (Throwable $e) {
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @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\Listener;
|
||||
|
||||
use OCA\Mail\Events\BeforeMessageSentEvent;
|
||||
use OCA\Mail\Service\AntiAbuseService;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\IUserManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event|BeforeMessageSentEvent>
|
||||
*/
|
||||
class AntiAbuseListener implements IEventListener {
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var AntiAbuseService */
|
||||
private $service;
|
||||
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
|
||||
public function __construct(IUserManager $userManager,
|
||||
AntiAbuseService $service,
|
||||
LoggerInterface $logger) {
|
||||
$this->service = $service;
|
||||
$this->userManager = $userManager;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof BeforeMessageSentEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->userManager->get($event->getAccount()->getUserId());
|
||||
if ($user === null) {
|
||||
$this->logger->error('User {user} for mail account {id} does not exist', [
|
||||
'user' => $event->getAccount()->getUserId(),
|
||||
'id' => $event->getAccount()->getId(),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->service->onBeforeMessageSent(
|
||||
$user,
|
||||
$event->getNewMessageData(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @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\Listener;
|
||||
|
||||
use Horde_Imap_Client;
|
||||
use Horde_Imap_Client_Exception;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Db\MessageMapper as DbMessageMapper;
|
||||
use OCA\Mail\Events\MessageSentEvent;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event|MessageSentEvent>
|
||||
*/
|
||||
class FlagRepliedMessageListener implements IEventListener {
|
||||
/** @var IMAPClientFactory */
|
||||
private $imapClientFactory;
|
||||
|
||||
/** @var MailboxMapper */
|
||||
private $mailboxMapper;
|
||||
|
||||
/** @var MessageMapper */
|
||||
private $messageMapper;
|
||||
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
|
||||
/** @var DbMessageMapper */
|
||||
private $dbMessageMapper;
|
||||
|
||||
public function __construct(IMAPClientFactory $imapClientFactory,
|
||||
MailboxMapper $mailboxMapper,
|
||||
DbMessageMapper $dbMessageMapper,
|
||||
MessageMapper $mapper,
|
||||
LoggerInterface $logger) {
|
||||
$this->imapClientFactory = $imapClientFactory;
|
||||
$this->mailboxMapper = $mailboxMapper;
|
||||
$this->dbMessageMapper = $dbMessageMapper;
|
||||
$this->messageMapper = $mapper;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof MessageSentEvent) || $event->getRepliedToMessageId() === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$messages = $this->dbMessageMapper->findByMessageId($event->getAccount(), $event->getRepliedToMessageId());
|
||||
if ($messages === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->imapClientFactory->getClient($event->getAccount());
|
||||
foreach ($messages as $message) {
|
||||
try {
|
||||
$mailbox = $this->mailboxMapper->findById($message->getMailboxId());
|
||||
//ignore read-only mailboxes
|
||||
if ($mailbox->getMyAcls() !== null && !strpos($mailbox->getMyAcls(), "w")) {
|
||||
continue;
|
||||
}
|
||||
// ignore drafts and sent
|
||||
if ($mailbox->isSpecialUse('sent') || $mailbox->isSpecialUse('drafts')) {
|
||||
continue;
|
||||
}
|
||||
// Mark all other mailboxes that contain the message with the same imap message id as replied
|
||||
$this->messageMapper->addFlag(
|
||||
$client,
|
||||
$mailbox,
|
||||
[$message->getUid()],
|
||||
Horde_Imap_Client::FLAG_ANSWERED
|
||||
);
|
||||
} catch (DoesNotExistException | Horde_Imap_Client_Exception $e) {
|
||||
$this->logger->warning('Could not flag replied message: ' . $e, [
|
||||
'exception' => $e,
|
||||
]);
|
||||
}
|
||||
|
||||
$message->setFlagAnswered(true);
|
||||
$this->dbMessageMapper->update($message);
|
||||
}
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -70,16 +70,19 @@ class InteractionListener implements IEventListener {
|
|||
$this->logger->debug('no user object found');
|
||||
return;
|
||||
}
|
||||
$recipients = $event->getMessage()->getTo()
|
||||
->merge($event->getMessage()->getCC())
|
||||
->merge($event->getMessage()->getBCC());
|
||||
foreach ($recipients->iterate() as $recipient) {
|
||||
$message = $event->getLocalMessage();
|
||||
$emails = [];
|
||||
foreach ($message->getRecipients() as $recipient) {
|
||||
if (in_array($recipient->getEmail(), $emails)) {
|
||||
continue;
|
||||
}
|
||||
$interactionEvent = new ContactInteractedWithEvent($user);
|
||||
$email = $recipient->getEmail();
|
||||
if ($email === null) {
|
||||
// Weird, bot ok
|
||||
continue;
|
||||
}
|
||||
$emails[] = $email;
|
||||
$interactionEvent->setEmail($email);
|
||||
$this->dispatcher->dispatch(ContactInteractedWithEvent::class, $interactionEvent);
|
||||
}
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @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\Listener;
|
||||
|
||||
use Horde_Imap_Client_Exception;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Events\MessageSentEvent;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event|MessageSentEvent>
|
||||
*/
|
||||
class SaveSentMessageListener implements IEventListener {
|
||||
/** @var MailboxMapper */
|
||||
private $mailboxMapper;
|
||||
|
||||
/** @var IMAPClientFactory */
|
||||
private $imapClientFactory;
|
||||
|
||||
/** @var MessageMapper */
|
||||
private $messageMapper;
|
||||
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
|
||||
public function __construct(MailboxMapper $mailboxMapper,
|
||||
IMAPClientFactory $imapClientFactory,
|
||||
MessageMapper $messageMapper,
|
||||
LoggerInterface $logger) {
|
||||
$this->mailboxMapper = $mailboxMapper;
|
||||
$this->imapClientFactory = $imapClientFactory;
|
||||
$this->messageMapper = $messageMapper;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof MessageSentEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sentMailboxId = $event->getAccount()->getMailAccount()->getSentMailboxId();
|
||||
if ($sentMailboxId === null) {
|
||||
$this->logger->warning("No sent mailbox exists, can't save sent message");
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the message in the sent mailbox
|
||||
try {
|
||||
$sentMailbox = $this->mailboxMapper->findById(
|
||||
$sentMailboxId
|
||||
);
|
||||
} catch (DoesNotExistException $e) {
|
||||
$this->logger->error("Sent mailbox could not be found", [
|
||||
'exception' => $e,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$client = $this->imapClientFactory->getClient($event->getAccount());
|
||||
try {
|
||||
$this->messageMapper->save(
|
||||
$client,
|
||||
$sentMailbox,
|
||||
$event->getMail()
|
||||
);
|
||||
} catch (Horde_Imap_Client_Exception $e) {
|
||||
throw new ServiceException('Could not save sent message on IMAP', 0, $e);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @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\DB\Types;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
class Version3600Date20240220134813 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure(): ISchemaWrapper $schemaClosure
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
$localMessagesTable = $schema->getTable('mail_local_messages');
|
||||
if (!$localMessagesTable->hasColumn('status')) {
|
||||
$localMessagesTable->addColumn('status', Types::INTEGER, [
|
||||
'notnull' => false,
|
||||
'default' => 0,
|
||||
]);
|
||||
}
|
||||
if (!$localMessagesTable->hasColumn('raw')) {
|
||||
$localMessagesTable->addColumn('raw', Types::TEXT, [
|
||||
'notnull' => false,
|
||||
'default' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
namespace OCA\Mail\Send;
|
||||
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
|
||||
abstract class AHandler {
|
||||
|
||||
protected AHandler|null $next = null;
|
||||
public function setNext(AHandler $next): AHandler {
|
||||
$this->next = $next;
|
||||
return $next;
|
||||
}
|
||||
|
||||
abstract public function process(Account $account, LocalMessage $localMessage): LocalMessage;
|
||||
|
||||
protected function processNext(Account $account, LocalMessage $localMessage): LocalMessage {
|
||||
if ($this->next !== null) {
|
||||
return $this->next->process($account, $localMessage);
|
||||
}
|
||||
return $localMessage;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Send;
|
||||
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Service\AntiAbuseService;
|
||||
use OCP\IUserManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class AntiAbuseHandler extends AHandler {
|
||||
|
||||
public function __construct(private IUserManager $userManager,
|
||||
private AntiAbuseService $service,
|
||||
private LoggerInterface $logger) {
|
||||
}
|
||||
public function process(Account $account, LocalMessage $localMessage): LocalMessage {
|
||||
if ($localMessage->getStatus() === LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL
|
||||
|| $localMessage->getStatus() === LocalMessage::STATUS_PROCESSED) {
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
|
||||
$user = $this->userManager->get($account->getUserId());
|
||||
if ($user === null) {
|
||||
$this->logger->error('User {user} for mail account {id} does not exist', [
|
||||
'user' => $account->getUserId(),
|
||||
'id' => $account->getId(),
|
||||
]);
|
||||
// What to do here?
|
||||
return $localMessage;
|
||||
}
|
||||
|
||||
$this->service->onBeforeMessageSent(
|
||||
$user,
|
||||
$localMessage,
|
||||
);
|
||||
// We don't react to a ratelimited message / a message that has too many recipients
|
||||
// at this point.
|
||||
// Any future improvement from https://github.com/nextcloud/mail/issues/6461
|
||||
// should refactor the chain to stop at this point unless the force send option is true
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Send;
|
||||
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\LocalMessageMapper;
|
||||
use OCA\Mail\Service\Attachment\AttachmentService;
|
||||
|
||||
class Chain {
|
||||
public function __construct(private SentMailboxHandler $sentMailboxHandler,
|
||||
private AntiAbuseHandler $antiAbuseHandler,
|
||||
private SendHandler $sendHandler,
|
||||
private CopySentMessageHandler $copySentMessageHandler,
|
||||
private FlagRepliedMessageHandler $flagRepliedMessageHandler,
|
||||
private AttachmentService $attachmentService,
|
||||
private LocalMessageMapper $localMessageMapper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(Account $account, LocalMessage $localMessage): void {
|
||||
$handlers = $this->sentMailboxHandler;
|
||||
$handlers->setNext($this->antiAbuseHandler)
|
||||
->setNext($this->sendHandler)
|
||||
->setNext($this->copySentMessageHandler)
|
||||
->setNext($this->flagRepliedMessageHandler);
|
||||
|
||||
$result = $handlers->process($account, $localMessage);
|
||||
if ($result->getStatus() === LocalMessage::STATUS_PROCESSED) {
|
||||
$this->attachmentService->deleteLocalMessageAttachments($account->getUserId(), $result->getId());
|
||||
$this->localMessageMapper->deleteWithRecipients($result);
|
||||
return;
|
||||
}
|
||||
$this->localMessageMapper->update($result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Send;
|
||||
|
||||
use Horde_Imap_Client_Exception;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class CopySentMessageHandler extends AHandler {
|
||||
public function __construct(private IMAPClientFactory $imapClientFactory,
|
||||
private MailboxMapper $mailboxMapper,
|
||||
private LoggerInterface $logger,
|
||||
private MessageMapper $messageMapper
|
||||
) {
|
||||
}
|
||||
public function process(Account $account, LocalMessage $localMessage): LocalMessage {
|
||||
if ($localMessage->getStatus() === LocalMessage::STATUS_PROCESSED) {
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
|
||||
$sentMailboxId = $account->getMailAccount()->getSentMailboxId();
|
||||
if ($sentMailboxId === null) {
|
||||
// We can't write the "sent mailbox" status here bc that would trigger an additional send.
|
||||
// Thus, we leave the "imap copy to sent mailbox" status.
|
||||
$localMessage->setStatus(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
$this->logger->warning("No sent mailbox exists, can't save sent message");
|
||||
return $localMessage;
|
||||
}
|
||||
|
||||
// Save the message in the sent mailbox
|
||||
try {
|
||||
$sentMailbox = $this->mailboxMapper->findById(
|
||||
$sentMailboxId
|
||||
);
|
||||
} catch (DoesNotExistException $e) {
|
||||
// We can't write the "sent mailbox" status here bc that would trigger an additional send.
|
||||
// Thus, we leave the "imap copy to sent mailbox" status.
|
||||
$localMessage->setStatus(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
$this->logger->error('Sent mailbox could not be found', [
|
||||
'exception' => $e,
|
||||
]);
|
||||
|
||||
return $localMessage;
|
||||
}
|
||||
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
try {
|
||||
$this->messageMapper->save(
|
||||
$client,
|
||||
$sentMailbox,
|
||||
$localMessage->getRaw()
|
||||
);
|
||||
$localMessage->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
} catch (Horde_Imap_Client_Exception $e) {
|
||||
$localMessage->setStatus(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
$this->logger->error('Could not copy message to sent mailbox', [
|
||||
'exception' => $e,
|
||||
]);
|
||||
return $localMessage;
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Send;
|
||||
|
||||
use Horde_Imap_Client;
|
||||
use Horde_Imap_Client_Exception;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Db\MessageMapper as DbMessageMapper;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class FlagRepliedMessageHandler extends AHandler {
|
||||
public function __construct(private IMAPClientFactory $imapClientFactory,
|
||||
private MailboxMapper $mailboxMapper,
|
||||
private LoggerInterface $logger,
|
||||
private MessageMapper $messageMapper,
|
||||
private DbMessageMapper $dbMessageMapper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(Account $account, LocalMessage $localMessage): LocalMessage {
|
||||
if ($localMessage->getStatus() !== LocalMessage::STATUS_PROCESSED) {
|
||||
return $localMessage;
|
||||
}
|
||||
|
||||
if ($localMessage->getInReplyToMessageId() === null) {
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
|
||||
$messages = $this->dbMessageMapper->findByMessageId($account, $localMessage->getInReplyToMessageId());
|
||||
if ($messages === []) {
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
foreach ($messages as $message) {
|
||||
try {
|
||||
$mailbox = $this->mailboxMapper->findById($message->getMailboxId());
|
||||
//ignore read-only mailboxes
|
||||
if ($mailbox->getMyAcls() !== null && !strpos($mailbox->getMyAcls(), 'w')) {
|
||||
continue;
|
||||
}
|
||||
// ignore drafts and sent
|
||||
if ($mailbox->isSpecialUse('sent') || $mailbox->isSpecialUse('drafts')) {
|
||||
continue;
|
||||
}
|
||||
// Mark all other mailboxes that contain the message with the same imap message id as replied
|
||||
$this->messageMapper->addFlag(
|
||||
$client,
|
||||
$mailbox,
|
||||
[$message->getUid()],
|
||||
Horde_Imap_Client::FLAG_ANSWERED
|
||||
);
|
||||
$message->setFlagAnswered(true);
|
||||
$this->dbMessageMapper->update($message);
|
||||
} catch (DoesNotExistException|Horde_Imap_Client_Exception $e) {
|
||||
$this->logger->warning('Could not flag replied message: ' . $e, [
|
||||
'exception' => $e,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Send;
|
||||
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Contracts\IMailTransmission;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
|
||||
class SendHandler extends AHandler {
|
||||
public function __construct(private IMailTransmission $transmission,
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(Account $account, LocalMessage $localMessage): LocalMessage {
|
||||
if ($localMessage->getStatus() === LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL
|
||||
|| $localMessage->getStatus() === LocalMessage::STATUS_PROCESSED) {
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
|
||||
$this->transmission->sendMessage($account, $localMessage);
|
||||
|
||||
if ($localMessage->getStatus() === LocalMessage::STATUS_RAW || $localMessage->getStatus() === null) {
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
// Something went wrong during the sending
|
||||
return $localMessage;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Send;
|
||||
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
|
||||
class SentMailboxHandler extends AHandler {
|
||||
public function process(Account $account, LocalMessage $localMessage): LocalMessage {
|
||||
if ($account->getMailAccount()->getSentMailboxId() === null) {
|
||||
$localMessage->setStatus(LocalMessage::STATUS_NO_SENT_MAILBOX);
|
||||
return $localMessage;
|
||||
}
|
||||
return $this->processNext($account, $localMessage);
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ declare(strict_types=1);
|
|||
namespace OCA\Mail\Service;
|
||||
|
||||
use OCA\Mail\AppInfo\Application;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
|
@ -58,8 +58,7 @@ class AntiAbuseService {
|
|||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function onBeforeMessageSent(IUser $user,
|
||||
NewMessageData $messageData): void {
|
||||
public function onBeforeMessageSent(IUser $user, LocalMessage $localMessage): void {
|
||||
$abuseDetection = $this->config->getAppValue(
|
||||
Application::APP_ID,
|
||||
'abuse_detection',
|
||||
|
@ -70,12 +69,11 @@ class AntiAbuseService {
|
|||
return;
|
||||
}
|
||||
|
||||
$this->checkNumberOfRecipients($user, $messageData);
|
||||
$this->checkRateLimits($user, $messageData);
|
||||
$this->checkNumberOfRecipients($user, $localMessage);
|
||||
$this->checkRateLimits($user, $localMessage);
|
||||
}
|
||||
|
||||
private function checkNumberOfRecipients(IUser $user,
|
||||
NewMessageData $messageData): void {
|
||||
private function checkNumberOfRecipients(IUser $user, LocalMessage $message): void {
|
||||
$numberOfRecipientsThreshold = (int)$this->config->getAppValue(
|
||||
Application::APP_ID,
|
||||
'abuse_number_of_recipients_per_message_threshold',
|
||||
|
@ -85,11 +83,10 @@ class AntiAbuseService {
|
|||
return;
|
||||
}
|
||||
|
||||
$actualNumberOfRecipients = count($messageData->getTo())
|
||||
+ count($messageData->getCc())
|
||||
+ count($messageData->getBcc());
|
||||
$actualNumberOfRecipients = count($message->getRecipients());
|
||||
|
||||
if ($actualNumberOfRecipients >= $numberOfRecipientsThreshold) {
|
||||
$message->setStatus(LocalMessage::STATUS_TOO_MANY_RECIPIENTS);
|
||||
$this->logger->alert('User {user} sends to a suspicious number of recipients. {expected} are allowed. {actual} are used', [
|
||||
'user' => $user->getUID(),
|
||||
'expected' => $numberOfRecipientsThreshold,
|
||||
|
@ -98,8 +95,7 @@ class AntiAbuseService {
|
|||
}
|
||||
}
|
||||
|
||||
private function checkRateLimits(IUser $user,
|
||||
NewMessageData $messageData): void {
|
||||
private function checkRateLimits(IUser $user, LocalMessage $message): void {
|
||||
if (!$this->cacheFactory->isAvailable()) {
|
||||
// No cache, no rate limits
|
||||
return;
|
||||
|
@ -110,16 +106,21 @@ class AntiAbuseService {
|
|||
return;
|
||||
}
|
||||
|
||||
$this->checkRateLimitsForPeriod($user, $messageData, $cache, '15m', 15 * 60);
|
||||
$this->checkRateLimitsForPeriod($user, $messageData, $cache, '1h', 60 * 60);
|
||||
$this->checkRateLimitsForPeriod($user, $messageData, $cache, '1d', 24 * 60 * 60);
|
||||
$ratelimited = (
|
||||
$this->checkRateLimitsForPeriod($user, $cache, '15m', 15 * 60, $message) ||
|
||||
$this->checkRateLimitsForPeriod($user, $cache, '1h', 60 * 60, $message) ||
|
||||
$this->checkRateLimitsForPeriod($user, $cache, '1d', 24 * 60 * 60, $message)
|
||||
);
|
||||
if ($ratelimited) {
|
||||
$message->setStatus(LocalMessage::STATUS_RATELIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
private function checkRateLimitsForPeriod(IUser $user,
|
||||
NewMessageData $messageData,
|
||||
IMemcache $cache,
|
||||
string $id,
|
||||
int $period): void {
|
||||
int $period,
|
||||
LocalMessage $message): bool {
|
||||
$maxNumberOfMessages = (int)$this->config->getAppValue(
|
||||
Application::APP_ID,
|
||||
'abuse_number_of_messages_per_' . $id,
|
||||
|
@ -127,7 +128,7 @@ class AntiAbuseService {
|
|||
);
|
||||
if ($maxNumberOfMessages === 0) {
|
||||
// No limit set
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
$now = $this->timeFactory->getTime();
|
||||
|
@ -136,7 +137,7 @@ class AntiAbuseService {
|
|||
$periodStart = ((int)($now / $period)) * $period;
|
||||
$cacheKey = implode('_', ['counter', $id, $periodStart]);
|
||||
$cache->add($cacheKey, 0);
|
||||
$counter = $cache->inc($cacheKey, count($messageData->getTo()) + count($messageData->getCc()) + count($messageData->getBcc()));
|
||||
$counter = $cache->inc($cacheKey, count($message->getRecipients()));
|
||||
|
||||
if ($counter >= $maxNumberOfMessages) {
|
||||
$this->logger->alert('User {user} sends a supcious number of messages within {period}. {expected} are allowed. {actual} have been sent', [
|
||||
|
@ -145,6 +146,8 @@ class AntiAbuseService {
|
|||
'expected' => $maxNumberOfMessages,
|
||||
'actual' => $counter,
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,34 +25,36 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Mail\Service;
|
||||
|
||||
use Horde_Imap_Client_Exception;
|
||||
use Horde_Mime_Exception;
|
||||
use Horde_Mime_Mail;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Contracts\IMailTransmission;
|
||||
use OCA\Mail\Address;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\MessageMapper;
|
||||
use OCA\Mail\Exception\SentMailboxNotSetException;
|
||||
use OCA\Mail\Exception\ClientException;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper as ImapMessageMapper;
|
||||
use OCA\Mail\Service\DataUri\DataUriParser;
|
||||
use OCA\Mail\SMTP\SmtpClientFactory;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\IConfig;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class AntiSpamService {
|
||||
private const NAME = 'antispam_reporting';
|
||||
private const MESSAGE_TYPE = 'message/rfc822';
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var MessageMapper */
|
||||
private $messageMapper;
|
||||
|
||||
/** @var IMailTransmission */
|
||||
private $transmission;
|
||||
|
||||
public function __construct(IConfig $config,
|
||||
MessageMapper $messageMapper,
|
||||
IMailTransmission $transmission) {
|
||||
$this->config = $config;
|
||||
$this->messageMapper = $messageMapper;
|
||||
$this->transmission = $transmission;
|
||||
public function __construct(private IConfig $config,
|
||||
private MessageMapper $dbMessageMapper,
|
||||
private MailManager $mailManager,
|
||||
private IMAPClientFactory $imapClientFactory,
|
||||
private SmtpClientFactory $smtpClientFactory,
|
||||
private ImapMessageMapper $messageMapper,
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getSpamEmail(): string {
|
||||
|
@ -99,25 +101,126 @@ class AntiSpamService {
|
|||
$subject = ($flag === '$junk') ? $this->getSpamSubject() : $this->getHamSubject();
|
||||
|
||||
// Message to attach not found
|
||||
$messageId = $this->messageMapper->getIdForUid($mailbox, $uid);
|
||||
$messageId = $this->dbMessageMapper->getIdForUid($mailbox, $uid);
|
||||
if ($messageId === null) {
|
||||
throw new ServiceException('Could not find reported message');
|
||||
}
|
||||
|
||||
$messageData = NewMessageData::fromRequest(
|
||||
$account,
|
||||
$reportEmail,
|
||||
null,
|
||||
null,
|
||||
$subject,
|
||||
$subject, // add any message body - not all IMAP servers accept empty emails
|
||||
[['id' => $messageId, 'type' => self::MESSAGE_TYPE]]
|
||||
if ($account->getMailAccount()->getSentMailboxId() === null) {
|
||||
throw new ServiceException('Could not find sent mailbox');
|
||||
}
|
||||
|
||||
$message = $account->newMessage();
|
||||
$from = new AddressList([
|
||||
Address::fromRaw($account->getName(), $account->getEMailAddress()),
|
||||
]);
|
||||
$to = new AddressList([
|
||||
Address::fromRaw($reportEmail, $reportEmail),
|
||||
]);
|
||||
$message->setTo($to);
|
||||
$message->setSubject($subject);
|
||||
$message->setFrom($from);
|
||||
$message->setContent($subject);
|
||||
|
||||
// Gets original of other message
|
||||
$userId = $account->getMailAccount()->getUserId();
|
||||
try {
|
||||
$attachmentMessage = $this->mailManager->getMessage($userId, $messageId);
|
||||
} catch (DoesNotExistException $e) {
|
||||
$this->logger->error('Could not find reported email with message ID #' . $messageId, ['exception' => $e]);
|
||||
return;
|
||||
}
|
||||
|
||||
$mailbox = $this->mailManager->getMailbox($userId, $attachmentMessage->getMailboxId());
|
||||
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
try {
|
||||
$fullText = $this->messageMapper->getFullText(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$attachmentMessage->getUid(),
|
||||
$userId
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
||||
$message->addEmbeddedMessageAttachment(
|
||||
$attachmentMessage->getSubject() . '.eml',
|
||||
$fullText
|
||||
);
|
||||
|
||||
$transport = $this->smtpClientFactory->create($account);
|
||||
// build mime body
|
||||
$headers = [
|
||||
'From' => $message->getFrom()->first()->toHorde(),
|
||||
'To' => $message->getTo()->toHorde(),
|
||||
'Cc' => $message->getCC()->toHorde(),
|
||||
'Bcc' => $message->getBCC()->toHorde(),
|
||||
'Subject' => $message->getSubject(),
|
||||
];
|
||||
|
||||
if (($inReplyTo = $message->getInReplyTo()) !== null) {
|
||||
$headers['References'] = $inReplyTo;
|
||||
$headers['In-Reply-To'] = $inReplyTo;
|
||||
}
|
||||
|
||||
$mail = new Horde_Mime_Mail();
|
||||
$mail->addHeaders($headers);
|
||||
|
||||
$mimeMessage = new MimeMessage(
|
||||
new DataUriParser()
|
||||
);
|
||||
$mimePart = $mimeMessage->build(
|
||||
true,
|
||||
$message->getContent(),
|
||||
$message->getAttachments()
|
||||
);
|
||||
|
||||
$mail->setBasePart($mimePart);
|
||||
|
||||
// Send the message
|
||||
try {
|
||||
$this->transmission->sendMessage($messageData);
|
||||
} catch (SentMailboxNotSetException | ServiceException $e) {
|
||||
throw new ServiceException('Could not send report email from anti spam email service', 0, $e);
|
||||
$mail->send($transport, false, false);
|
||||
} catch (Horde_Mime_Exception $e) {
|
||||
throw new ServiceException(
|
||||
'Could not send message: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
$sentMailboxId = $account->getMailAccount()->getSentMailboxId();
|
||||
if ($sentMailboxId === null) {
|
||||
$this->logger->warning("No sent mailbox exists, can't save sent message");
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the message in the sent mailbox
|
||||
try {
|
||||
$sentMailbox = $this->mailManager->getMailbox(
|
||||
$account->getUserId(),
|
||||
$sentMailboxId
|
||||
);
|
||||
} catch (ClientException $e) {
|
||||
$this->logger->error('Sent mailbox could not be found', [
|
||||
'exception' => $e,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
try {
|
||||
$this->messageMapper->save(
|
||||
$client,
|
||||
$sentMailbox,
|
||||
$mail->getRaw(false)
|
||||
);
|
||||
} catch (Horde_Imap_Client_Exception $e) {
|
||||
$this->logger->error('Could not move report email to sent mailbox, but the report email was sent. Reported email was id: #' . $messageId, ['exception' => $e]);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -119,6 +119,10 @@ class DraftsService {
|
|||
throw new ClientException('Cannot convert message to outbox message without at least one recipient');
|
||||
}
|
||||
|
||||
// Explicitly reset the status, so we can try sending from scratch again
|
||||
// in case the user has updated a failing component
|
||||
$message->setStatus(LocalMessage::STATUS_RAW);
|
||||
|
||||
$message = $this->mapper->saveWithRecipients($message, $toRecipients, $ccRecipients, $bccRecipients);
|
||||
|
||||
if ($attachments === []) {
|
||||
|
|
|
@ -41,156 +41,79 @@ use Horde_Mime_Mdn;
|
|||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Address;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Contracts\IAttachmentService;
|
||||
use OCA\Mail\Contracts\IMailManager;
|
||||
use OCA\Mail\Contracts\IMailTransmission;
|
||||
use OCA\Mail\Db\Alias;
|
||||
use OCA\Mail\Db\LocalAttachment;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Events\BeforeMessageSentEvent;
|
||||
use OCA\Mail\Events\DraftSavedEvent;
|
||||
use OCA\Mail\Events\MessageSentEvent;
|
||||
use OCA\Mail\Events\SaveDraftEvent;
|
||||
use OCA\Mail\Exception\AttachmentNotFoundException;
|
||||
use OCA\Mail\Exception\ClientException;
|
||||
use OCA\Mail\Exception\SentMailboxNotSetException;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\Exception\SmimeEncryptException;
|
||||
use OCA\Mail\Exception\SmimeSignException;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCA\Mail\Model\IMessage;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\Service\DataUri\DataUriParser;
|
||||
use OCA\Mail\SMTP\SmtpClientFactory;
|
||||
use OCA\Mail\Support\PerformanceLogger;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
|
||||
class MailTransmission implements IMailTransmission {
|
||||
private SmimeService $smimeService;
|
||||
|
||||
/** @var Folder */
|
||||
private $userFolder;
|
||||
|
||||
/** @var IAttachmentService */
|
||||
private $attachmentService;
|
||||
|
||||
/** @var IMailManager */
|
||||
private $mailManager;
|
||||
|
||||
/** @var IMAPClientFactory */
|
||||
private $imapClientFactory;
|
||||
|
||||
/** @var SmtpClientFactory */
|
||||
private $smtpClientFactory;
|
||||
|
||||
/** @var IEventDispatcher */
|
||||
private $eventDispatcher;
|
||||
|
||||
/** @var MailboxMapper */
|
||||
private $mailboxMapper;
|
||||
|
||||
/** @var MessageMapper */
|
||||
private $messageMapper;
|
||||
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
|
||||
/** @var PerformanceLogger */
|
||||
private $performanceLogger;
|
||||
|
||||
/** @var AliasesService */
|
||||
private $aliasesService;
|
||||
|
||||
/** @var GroupsIntegration */
|
||||
private $groupsIntegration;
|
||||
|
||||
/**
|
||||
* @param Folder $userFolder
|
||||
*/
|
||||
public function __construct($userFolder,
|
||||
IAttachmentService $attachmentService,
|
||||
IMailManager $mailManager,
|
||||
IMAPClientFactory $imapClientFactory,
|
||||
SmtpClientFactory $smtpClientFactory,
|
||||
IEventDispatcher $eventDispatcher,
|
||||
MailboxMapper $mailboxMapper,
|
||||
MessageMapper $messageMapper,
|
||||
LoggerInterface $logger,
|
||||
PerformanceLogger $performanceLogger,
|
||||
AliasesService $aliasesService,
|
||||
GroupsIntegration $groupsIntegration,
|
||||
SmimeService $smimeService) {
|
||||
$this->userFolder = $userFolder;
|
||||
$this->attachmentService = $attachmentService;
|
||||
$this->mailManager = $mailManager;
|
||||
$this->imapClientFactory = $imapClientFactory;
|
||||
$this->smtpClientFactory = $smtpClientFactory;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->mailboxMapper = $mailboxMapper;
|
||||
$this->messageMapper = $messageMapper;
|
||||
$this->logger = $logger;
|
||||
$this->performanceLogger = $performanceLogger;
|
||||
$this->aliasesService = $aliasesService;
|
||||
$this->groupsIntegration = $groupsIntegration;
|
||||
$this->smimeService = $smimeService;
|
||||
public function __construct(
|
||||
private IMAPClientFactory $imapClientFactory,
|
||||
private SmtpClientFactory $smtpClientFactory,
|
||||
private IEventDispatcher $eventDispatcher,
|
||||
private MailboxMapper $mailboxMapper,
|
||||
private MessageMapper $messageMapper,
|
||||
private LoggerInterface $logger,
|
||||
private PerformanceLogger $performanceLogger,
|
||||
private AliasesService $aliasesService,
|
||||
private TransmissionService $transmissionService
|
||||
) {
|
||||
}
|
||||
|
||||
public function sendMessage(NewMessageData $messageData,
|
||||
?string $repliedToMessageId = null,
|
||||
?Alias $alias = null,
|
||||
?Message $draft = null): void {
|
||||
$account = $messageData->getAccount();
|
||||
if ($account->getMailAccount()->getSentMailboxId() === null) {
|
||||
throw new SentMailboxNotSetException();
|
||||
}
|
||||
public function sendMessage(Account $account, LocalMessage $localMessage): void {
|
||||
$to = $this->transmissionService->getAddressList($localMessage, Recipient::TYPE_TO);
|
||||
$cc = $this->transmissionService->getAddressList($localMessage, Recipient::TYPE_CC);
|
||||
$bcc = $this->transmissionService->getAddressList($localMessage, Recipient::TYPE_BCC);
|
||||
$attachments = $this->transmissionService->getAttachments($localMessage);
|
||||
|
||||
if ($repliedToMessageId !== null) {
|
||||
$message = $this->buildReplyMessage($account, $messageData, $repliedToMessageId);
|
||||
} else {
|
||||
$message = $this->buildNewMessage($account, $messageData);
|
||||
$alias = null;
|
||||
if ($localMessage->getAliasId() !== null) {
|
||||
$alias = $this->aliasesService->find($localMessage->getAliasId(), $account->getUserId());
|
||||
}
|
||||
|
||||
$account->setAlias($alias);
|
||||
$fromEmail = $alias ? $alias->getAlias() : $account->getEMailAddress();
|
||||
$from = new AddressList([
|
||||
Address::fromRaw($account->getName(), $fromEmail),
|
||||
]);
|
||||
$message->setFrom($from);
|
||||
$message->setCC($messageData->getCc());
|
||||
$message->setBcc($messageData->getBcc());
|
||||
$message->setContent($messageData->getBody());
|
||||
$this->handleAttachments($account, $messageData, $message); // only ever going to be local attachments
|
||||
|
||||
$attachmentParts = [];
|
||||
foreach ($attachments as $attachment) {
|
||||
$part = $this->transmissionService->handleAttachment($account, $attachment);
|
||||
if ($part !== null) {
|
||||
$attachmentParts[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
$transport = $this->smtpClientFactory->create($account);
|
||||
// build mime body
|
||||
$headers = [
|
||||
'From' => $message->getFrom()->first()->toHorde(),
|
||||
'To' => $message->getTo()->toHorde(),
|
||||
'Cc' => $message->getCC()->toHorde(),
|
||||
'Bcc' => $message->getBCC()->toHorde(),
|
||||
'Subject' => $message->getSubject(),
|
||||
'From' => $from->first()->toHorde(),
|
||||
'To' => $to->toHorde(),
|
||||
'Cc' => $cc->toHorde(),
|
||||
'Bcc' => $bcc->toHorde(),
|
||||
'Subject' => $localMessage->getSubject(),
|
||||
];
|
||||
|
||||
if (($inReplyTo = $message->getInReplyTo()) !== null) {
|
||||
if (($inReplyTo = $localMessage->getInReplyToMessageId()) !== null) {
|
||||
$headers['References'] = $inReplyTo;
|
||||
$headers['In-Reply-To'] = $inReplyTo;
|
||||
}
|
||||
|
||||
if ($messageData->isMdnRequested()) {
|
||||
$headers[Horde_Mime_Mdn::MDN_HEADER] = $message->getFrom()->first()->toHorde();
|
||||
}
|
||||
|
||||
$mail = new Horde_Mime_Mail();
|
||||
$mail->addHeaders($headers);
|
||||
|
||||
|
@ -198,170 +121,59 @@ class MailTransmission implements IMailTransmission {
|
|||
new DataUriParser()
|
||||
);
|
||||
$mimePart = $mimeMessage->build(
|
||||
$messageData->isHtml(),
|
||||
$message->getContent(),
|
||||
$message->getAttachments()
|
||||
$localMessage->isHtml(),
|
||||
$localMessage->getBody(),
|
||||
$attachmentParts
|
||||
);
|
||||
|
||||
// TODO: add smimeEncrypt check if implemented
|
||||
if ($messageData->getSmimeSign()) {
|
||||
if ($messageData->getSmimeCertificateId() === null) {
|
||||
throw new ServiceException('Could not send message: Requested S/MIME signature without certificate id');
|
||||
}
|
||||
|
||||
try {
|
||||
$certificate = $this->smimeService->findCertificate(
|
||||
$messageData->getSmimeCertificateId(),
|
||||
$account->getUserId(),
|
||||
);
|
||||
$mimePart = $this->smimeService->signMimePart($mimePart, $certificate);
|
||||
} catch (DoesNotExistException $e) {
|
||||
throw new ServiceException(
|
||||
'Could not send message: Certificate does not exist: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e,
|
||||
);
|
||||
} catch (SmimeSignException | ServiceException $e) {
|
||||
throw new ServiceException(
|
||||
'Could not send message: Failed to sign MIME part: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($messageData->getSmimeEncrypt()) {
|
||||
if ($messageData->getSmimeCertificateId() === null) {
|
||||
throw new ServiceException('Could not send message: Requested S/MIME signature without certificate id');
|
||||
}
|
||||
|
||||
try {
|
||||
$addressList = $messageData->getTo()
|
||||
->merge($messageData->getCc())
|
||||
->merge($messageData->getBcc());
|
||||
$certificates = $this->smimeService->findCertificatesByAddressList($addressList, $account->getUserId());
|
||||
|
||||
$senderCertificate = $this->smimeService->findCertificate($messageData->getSmimeCertificateId(), $account->getUserId());
|
||||
$certificates[] = $senderCertificate;
|
||||
|
||||
$mimePart = $this->smimeService->encryptMimePart($mimePart, $certificates);
|
||||
} catch (DoesNotExistException $e) {
|
||||
throw new ServiceException(
|
||||
'Could not send message: Certificate does not exist: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e,
|
||||
);
|
||||
} catch (SmimeEncryptException | ServiceException $e) {
|
||||
throw new ServiceException(
|
||||
'Could not send message: Failed to encrypt MIME part: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e,
|
||||
);
|
||||
}
|
||||
try {
|
||||
$mimePart = $this->transmissionService->getSignMimePart($localMessage, $account, $mimePart);
|
||||
$mimePart = $this->transmissionService->getEncryptMimePart($localMessage, $to, $cc, $bcc, $account, $mimePart);
|
||||
} catch (ServiceException $e) {
|
||||
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
||||
return;
|
||||
}
|
||||
|
||||
$mail->setBasePart($mimePart);
|
||||
|
||||
$this->eventDispatcher->dispatchTyped(
|
||||
new BeforeMessageSentEvent($account, $messageData, $repliedToMessageId, $draft, $message, $mail)
|
||||
);
|
||||
|
||||
// Send the message
|
||||
try {
|
||||
$mail->send($transport, false, false);
|
||||
$localMessage->setRaw($mail->getRaw(false));
|
||||
} catch (Horde_Mime_Exception $e) {
|
||||
throw new ServiceException(
|
||||
'Could not send message: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e
|
||||
);
|
||||
$localMessage->setStatus(LocalMessage::STATUS_SMPT_SEND_FAIL);
|
||||
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatchTyped(
|
||||
new MessageSentEvent($account, $messageData, $repliedToMessageId, $draft, $message, $mail)
|
||||
new MessageSentEvent($account, $localMessage)
|
||||
);
|
||||
}
|
||||
|
||||
public function sendLocalMessage(Account $account, LocalMessage $message): void {
|
||||
$to = new AddressList(
|
||||
array_map(
|
||||
static function ($recipient) {
|
||||
return Address::fromRaw($recipient->getLabel() ?? $recipient->getEmail(), $recipient->getEmail());
|
||||
},
|
||||
$this->groupsIntegration->expand(array_filter($message->getRecipients(), static function (Recipient $recipient) {
|
||||
return $recipient->getType() === Recipient::TYPE_TO;
|
||||
}))
|
||||
)
|
||||
);
|
||||
$cc = new AddressList(
|
||||
array_map(
|
||||
static function ($recipient) {
|
||||
return Address::fromRaw($recipient->getLabel() ?? $recipient->getEmail(), $recipient->getEmail());
|
||||
},
|
||||
$this->groupsIntegration->expand(array_filter($message->getRecipients(), static function (Recipient $recipient) {
|
||||
return $recipient->getType() === Recipient::TYPE_CC;
|
||||
}))
|
||||
)
|
||||
);
|
||||
$bcc = new AddressList(
|
||||
array_map(
|
||||
static function ($recipient) {
|
||||
return Address::fromRaw($recipient->getLabel() ?? $recipient->getEmail(), $recipient->getEmail());
|
||||
},
|
||||
$this->groupsIntegration->expand(array_filter($message->getRecipients(), static function (Recipient $recipient) {
|
||||
return $recipient->getType() === Recipient::TYPE_BCC;
|
||||
}))
|
||||
)
|
||||
);
|
||||
$attachments = array_map(static function (LocalAttachment $attachment) {
|
||||
// Convert to the untyped nested array used in \OCA\Mail\Controller\AccountsController::send
|
||||
return [
|
||||
'type' => 'local',
|
||||
'id' => $attachment->getId(),
|
||||
];
|
||||
}, $message->getAttachments());
|
||||
$messageData = new NewMessageData(
|
||||
$account,
|
||||
$to,
|
||||
$cc,
|
||||
$bcc,
|
||||
$message->getSubject(),
|
||||
$message->getBody(),
|
||||
$attachments,
|
||||
$message->isHtml(),
|
||||
false,
|
||||
$message->getSmimeCertificateId(),
|
||||
$message->getSmimeSign() ?? false,
|
||||
$message->getSmimeEncrypt() ?? false,
|
||||
);
|
||||
|
||||
if ($message->getAliasId() !== null) {
|
||||
$alias = $this->aliasesService->find($message->getAliasId(), $account->getUserId());
|
||||
}
|
||||
|
||||
try {
|
||||
$this->sendMessage($messageData, $message->getInReplyToMessageId(), $alias ?? null);
|
||||
} catch (SentMailboxNotSetException $e) {
|
||||
throw new ClientException('Could not send message: ' . $e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function saveLocalDraft(Account $account, LocalMessage $message): void {
|
||||
$messageData = $this->getNewMessageData($message, $account);
|
||||
$to = $this->transmissionService->getAddressList($message, Recipient::TYPE_TO);
|
||||
$cc = $this->transmissionService->getAddressList($message, Recipient::TYPE_CC);
|
||||
$bcc = $this->transmissionService->getAddressList($message, Recipient::TYPE_BCC);
|
||||
$attachments = $this->transmissionService->getAttachments($message);
|
||||
|
||||
$perfLogger = $this->performanceLogger->start('save local draft');
|
||||
|
||||
$account = $messageData->getAccount();
|
||||
$imapMessage = $account->newMessage();
|
||||
$imapMessage->setTo($messageData->getTo());
|
||||
$imapMessage->setSubject($messageData->getSubject());
|
||||
$imapMessage->setTo($to);
|
||||
$imapMessage->setSubject($message->getSubject());
|
||||
$from = new AddressList([
|
||||
Address::fromRaw($account->getName(), $account->getEMailAddress()),
|
||||
]);
|
||||
$imapMessage->setFrom($from);
|
||||
$imapMessage->setCC($messageData->getCc());
|
||||
$imapMessage->setBcc($messageData->getBcc());
|
||||
$imapMessage->setContent($messageData->getBody());
|
||||
$imapMessage->setCC($cc);
|
||||
$imapMessage->setBcc($bcc);
|
||||
$imapMessage->setContent($message->getBody());
|
||||
|
||||
foreach ($attachments as $attachment) {
|
||||
$this->transmissionService->handleAttachment($account, $attachment);
|
||||
}
|
||||
|
||||
// build mime body
|
||||
$headers = [
|
||||
|
@ -398,7 +210,7 @@ class MailTransmission implements IMailTransmission {
|
|||
$this->messageMapper->save(
|
||||
$client,
|
||||
$draftsMailbox,
|
||||
$mail,
|
||||
$mail->getRaw(false),
|
||||
[Horde_Imap_Client::FLAG_DRAFT]
|
||||
);
|
||||
$perfLogger->step('save local draft message on IMAP');
|
||||
|
@ -410,7 +222,7 @@ class MailTransmission implements IMailTransmission {
|
|||
$client->logout();
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatchTyped(new DraftSavedEvent($account, $messageData, null));
|
||||
$this->eventDispatcher->dispatchTyped(new DraftSavedEvent($account, null));
|
||||
$perfLogger->step('emit post local draft save event');
|
||||
|
||||
$perfLogger->end();
|
||||
|
@ -480,7 +292,7 @@ class MailTransmission implements IMailTransmission {
|
|||
$newUid = $this->messageMapper->save(
|
||||
$client,
|
||||
$draftsMailbox,
|
||||
$mail,
|
||||
$mail->getRaw(false),
|
||||
[Horde_Imap_Client::FLAG_DRAFT]
|
||||
);
|
||||
$perfLogger->step('save message on IMAP');
|
||||
|
@ -502,201 +314,6 @@ class MailTransmission implements IMailTransmission {
|
|||
return [$account, $draftsMailbox, $newUid];
|
||||
}
|
||||
|
||||
private function buildReplyMessage(Account $account,
|
||||
NewMessageData $messageData,
|
||||
string $repliedToMessageId): IMessage {
|
||||
// Reply
|
||||
$message = $account->newMessage();
|
||||
$message->setSubject($messageData->getSubject());
|
||||
$message->setTo($messageData->getTo());
|
||||
$message->setInReplyTo($repliedToMessageId);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
private function buildNewMessage(Account $account, NewMessageData $messageData): IMessage {
|
||||
// New message
|
||||
$message = $account->newMessage();
|
||||
$message->setTo($messageData->getTo());
|
||||
$message->setSubject($messageData->getSubject());
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param NewMessageData $messageData
|
||||
* @param IMessage $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handleAttachments(Account $account, NewMessageData $messageData, IMessage $message): void {
|
||||
foreach ($messageData->getAttachments() as $attachment) {
|
||||
if (isset($attachment['type']) && $attachment['type'] === 'local') {
|
||||
// Adds an uploaded attachment
|
||||
$this->handleLocalAttachment($account, $attachment, $message);
|
||||
} elseif (isset($attachment['type']) && $attachment['type'] === 'message') {
|
||||
// Adds another message as attachment
|
||||
$this->handleForwardedMessageAttachment($account, $attachment, $message);
|
||||
} elseif (isset($attachment['type']) && $attachment['type'] === 'message/rfc822') {
|
||||
// Adds another message as attachment with mime type 'message/rfc822
|
||||
$this->handleEmbeddedMessageAttachments($account, $attachment, $message);
|
||||
} elseif (isset($attachment['type']) && $attachment['type'] === 'message-attachment') {
|
||||
// Adds an attachment from another email (use case is, eg., a mail forward)
|
||||
$this->handleForwardedAttachment($account, $attachment, $message);
|
||||
} else {
|
||||
// Adds an attachment from Files
|
||||
$this->handleCloudAttachment($attachment, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param array $attachment
|
||||
* @param IMessage $message
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function handleLocalAttachment(Account $account, array $attachment, IMessage $message) {
|
||||
if (!isset($attachment['id'])) {
|
||||
$this->logger->warning('ignoring local attachment because its id is unknown');
|
||||
return null;
|
||||
}
|
||||
|
||||
$id = (int)$attachment['id'];
|
||||
|
||||
try {
|
||||
[$localAttachment, $file] = $this->attachmentService->getAttachment($account->getMailAccount()->getUserId(), $id);
|
||||
$message->addLocalAttachment($localAttachment, $file);
|
||||
} catch (AttachmentNotFoundException $ex) {
|
||||
$this->logger->warning('ignoring local attachment because it does not exist');
|
||||
// TODO: rethrow?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attachment that's coming from another message's attachment (typical use case: email forwarding)
|
||||
*
|
||||
* @param Account $account
|
||||
* @param mixed[] $attachment
|
||||
* @param IMessage $message
|
||||
*/
|
||||
private function handleForwardedMessageAttachment(Account $account, array $attachment, IMessage $message): void {
|
||||
// Gets original of other message
|
||||
$userId = $account->getMailAccount()->getUserId();
|
||||
$attachmentMessage = $this->mailManager->getMessage($userId, (int)$attachment['id']);
|
||||
$mailbox = $this->mailManager->getMailbox($userId, $attachmentMessage->getMailboxId());
|
||||
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
try {
|
||||
$fullText = $this->messageMapper->getFullText(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$attachmentMessage->getUid(),
|
||||
$userId
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
||||
$message->addRawAttachment(
|
||||
$attachment['displayName'] ?? $attachmentMessage->getSubject() . '.eml',
|
||||
$fullText
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an email as attachment
|
||||
*
|
||||
* @param Account $account
|
||||
* @param mixed[] $attachment
|
||||
* @param IMessage $message
|
||||
*/
|
||||
private function handleEmbeddedMessageAttachments(Account $account, array $attachment, IMessage $message): void {
|
||||
// Gets original of other message
|
||||
$userId = $account->getMailAccount()->getUserId();
|
||||
$attachmentMessage = $this->mailManager->getMessage($userId, (int)$attachment['id']);
|
||||
$mailbox = $this->mailManager->getMailbox($userId, $attachmentMessage->getMailboxId());
|
||||
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
try {
|
||||
$fullText = $this->messageMapper->getFullText(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$attachmentMessage->getUid(),
|
||||
$userId
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
||||
$message->addEmbeddedMessageAttachment(
|
||||
$attachment['displayName'] ?? $attachmentMessage->getSubject() . '.eml',
|
||||
$fullText
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds an attachment that's coming from another message's attachment (typical use case: email forwarding)
|
||||
*
|
||||
* @param Account $account
|
||||
* @param mixed[] $attachment
|
||||
* @param IMessage $message
|
||||
*/
|
||||
private function handleForwardedAttachment(Account $account, array $attachment, IMessage $message): void {
|
||||
// Gets attachment from other message
|
||||
$userId = $account->getMailAccount()->getUserId();
|
||||
$attachmentMessage = $this->mailManager->getMessage($userId, (int)$attachment['messageId']);
|
||||
$mailbox = $this->mailManager->getMailbox($userId, $attachmentMessage->getMailboxId());
|
||||
$client = $this->imapClientFactory->getClient($account);
|
||||
try {
|
||||
$attachments = $this->messageMapper->getRawAttachments(
|
||||
$client,
|
||||
$mailbox->getName(),
|
||||
$attachmentMessage->getUid(),
|
||||
$userId,
|
||||
[
|
||||
$attachment['id']
|
||||
]
|
||||
);
|
||||
} finally {
|
||||
$client->logout();
|
||||
}
|
||||
|
||||
// Attaches attachment to new message
|
||||
$message->addRawAttachment($attachment['fileName'], $attachments[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attachment
|
||||
* @param IMessage $message
|
||||
*
|
||||
* @return File|null
|
||||
*/
|
||||
private function handleCloudAttachment(array $attachment, IMessage $message) {
|
||||
if (!isset($attachment['fileName'])) {
|
||||
$this->logger->warning('ignoring cloud attachment because its fileName is unknown');
|
||||
return null;
|
||||
}
|
||||
|
||||
$fileName = $attachment['fileName'];
|
||||
if (!$this->userFolder->nodeExists($fileName)) {
|
||||
$this->logger->warning('ignoring cloud attachment because the node does not exist');
|
||||
return null;
|
||||
}
|
||||
|
||||
$file = $this->userFolder->get($fileName);
|
||||
if (!$file instanceof File) {
|
||||
$this->logger->warning('ignoring cloud attachment because the node is not a file');
|
||||
return null;
|
||||
}
|
||||
|
||||
$message->addAttachmentFromFiles($file);
|
||||
}
|
||||
|
||||
public function sendMdn(Account $account, Mailbox $mailbox, Message $message): void {
|
||||
$query = new Horde_Imap_Client_Fetch_Query();
|
||||
$query->flags();
|
||||
|
@ -766,59 +383,4 @@ class MailTransmission implements IMailTransmission {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LocalMessage $message
|
||||
* @param Account $account
|
||||
* @return NewMessageData
|
||||
*/
|
||||
private function getNewMessageData(LocalMessage $message, Account $account): NewMessageData {
|
||||
$to = new AddressList(
|
||||
array_map(
|
||||
static function ($recipient) {
|
||||
return Address::fromRaw($recipient->getLabel() ?? $recipient->getEmail(), $recipient->getEmail());
|
||||
},
|
||||
$this->groupsIntegration->expand(array_filter($message->getRecipients(), static function (Recipient $recipient) {
|
||||
return $recipient->getType() === Recipient::TYPE_TO;
|
||||
}))
|
||||
)
|
||||
);
|
||||
|
||||
$cc = new AddressList(
|
||||
array_map(
|
||||
static function ($recipient) {
|
||||
return Address::fromRaw($recipient->getLabel() ?? $recipient->getEmail(), $recipient->getEmail());
|
||||
},
|
||||
$this->groupsIntegration->expand(array_filter($message->getRecipients(), static function (Recipient $recipient) {
|
||||
return $recipient->getType() === Recipient::TYPE_CC;
|
||||
}))
|
||||
)
|
||||
);
|
||||
$bcc = new AddressList(
|
||||
array_map(
|
||||
static function ($recipient) {
|
||||
return Address::fromRaw($recipient->getLabel() ?? $recipient->getEmail(), $recipient->getEmail());
|
||||
},
|
||||
$this->groupsIntegration->expand(array_filter($message->getRecipients(), static function (Recipient $recipient) {
|
||||
return $recipient->getType() === Recipient::TYPE_BCC;
|
||||
}))
|
||||
)
|
||||
);
|
||||
$attachments = array_map(function (LocalAttachment $attachment) {
|
||||
// Convert to the untyped nested array used in \OCA\Mail\Controller\AccountsController::send
|
||||
return [
|
||||
'type' => 'local',
|
||||
'id' => $attachment->getId(),
|
||||
];
|
||||
}, $message->getAttachments());
|
||||
return new NewMessageData(
|
||||
$account,
|
||||
$to,
|
||||
$cc,
|
||||
$bcc,
|
||||
$message->getSubject(),
|
||||
$message->getBody(),
|
||||
$attachments,
|
||||
$message->isHtml()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ use OCA\Mail\Db\LocalMessageMapper;
|
|||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Events\OutboxMessageCreatedEvent;
|
||||
use OCA\Mail\Exception\ClientException;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\Send\Chain;
|
||||
use OCA\Mail\Service\Attachment\AttachmentService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
|
@ -79,7 +79,9 @@ class OutboxService {
|
|||
IMailManager $mailManager,
|
||||
AccountService $accountService,
|
||||
ITimeFactory $timeFactory,
|
||||
LoggerInterface $logger) {
|
||||
LoggerInterface $logger,
|
||||
private Chain $sendChain,
|
||||
) {
|
||||
$this->transmission = $transmission;
|
||||
$this->mapper = $mapper;
|
||||
$this->attachmentService = $attachmentService;
|
||||
|
@ -130,24 +132,9 @@ class OutboxService {
|
|||
$this->mapper->deleteWithRecipients($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LocalMessage $message
|
||||
* @param Account $account
|
||||
* @return void
|
||||
* @throws ClientException
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function sendMessage(LocalMessage $message, Account $account): void {
|
||||
try {
|
||||
$this->transmission->sendLocalMessage($account, $message);
|
||||
} catch (ClientException|ServiceException $e) {
|
||||
// Mark as failed so the message is not sent repeatedly in background
|
||||
$message->setFailed(true);
|
||||
$this->mapper->update($message);
|
||||
throw $e;
|
||||
}
|
||||
$this->attachmentService->deleteLocalMessageAttachments($account->getUserId(), $message->getId());
|
||||
$this->mapper->deleteWithRecipients($message);
|
||||
public function sendMessage(LocalMessage $message, Account $account): LocalMessage {
|
||||
$this->sendChain->process($account, $message);
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,6 +181,7 @@ class OutboxService {
|
|||
$toRecipients = self::convertToRecipient($to, Recipient::TYPE_TO);
|
||||
$ccRecipients = self::convertToRecipient($cc, Recipient::TYPE_CC);
|
||||
$bccRecipients = self::convertToRecipient($bcc, Recipient::TYPE_BCC);
|
||||
|
||||
$message = $this->mapper->updateWithRecipients($message, $toRecipients, $ccRecipients, $bccRecipients);
|
||||
|
||||
if ($attachments === []) {
|
||||
|
@ -251,16 +239,13 @@ class OutboxService {
|
|||
}, $accountIds));
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$account = $accounts[$message->getAccountId()];
|
||||
if ($account === null) {
|
||||
// Ignore message of non-existent account
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$account = $accounts[$message->getAccountId()];
|
||||
if ($account === null) {
|
||||
// Ignore message of non-existent account
|
||||
continue;
|
||||
}
|
||||
$this->sendMessage(
|
||||
$message,
|
||||
$account,
|
||||
);
|
||||
$this->sendChain->process($account, $message);
|
||||
$this->logger->debug('Outbox message {id} sent', [
|
||||
'id' => $message->getId(),
|
||||
]);
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Service;
|
||||
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Address;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Db\LocalAttachment;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Exception\AttachmentNotFoundException;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\Exception\SmimeEncryptException;
|
||||
use OCA\Mail\Exception\SmimeSignException;
|
||||
use OCA\Mail\Service\Attachment\AttachmentService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class TransmissionService {
|
||||
|
||||
public function __construct(private GroupsIntegration $groupsIntegration,
|
||||
private AttachmentService $attachmentService,
|
||||
private LoggerInterface $logger,
|
||||
private SmimeService $smimeService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LocalMessage $message
|
||||
* @param int $type
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getAddressList(LocalMessage $message, int $type): AddressList {
|
||||
return new AddressList(
|
||||
array_map(
|
||||
static function ($recipient) use ($type) {
|
||||
return Address::fromRaw($recipient->getLabel() ?? $recipient->getEmail(), $recipient->getEmail());
|
||||
},
|
||||
$this->groupsIntegration->expand(
|
||||
array_filter($message->getRecipients(), static function (Recipient $recipient) use ($type) {
|
||||
return $recipient->getType() === $type;
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LocalMessage $message
|
||||
* @return array|array[]
|
||||
*/
|
||||
public function getAttachments(LocalMessage $message): array {
|
||||
if(empty($message->getAttachments())) {
|
||||
return [];
|
||||
}
|
||||
return array_map(static function (LocalAttachment $attachment) {
|
||||
// Convert to the untyped nested array used in \OCA\Mail\Controller\AccountsController::send
|
||||
return [
|
||||
'type' => 'local',
|
||||
'id' => $attachment->getId(),
|
||||
];
|
||||
}, $message->getAttachments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param array $attachment
|
||||
* @return \Horde_Mime_Part|null
|
||||
*/
|
||||
public function handleAttachment(Account $account, array $attachment): ?\Horde_Mime_Part {
|
||||
if (!isset($attachment['id'])) {
|
||||
$this->logger->warning('ignoring local attachment because its id is unknown');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
[$localAttachment, $file] = $this->attachmentService->getAttachment($account->getMailAccount()->getUserId(), (int)$attachment['id']);
|
||||
$part = new \Horde_Mime_Part();
|
||||
$part->setCharset('us-ascii');
|
||||
$part->setDisposition('attachment');
|
||||
$part->setName($localAttachment->getFileName());
|
||||
$part->setContents($file->getContent());
|
||||
$part->setType($localAttachment->getMimeType());
|
||||
return $part;
|
||||
} catch (AttachmentNotFoundException $e) {
|
||||
$this->logger->warning('Ignoring local attachment because it does not exist', ['exception' => $e]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LocalMessage $localMessage
|
||||
* @param Account $account
|
||||
* @param \Horde_Mime_Part $mimePart
|
||||
* @return \Horde_Mime_Part
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function getSignMimePart(LocalMessage $localMessage, Account $account, \Horde_Mime_Part $mimePart): \Horde_Mime_Part {
|
||||
if ($localMessage->getSmimeSign()) {
|
||||
if ($localMessage->getSmimeCertificateId() === null) {
|
||||
$localMessage->setStatus(LocalMessage::STATUS_SMIME_SIGN_NO_CERT_ID);
|
||||
throw new ServiceException('Could not send message: Requested S/MIME signature without certificate id');
|
||||
}
|
||||
|
||||
try {
|
||||
$certificate = $this->smimeService->findCertificate(
|
||||
$localMessage->getSmimeCertificateId(),
|
||||
$account->getUserId(),
|
||||
);
|
||||
$mimePart = $this->smimeService->signMimePart($mimePart, $certificate);
|
||||
} catch (DoesNotExistException $e) {
|
||||
$localMessage->setStatus(LocalMessage::STATUS_SMIME_SIGN_CERT);
|
||||
throw new ServiceException(
|
||||
'Could not send message: Certificate does not exist: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e,
|
||||
);
|
||||
} catch (SmimeSignException|ServiceException $e) {
|
||||
$localMessage->setStatus(LocalMessage::STATUS_SMIME_SIGN_FAIL);
|
||||
throw new ServiceException(
|
||||
'Could not send message: Failed to sign MIME part: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e,
|
||||
);
|
||||
}
|
||||
}
|
||||
return $mimePart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LocalMessage $localMessage
|
||||
* @param AddressList $to
|
||||
* @param AddressList $cc
|
||||
* @param AddressList $bcc
|
||||
* @param Account $account
|
||||
* @param \Horde_Mime_Part $mimePart
|
||||
* @return \Horde_Mime_Part
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function getEncryptMimePart(LocalMessage $localMessage, AddressList $to, AddressList $cc, AddressList $bcc, Account $account, \Horde_Mime_Part $mimePart): \Horde_Mime_Part {
|
||||
if ($localMessage->getSmimeEncrypt()) {
|
||||
if ($localMessage->getSmimeCertificateId() === null) {
|
||||
$localMessage->setStatus(LocalMessage::STATUS_SMIME_ENCRYPT_NO_CERT_ID);
|
||||
throw new ServiceException('Could not send message: Requested S/MIME signature without certificate id');
|
||||
}
|
||||
|
||||
try {
|
||||
$addressList = $to
|
||||
->merge($cc)
|
||||
->merge($bcc);
|
||||
$certificates = $this->smimeService->findCertificatesByAddressList($addressList, $account->getUserId());
|
||||
|
||||
$senderCertificate = $this->smimeService->findCertificate($localMessage->getSmimeCertificateId(), $account->getUserId());
|
||||
$certificates[] = $senderCertificate;
|
||||
|
||||
$mimePart = $this->smimeService->encryptMimePart($mimePart, $certificates);
|
||||
} catch (DoesNotExistException $e) {
|
||||
$localMessage->setStatus(LocalMessage::STATUS_SMIME_ENCRYPT_CERT);
|
||||
throw new ServiceException(
|
||||
'Could not send message: Certificate does not exist: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e,
|
||||
);
|
||||
} catch (SmimeEncryptException|ServiceException $e) {
|
||||
$localMessage->setStatus(LocalMessage::STATUS_SMIME_ENCRYT_FAIL);
|
||||
throw new ServiceException(
|
||||
'Could not send message: Failed to encrypt MIME part: ' . $e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e,
|
||||
);
|
||||
}
|
||||
}
|
||||
return $mimePart;
|
||||
}
|
||||
|
||||
}
|
|
@ -396,7 +396,9 @@ export default {
|
|||
|
||||
if (!data.sendAt || data.sendAt < Math.floor((now + UNDO_DELAY) / 1000)) {
|
||||
// Awaiting here would keep the modal open for a long time and thus block the user
|
||||
this.$store.dispatch('outbox/sendMessageWithUndo', { id: dataForServer.id })
|
||||
this.$store.dispatch('outbox/sendMessageWithUndo', { id: dataForServer.id }).catch((error) => {
|
||||
logger.debug('Could not send message', { error })
|
||||
})
|
||||
}
|
||||
if (dataForServer.id) {
|
||||
// Remove old draft envelope
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<ListItem class="outbox-message"
|
||||
<ListItem v-if="message.status !== statusImapSentMailboxFail()"
|
||||
class="outbox-message"
|
||||
:class="{ selected }"
|
||||
:name="title"
|
||||
:details="details"
|
||||
|
@ -50,6 +51,35 @@
|
|||
</ActionButton>
|
||||
</template>
|
||||
</ListItem>
|
||||
<ListItem v-else
|
||||
class="outbox-message"
|
||||
:name="title"
|
||||
:class="{ selected }"
|
||||
:details="details">
|
||||
<template #icon>
|
||||
<Avatar :display-name="avatarDisplayName" :email="avatarEmail" />
|
||||
</template>
|
||||
<template #subtitle>
|
||||
{{ subjectForSubtitle }}
|
||||
</template>
|
||||
<template slot="actions">
|
||||
<ActionButton :close-after-click="true"
|
||||
@click="sendMessageNow">
|
||||
{{ t('mail', 'Copy to "Sent" Mailbox') }}
|
||||
<template #icon>
|
||||
<Send :title="t('mail', 'Copy to Sent Mailbox')"
|
||||
:size="20" />
|
||||
</template>
|
||||
</ActionButton>
|
||||
<ActionButton :close-after-click="true"
|
||||
@click="deleteMessage">
|
||||
<template #icon>
|
||||
<IconDelete :size="20" />
|
||||
</template>
|
||||
{{ t('mail', 'Delete') }}
|
||||
</ActionButton>
|
||||
</template>
|
||||
</ListItem>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -64,7 +94,10 @@ import { showError, showSuccess } from '@nextcloud/dialogs'
|
|||
import { matchError } from '../errors/match.js'
|
||||
import { html, plain } from '../util/text.js'
|
||||
import Send from 'vue-material-design-icons/Send.vue'
|
||||
import { UNDO_DELAY } from '../store/constants.js'
|
||||
import {
|
||||
STATUS_IMAP_SENT_MAILBOX_FAIL,
|
||||
UNDO_DELAY,
|
||||
} from '../store/constants.js'
|
||||
|
||||
export default {
|
||||
name: 'OutboxMessageListItem',
|
||||
|
@ -97,7 +130,9 @@ export default {
|
|||
return formatter.format(recipients)
|
||||
},
|
||||
details() {
|
||||
if (this.message.failed) {
|
||||
if (this.message.status === 11) {
|
||||
return this.t('mail', 'Could not copy to "Sent" mailbox')
|
||||
} else if (this.message.status !== 0) {
|
||||
return this.t('mail', 'Message could not be sent')
|
||||
}
|
||||
if (!this.message.sendAt) {
|
||||
|
@ -117,6 +152,9 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
statusImapSentMailboxFail() {
|
||||
return STATUS_IMAP_SENT_MAILBOX_FAIL
|
||||
},
|
||||
async deleteMessage() {
|
||||
try {
|
||||
await this.$store.dispatch('outbox/deleteMessage', {
|
||||
|
@ -139,9 +177,23 @@ export default {
|
|||
sendAt: (new Date().getTime() + UNDO_DELAY) / 1000,
|
||||
}
|
||||
await this.$store.dispatch('outbox/updateMessage', { message, id: message.id })
|
||||
await this.$store.dispatch('outbox/sendMessageWithUndo', { id: message.id })
|
||||
try {
|
||||
if (this.message.status !== STATUS_IMAP_SENT_MAILBOX_FAIL) {
|
||||
await this.$store.dispatch('outbox/sendMessageWithUndo', { id: message.id })
|
||||
} else {
|
||||
await this.$store.dispatch('outbox/copyMessageToSentMailbox', { id: message.id })
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Could not send or copy message', { error })
|
||||
if (error.data !== undefined) {
|
||||
await this.$store.dispatch('outbox/updateMessage', { message: error.data[0], id: message.id })
|
||||
}
|
||||
}
|
||||
},
|
||||
async openModal() {
|
||||
if (this.message.status === 11) {
|
||||
return
|
||||
}
|
||||
await this.$store.dispatch('startComposerSession', {
|
||||
type: 'outbox',
|
||||
data: {
|
||||
|
|
|
@ -28,3 +28,5 @@ export const PAGE_SIZE = 20
|
|||
export const UNDO_DELAY = TOAST_UNDO_TIMEOUT
|
||||
export const EDITOR_MODE_HTML = 'richtext'
|
||||
export const EDITOR_MODE_TEXT = 'plaintext'
|
||||
|
||||
export const STATUS_IMAP_SENT_MAILBOX_FAIL = 11
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
import * as OutboxService from '../../service/OutboxService.js'
|
||||
import logger from '../../logger.js'
|
||||
import { showError, showUndo } from '@nextcloud/dialogs'
|
||||
import { showError, showSuccess, showUndo } from '@nextcloud/dialogs'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { html, plain } from '../../util/text.js'
|
||||
import { UNDO_DELAY } from '../constants.js'
|
||||
|
@ -131,6 +131,8 @@ export default {
|
|||
await OutboxService.sendMessage(id)
|
||||
logger.debug(`Outbox message ${id} sent`)
|
||||
} catch (error) {
|
||||
const m = error.response.data.data[0]
|
||||
commit('updateMessage', { message: m })
|
||||
logger.error(`Failed to send message ${id} from outbox`, { error })
|
||||
throw error
|
||||
}
|
||||
|
@ -185,4 +187,27 @@ export default {
|
|||
}, UNDO_DELAY)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* "Send" a message
|
||||
* The backend chain will handle the actual copying
|
||||
* We need different toast texts and can do this without UNDO.
|
||||
*
|
||||
* @param {object} store Vuex destructuring object
|
||||
* @param {Function} store.dispatch Vuex dispatch object
|
||||
* @param {object} store.getters Vuex getters object
|
||||
* @param {object} data Action data
|
||||
* @param {number} data.id Id of outbox message to send
|
||||
*/
|
||||
async copyMessageToSentMailbox({ getters, dispatch }, { id }) {
|
||||
const message = getters.getMessage(id)
|
||||
|
||||
try {
|
||||
await dispatch('sendMessage', { id: message.id })
|
||||
showSuccess(t('mail', 'Message copied to "Sent" mailbox'))
|
||||
} catch (error) {
|
||||
showError(t('mail', 'Could not copy message to "Sent" mailbox'))
|
||||
logger.error('Could not copy message to "Sent" mailbox ' + message.id, { message })
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -39,7 +39,11 @@ export default {
|
|||
Vue.delete(message, 'sendAt')
|
||||
},
|
||||
updateMessage(state, { message }) {
|
||||
const existing = state.messages[message.id]
|
||||
Vue.set(state.messages, message.id, Object.assign({}, existing, message))
|
||||
const existing = state.messages[message.id] ?? {}
|
||||
Vue.set(state.messages, message.id, Object.assign(existing, message))
|
||||
// Add the message only if it's new
|
||||
if (state.messageList.indexOf(message.id) === -1) {
|
||||
state.messageList.unshift(message.id)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ class LocalAttachmentMapperTest extends TestCase {
|
|||
/** @var string */
|
||||
private $user2 = 'dontFindMe';
|
||||
private array $localMessageIds;
|
||||
private array $attachmentIds;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
|
|
@ -27,24 +27,29 @@ use ChristophWurst\Nextcloud\Testing\TestUser;
|
|||
use OC;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Contracts\IAttachmentService;
|
||||
use OCA\Mail\Contracts\IMailManager;
|
||||
use OCA\Mail\Contracts\IMailTransmission;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\LocalMessageMapper;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\MailAccountMapper;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Db\RecipientMapper;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MailboxSync;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\Model\RepliedMessageData;
|
||||
use OCA\Mail\Send\AntiAbuseHandler;
|
||||
use OCA\Mail\Send\Chain;
|
||||
use OCA\Mail\Send\CopySentMessageHandler;
|
||||
use OCA\Mail\Send\FlagRepliedMessageHandler;
|
||||
use OCA\Mail\Send\SendHandler;
|
||||
use OCA\Mail\Send\SentMailboxHandler;
|
||||
use OCA\Mail\Service\AliasesService;
|
||||
use OCA\Mail\Service\Attachment\UploadedFile;
|
||||
use OCA\Mail\Service\GroupsIntegration;
|
||||
use OCA\Mail\Service\MailTransmission;
|
||||
use OCA\Mail\Service\SmimeService;
|
||||
use OCA\Mail\Service\TransmissionService;
|
||||
use OCA\Mail\SMTP\SmtpClientFactory;
|
||||
use OCA\Mail\Support\PerformanceLogger;
|
||||
use OCA\Mail\Tests\Integration\Framework\ImapTest;
|
||||
|
@ -71,6 +76,10 @@ class MailTransmissionIntegrationTest extends TestCase {
|
|||
|
||||
/** @var IMailTransmission */
|
||||
private $transmission;
|
||||
private Chain $chain;
|
||||
|
||||
private LocalMessageMapper $localMessageMapper;
|
||||
private LocalMessage $message;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
@ -104,16 +113,38 @@ class MailTransmissionIntegrationTest extends TestCase {
|
|||
$this->attachmentService = Server::get(IAttachmentService::class);
|
||||
$userFolder = OC::$server->getUserFolder($this->user->getUID());
|
||||
|
||||
$recipientMapper = Server::get(RecipientMapper::class);
|
||||
$recipient = new Recipient();
|
||||
$recipient->setType(Recipient::TYPE_TO);
|
||||
$recipient->setEmail('recipient@domain.com');
|
||||
$recipientMapper->insert($recipient);
|
||||
|
||||
$this->localMessageMapper = Server::get(LocalMessageMapper::class);
|
||||
$this->message = new LocalMessage();
|
||||
$this->message->setAccountId($this->account->getId());
|
||||
$this->message->setSubject('greetings');
|
||||
$this->message->setBody('hello there');
|
||||
$this->message->setType(LocalMessage::TYPE_OUTGOING);
|
||||
$this->message->setHtml(false);
|
||||
$this->message->setRecipients([$recipient]);
|
||||
$this->message->setStatus(LocalMessage::STATUS_RAW);
|
||||
$this->localMessageMapper->insert($this->message);
|
||||
// Make sure the mailbox preferences are set
|
||||
/** @var MailboxSync $mbSync */
|
||||
$mbSync = Server::get(MailboxSync::class);
|
||||
$mbSync->sync($this->account, new NullLogger(), true);
|
||||
|
||||
$this->transmission = new MailTransmission(
|
||||
$userFolder,
|
||||
$this->chain = new Chain(
|
||||
Server::get(SentMailboxHandler::class),
|
||||
Server::get(AntiAbuseHandler::class),
|
||||
Server::get(SendHandler::class),
|
||||
Server::get(CopySentMessageHandler::class),
|
||||
Server::get(FlagRepliedMessageHandler::class),
|
||||
$this->attachmentService,
|
||||
Server::get(IMailManager::class),
|
||||
Server::get(IMAPClientFactory::class),
|
||||
$this->localMessageMapper,
|
||||
);
|
||||
|
||||
$this->transmission = new MailTransmission(Server::get(IMAPClientFactory::class),
|
||||
Server::get(SmtpClientFactory::class),
|
||||
Server::get(IEventDispatcher::class),
|
||||
Server::get(MailboxMapper::class),
|
||||
|
@ -121,15 +152,12 @@ class MailTransmissionIntegrationTest extends TestCase {
|
|||
Server::get(LoggerInterface::class),
|
||||
Server::get(PerformanceLogger::class),
|
||||
Server::get(AliasesService::class),
|
||||
Server::get(GroupsIntegration::class),
|
||||
Server::get(SmimeService::class),
|
||||
Server::get(TransmissionService::class),
|
||||
);
|
||||
}
|
||||
|
||||
public function testSendMail() {
|
||||
$message = NewMessageData::fromRequest($this->account, 'recipient@domain.com', null, null, 'greetings', 'hello there', []);
|
||||
|
||||
$this->transmission->sendMessage($message, null);
|
||||
$this->chain->process($this->account, $this->message);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
@ -139,30 +167,12 @@ class MailTransmissionIntegrationTest extends TestCase {
|
|||
'name' => 'text.txt',
|
||||
'tmp_name' => dirname(__FILE__) . '/../../data/mail-message-123.txt',
|
||||
]);
|
||||
$this->attachmentService->addFile($this->user->getUID(), $file);
|
||||
$message = NewMessageData::fromRequest($this->account, 'recipient@domain.com', null, null, 'greetings', 'hello there', [
|
||||
[
|
||||
'type' => 'local',
|
||||
'id' => 13,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->transmission->sendMessage($message, null);
|
||||
$localAttachment = $this->attachmentService->addFile($this->user->getUID(), $file);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
$this->message->setAttachments([$localAttachment]);
|
||||
|
||||
public function testSendMailWithCloudAttachment() {
|
||||
$userFolder = OC::$server->getUserFolder($this->user->getUID());
|
||||
$userFolder->newFile('text.txt');
|
||||
$message = NewMessageData::fromRequest($this->account, 'recipient@domain.com', null, null, 'greetings', 'hello there', [
|
||||
[
|
||||
'type' => 'Files',
|
||||
'fileName' => 'text.txt',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->transmission->sendMessage($message, null);
|
||||
$this->chain->process($this->account, $this->message);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
@ -184,9 +194,9 @@ class MailTransmissionIntegrationTest extends TestCase {
|
|||
$messageInReply->setUid($originalUID);
|
||||
$messageInReply->setMessageId('message@server');
|
||||
$messageInReply->setMailboxId($inbox->getId());
|
||||
$this->message->setInReplyToMessageId($messageInReply->getInReplyTo());
|
||||
|
||||
$message = NewMessageData::fromRequest($this->account, 'recipient@domain.com', null, null, 'greetings', 'hello there', []);
|
||||
$this->transmission->sendMessage($message, $messageInReply->getMessageId());
|
||||
$this->chain->process($this->account, $this->message);
|
||||
|
||||
$this->assertMailboxExists('Sent');
|
||||
$this->assertMessageCount(1, 'Sent');
|
||||
|
@ -209,10 +219,10 @@ class MailTransmissionIntegrationTest extends TestCase {
|
|||
$messageInReply->setUid($originalUID);
|
||||
$messageInReply->setMessageId('message@server');
|
||||
$messageInReply->setMailboxId($inbox->getId());
|
||||
$this->message->setSubject('');
|
||||
$this->message->setInReplyToMessageId($messageInReply->getInReplyTo());
|
||||
|
||||
$message = NewMessageData::fromRequest($this->account, 'recipient@domain.com', null, null, '', 'hello there', []);
|
||||
$reply = new RepliedMessageData($this->account, $messageInReply);
|
||||
$this->transmission->sendMessage($message, $messageInReply->getMessageId());
|
||||
$this->chain->process($this->account, $this->message);
|
||||
|
||||
$this->assertMailboxExists('Sent');
|
||||
$this->assertMessageCount(1, 'Sent');
|
||||
|
@ -235,10 +245,10 @@ class MailTransmissionIntegrationTest extends TestCase {
|
|||
$messageInReply->setUid($originalUID);
|
||||
$messageInReply->setMessageId('message@server');
|
||||
$messageInReply->setMailboxId($inbox->getId());
|
||||
$this->message->setSubject('Re: reply test');
|
||||
$this->message->setInReplyToMessageId($messageInReply->getInReplyTo());
|
||||
|
||||
$message = NewMessageData::fromRequest($this->account, 'recipient@domain.com', null, null, 'Re: reply test', 'hello there', []);
|
||||
$reply = new RepliedMessageData($this->account, $messageInReply);
|
||||
$this->transmission->sendMessage($message, $messageInReply->getMessageId());
|
||||
$this->chain->process($this->account, $this->message);
|
||||
|
||||
$this->assertMailboxExists('Sent');
|
||||
$this->assertMessageCount(1, 'Sent');
|
||||
|
@ -265,23 +275,4 @@ class MailTransmissionIntegrationTest extends TestCase {
|
|||
|
||||
$this->assertMessageCount(1, 'Drafts');
|
||||
}
|
||||
|
||||
public function testSendLocalMessage(): void {
|
||||
$localMessage = new LocalMessage();
|
||||
$to = new Recipient();
|
||||
$to->setLabel('Penny');
|
||||
$to->setEmail('library@stardewvalley.edu');
|
||||
$to->setType(Recipient::TYPE_TO);
|
||||
$localMessage->setType(LocalMessage::TYPE_OUTGOING);
|
||||
$localMessage->setSubject('hello');
|
||||
$localMessage->setBody('This is a test');
|
||||
$localMessage->setHtml(false);
|
||||
$localMessage->setRecipients([$to]);
|
||||
$localMessage->setAttachments([]);
|
||||
|
||||
$this->transmission->sendLocalMessage($this->account, $localMessage);
|
||||
|
||||
$this->assertMailboxExists('Sent');
|
||||
$this->assertMessageCount(1, 'Sent');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ use OCA\Mail\Db\MailAccount;
|
|||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Db\MessageMapper;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\Send\Chain;
|
||||
use OCA\Mail\Service\AccountService;
|
||||
use OCA\Mail\Service\Attachment\AttachmentService;
|
||||
use OCA\Mail\Service\Attachment\AttachmentStorage;
|
||||
|
@ -93,6 +94,8 @@ class OutboxServiceIntegrationTest extends TestCase {
|
|||
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
private \PHPUnit\Framework\MockObject\MockObject|Chain $chain;
|
||||
private \OCP\IDBConnection $db;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
@ -121,14 +124,14 @@ class OutboxServiceIntegrationTest extends TestCase {
|
|||
$this->clientFactory = Server::get(IMAPClientFactory::class);
|
||||
$this->accountService = Server::get(AccountService::class);
|
||||
$this->timeFactory = Server::get(ITimeFactory::class);
|
||||
$this->chain = Server::get(Chain::class);
|
||||
|
||||
$this->db = OC::$server->getDatabaseConnection();
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$delete = $qb->delete($this->mapper->getTableName());
|
||||
$delete->execute();
|
||||
|
||||
$this->outbox = new OutboxService(
|
||||
$this->transmission,
|
||||
$this->outbox = new OutboxService($this->transmission,
|
||||
$this->mapper,
|
||||
$this->attachmentService,
|
||||
$this->eventDispatcher,
|
||||
|
@ -136,7 +139,8 @@ class OutboxServiceIntegrationTest extends TestCase {
|
|||
$mailManager,
|
||||
$this->accountService,
|
||||
$this->timeFactory,
|
||||
$this->createMock(LoggerInterface::class)
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->chain
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -357,7 +361,7 @@ class OutboxServiceIntegrationTest extends TestCase {
|
|||
$this->assertCount(1, $saved->getRecipients());
|
||||
$this->assertEmpty($message->getAttachments());
|
||||
|
||||
$this->outbox->sendMessage($saved, new Account($this->account));
|
||||
$actual = $this->outbox->sendMessage($saved, new Account($this->account));
|
||||
|
||||
$this->expectException(DoesNotExistException::class);
|
||||
$this->outbox->getMessage($message->getId(), $this->user->getUID());
|
||||
|
|
|
@ -144,6 +144,10 @@ class OutboxControllerTest extends TestCase {
|
|||
$message = new LocalMessage();
|
||||
$message->setId(1);
|
||||
$message->setAccountId(1);
|
||||
$newMessage = new LocalMessage();
|
||||
$newMessage->setId(1);
|
||||
$newMessage->setAccountId(1);
|
||||
$newMessage->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
$account = new Account(new MailAccount());
|
||||
|
||||
$this->service->expects(self::once())
|
||||
|
@ -156,7 +160,8 @@ class OutboxControllerTest extends TestCase {
|
|||
->willReturn($account);
|
||||
$this->service->expects(self::once())
|
||||
->method('sendMessage')
|
||||
->with($message, $account);
|
||||
->with($message, $account)
|
||||
->willReturn($newMessage);
|
||||
|
||||
$expected = JsonResponse::success('Message sent', Http::STATUS_ACCEPTED);
|
||||
$actual = $this->controller->send($message->getId());
|
||||
|
|
|
@ -26,17 +26,17 @@ declare(strict_types=1);
|
|||
namespace OCA\Mail\Tests\Unit\Listener;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use Horde_Mime_Mail;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Address;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Contracts\IUserPreferences;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Events\MessageSentEvent;
|
||||
use OCA\Mail\Listener\AddressCollectionListener;
|
||||
use OCA\Mail\Model\IMessage;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\Model\RepliedMessageData;
|
||||
use OCA\Mail\Service\AutoCompletion\AddressCollector;
|
||||
use OCA\Mail\Service\TransmissionService;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
@ -55,17 +55,21 @@ class AddressCollectionListenerTest extends TestCase {
|
|||
/** @var IEventListener */
|
||||
private $listener;
|
||||
|
||||
private MockObject|TransmissionService $transmission;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->preferences = $this->createMock(IUserPreferences::class);
|
||||
$this->addressCollector = $this->createMock(AddressCollector::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->transmission = $this->createMock(TransmissionService::class);
|
||||
|
||||
$this->listener = new AddressCollectionListener(
|
||||
$this->preferences,
|
||||
$this->addressCollector,
|
||||
$this->logger
|
||||
$this->logger,
|
||||
$this->transmission,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -102,44 +106,47 @@ class AddressCollectionListenerTest extends TestCase {
|
|||
$account = $this->createConfiguredMock(Account::class, [
|
||||
'getUserId' => 'test'
|
||||
]);
|
||||
/** @var NewMessageData|MockObject $newMessageData */
|
||||
$newMessageData = $this->createMock(NewMessageData::class);
|
||||
/** @var RepliedMessageData|MockObject $repliedMessageData */
|
||||
$repliedMessageData = $this->createMock(RepliedMessageData::class);
|
||||
/** @var IMessage|MockObject $message */
|
||||
$message = $this->createMock(IMessage::class);
|
||||
$message = $this->createMock(LocalMessage::class);
|
||||
$message->setRecipients([
|
||||
Recipient::fromParams([
|
||||
'email' => 'to@email',
|
||||
'type' => Recipient::TYPE_TO,
|
||||
]),
|
||||
Recipient::fromParams([
|
||||
'email' => 'cc@email',
|
||||
'type' => Recipient::TYPE_CC,
|
||||
]),
|
||||
Recipient::fromParams([
|
||||
'email' => 'bcc@email',
|
||||
'type' => Recipient::TYPE_BCC,
|
||||
])
|
||||
]);
|
||||
$event = new MessageSentEvent(
|
||||
$account,
|
||||
new LocalMessage(),
|
||||
);
|
||||
$to = new AddressList([Address::fromRaw('to', 'to@email')]);
|
||||
$cc = new AddressList([Address::fromRaw('cc', 'cc@email')]);
|
||||
$bcc = new AddressList([Address::fromRaw('bcc', 'bcc@email')]);
|
||||
$addresses = $to->merge($cc)->merge($bcc);
|
||||
|
||||
$this->preferences->expects($this->once())
|
||||
->method('getPreference')
|
||||
->with('test', 'collect-data', 'true')
|
||||
->willReturn('true');
|
||||
/** @var Horde_Mime_Mail|MockObject $mail */
|
||||
$mail = $this->createMock(Horde_Mime_Mail::class);
|
||||
$event = new MessageSentEvent(
|
||||
$account,
|
||||
$newMessageData,
|
||||
'abc123',
|
||||
null,
|
||||
$message,
|
||||
$mail
|
||||
);
|
||||
$message->expects($this->once())
|
||||
->method('getTo')
|
||||
->willReturn(new AddressList([Address::fromRaw('to', 'to@email')]));
|
||||
$message->expects($this->once())
|
||||
->method('getCC')
|
||||
->willReturn(new AddressList([Address::fromRaw('cc', 'cc@email')]));
|
||||
$message->expects($this->once())
|
||||
->method('getBCC')
|
||||
->willReturn(new AddressList([Address::fromRaw('bcc', 'bcc@email')]));
|
||||
$this->transmission->expects($this->exactly(3))
|
||||
->method('getAddressList')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
$to,
|
||||
$cc,
|
||||
$bcc,
|
||||
);
|
||||
$this->addressCollector->expects($this->once())
|
||||
->method('addAddresses')
|
||||
->with(
|
||||
'test',
|
||||
$this->equalTo(new AddressList([
|
||||
Address::fromRaw('to', 'to@email'),
|
||||
Address::fromRaw('cc', 'cc@email'),
|
||||
Address::fromRaw('bcc', 'bcc@email'),
|
||||
]))
|
||||
$account->getUserId(),
|
||||
$this->equalTo($addresses)
|
||||
);
|
||||
$this->logger->expects($this->never())->method($this->anything());
|
||||
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @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\Test\Listener;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Db\MessageMapper as DbMessageMapper;
|
||||
use OCA\Mail\Events\MessageSentEvent;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCA\Mail\Listener\FlagRepliedMessageListener;
|
||||
use OCA\Mail\Model\IMessage;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\Model\RepliedMessageData;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class FlagRepliedMessageListenerTest extends TestCase {
|
||||
/** @var IMAPClientFactory|MockObject */
|
||||
private $imapClientFactory;
|
||||
|
||||
/** @var MailboxMapper|MockObject */
|
||||
private $mailboxMapper;
|
||||
|
||||
/** @var MessageMapper|MockObject */
|
||||
private $messageMapper;
|
||||
|
||||
/** @var LoggerInterface|MockObject */
|
||||
private $logger;
|
||||
|
||||
/** @var FlagRepliedMessageListener */
|
||||
private $listener;
|
||||
|
||||
/** @var DbMessageMapper|MockObject */
|
||||
private $dbMessageMapper;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->imapClientFactory = $this->createMock(IMAPClientFactory::class);
|
||||
$this->mailboxMapper = $this->createMock(MailboxMapper::class);
|
||||
$this->dbMessageMapper = $this->createMock(DbMessageMapper::class);
|
||||
$this->messageMapper = $this->createMock(MessageMapper::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$this->listener = new FlagRepliedMessageListener(
|
||||
$this->imapClientFactory,
|
||||
$this->mailboxMapper,
|
||||
$this->dbMessageMapper,
|
||||
$this->messageMapper,
|
||||
$this->logger
|
||||
);
|
||||
}
|
||||
|
||||
public function testHandleUnrelated(): void {
|
||||
$event = new Event();
|
||||
|
||||
$this->listener->handle($event);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testHandleMessageSentEventMailboxNotAReply(): void {
|
||||
/** @var Account|MockObject $account */
|
||||
$account = $this->createMock(Account::class);
|
||||
/** @var NewMessageData|MockObject $newMessageData */
|
||||
$newMessageData = $this->createMock(NewMessageData::class);
|
||||
/** @var IMessage|MockObject $message */
|
||||
$message = $this->createMock(IMessage::class);
|
||||
/** @var \Horde_Mime_Mail|MockObject $mail */
|
||||
$mail = $this->createMock(\Horde_Mime_Mail::class);
|
||||
$draft = new Message();
|
||||
$draft->setUid(123);
|
||||
$event = new MessageSentEvent(
|
||||
$account,
|
||||
$newMessageData,
|
||||
null,
|
||||
$draft,
|
||||
$message,
|
||||
$mail
|
||||
);
|
||||
$this->dbMessageMapper->expects($this->never())
|
||||
->method('findByMessageId');
|
||||
$this->mailboxMapper->expects($this->never())
|
||||
->method('find');
|
||||
$this->logger->expects($this->never())
|
||||
->method('warning');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testHandleMessageSentEvent(): void {
|
||||
/** @var Account|MockObject $account */
|
||||
$account = $this->createMock(Account::class);
|
||||
/** @var NewMessageData|MockObject $newMessageData */
|
||||
$newMessageData = $this->createMock(NewMessageData::class);
|
||||
/** @var RepliedMessageData|MockObject $repliedMessageData */
|
||||
$repliedMessageData = $this->createMock(RepliedMessageData::class);
|
||||
/** @var IMessage|MockObject $message */
|
||||
$message = $this->createMock(IMessage::class);
|
||||
/** @var \Horde_Mime_Mail|MockObject $mail */
|
||||
$mail = $this->createMock(\Horde_Mime_Mail::class);
|
||||
$draft = new Message();
|
||||
$draft->setUid(123);
|
||||
$event = new MessageSentEvent(
|
||||
$account,
|
||||
$newMessageData,
|
||||
'<abc123@123.com>',
|
||||
$draft,
|
||||
$message,
|
||||
$mail
|
||||
);
|
||||
$messageInReply = new Message();
|
||||
$messageInReply->setUid(321);
|
||||
$messageInReply->setMailboxId(654);
|
||||
$messageInReply->setMessageId('abc123@123.com');
|
||||
$repliedMessageData->method('getMessage')
|
||||
->willReturn($messageInReply);
|
||||
$this->dbMessageMapper->expects($this->once())
|
||||
->method('findByMessageId')
|
||||
->with(
|
||||
$event->getAccount(),
|
||||
'<abc123@123.com>'
|
||||
)->willReturn([$messageInReply]);
|
||||
$mailbox = new Mailbox();
|
||||
$this->mailboxMapper->expects($this->once())
|
||||
->method('findById')
|
||||
->with(654)
|
||||
->willReturn($mailbox);
|
||||
$client = $this->createMock(\Horde_Imap_Client_Socket::class);
|
||||
$this->imapClientFactory->expects($this->once())
|
||||
->method('getClient')
|
||||
->with($account)
|
||||
->willReturn($client);
|
||||
$this->messageMapper->expects($this->once())
|
||||
->method('addFlag')
|
||||
->with(
|
||||
$client,
|
||||
$mailbox,
|
||||
[321],
|
||||
\Horde_Imap_Client::FLAG_ANSWERED
|
||||
);
|
||||
$this->logger->expects($this->never())
|
||||
->method('warning');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
}
|
|
@ -27,11 +27,11 @@ namespace OCA\Mail\Tests\Unit\Listener;
|
|||
|
||||
use ChristophWurst\Nextcloud\Testing\ServiceMockObject;
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Mail\Address;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Events\MessageSentEvent;
|
||||
use OCA\Mail\Listener\InteractionListener;
|
||||
use OCA\Mail\Model\IMessage;
|
||||
use OCP\Contacts\Events\ContactInteractedWithEvent;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\IUser;
|
||||
|
@ -66,31 +66,38 @@ class InteractionListenerTest extends TestCase {
|
|||
return;
|
||||
}
|
||||
|
||||
$to = new AddressList([
|
||||
Address::fromRaw('rec 1', 'u1@domain.tld'),
|
||||
Address::fromRaw('rec 1', 'u2@domain.tld'),
|
||||
$message = new LocalMessage();
|
||||
$message->setRecipients([
|
||||
Recipient::fromParams([
|
||||
'label' => 'rec 1',
|
||||
'email' => 'u1@domain.tld',
|
||||
'type' => Recipient::TYPE_TO,
|
||||
]),
|
||||
Recipient::fromParams([
|
||||
'label' => 'rec 1',
|
||||
'email' => 'u2@domain.tld',
|
||||
'type' => Recipient::TYPE_TO,
|
||||
]),
|
||||
Recipient::fromParams([
|
||||
'label' => 'rec 1',
|
||||
'email' => 'u3@domain.tld',
|
||||
'type' => Recipient::TYPE_CC,
|
||||
]),
|
||||
Recipient::fromParams([
|
||||
'label' => 'rec 1',
|
||||
'email' => 'u4@domain.tld',
|
||||
'type' => Recipient::TYPE_BCC,
|
||||
]),
|
||||
Recipient::fromParams([
|
||||
'label' => 'rec 1',
|
||||
'email' => 'u2@domain.tld',
|
||||
'type' => Recipient::TYPE_CC,
|
||||
]),
|
||||
]);
|
||||
$cc = new AddressList([
|
||||
Address::fromRaw('rec 1', 'u3@domain.tld'),
|
||||
]);
|
||||
$bcc = new AddressList([
|
||||
Address::fromRaw('rec 1', 'u4@domain.tld'),
|
||||
Address::fromRaw('rec 1', 'u2@domain.tld'), // intentional duplicate
|
||||
]);
|
||||
$event = $this->createMock(MessageSentEvent::class);
|
||||
$message = $this->createMock(IMessage::class);
|
||||
$event
|
||||
->method('getMessage')
|
||||
->willReturn($message);
|
||||
$message
|
||||
->method('getTo')
|
||||
->willReturn($to);
|
||||
$message
|
||||
->method('getCC')
|
||||
->willReturn($cc);
|
||||
$message
|
||||
->method('getBCC')
|
||||
->willReturn($bcc);
|
||||
$event = new MessageSentEvent(
|
||||
$this->createMock(Account::class),
|
||||
$message,
|
||||
);
|
||||
$user = $this->createMock(IUser::class);
|
||||
$this->serviceMock->getParameter('userSession')
|
||||
->method('getUser')
|
||||
|
|
|
@ -1,230 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @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\Tests\Unit\Listener;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use Horde_Imap_Client_Exception;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Events\MessageSentEvent;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCA\Mail\Listener\SaveSentMessageListener;
|
||||
use OCA\Mail\Model\IMessage;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class SaveSentMessageListenerTest extends TestCase {
|
||||
/** @var MailboxMapper|MockObject */
|
||||
private $mailboxMapper;
|
||||
|
||||
/** @var IMAPClientFactory|MockObject */
|
||||
private $imapClientFactory;
|
||||
|
||||
/** @var MessageMapper|MockObject */
|
||||
private $messageMapper;
|
||||
|
||||
/** @var LoggerInterface|MockObject */
|
||||
private $logger;
|
||||
|
||||
/** @var IEventListener */
|
||||
private $listener;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->mailboxMapper = $this->createMock(MailboxMapper::class);
|
||||
$this->imapClientFactory = $this->createMock(IMAPClientFactory::class);
|
||||
$this->messageMapper = $this->createMock(MessageMapper::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$this->listener = new SaveSentMessageListener(
|
||||
$this->mailboxMapper,
|
||||
$this->imapClientFactory,
|
||||
$this->messageMapper,
|
||||
$this->logger
|
||||
);
|
||||
}
|
||||
|
||||
public function testHandleUnrelated(): void {
|
||||
$event = new Event();
|
||||
|
||||
$this->listener->handle($event);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testHandleMessageSentMailboxNotSet(): void {
|
||||
/** @var Account|MockObject $account */
|
||||
$account = $this->createMock(Account::class);
|
||||
$mailAccount = new MailAccount();
|
||||
$account->method('getMailAccount')->willReturn($mailAccount);
|
||||
/** @var NewMessageData|MockObject $newMessageData */
|
||||
$newMessageData = $this->createMock(NewMessageData::class);
|
||||
/** @var IMessage|MockObject $message */
|
||||
$message = $this->createMock(IMessage::class);
|
||||
/** @var \Horde_Mime_Mail|MockObject $mail */
|
||||
$mail = $this->createMock(\Horde_Mime_Mail::class);
|
||||
$draft = new Message();
|
||||
$draft->setUid(123);
|
||||
$event = new MessageSentEvent(
|
||||
$account,
|
||||
$newMessageData,
|
||||
'abc123',
|
||||
$draft,
|
||||
$message,
|
||||
$mail
|
||||
);
|
||||
$this->mailboxMapper->expects($this->never())
|
||||
->method('findById');
|
||||
$this->logger->expects($this->once())
|
||||
->method('warning');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testHandleMessageSentMailboxDoesNotExist(): void {
|
||||
/** @var Account|MockObject $account */
|
||||
$account = $this->createMock(Account::class);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(123);
|
||||
$account->method('getMailAccount')->willReturn($mailAccount);
|
||||
/** @var NewMessageData|MockObject $newMessageData */
|
||||
$newMessageData = $this->createMock(NewMessageData::class);
|
||||
/** @var IMessage|MockObject $message */
|
||||
$message = $this->createMock(IMessage::class);
|
||||
/** @var \Horde_Mime_Mail|MockObject $mail */
|
||||
$mail = $this->createMock(\Horde_Mime_Mail::class);
|
||||
$draft = new Message();
|
||||
$draft->setUid(123);
|
||||
$event = new MessageSentEvent(
|
||||
$account,
|
||||
$newMessageData,
|
||||
'abc123',
|
||||
$draft,
|
||||
$message,
|
||||
$mail
|
||||
);
|
||||
$this->mailboxMapper->expects($this->once())
|
||||
->method('findById')
|
||||
->with(123)
|
||||
->willThrowException(new DoesNotExistException(''));
|
||||
$this->messageMapper->expects($this->never())
|
||||
->method('save');
|
||||
$this->logger->expects($this->once())
|
||||
->method('error');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testHandleMessageSentSavingError(): void {
|
||||
/** @var Account|MockObject $account */
|
||||
$account = $this->createMock(Account::class);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(123);
|
||||
$account->method('getMailAccount')->willReturn($mailAccount);
|
||||
/** @var NewMessageData|MockObject $newMessageData */
|
||||
$newMessageData = $this->createMock(NewMessageData::class);
|
||||
/** @var IMessage|MockObject $message */
|
||||
$message = $this->createMock(IMessage::class);
|
||||
/** @var \Horde_Mime_Mail|MockObject $mail */
|
||||
$mail = $this->createMock(\Horde_Mime_Mail::class);
|
||||
$draft = new Message();
|
||||
$draft->setUid(123);
|
||||
$event = new MessageSentEvent(
|
||||
$account,
|
||||
$newMessageData,
|
||||
'abc123',
|
||||
$draft,
|
||||
$message,
|
||||
$mail
|
||||
);
|
||||
$mailbox = new Mailbox();
|
||||
$this->mailboxMapper->expects($this->once())
|
||||
->method('findById')
|
||||
->with(123)
|
||||
->willReturn($mailbox);
|
||||
$this->messageMapper->expects($this->once())
|
||||
->method('save')
|
||||
->with(
|
||||
$this->anything(),
|
||||
$mailbox,
|
||||
$mail
|
||||
)
|
||||
->willThrowException(new Horde_Imap_Client_Exception('', 0));
|
||||
$this->expectException(ServiceException::class);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testHandleMessageSent(): void {
|
||||
/** @var Account|MockObject $account */
|
||||
$account = $this->createMock(Account::class);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(123);
|
||||
$account->method('getMailAccount')->willReturn($mailAccount);
|
||||
/** @var NewMessageData|MockObject $newMessageData */
|
||||
$newMessageData = $this->createMock(NewMessageData::class);
|
||||
/** @var IMessage|MockObject $message */
|
||||
$message = $this->createMock(IMessage::class);
|
||||
/** @var \Horde_Mime_Mail|MockObject $mail */
|
||||
$mail = $this->createMock(\Horde_Mime_Mail::class);
|
||||
$draft = new Message();
|
||||
$draft->setUid(123);
|
||||
$event = new MessageSentEvent(
|
||||
$account,
|
||||
$newMessageData,
|
||||
'abc123',
|
||||
$draft,
|
||||
$message,
|
||||
$mail
|
||||
);
|
||||
$mailbox = new Mailbox();
|
||||
$this->mailboxMapper->expects($this->once())
|
||||
->method('findById')
|
||||
->with(123)
|
||||
->willReturn($mailbox);
|
||||
$this->messageMapper->expects($this->once())
|
||||
->method('save')
|
||||
->with(
|
||||
$this->anything(),
|
||||
$mailbox,
|
||||
$mail
|
||||
);
|
||||
$this->logger->expects($this->never())->method('warning');
|
||||
$this->logger->expects($this->never())->method('error');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Unit\Send;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Send\AntiAbuseHandler;
|
||||
use OCA\Mail\Send\SendHandler;
|
||||
use OCA\Mail\Service\AntiAbuseService;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class AntiAbuseHandlerTest extends TestCase {
|
||||
private IUserManager|MockObject $userManager;
|
||||
private MockObject|AntiAbuseService $antiAbuseService;
|
||||
private LoggerInterface|MockObject $logger;
|
||||
private SendHandler|MockObject $sendHandler;
|
||||
private AntiAbuseHandler $handler;
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->antiAbuseService = $this->createMock(AntiAbuseService::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->sendHandler = $this->createMock(SendHandler::class);
|
||||
$this->handler = new AntiAbuseHandler(
|
||||
$this->userManager,
|
||||
$this->antiAbuseService,
|
||||
$this->logger,
|
||||
);
|
||||
$this->handler->setNext($this->sendHandler);
|
||||
}
|
||||
|
||||
public function testProcess(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setStatus(LocalMessage::STATUS_RAW);
|
||||
|
||||
$this->userManager->expects(self::once())
|
||||
->method('get')
|
||||
->willReturn($this->createMock(IUser::class));
|
||||
$this->logger->expects(self::never())
|
||||
->method('error');
|
||||
$this->antiAbuseService->expects(self::once())
|
||||
->method('onBeforeMessageSent');
|
||||
$this->sendHandler->expects(self::once())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessNoUser(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$mailAccount->setId(123);
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setStatus(LocalMessage::STATUS_RAW);
|
||||
|
||||
$this->userManager->expects(self::once())
|
||||
->method('get')
|
||||
->willReturn(null);
|
||||
$this->logger->expects(self::once())
|
||||
->method('error');
|
||||
$this->antiAbuseService->expects(self::never())
|
||||
->method('onBeforeMessageSent');
|
||||
$this->sendHandler->expects(self::never())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessAlreadyProcessed(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$mailAccount->setId(123);
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setStatus(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
|
||||
$this->userManager->expects(self::never())
|
||||
->method('get');
|
||||
$this->logger->expects(self::never())
|
||||
->method('error');
|
||||
$this->antiAbuseService->expects(self::never())
|
||||
->method('onBeforeMessageSent');
|
||||
$this->sendHandler->expects(self::once())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Unit\Send;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\LocalMessageMapper;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\MessageMapper;
|
||||
use OCA\Mail\Send\AntiAbuseHandler;
|
||||
use OCA\Mail\Send\Chain;
|
||||
use OCA\Mail\Send\CopySentMessageHandler;
|
||||
use OCA\Mail\Send\FlagRepliedMessageHandler;
|
||||
use OCA\Mail\Send\SendHandler;
|
||||
use OCA\Mail\Send\SentMailboxHandler;
|
||||
use OCA\Mail\Service\Attachment\AttachmentService;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class ChainTest extends TestCase {
|
||||
private Chain $chain;
|
||||
private SentMailboxHandler|MockObject $sentMailboxHandler;
|
||||
private MockObject|AntiAbuseHandler $antiAbuseHandler;
|
||||
private SendHandler|MockObject $sendHandler;
|
||||
private MockObject|CopySentMessageHandler $copySentMessageHandler;
|
||||
private MockObject|FlagRepliedMessageHandler $flagRepliedMessageHandler;
|
||||
private MockObject|MessageMapper $messageMapper;
|
||||
private AttachmentService|MockObject $attachmentService;
|
||||
private MockObject|LocalMessageMapper $localMessageMapper;
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->sentMailboxHandler = $this->createMock(SentMailboxHandler::class);
|
||||
$this->antiAbuseHandler = $this->createMock(AntiAbuseHandler::class);
|
||||
$this->sendHandler = $this->createMock(SendHandler::class);
|
||||
$this->copySentMessageHandler = $this->createMock(CopySentMessageHandler::class);
|
||||
$this->flagRepliedMessageHandler = $this->createMock(FlagRepliedMessageHandler::class);
|
||||
$this->attachmentService = $this->createMock(AttachmentService::class);
|
||||
$this->localMessageMapper = $this->createMock(LocalMessageMapper::class);
|
||||
$this->chain = new Chain($this->sentMailboxHandler,
|
||||
$this->antiAbuseHandler,
|
||||
$this->sendHandler,
|
||||
$this->copySentMessageHandler,
|
||||
$this->flagRepliedMessageHandler,
|
||||
$this->attachmentService,
|
||||
$this->localMessageMapper,
|
||||
);
|
||||
}
|
||||
|
||||
public function testProcess(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setId(100);
|
||||
$localMessage->setStatus(LocalMessage::STATUS_RAW);
|
||||
$expected = new LocalMessage();
|
||||
$expected->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
$expected->setId(100);
|
||||
|
||||
|
||||
$this->sentMailboxHandler->expects(self::any())
|
||||
->method('setNext')
|
||||
->withConsecutive(
|
||||
[$this->antiAbuseHandler],
|
||||
[$this->sentMailboxHandler],
|
||||
[$this->copySentMessageHandler],
|
||||
[$this->flagRepliedMessageHandler],
|
||||
);
|
||||
$this->sentMailboxHandler->expects(self::once())
|
||||
->method('process')
|
||||
->with($account, $localMessage)
|
||||
->willReturn($expected);
|
||||
$this->attachmentService->expects(self::once())
|
||||
->method('deleteLocalMessageAttachments')
|
||||
->with($account->getUserId(), $expected->getId());
|
||||
$this->localMessageMapper->expects(self::once())
|
||||
->method('deleteWithRecipients')
|
||||
->with($expected);
|
||||
$this->localMessageMapper->expects(self::never())
|
||||
->method('update');
|
||||
|
||||
$this->chain->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessNotProcessed() {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setId(100);
|
||||
$localMessage->setStatus(LocalMessage::STATUS_RAW);
|
||||
$expected = new LocalMessage();
|
||||
$expected->setStatus(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
$expected->setId(100);
|
||||
|
||||
|
||||
$this->sentMailboxHandler->expects(self::any())
|
||||
->method('setNext')
|
||||
->withConsecutive(
|
||||
[$this->antiAbuseHandler],
|
||||
[$this->sentMailboxHandler],
|
||||
[$this->copySentMessageHandler],
|
||||
[$this->flagRepliedMessageHandler],
|
||||
);
|
||||
$this->sentMailboxHandler->expects(self::once())
|
||||
->method('process')
|
||||
->with($account, $localMessage)
|
||||
->willReturn($expected);
|
||||
$this->attachmentService->expects(self::never())
|
||||
->method('deleteLocalMessageAttachments');
|
||||
$this->localMessageMapper->expects(self::never())
|
||||
->method('deleteWithRecipients');
|
||||
$this->localMessageMapper->expects(self::once())
|
||||
->method('update')
|
||||
->with($expected);
|
||||
|
||||
$this->chain->process($account, $localMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Unit\Send;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use Horde_Imap_Client_Exception;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCA\Mail\Send\CopySentMessageHandler;
|
||||
use OCA\Mail\Send\FlagRepliedMessageHandler;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class CopySendMessageHandlerTest extends TestCase {
|
||||
private IMAPClientFactory|MockObject $imapClientFactory;
|
||||
private MailboxMapper|MockObject $mailboxMapper;
|
||||
private LoggerInterface|MockObject $loggerInterface;
|
||||
private MockObject|MessageMapper $messageMapper;
|
||||
private MockObject|FlagRepliedMessageHandler $flagRepliedMessageHandler;
|
||||
private CopySentMessageHandler $handler;
|
||||
|
||||
protected function setUp(): void {
|
||||
|
||||
$this->imapClientFactory = $this->createMock(IMAPClientFactory::class);
|
||||
$this->mailboxMapper = $this->createMock(MailboxMapper::class);
|
||||
$this->loggerInterface = $this->createMock(LoggerInterface::class);
|
||||
$this->messageMapper = $this->createMock(MessageMapper::class);
|
||||
$this->flagRepliedMessageHandler = $this->createMock(FlagRepliedMessageHandler::class);
|
||||
$this->handler = new CopySentMessageHandler(
|
||||
$this->imapClientFactory,
|
||||
$this->mailboxMapper,
|
||||
$this->loggerInterface,
|
||||
$this->messageMapper,
|
||||
);
|
||||
$this->handler->setNext($this->flagRepliedMessageHandler);
|
||||
}
|
||||
|
||||
public function testProcess(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = $this->getMockBuilder(LocalMessage::class);
|
||||
$localMessage->addMethods(['getStatus','setStatus', 'getRaw']);
|
||||
$mock = $localMessage->getMock();
|
||||
$mailbox = new Mailbox();
|
||||
$client = $this->createMock(Horde_Imap_Client_Socket::class);
|
||||
|
||||
$mock->expects(self::once())
|
||||
->method('getStatus')
|
||||
->willReturn(LocalMessage::STATUS_RAW);
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('warning');
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('error');
|
||||
$this->mailboxMapper->expects(self::once())
|
||||
->method('findById')
|
||||
->willReturn($mailbox);
|
||||
$this->imapClientFactory->expects(self::once())
|
||||
->method('getClient')
|
||||
->willReturn($client);
|
||||
$mock->expects(self::once())
|
||||
->method('getRaw')
|
||||
->willReturn('Test');
|
||||
$this->messageMapper->expects(self::once())
|
||||
->method('save');
|
||||
$mock->expects(self::once())
|
||||
->method('setStatus')
|
||||
->willReturn(LocalMessage::STATUS_PROCESSED);
|
||||
$this->flagRepliedMessageHandler->expects(self::once())
|
||||
->method('process')
|
||||
->with($account, $mock);
|
||||
|
||||
|
||||
$this->handler->process($account, $mock);
|
||||
}
|
||||
|
||||
public function testProcessNoSentMailbox(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = $this->getMockBuilder(LocalMessage::class);
|
||||
$localMessage->addMethods(['getStatus','setStatus']);
|
||||
$mock = $localMessage->getMock();
|
||||
|
||||
$this->loggerInterface->expects(self::once())
|
||||
->method('warning');
|
||||
$mock->expects(self::once())
|
||||
->method('getStatus')
|
||||
->willReturn(LocalMessage::STATUS_RAW);
|
||||
$mock->expects(self::once())
|
||||
->method('setStatus')
|
||||
->with(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('error');
|
||||
$this->mailboxMapper->expects(self::never())
|
||||
->method('findById');
|
||||
$this->imapClientFactory->expects(self::never())
|
||||
->method('getClient');
|
||||
$this->messageMapper->expects(self::never())
|
||||
->method('save');
|
||||
$this->flagRepliedMessageHandler->expects(self::never())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $mock);
|
||||
}
|
||||
|
||||
public function testProcessNoSentMailboxFound(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = $this->getMockBuilder(LocalMessage::class);
|
||||
$localMessage->addMethods(['getStatus','setStatus']);
|
||||
$mock = $localMessage->getMock();
|
||||
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('warning');
|
||||
$mock->expects(self::once())
|
||||
->method('getStatus')
|
||||
->willReturn(LocalMessage::STATUS_RAW);
|
||||
$mock->expects(self::once())
|
||||
->method('setStatus')
|
||||
->with(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
$this->mailboxMapper->expects(self::once())
|
||||
->method('findById')
|
||||
->willThrowException(new DoesNotExistException(''));
|
||||
$this->loggerInterface->expects(self::once())
|
||||
->method('error');
|
||||
$this->imapClientFactory->expects(self::never())
|
||||
->method('getClient');
|
||||
$this->messageMapper->expects(self::never())
|
||||
->method('save');
|
||||
$this->flagRepliedMessageHandler->expects(self::never())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $mock);
|
||||
}
|
||||
|
||||
public function testProcessCouldNotCopy(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = $this->getMockBuilder(LocalMessage::class);
|
||||
$localMessage->addMethods(['getStatus','setStatus', 'getRaw']);
|
||||
$mock = $localMessage->getMock();
|
||||
$mailbox = new Mailbox();
|
||||
$client = $this->createMock(Horde_Imap_Client_Socket::class);
|
||||
|
||||
$mock->expects(self::once())
|
||||
->method('getStatus')
|
||||
->willReturn(LocalMessage::STATUS_RAW);
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('warning');
|
||||
$this->mailboxMapper->expects(self::once())
|
||||
->method('findById')
|
||||
->willReturn($mailbox);
|
||||
$this->imapClientFactory->expects(self::once())
|
||||
->method('getClient')
|
||||
->willReturn($client);
|
||||
$mock->expects(self::once())
|
||||
->method('getRaw')
|
||||
->willReturn('123 Content');
|
||||
$this->messageMapper->expects(self::once())
|
||||
->method('save')
|
||||
->willThrowException(new Horde_Imap_Client_Exception());
|
||||
$mock->expects(self::once())
|
||||
->method('setStatus')
|
||||
->with(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
$this->loggerInterface->expects(self::once())
|
||||
->method('error');
|
||||
$this->flagRepliedMessageHandler->expects(self::never())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $mock);
|
||||
}
|
||||
|
||||
public function testProcessAlreadyProcessed(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = $this->getMockBuilder(LocalMessage::class);
|
||||
$localMessage->addMethods(['getStatus']);
|
||||
$mock = $localMessage->getMock();
|
||||
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('warning');
|
||||
$mock->expects(self::once())
|
||||
->method('getStatus')
|
||||
->willReturn(LocalMessage::STATUS_PROCESSED);
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('error');
|
||||
$this->mailboxMapper->expects(self::never())
|
||||
->method('findById');
|
||||
$this->imapClientFactory->expects(self::never())
|
||||
->method('getClient');
|
||||
$this->messageMapper->expects(self::never())
|
||||
->method('save');
|
||||
$this->flagRepliedMessageHandler->expects(self::once())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $mock);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Unit\Send;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use Horde_Imap_Client_Socket;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\MailboxMapper;
|
||||
use OCA\Mail\Db\Message;
|
||||
use OCA\Mail\Db\MessageMapper as DbMessageMapper;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper;
|
||||
use OCA\Mail\Send\FlagRepliedMessageHandler;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class FlagRepliedMessageHandlerTest extends TestCase {
|
||||
private IMAPClientFactory|MockObject $imapClientFactory;
|
||||
private MailboxMapper|MockObject $mailboxMapper;
|
||||
private LoggerInterface|MockObject $loggerInterface;
|
||||
private MockObject|MessageMapper $messageMapper;
|
||||
private FlagRepliedMessageHandler $handler;
|
||||
private MockObject|DbMessageMapper $dbMessageMapper;
|
||||
|
||||
protected function setUp(): void {
|
||||
|
||||
$this->imapClientFactory = $this->createMock(IMAPClientFactory::class);
|
||||
$this->mailboxMapper = $this->createMock(MailboxMapper::class);
|
||||
$this->loggerInterface = $this->createMock(LoggerInterface::class);
|
||||
$this->messageMapper = $this->createMock(MessageMapper::class);
|
||||
$this->dbMessageMapper = $this->createMock(DbMessageMapper::class);
|
||||
$this->handler = new FlagRepliedMessageHandler(
|
||||
$this->imapClientFactory,
|
||||
$this->mailboxMapper,
|
||||
$this->loggerInterface,
|
||||
$this->messageMapper,
|
||||
$this->dbMessageMapper,
|
||||
);
|
||||
}
|
||||
|
||||
public function testProcess(): void {
|
||||
$account = new Account(new MailAccount());
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setInReplyToMessageId('ab123');
|
||||
$localMessage->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
$dbMessage = new Message();
|
||||
$dbMessage->setUid(99);
|
||||
$dbMessage->setMailboxId(1);
|
||||
$mailbox = new Mailbox();
|
||||
$mailbox->setMyAcls('rw');
|
||||
$client = $this->createMock(Horde_Imap_Client_Socket::class);
|
||||
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('findByMessageId')
|
||||
->willReturn([$dbMessage]);
|
||||
$this->mailboxMapper->expects(self::once())
|
||||
->method('findById')
|
||||
->willReturn($mailbox);
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('warning');
|
||||
$this->imapClientFactory->expects(self::once())
|
||||
->method('getClient')
|
||||
->willReturn($client);
|
||||
$this->messageMapper->expects(self::once())
|
||||
->method('addFlag');
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('update');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessError(): void {
|
||||
$account = new Account(new MailAccount());
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setInReplyToMessageId('ab123');
|
||||
$localMessage->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
$dbMessage = new Message();
|
||||
$dbMessage->setUid(99);
|
||||
$dbMessage->setMailboxId(1);
|
||||
$mailbox = new Mailbox();
|
||||
$mailbox->setMyAcls('rw');
|
||||
$client = $this->createMock(Horde_Imap_Client_Socket::class);
|
||||
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('findByMessageId')
|
||||
->willReturn([$dbMessage]);
|
||||
$this->mailboxMapper->expects(self::once())
|
||||
->method('findById')
|
||||
->willReturn($mailbox);
|
||||
$this->imapClientFactory->expects(self::once())
|
||||
->method('getClient')
|
||||
->willReturn($client);
|
||||
$this->messageMapper->expects(self::once())
|
||||
->method('addFlag')
|
||||
->willThrowException(new DoesNotExistException(''));
|
||||
$this->loggerInterface->expects(self::once())
|
||||
->method('warning');
|
||||
$this->dbMessageMapper->expects(self::never())
|
||||
->method('update');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessReadOnly(): void {
|
||||
$account = new Account(new MailAccount());
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setInReplyToMessageId('ab123');
|
||||
$localMessage->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
$dbMessage = new Message();
|
||||
$dbMessage->setUid(99);
|
||||
$dbMessage->setMailboxId(1);
|
||||
$mailbox = new Mailbox();
|
||||
$mailbox->setMyAcls('r');
|
||||
$client = $this->createMock(Horde_Imap_Client_Socket::class);
|
||||
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('findByMessageId')
|
||||
->willReturn([$dbMessage]);
|
||||
$this->mailboxMapper->expects(self::once())
|
||||
->method('findById')
|
||||
->willReturn($mailbox);
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('warning');
|
||||
$this->imapClientFactory->expects(self::once())
|
||||
->method('getClient')
|
||||
->willReturn($client);
|
||||
$this->messageMapper->expects(self::never())
|
||||
->method('addFlag');
|
||||
$this->dbMessageMapper->expects(self::never())
|
||||
->method('update');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessNotFound(): void {
|
||||
$account = new Account(new MailAccount());
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setInReplyToMessageId('ab123');
|
||||
$localMessage->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('findByMessageId')
|
||||
->willReturn([]);
|
||||
$this->mailboxMapper->expects(self::never())
|
||||
->method('findById');
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('warning');
|
||||
$this->imapClientFactory->expects(self::never())
|
||||
->method('getClient');
|
||||
$this->messageMapper->expects(self::never())
|
||||
->method('addFlag');
|
||||
$this->dbMessageMapper->expects(self::never())
|
||||
->method('update');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessNoRepliedMessageId(): void {
|
||||
$account = new Account(new MailAccount());
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
|
||||
$this->dbMessageMapper->expects(self::never())
|
||||
->method('findByMessageId');
|
||||
$this->mailboxMapper->expects(self::never())
|
||||
->method('findById');
|
||||
$this->loggerInterface->expects(self::never())
|
||||
->method('warning');
|
||||
$this->imapClientFactory->expects(self::never())
|
||||
->method('getClient');
|
||||
$this->messageMapper->expects(self::never())
|
||||
->method('addFlag');
|
||||
$this->dbMessageMapper->expects(self::never())
|
||||
->method('update');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
/*
|
||||
* @copyright 2023 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Unit\Send;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Contracts\IMailTransmission;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Send\CopySentMessageHandler;
|
||||
use OCA\Mail\Send\FlagRepliedMessageHandler;
|
||||
use OCA\Mail\Send\SendHandler;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class SendHandlerTest extends TestCase {
|
||||
private MockObject|IMailTransmission $transmission;
|
||||
private MockObject|CopySentMessageHandler $copySentMessageHandler;
|
||||
private MockObject|FlagRepliedMessageHandler $flagRepliedMessageHandler;
|
||||
private SendHandler $handler;
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->transmission = $this->createMock(IMailTransmission::class);
|
||||
$this->copySentMessageHandler = $this->createMock(CopySentMessageHandler::class);
|
||||
$this->flagRepliedMessageHandler = $this->createMock(FlagRepliedMessageHandler::class);
|
||||
$this->handler = new SendHandler($this->transmission);
|
||||
$this->handler->setNext($this->copySentMessageHandler)
|
||||
->setNext($this->flagRepliedMessageHandler);
|
||||
}
|
||||
|
||||
public function testProcess(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setId(100);
|
||||
$localMessage->setStatus(LocalMessage::STATUS_RAW);
|
||||
|
||||
$this->transmission->expects(self::once())
|
||||
->method('sendMessage')
|
||||
->with($account, $localMessage);
|
||||
$this->copySentMessageHandler->expects(self::once())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessAlreadyProcessed(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setId(100);
|
||||
$localMessage->setStatus(LocalMessage::STATUS_IMAP_SENT_MAILBOX_FAIL);
|
||||
|
||||
$this->transmission->expects(self::never())
|
||||
->method('sendMessage');
|
||||
$this->copySentMessageHandler->expects(self::once())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testProcessError(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = $this->getMockBuilder(LocalMessage::class);
|
||||
$localMessage->addMethods(['getStatus']);
|
||||
$mock = $localMessage->getMock();
|
||||
$mock->setStatus(10);
|
||||
$mock->expects(self::any())
|
||||
->method('getStatus')
|
||||
->willReturn(LocalMessage::STATUS_SMPT_SEND_FAIL);
|
||||
$this->transmission->expects(self::once())
|
||||
->method('sendMessage');
|
||||
$this->copySentMessageHandler->expects(self::never())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $mock);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Unit\Send;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Send\AntiAbuseHandler;
|
||||
use OCA\Mail\Send\SentMailboxHandler;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class SentMailboxHandlerTest extends TestCase {
|
||||
private AntiAbuseHandler|MockObject $antiAbuseHandler;
|
||||
private SentMailboxHandler $handler;
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->antiAbuseHandler = $this->createMock(AntiAbuseHandler::class);
|
||||
$this->handler = new SentMailboxHandler();
|
||||
$this->handler->setNext($this->antiAbuseHandler);
|
||||
}
|
||||
|
||||
public function testProcess(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$mailAccount->setSentMailboxId(1);
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setStatus(LocalMessage::STATUS_RAW);
|
||||
|
||||
$this->antiAbuseHandler->expects(self::once())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $localMessage);
|
||||
}
|
||||
|
||||
public function testNoSentMailbox(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$mailAccount->setId(123);
|
||||
$account = new Account($mailAccount);
|
||||
$localMessage = $this->getMockBuilder(LocalMessage::class);
|
||||
$localMessage->addMethods(['setStatus']);
|
||||
$mock = $localMessage->getMock();
|
||||
|
||||
$mock->expects(self::once())
|
||||
->method('setStatus')
|
||||
->with(LocalMessage::STATUS_NO_SENT_MAILBOX);
|
||||
$this->antiAbuseHandler->expects(self::never())
|
||||
->method('process');
|
||||
|
||||
$this->handler->process($account, $mock);
|
||||
}
|
||||
}
|
|
@ -27,10 +27,8 @@ namespace OCA\Mail\Tests\Unit\Service;
|
|||
|
||||
use ChristophWurst\Nextcloud\Testing\ServiceMockObject;
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Address;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Service\AntiAbuseService;
|
||||
use OCP\IMemcache;
|
||||
use OCP\IUser;
|
||||
|
@ -54,15 +52,7 @@ class AntiAbuseServiceTest extends TestCase {
|
|||
public function testThresholdDisabled(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn('user123');
|
||||
$account = $this->createMock(Account::class);
|
||||
$messageData = new NewMessageData(
|
||||
$account,
|
||||
new AddressList([]),
|
||||
new AddressList([]),
|
||||
new AddressList([]),
|
||||
'subject',
|
||||
'henlo',
|
||||
);
|
||||
$messageData = new LocalMessage();
|
||||
$this->serviceMock->getParameter('config')
|
||||
->expects(self::once())
|
||||
->method('getAppValue')
|
||||
|
@ -84,30 +74,16 @@ class AntiAbuseServiceTest extends TestCase {
|
|||
public function testThresholdReached(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn('user123');
|
||||
$account = $this->createMock(Account::class);
|
||||
$messageData = new NewMessageData(
|
||||
$account,
|
||||
new AddressList(array_map(static function (int $i) {
|
||||
return Address::fromRaw(
|
||||
"user$i@domain.tld",
|
||||
"user$i@domain.tld",
|
||||
);
|
||||
}, range(1, 50))),
|
||||
new AddressList(array_map(static function (int $i) {
|
||||
return Address::fromRaw(
|
||||
"user$i@domain.tld",
|
||||
"user$i@domain.tld",
|
||||
);
|
||||
}, range(51, 60))),
|
||||
new AddressList(array_map(static function (int $i) {
|
||||
return Address::fromRaw(
|
||||
"user$i@domain.tld",
|
||||
"user$i@domain.tld",
|
||||
);
|
||||
}, range(51, 70))),
|
||||
'subject',
|
||||
'henlo',
|
||||
);
|
||||
$messageData = new LocalMessage();
|
||||
$recipients = array_map(static function (int $i) {
|
||||
return Recipient::fromParams([
|
||||
'label' => 'rec ' . $i,
|
||||
'email' => $i . '@domain.tld',
|
||||
'type' => Recipient::TYPE_TO,
|
||||
]);
|
||||
}, range(0, 70));
|
||||
$messageData->setRecipients($recipients);
|
||||
|
||||
$this->serviceMock->getParameter('config')
|
||||
->method('getAppValue')
|
||||
->withConsecutive(
|
||||
|
@ -123,7 +99,7 @@ class AntiAbuseServiceTest extends TestCase {
|
|||
->with(self::anything(), [
|
||||
'user' => 'user123',
|
||||
'expected' => 50,
|
||||
'actual' => 80,
|
||||
'actual' => 71,
|
||||
]);
|
||||
|
||||
$this->service->onBeforeMessageSent(
|
||||
|
@ -135,20 +111,14 @@ class AntiAbuseServiceTest extends TestCase {
|
|||
public function test15mThreshold(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn('user123');
|
||||
$account = $this->createMock(Account::class);
|
||||
$messageData = new NewMessageData(
|
||||
$account,
|
||||
new AddressList([
|
||||
Address::fromRaw(
|
||||
"user@domain.tld",
|
||||
"user@domain.tld",
|
||||
)
|
||||
]),
|
||||
new AddressList([]),
|
||||
new AddressList([]),
|
||||
'subject',
|
||||
'henlo',
|
||||
);
|
||||
$messageData = new LocalMessage();
|
||||
$recipients = Recipient::fromParams([
|
||||
'label' => 'rec 1',
|
||||
'email' => 'u1@domain.tld',
|
||||
'type' => Recipient::TYPE_TO,
|
||||
]);
|
||||
$messageData->setRecipients([$recipients]);
|
||||
|
||||
$this->serviceMock->getParameter('config')
|
||||
->method('getAppValue')
|
||||
->withConsecutive(
|
||||
|
|
|
@ -26,39 +26,51 @@ namespace OCA\Mail\Tests\Unit\Service;
|
|||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Contracts\IMailTransmission;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\Mailbox;
|
||||
use OCA\Mail\Db\MessageMapper;
|
||||
use OCA\Mail\Db\Message as DbMessage;
|
||||
use OCA\Mail\Db\MessageMapper as DbMessageMapper;
|
||||
use OCA\Mail\Events\MessageFlaggedEvent;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\IMAP\MessageMapper as ImapMessageMapper;
|
||||
use OCA\Mail\Model\NewMessageData;
|
||||
use OCA\Mail\Service\AntiSpamService;
|
||||
use OCA\Mail\Service\MailManager;
|
||||
use OCA\Mail\SMTP\SmtpClientFactory;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class AntiSpamServiceTest extends TestCase {
|
||||
/** @var AntiSpamService */
|
||||
private $service;
|
||||
|
||||
/** @var IConfig|MockObject */
|
||||
private $config;
|
||||
|
||||
/** @var MessageMapper|MockObject */
|
||||
private $messageMapper;
|
||||
|
||||
/** @var IMailTransmission|MockObject */
|
||||
private $transmission;
|
||||
private AntiSpamService $service;
|
||||
private IConfig|MockObject $config;
|
||||
private DbMessageMapper|MockObject $dbMessageMapper;
|
||||
private IMAPClientFactory|MockObject $imapClientFactory;
|
||||
private SmtpClientFactory|MockObject $smtpClientFactory;
|
||||
private MockObject|ImapMessageMapper $imapMessageMapper;
|
||||
private LoggerInterface|MockObject $logger;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->messageMapper = $this->createMock(MessageMapper::class);
|
||||
$this->dbMessageMapper = $this->createMock(DbMessageMapper::class);
|
||||
$this->transmission = $this->createMock(IMailTransmission::class);
|
||||
$this->mailManager = $this->createMock(MailManager::class);
|
||||
$this->imapClientFactory = $this->createMock(IMAPClientFactory::class);
|
||||
$this->smtpClientFactory = $this->createMock(SmtpClientFactory::class);
|
||||
$this->imapMessageMapper = $this->createMock(ImapMessageMapper::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$this->service = new AntiSpamService(
|
||||
$this->config,
|
||||
$this->messageMapper,
|
||||
$this->transmission
|
||||
$this->dbMessageMapper,
|
||||
$this->mailManager,
|
||||
$this->imapClientFactory,
|
||||
$this->smtpClientFactory,
|
||||
$this->imapMessageMapper,
|
||||
$this->logger,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -73,10 +85,8 @@ class AntiSpamServiceTest extends TestCase {
|
|||
->method('getAppValue')
|
||||
->with('mail', 'antispam_reporting_spam')
|
||||
->willReturn('');
|
||||
$this->messageMapper->expects(self::never())
|
||||
$this->dbMessageMapper->expects(self::never())
|
||||
->method('getIdForUid');
|
||||
$this->transmission->expects(self::never())
|
||||
->method('sendMessage');
|
||||
|
||||
$this->service->sendReportEmail($event->getAccount(), $event->getMailbox(), 123, $event->getFlag());
|
||||
}
|
||||
|
@ -92,13 +102,11 @@ class AntiSpamServiceTest extends TestCase {
|
|||
->method('getAppValue')
|
||||
->with('mail', 'antispam_reporting_spam')
|
||||
->willReturn('test@test.com');
|
||||
$this->messageMapper->expects(self::once())
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('getIdForUid')
|
||||
->with($event->getMailbox(), 123)
|
||||
->willReturn(null);
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->transmission->expects(self::never())
|
||||
->method('sendMessage');
|
||||
|
||||
$this->service->sendReportEmail($event->getAccount(), $event->getMailbox(), 123, $event->getFlag());
|
||||
}
|
||||
|
@ -124,79 +132,124 @@ class AntiSpamServiceTest extends TestCase {
|
|||
[['id' => 123, 'type' => 'message/rfc822']]
|
||||
);
|
||||
|
||||
$this->messageMapper->expects(self::once())
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('getIdForUid')
|
||||
->with($event->getMailbox(), 123)
|
||||
->willReturn(123);
|
||||
$this->transmission->expects(self::once())
|
||||
->method('sendMessage')
|
||||
->with($messageData)
|
||||
->willThrowException(new ServiceException());
|
||||
$this->expectException(ServiceException::class);
|
||||
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->service->sendReportEmail($event->getAccount(), $event->getMailbox(), 123, $event->getFlag());
|
||||
}
|
||||
|
||||
public function testSendReportEmail(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(10);
|
||||
$mailAccount->setName('Test');
|
||||
$mailAccount->setEmail('test@test.com');
|
||||
$mailAccount->setUserId('test');
|
||||
$account = new Account($mailAccount);
|
||||
$dbMessage = new DbMessage();
|
||||
$dbMessage->setMailboxId(55);
|
||||
$dbMessage->setUid(123);
|
||||
$dbMessage->setSubject('Spam Spam and Eggs and Spam');
|
||||
$mailbox = new Mailbox();
|
||||
$mailbox->setName('INBOX');
|
||||
$event = $this->createConfiguredMock(MessageFlaggedEvent::class, [
|
||||
'getAccount' => $this->createMock(Account::class),
|
||||
'getAccount' => $account,
|
||||
'getMailbox' => $this->createMock(Mailbox::class),
|
||||
'getFlag' => '$junk'
|
||||
]);
|
||||
$client = $this->createMock(\Horde_Imap_Client_Socket::class);
|
||||
|
||||
$this->config->expects(self::once())
|
||||
->method('getAppValue')
|
||||
->with('mail', 'antispam_reporting_spam')
|
||||
->willReturn('test@test.com');
|
||||
$messageData = NewMessageData::fromRequest(
|
||||
$event->getAccount(),
|
||||
'test@test.com',
|
||||
null,
|
||||
null,
|
||||
'Learn as Junk',
|
||||
'Learn as Junk',
|
||||
[['id' => 123, 'type' => 'message/rfc822']]
|
||||
);
|
||||
|
||||
$this->messageMapper->expects(self::once())
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('getIdForUid')
|
||||
->with($event->getMailbox(), 123)
|
||||
->willReturn(123);
|
||||
$this->transmission->expects(self::once())
|
||||
->method('sendMessage')
|
||||
->with($messageData);
|
||||
$this->mailManager->expects(self::once())
|
||||
->method('getMessage')
|
||||
->with('test', 123)
|
||||
->willReturn($dbMessage);
|
||||
$this->mailManager->expects(self::exactly(2))
|
||||
->method('getMailbox')
|
||||
->willReturn($mailbox);
|
||||
$this->imapClientFactory->expects(self::exactly(2))
|
||||
->method('getClient')
|
||||
->willReturn($client);
|
||||
$client->expects(self::exactly(2))
|
||||
->method('logout');
|
||||
$this->imapMessageMapper->expects(self::once())
|
||||
->method('getFullText')
|
||||
->with($client, $mailbox->getName(), $dbMessage->getUid(), 'test')
|
||||
->willReturn('Test');
|
||||
$this->smtpClientFactory->expects(self::once())
|
||||
->method('create')
|
||||
->with($account)
|
||||
->willReturn($this->createMock(\Horde_Mail_Transport::class));
|
||||
$this->imapMessageMapper->expects(self::once())
|
||||
->method('save');
|
||||
$this->logger->expects(self::never())
|
||||
->method(self::anything());
|
||||
|
||||
$this->service->sendReportEmail($event->getAccount(), $event->getMailbox(), 123, $event->getFlag());
|
||||
}
|
||||
|
||||
public function testSendReportEmailForHam(): void {
|
||||
public function testSendReportEmailNoSentCopy(): void {
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setSentMailboxId(10);
|
||||
$mailAccount->setName('Test');
|
||||
$mailAccount->setEmail('test@test.com');
|
||||
$mailAccount->setUserId('test');
|
||||
$account = new Account($mailAccount);
|
||||
$dbMessage = new DbMessage();
|
||||
$dbMessage->setMailboxId(55);
|
||||
$dbMessage->setUid(123);
|
||||
$dbMessage->setSubject('Spam Spam and Eggs and Spam');
|
||||
$mailbox = new Mailbox();
|
||||
$mailbox->setName('INBOX');
|
||||
$event = $this->createConfiguredMock(MessageFlaggedEvent::class, [
|
||||
'getAccount' => $this->createMock(Account::class),
|
||||
'getAccount' => $account,
|
||||
'getMailbox' => $this->createMock(Mailbox::class),
|
||||
'getFlag' => '$notjunk'
|
||||
'getFlag' => '$junk'
|
||||
]);
|
||||
$client = $this->createMock(\Horde_Imap_Client_Socket::class);
|
||||
|
||||
$this->config->expects(self::once())
|
||||
->method('getAppValue')
|
||||
->with('mail', 'antispam_reporting_ham')
|
||||
->with('mail', 'antispam_reporting_spam')
|
||||
->willReturn('test@test.com');
|
||||
$messageData = NewMessageData::fromRequest(
|
||||
$event->getAccount(),
|
||||
'test@test.com',
|
||||
null,
|
||||
null,
|
||||
'Learn as Not Junk',
|
||||
'Learn as Not Junk',
|
||||
[['id' => 123, 'type' => 'message/rfc822']]
|
||||
);
|
||||
|
||||
$this->messageMapper->expects(self::once())
|
||||
$this->dbMessageMapper->expects(self::once())
|
||||
->method('getIdForUid')
|
||||
->with($event->getMailbox(), 123)
|
||||
->willReturn(123);
|
||||
$this->transmission->expects(self::once())
|
||||
->method('sendMessage')
|
||||
->with($messageData);
|
||||
$this->mailManager->expects(self::once())
|
||||
->method('getMessage')
|
||||
->with('test', 123)
|
||||
->willReturn($dbMessage);
|
||||
$this->mailManager->expects(self::exactly(2))
|
||||
->method('getMailbox')
|
||||
->willReturn($mailbox);
|
||||
$this->imapClientFactory->expects(self::exactly(2))
|
||||
->method('getClient')
|
||||
->willReturn($client);
|
||||
$client->expects(self::exactly(2))
|
||||
->method('logout');
|
||||
$this->imapMessageMapper->expects(self::once())
|
||||
->method('getFullText')
|
||||
->with($client, $mailbox->getName(), $dbMessage->getUid(), 'test')
|
||||
->willReturn('Test');
|
||||
$this->smtpClientFactory->expects(self::once())
|
||||
->method('create')
|
||||
->with($account)
|
||||
->willReturn($this->createMock(\Horde_Mail_Transport::class));
|
||||
$this->imapMessageMapper->expects(self::once())
|
||||
->method('save')
|
||||
->willThrowException(new \Horde_Imap_Client_Exception());
|
||||
$this->logger->expects(self::once())
|
||||
->method('error');
|
||||
|
||||
$this->service->sendReportEmail($event->getAccount(), $event->getMailbox(), 123, $event->getFlag());
|
||||
}
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -36,6 +36,7 @@ use OCA\Mail\Db\LocalMessageMapper;
|
|||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Exception\ClientException;
|
||||
use OCA\Mail\IMAP\IMAPClientFactory;
|
||||
use OCA\Mail\Send\Chain;
|
||||
use OCA\Mail\Service\AccountService;
|
||||
use OCA\Mail\Service\Attachment\AttachmentService;
|
||||
use OCA\Mail\Service\MailTransmission;
|
||||
|
@ -91,6 +92,7 @@ class OutboxServiceTest extends TestCase {
|
|||
$this->accountService = $this->createMock(AccountService::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->chain = $this->createMock(Chain::class);
|
||||
$this->outboxService = new OutboxService(
|
||||
$this->transmission,
|
||||
$this->mapper,
|
||||
|
@ -101,6 +103,7 @@ class OutboxServiceTest extends TestCase {
|
|||
$this->accountService,
|
||||
$this->timeFactory,
|
||||
$this->logger,
|
||||
$this->chain,
|
||||
);
|
||||
$this->userId = 'linus';
|
||||
$this->time = $this->createMock(ITimeFactory::class);
|
||||
|
@ -121,8 +124,9 @@ class OutboxServiceTest extends TestCase {
|
|||
'body' => 'Test',
|
||||
'html' => false,
|
||||
'reply_to_id' => null,
|
||||
'draft_id' => 99
|
||||
|
||||
'draft_id' => 99,
|
||||
'status' => 0,
|
||||
'raw' => 'Test',
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
|
@ -134,7 +138,9 @@ class OutboxServiceTest extends TestCase {
|
|||
'body' => 'Second Test',
|
||||
'html' => true,
|
||||
'reply_to_id' => null,
|
||||
'draft_id' => null
|
||||
'draft_id' => null,
|
||||
'status' => 0,
|
||||
'raw' => 'Second Test',
|
||||
]
|
||||
]);
|
||||
|
||||
|
@ -385,6 +391,7 @@ class OutboxServiceTest extends TestCase {
|
|||
$account = $this->createConfiguredMock(Account::class, [
|
||||
'getUserId' => $this->userId
|
||||
]);
|
||||
|
||||
$this->mapper->expects(self::once())
|
||||
->method('updateWithRecipients')
|
||||
->with($message, [$rTo], $cc, $bcc)
|
||||
|
@ -396,6 +403,7 @@ class OutboxServiceTest extends TestCase {
|
|||
->method('getClient');
|
||||
$this->attachmentService->expects(self::never())
|
||||
->method('handleAttachments');
|
||||
|
||||
$result = $this->outboxService->updateMessage($account, $message, $to, $cc, $bcc, $attachments);
|
||||
$this->assertEmpty($result->getAttachments());
|
||||
}
|
||||
|
@ -436,6 +444,7 @@ class OutboxServiceTest extends TestCase {
|
|||
public function testSendMessage(): void {
|
||||
$message = new LocalMessage();
|
||||
$message->setId(1);
|
||||
$message->setStatus(LocalMessage::STATUS_RAW);
|
||||
$recipient = new Recipient();
|
||||
$recipient->setEmail('museum@startdewvalley.com');
|
||||
$recipient->setLabel('Gunther');
|
||||
|
@ -452,15 +461,36 @@ class OutboxServiceTest extends TestCase {
|
|||
'getUserId' => $this->userId
|
||||
]);
|
||||
|
||||
$this->transmission->expects(self::once())
|
||||
->method('sendLocalMessage')
|
||||
$this->chain->expects(self::once())
|
||||
->method('process')
|
||||
->with($account, $message);
|
||||
|
||||
$this->outboxService->sendMessage($message, $account);
|
||||
}
|
||||
|
||||
public function testSendMessageAlreadyProcessed(): void {
|
||||
$message = new LocalMessage();
|
||||
$message->setId(1);
|
||||
$message->setStatus(LocalMessage::STATUS_PROCESSED);
|
||||
$recipient = new Recipient();
|
||||
$recipient->setEmail('museum@startdewvalley.com');
|
||||
$recipient->setLabel('Gunther');
|
||||
$recipient->setType(Recipient::TYPE_TO);
|
||||
$recipients = [$recipient];
|
||||
$attachment = new LocalAttachment();
|
||||
$attachment->setMimeType('image/png');
|
||||
$attachment->setFileName('SlimesInTheMines.png');
|
||||
$attachment->setCreatedAt($this->time->getTime());
|
||||
$attachments = [$attachment];
|
||||
$message->setRecipients($recipients);
|
||||
$message->setAttachments($attachments);
|
||||
$account = $this->createConfiguredMock(Account::class, [
|
||||
'getUserId' => $this->userId
|
||||
]);
|
||||
|
||||
$this->chain->expects(self::once())
|
||||
->method('process')
|
||||
->with($account, $message);
|
||||
$this->attachmentService->expects(self::once())
|
||||
->method('deleteLocalMessageAttachments')
|
||||
->with($account->getUserId(), $message->getId());
|
||||
$this->mapper->expects(self::once())
|
||||
->method('deleteWithRecipients')
|
||||
->with($message);
|
||||
|
||||
$this->outboxService->sendMessage($message, $account);
|
||||
}
|
||||
|
@ -468,6 +498,7 @@ class OutboxServiceTest extends TestCase {
|
|||
public function testSendMessageTransmissionError(): void {
|
||||
$message = new LocalMessage();
|
||||
$message->setId(1);
|
||||
$message->setStatus(LocalMessage::STATUS_NO_SENT_MAILBOX);
|
||||
$recipient = new Recipient();
|
||||
$recipient->setEmail('museum@startdewvalley.com');
|
||||
$recipient->setLabel('Gunther');
|
||||
|
@ -484,17 +515,12 @@ class OutboxServiceTest extends TestCase {
|
|||
'getUserId' => $this->userId
|
||||
]);
|
||||
|
||||
$this->transmission->expects(self::once())
|
||||
->method('sendLocalMessage')
|
||||
->with($account, $message)
|
||||
->willThrowException(new ClientException());
|
||||
$this->attachmentService->expects(self::never())
|
||||
->method('deleteLocalMessageAttachments');
|
||||
$this->mapper->expects(self::never())
|
||||
->method('deleteWithRecipients');
|
||||
$this->chain->expects(self::once())
|
||||
->method('process')
|
||||
->with($account, $message);
|
||||
|
||||
$this->expectException(ClientException::class);
|
||||
$this->outboxService->sendMessage($message, $account);
|
||||
$this->assertEquals(LocalMessage::STATUS_NO_SENT_MAILBOX, $message->getStatus());
|
||||
}
|
||||
|
||||
public function testConvertToOutboxMessageNoRecipients(): void {
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* @copyright 2024 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
namespace OCA\Mail\Tests\Unit\Service;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Mail\Account;
|
||||
use OCA\Mail\Address;
|
||||
use OCA\Mail\AddressList;
|
||||
use OCA\Mail\Db\LocalAttachment;
|
||||
use OCA\Mail\Db\LocalMessage;
|
||||
use OCA\Mail\Db\MailAccount;
|
||||
use OCA\Mail\Db\Recipient;
|
||||
use OCA\Mail\Db\SmimeCertificate;
|
||||
use OCA\Mail\Exception\AttachmentNotFoundException;
|
||||
use OCA\Mail\Exception\ServiceException;
|
||||
use OCA\Mail\Exception\SmimeSignException;
|
||||
use OCA\Mail\Model\Message;
|
||||
use OCA\Mail\Service\Attachment\AttachmentService;
|
||||
use OCA\Mail\Service\GroupsIntegration;
|
||||
use OCA\Mail\Service\SmimeService;
|
||||
use OCA\Mail\Service\TransmissionService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class TransmissionServiceTest extends TestCase {
|
||||
|
||||
private GroupsIntegration|MockObject $groupsIntegration;
|
||||
private AttachmentService|MockObject $attachmentService;
|
||||
private LoggerInterface|MockObject $logger;
|
||||
private SmimeService|MockObject $smimeService;
|
||||
private MockObject|TransmissionService $transmissionService;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->attachmentService = $this->createMock(AttachmentService::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->smimeService = $this->createMock(SmimeService::class);
|
||||
$this->groupsIntegration = $this->createMock(GroupsIntegration::class);
|
||||
$this->transmissionService = new TransmissionService(
|
||||
$this->groupsIntegration,
|
||||
$this->attachmentService,
|
||||
$this->logger,
|
||||
$this->smimeService,
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetAddressList() {
|
||||
$expected = new AddressList([Address::fromRaw('Bob', 'bob@test.com')]);
|
||||
$recipient = new Recipient();
|
||||
$recipient->setLabel('Bob');
|
||||
$recipient->setEmail('bob@test.com');
|
||||
$recipient->setType(Recipient::TYPE_TO);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setRecipients([$recipient]);
|
||||
|
||||
$this->groupsIntegration->expects(self::once())
|
||||
->method('expand')
|
||||
->willReturn([$recipient]);
|
||||
|
||||
$actual = $this->transmissionService->getAddressList($localMessage, Recipient::TYPE_TO);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testGetAttachments() {
|
||||
$id = 1;
|
||||
$expected = [[
|
||||
'type' => 'local',
|
||||
'id' => $id
|
||||
]];
|
||||
$attachment = new LocalAttachment();
|
||||
$attachment->setId($id);
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setAttachments([$attachment]);
|
||||
|
||||
$actual = $this->transmissionService->getAttachments($localMessage);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testHandleAttachment() {
|
||||
$id = 1;
|
||||
$expected = [
|
||||
'type' => 'local',
|
||||
'id' => $id
|
||||
];
|
||||
[$localAttachment, $file] = [
|
||||
new LocalAttachment(),
|
||||
$this->createMock(ISimpleFile::class)
|
||||
];
|
||||
$message = $this->createMock(Message::class);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
|
||||
$this->attachmentService->expects(self::once())
|
||||
->method('getAttachment')
|
||||
->willReturn([$localAttachment, $file]);
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
|
||||
$this->transmissionService->handleAttachment($account, $expected);
|
||||
}
|
||||
|
||||
public function testHandleAttachmentNoId() {
|
||||
$attachment = [[
|
||||
'type' => 'local',
|
||||
]];
|
||||
$message = $this->createMock(Message::class);
|
||||
$account = new Account(new MailAccount());
|
||||
|
||||
$this->logger->expects(self::once())
|
||||
->method('warning');
|
||||
|
||||
$this->transmissionService->handleAttachment($account, $attachment);
|
||||
}
|
||||
|
||||
public function testHandleAttachmentNotFound() {
|
||||
$attachment = [
|
||||
'id' => 1,
|
||||
'type' => 'local',
|
||||
];
|
||||
|
||||
$message = $this->createMock(Message::class);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
|
||||
$this->attachmentService->expects(self::once())
|
||||
->method('getAttachment')
|
||||
->willThrowException(new AttachmentNotFoundException());
|
||||
$this->logger->expects(self::once())
|
||||
->method('warning');
|
||||
|
||||
$this->transmissionService->handleAttachment($account, $attachment);
|
||||
}
|
||||
|
||||
public function testGetSignMimePart() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeSign(true);
|
||||
$localMessage->setSmimeCertificateId(1);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$smimeCertificate = new SmimeCertificate();
|
||||
$smimeCertificate->setCertificate('123');
|
||||
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificate')
|
||||
->willReturn($smimeCertificate);
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('signMimePart');
|
||||
|
||||
$this->transmissionService->getSignMimePart($localMessage, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_RAW, $localMessage->getStatus());
|
||||
}
|
||||
|
||||
public function testGetSignMimePartNoCertId() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeSign(true);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
|
||||
$this->smimeService->expects(self::never())
|
||||
->method('findCertificate');
|
||||
$this->smimeService->expects(self::never())
|
||||
->method('signMimePart');
|
||||
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->transmissionService->getSignMimePart($localMessage, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_SMIME_SIGN_NO_CERT_ID, $localMessage->getStatus());
|
||||
}
|
||||
|
||||
public function testGetSignMimePartNoCertFound() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeSign(true);
|
||||
$localMessage->setSmimeCertificateId(1);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificate')
|
||||
->willThrowException(new DoesNotExistException(''));
|
||||
$this->smimeService->expects(self::never())
|
||||
->method('signMimePart');
|
||||
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->transmissionService->getSignMimePart($localMessage, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_SMIME_SIGN_CERT, $localMessage->getStatus());
|
||||
}
|
||||
|
||||
public function testGetSignMimePartFailedSigning() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeSign(true);
|
||||
$localMessage->setSmimeCertificateId(1);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$smimeCertificate = new SmimeCertificate();
|
||||
$smimeCertificate->setCertificate('123');
|
||||
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificate')
|
||||
->willReturn($smimeCertificate);
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('signMimePart')
|
||||
->willThrowException(new SmimeSignException());
|
||||
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->transmissionService->getSignMimePart($localMessage, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_SMIME_SIGN_FAIL, $localMessage->getStatus());
|
||||
}
|
||||
|
||||
public function testGetEncryptMimePart() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeEncrypt(true);
|
||||
$localMessage->setSmimeCertificateId(1);
|
||||
$to = new AddressList([Address::fromRaw('Bob', 'bob@test.com')]);
|
||||
$cc = new AddressList([]);
|
||||
$bcc = new AddressList([]);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$smimeCertificate = new SmimeCertificate();
|
||||
$smimeCertificate->setCertificate('123');
|
||||
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificatesByAddressList')
|
||||
->willReturn([$smimeCertificate]);
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificate')
|
||||
->willReturn($smimeCertificate);
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('encryptMimePart');
|
||||
|
||||
$this->transmissionService->getEncryptMimePart($localMessage, $to, $cc, $bcc, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_RAW, $localMessage->getStatus());
|
||||
}
|
||||
|
||||
public function testGetEncryptMimePartNoCertId() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeEncrypt(true);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$to = new AddressList([Address::fromRaw('Bob', 'bob@test.com')]);
|
||||
$cc = new AddressList([]);
|
||||
$bcc = new AddressList([]);
|
||||
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->transmissionService->getEncryptMimePart($localMessage, $to, $cc, $bcc, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_SMIME_ENCRYPT_NO_CERT_ID, $localMessage->getStatus());
|
||||
}
|
||||
|
||||
public function testGetEncryptMimePartNoAddressCerts() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeEncrypt(true);
|
||||
$localMessage->setSmimeCertificateId(1);
|
||||
$to = new AddressList([Address::fromRaw('Bob', 'bob@test.com')]);
|
||||
$cc = new AddressList([]);
|
||||
$bcc = new AddressList([]);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$smimeCertificate = new SmimeCertificate();
|
||||
$smimeCertificate->setCertificate('123');
|
||||
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificatesByAddressList')
|
||||
->willThrowException(new ServiceException());
|
||||
$this->smimeService->expects(self::never())
|
||||
->method('findCertificate');
|
||||
$this->smimeService->expects(self::never())
|
||||
->method('encryptMimePart');
|
||||
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->transmissionService->getEncryptMimePart($localMessage, $to, $cc, $bcc, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_SMIME_ENCRYT_FAIL, $localMessage->getStatus());
|
||||
}
|
||||
|
||||
public function testGetEncryptMimePartNoCert() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeEncrypt(true);
|
||||
$localMessage->setSmimeCertificateId(1);
|
||||
$to = new AddressList([Address::fromRaw('Bob', 'bob@test.com')]);
|
||||
$cc = new AddressList([]);
|
||||
$bcc = new AddressList([]);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$smimeCertificate = new SmimeCertificate();
|
||||
$smimeCertificate->setCertificate('123');
|
||||
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificatesByAddressList')
|
||||
->willReturn([$smimeCertificate]);
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificate')
|
||||
->willThrowException(new DoesNotExistException(''));
|
||||
$this->smimeService->expects(self::never())
|
||||
->method('encryptMimePart');
|
||||
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->transmissionService->getEncryptMimePart($localMessage, $to, $cc, $bcc, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_SMIME_ENCRYPT_CERT, $localMessage->getStatus());
|
||||
}
|
||||
|
||||
public function testGetEncryptMimePartEncryptFail() {
|
||||
$send = new \Horde_Mime_Part();
|
||||
$send->setContents('Test');
|
||||
$localMessage = new LocalMessage();
|
||||
$localMessage->setSmimeEncrypt(true);
|
||||
$localMessage->setSmimeCertificateId(1);
|
||||
$to = new AddressList([Address::fromRaw('Bob', 'bob@test.com')]);
|
||||
$cc = new AddressList([]);
|
||||
$bcc = new AddressList([]);
|
||||
$mailAccount = new MailAccount();
|
||||
$mailAccount->setUserId('bob');
|
||||
$account = new Account($mailAccount);
|
||||
$smimeCertificate = new SmimeCertificate();
|
||||
$smimeCertificate->setCertificate('123');
|
||||
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificatesByAddressList')
|
||||
->willReturn([$smimeCertificate]);
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('findCertificate')
|
||||
->willReturn($smimeCertificate);
|
||||
$this->smimeService->expects(self::once())
|
||||
->method('encryptMimePart')
|
||||
->willThrowException(new ServiceException());
|
||||
|
||||
$this->expectException(ServiceException::class);
|
||||
$this->transmissionService->getEncryptMimePart($localMessage, $to, $cc, $bcc, $account, $send);
|
||||
$this->assertEquals(LocalMessage::STATUS_SMIME_ENCRYT_FAIL, $localMessage->getStatus());
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче