← Back
Editing: MessageSend.php
<?php declare(strict_types=1); /** * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Mail\Provider\Command; use OCA\Mail\Db\LocalAttachment; use OCA\Mail\Db\LocalMessage; use OCA\Mail\Exception\ClientException; use OCA\Mail\Exception\UploadException; use OCA\Mail\Service\AccountService; use OCA\Mail\Service\Attachment\AttachmentService; use OCA\Mail\Service\OutboxService; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Mail\Provider\Exception\SendException; use OCP\Mail\Provider\IAddress; use OCP\Mail\Provider\IMessage; class MessageSend { public function __construct( protected ITimeFactory $time, protected AccountService $accountService, protected OutboxService $outboxService, protected AttachmentService $attachmentService, ) { } /** * Performs send operation * * @since 4.0.0 * * @param string $userId system user id * @param string $serviceId mail account id * @param IMessage $message mail message object with all required parameters to send a message * @param array $options array of options reserved for future use * * @return LocalMessage * * @throws SendException on failure, check message for reason * */ public function perform(string $userId, string $serviceId, IMessage $message, array $options = []): LocalMessage { // validate that at least one To address is present if (count($message->getTo()) === 0) { throw new SendException('Invalid Message Parameter: MUST contain at least one TO address with a valid address'); } // validate that all To, CC and BCC have email address $entries = array_merge($message->getTo(), $message->getCc(), $message->getBcc()); array_walk($entries, function ($entry) { if (empty($entry->getAddress())) { throw new SendException('Invalid Message Parameter: All TO, CC and BCC addresses MUST contain at least an email address'); } }); // validate that all attachments have a name, type, and contents $entries = $message->getAttachments(); array_walk($entries, function ($entry) { if (empty($entry->getType()) || empty($entry->getContents())) { throw new SendException('Invalid Attachment Parameter: MUST contain values for Type and Contents'); } }); // retrieve user mail account details try { $account = $this->accountService->find($userId, (int)$serviceId); } catch (ClientException $e) { throw new SendException('Error: occurred while retrieving mail account details', 0, $e); } // convert mail provider message to mail app message $localMessage = new LocalMessage(); $localMessage->setType($localMessage::TYPE_OUTGOING); $localMessage->setAccountId($account->getId()); $localMessage->setSubject((string)$message->getSubject()); $localMessage->setBodyPlain($message->getBodyPlain()); $localMessage->setBodyHtml($message->getBodyHtml()); if (!empty($message->getBodyHtml())) { $localMessage->setHtml(true); } else { $localMessage->setHtml(false); } $localMessage->setSendAt($this->time->getTime()); // convert mail provider addresses to recipient addresses $to = $this->convertAddressArray($message->getTo()); $cc = $this->convertAddressArray($message->getCc()); $bcc = $this->convertAddressArray($message->getBcc()); // save attachments $attachments = []; try { foreach ($message->getAttachments() as $entry) { /* * The mail provider API has no disposition field, so we infer it: * omit the Content-Disposition header (null) when name is null, * otherwise default to attachment. * * See https://github.com/nextcloud/mail/issues/10416 for the original motivation. * * Previously handled in TransmissionService::handleAttachment; * moved here now that LocalAttachment carries a disposition field. */ if ($entry->getName() === null) { $disposition = LocalAttachment::DISPOSITION_OMIT; } else { $disposition = LocalAttachment::DISPOSITION_ATTACHMENT; } $attachments[] = $this->attachmentService->addFileFromString( $userId, (string)$entry->getName(), (string)$entry->getType(), (string)$entry->getContents(), null, $disposition, ); } } catch (UploadException $e) { $this->purgeSavedAttachments($attachments); throw new SendException('Error: occurred while saving mail message attachment', 0, $e); } // save message $localMessage = $this->outboxService->saveMessage( $account, $localMessage, $to, $cc, $bcc, array_map(static fn (LocalAttachment $attachment) => $attachment->jsonSerialize(), $attachments) ); // send message try { $localMessage = $this->outboxService->sendMessage($localMessage, $account); } catch (\Throwable $e) { throw new SendException('Error: occurred while sending mail message', 0, $e); } return $localMessage; } /** * Converts IAddress objects collection to plain array * * @since 4.0.0 * * @param array<int,IAddress> $addresses collection of IAddress objects * * @return array<int, array{email: string, label?: string}> collection of addresses and labels */ protected function convertAddressArray(array $addresses): array { return array_map(static fn (IAddress $address) => !empty($address->getLabel()) ? ['email' => (string)$address->getAddress(), 'label' => (string)$address->getLabel()] : ['email' => (string)$address->getAddress()], $addresses); } /** * Removes attachments from data store * * @since 4.0.0 * * @param array<int, LocalAttachment> $attachments collection of local attachment objects */ protected function purgeSavedAttachments(array $attachments): void { foreach ($attachments as $attachment) { $this->attachmentService->deleteAttachment($attachment->getUserId(), $attachment->getId()); } } }
Save File
Cancel