0
0
mirror of https://github.com/nextcloud/server.git synced 2024-11-14 12:26:49 +00:00
nextcloud_server/lib/private/Mail/Message.php
Andy Scherzinger dae7c159f7
chore: Add SPDX header
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
2024-05-24 13:11:22 +02:00

323 lines
8.2 KiB
PHP

<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Mail;
use OCP\Mail\Headers\AutoSubmitted;
use OCP\Mail\IAttachment;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMessage;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Exception\RfcComplianceException;
/**
* Class Message provides a wrapper around Symfony\Component\Mime\Email (Used to be around SwiftMail)
*
* @package OC\Mail
*/
class Message implements IMessage {
private array $to = [];
private array $from = [];
private array $replyTo = [];
private array $cc = [];
private array $bcc = [];
public function __construct(
private Email $symfonyEmail,
private bool $plainTextOnly,
) {
}
/**
* @since 13.0.0
* @return $this
*/
public function attach(IAttachment $attachment): IMessage {
/** @var Attachment $attachment */
$attachment->attach($this->symfonyEmail);
return $this;
}
/**
* Can be used to "attach content inline" as message parts with specific MIME type and encoding.
* {@inheritDoc}
* @since 26.0.0
*/
public function attachInline(string $body, string $name, ?string $contentType = null): IMessage {
# To be sure this works with iCalendar messages, we encode with 8bit instead of
# quoted-printable encoding. We save the current encoder, replace the current
# encoder with an 8bit encoder and after we've finished, we reset the encoder
# to the previous one. Originally intended to be added after the message body,
# as it is curently unknown if all mail clients handle this properly if added
# before.
$this->symfonyEmail->embed($body, $name, $contentType);
return $this;
}
/**
* Converts the [['displayName' => 'email'], ['displayName2' => 'email2']] arrays to valid Adresses
*
* @param array $addresses Array of mail addresses
* @return Address[]
* @throws RfcComplianceException|InvalidArgumentException
*/
protected function convertAddresses(array $addresses): array {
$convertedAddresses = [];
if (empty($addresses)) {
return [];
}
array_walk($addresses, function ($readableName, $email) use (&$convertedAddresses) {
if (is_numeric($email)) {
$convertedAddresses[] = new Address($readableName);
} else {
$convertedAddresses[] = new Address($email, $readableName);
}
});
return $convertedAddresses;
}
/**
* Set the from address of this message.
*
* If no "From" address is used \OC\Mail\Mailer will use mail_from_address and mail_domain from config.php
*
* @param array $addresses Example: array('sender@domain.org', 'other@domain.org' => 'A name')
* @return $this
*/
public function setFrom(array $addresses): IMessage {
$this->from = $addresses;
return $this;
}
/**
* Get the from address of this message.
*/
public function getFrom(): array {
return $this->from;
}
/**
* Set the Reply-To address of this message
* @return $this
*/
public function setReplyTo(array $addresses): IMessage {
$this->replyTo = $addresses;
return $this;
}
/**
* Returns the Reply-To address of this message
*/
public function getReplyTo(): array {
return $this->replyTo;
}
/**
* Set the to addresses of this message.
*
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
* @return $this
*/
public function setTo(array $recipients): IMessage {
$this->to = $recipients;
return $this;
}
/**
* Get the to address of this message.
*/
public function getTo(): array {
return $this->to;
}
/**
* Set the CC recipients of this message.
*
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
* @return $this
*/
public function setCc(array $recipients): IMessage {
$this->cc = $recipients;
return $this;
}
/**
* Get the cc address of this message.
*/
public function getCc(): array {
return $this->cc;
}
/**
* Set the BCC recipients of this message.
*
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
* @return $this
*/
public function setBcc(array $recipients): IMessage {
$this->bcc = $recipients;
return $this;
}
/**
* Get the Bcc address of this message.
*/
public function getBcc(): array {
return $this->bcc;
}
/**
* @return $this
*/
public function setSubject(string $subject): IMessage {
$this->symfonyEmail->subject($subject);
return $this;
}
/**
* Get the from subject of this message.
*/
public function getSubject(): string {
return $this->symfonyEmail->getSubject() ?? '';
}
/**
* @return $this
*/
public function setPlainBody(string $body): IMessage {
$this->symfonyEmail->text($body);
return $this;
}
/**
* Get the plain body of this message.
*/
public function getPlainBody(): string {
/** @var string $body */
$body = $this->symfonyEmail->getTextBody() ?? '';
return $body;
}
/**
* @return $this
*/
public function setHtmlBody(string $body): IMessage {
if (!$this->plainTextOnly) {
$this->symfonyEmail->html($body);
}
return $this;
}
/**
* Set the underlying Email instance
*/
public function setSymfonyEmail(Email $symfonyEmail): void {
$this->symfonyEmail = $symfonyEmail;
}
/**
* Get the underlying Email instance
*/
public function getSymfonyEmail(): Email {
return $this->symfonyEmail;
}
/**
* @return $this
*/
public function setBody(string $body, string $contentType): IMessage {
if (!$this->plainTextOnly || $contentType !== 'text/html') {
if ($contentType === 'text/html') {
$this->symfonyEmail->html($body);
} else {
$this->symfonyEmail->text($body);
}
}
return $this;
}
/**
* Set the recipients on the symphony email
*
* Since
*
* setTo
* setFrom
* setReplyTo
* setCc
* setBcc
*
* could throw a \Symfony\Component\Mime\Exception\RfcComplianceException
* or a \Symfony\Component\Mime\Exception\InvalidArgumentException
* we wrap the calls here. We then have the validation errors all in one place and can
* throw shortly before \OC\Mail\Mailer::send
*
* @throws InvalidArgumentException|RfcComplianceException
*/
public function setRecipients(): void {
$this->symfonyEmail->to(...$this->convertAddresses($this->getTo()));
$this->symfonyEmail->from(...$this->convertAddresses($this->getFrom()));
$this->symfonyEmail->replyTo(...$this->convertAddresses($this->getReplyTo()));
$this->symfonyEmail->cc(...$this->convertAddresses($this->getCc()));
$this->symfonyEmail->bcc(...$this->convertAddresses($this->getBcc()));
}
/**
* @return $this
*/
public function useTemplate(IEMailTemplate $emailTemplate): IMessage {
$this->setSubject($emailTemplate->renderSubject());
$this->setPlainBody($emailTemplate->renderText());
if (!$this->plainTextOnly) {
$this->setHtmlBody($emailTemplate->renderHtml());
}
return $this;
}
/**
* Add the Auto-Submitted header to the email, preventing most automated
* responses to automated messages.
*
* @param AutoSubmitted::VALUE_* $value (one of AutoSubmitted::VALUE_NO, AutoSubmitted::VALUE_AUTO_GENERATED, AutoSubmitted::VALUE_AUTO_REPLIED)
* @return $this
*/
public function setAutoSubmitted(string $value): IMessage {
$headers = $this->symfonyEmail->getHeaders();
if ($headers->has(AutoSubmitted::HEADER)) {
// if the header already exsists, remove it.
// the value can be modified with some implementations
// of the interface \Swift_Mime_Header, however the
// interface doesn't, and this makes the static-code
// analysis unhappy.
// @todo check if symfony mailer can modify the autosubmitted header
$headers->remove(AutoSubmitted::HEADER);
}
$headers->addTextHeader(AutoSubmitted::HEADER, $value);
return $this;
}
/**
* Get the current value of the Auto-Submitted header. Defaults to "no"
* which is equivalent to the header not existing at all
*/
public function getAutoSubmitted(): string {
$headers = $this->symfonyEmail->getHeaders();
return $headers->has(AutoSubmitted::HEADER) ?
$headers->get(AutoSubmitted::HEADER)->getBodyAsString() : AutoSubmitted::VALUE_NO;
}
}