amazon_ses-2.0.x-dev/src/MessageBuilder.php
src/MessageBuilder.php
<?php
namespace Drupal\amazon_ses;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
/**
* Amazon SES message builder service.
*/
class MessageBuilder implements MessageBuilderInterface {
use StringTranslationTrait;
public function __construct(
protected LoggerChannelInterface $logger,
protected ConfigFactoryInterface $configFactory,
protected FileSystemInterface $fileSystem,
protected MimeTypeGuesserInterface $mimeTypeGuesser,
) {}
/**
* {@inheritdoc}
*/
public function buildMessage(array $message) {
$to = preg_split('/[,;]/', $message['to']);
$from = $this->parseAddress($message['from']);
$from_address = new Address($from['address'], $from['name']);
if ($message['body'] instanceof Markup) {
$message['body'] = $message['body']->__toString();
}
$email = (new Email())
->from($from_address)
->to(...$to)
->subject($message['subject']);
if (isset($message['reply-to'])) {
$email->replyTo($message['reply-to']);
}
if (isset($message['headers'])) {
$content_type = $this->getContentType($message['headers']);
if (!empty($message['headers']['Cc'])) {
if (is_array($message['headers']['Cc'])) {
$cc_addresses = $message['headers']['Cc'];
}
else {
$cc_addresses = preg_split('/[,;]/', $message['headers']['Cc']);
}
$email->cc(...$cc_addresses);
}
if (!empty($message['headers']['Bcc'])) {
if (is_array($message['headers']['Bcc'])) {
$bcc_addresses = $message['headers']['Bcc'];
}
else {
$bcc_addresses = preg_split('/[,;]/', $message['headers']['Bcc']);
}
$email->bcc(...$bcc_addresses);
}
}
else {
$content_type = 'text/plain';
}
switch ($content_type) {
case 'text/plain':
$email->text($message['body']);
break;
case 'text/html':
$email->html($message['body']);
break;
case 'multipart/mixed':
$parts = $this->getParts($message);
if ($parts) {
$email->text($parts['plain']);
$email->html($parts['html']);
}
break;
default:
$email->text($message['body']);
$warning = $this->t('Unsupported content type: @type', [
'@type' => $content_type,
]);
$this->logger->warning($warning);
break;
}
if (!empty($message['params']['attachments'])) {
foreach ($message['params']['attachments'] as $attachment) {
$file = $this->processAttachment($attachment);
if ($file['content']) {
$email->attach($file['content'], $file['name'], $file['mime']);
}
}
}
return $email;
}
/**
* Parse an email address into a separate name and email.
*
* @param string $address
* The email address to parse.
*
* @return array
* An array of email address components.
*/
protected function parseAddress(string $address) {
if (preg_match('/^"?(?<name>.*?)"?\s\<(?<address>.*)\>$/', $address, $matches)) {
return [
'name' => $matches['name'],
'address' => $matches['address'],
];
}
else {
return [
'name' => '',
'address' => $address,
];
}
}
/**
* Determine the content type of a message.
*
* @param array $headers
* An array of message headers.
*
* @return string
* The content type.
*/
protected function getContentType(array $headers) {
if (isset($headers['Content-Type'])) {
$content_type_parts = explode(';', $headers['Content-Type']);
$content_type = trim($content_type_parts[0]);
}
else {
$content_type = 'text/plain';
}
return $content_type;
}
/**
* Get the plain text and HTML parts of a multipart MIME message.
*
* @param array $message
* The message being sent.
*
* @return array|false
* An array of the part contents, or FALSE if it could not be parsed.
*/
protected function getParts(array $message) {
$message_parts = [];
$boundary = NULL;
// Parse the Content-Type header to find the boundary string.
$content_type_parts = explode(';', $message['headers']['Content-Type']);
foreach ($content_type_parts as $part) {
if (strpos($part, 'boundary') !== FALSE) {
$boundary_parts = explode('=', $part);
$boundary = trim($boundary_parts[1], '"');
}
}
// Exit if no boundary string is found.
if (!$boundary) {
return FALSE;
}
$body_parts = explode($boundary, $message['body']);
foreach ($body_parts as $part) {
if (strpos($part, 'multipart/alternative') !== FALSE) {
$boundary_start = strpos($part, 'boundary') + 10;
if ($boundary_start !== FALSE) {
$boundary_end = strpos($part, '"', $boundary_start);
$boundary_length = $boundary_end - $boundary_start;
$alternative_boundary = substr($part, $boundary_start, $boundary_length);
// Exit if alternative boundary could not be determined.
if (!$alternative_boundary) {
return FALSE;
}
$alternative_parts = explode($alternative_boundary, $part);
foreach ($alternative_parts as $part) {
if (strpos($part, 'text/plain') !== FALSE) {
$message_parts['plain'] = $this->getPartContent($part);
}
elseif (strpos($part, 'text/html') !== FALSE) {
$message_parts['html'] = $this->getPartContent($part);
}
}
}
}
}
return $message_parts;
}
/**
* Get the content from a MIME message part.
*
* @param string $part
* The message part.
*
* @return string|false
* The content, or FALSE if it could not be parsed.
*/
protected function getPartContent($part) {
$split = preg_split('#\r?\n\r?\n#', $part);
if ($split && isset($split[1])) {
unset($split[0]);
return implode('', $split);
}
return FALSE;
}
/**
* Process attachment parameters.
*
* @param array $attachment
* The attachment parameters.
*
* @return array
* An array of attachment data to add to the message.
*/
protected function processAttachment(array $attachment) {
$file = [
'content' => NULL,
'name' => NULL,
'mime' => NULL,
];
if (!empty($attachment['filepath'])) {
if (is_file($attachment['filepath'])) {
$file['content'] = file_get_contents($attachment['filepath']);
}
else {
$path = $this->fileSystem->realpath($attachment['filepath']);
if ($path) {
$file['content'] = file_get_contents($path);
}
}
if (!empty($attachment['filename'])) {
$file['name'] = $attachment['filename'];
}
else {
$file['name'] = basename($attachment['filepath']);
}
if (!empty($attachment['filemime'])) {
$file['mime'] = $attachment['filemime'];
}
else {
$file['mime'] = $this->mimeTypeGuesser->guessMimeType($file['name']);
}
}
elseif (!empty($attachment['filecontent'])) {
$file['content'] = $attachment['filecontent'];
if (!empty($attachment['filename'])) {
$file['name'] = $attachment['filename'];
}
else {
$file['name'] = 'attachment.dat';
}
if (!empty($attachment['filemime'])) {
$file['mime'] = $attachment['filemime'];
}
else {
$file['mime'] = $this->mimeTypeGuesser->guessMimeType($file['name']);
}
}
return $file;
}
}
