appwrite/src/Appwrite/Platform/Workers/Mails.php
2025-08-16 12:49:58 +02:00

206 lines
6.5 KiB
PHP

<?php
namespace Appwrite\Platform\Workers;
use Appwrite\Template\Template;
use Exception;
use PHPMailer\PHPMailer\PHPMailer;
use Swoole\Runtime;
use Utopia\Logger\Log;
use Utopia\Platform\Action;
use Utopia\Queue\Message;
use Utopia\Registry\Registry;
use Utopia\System\System;
class Mails extends Action
{
protected int $previewMaxLen = 150;
protected string $whitespaceCodes = '&#xa0;&#x200C;&#x200B;&#x200D;&#x200E;&#x200F;&#xFEFF;';
public static function getName(): string
{
return 'mails';
}
/**
* @throws Exception
*/
public function __construct()
{
$this
->desc('Mails worker')
->inject('message')
->inject('register')
->inject('log')
->callback($this->action(...));
}
/**
* @var array<string, string>
*/
protected array $richTextParams = [
'b' => '<strong>',
'/b' => '</strong>',
];
/**
* @param Message $message
* @param Registry $register
* @param Log $log
* @throws \PHPMailer\PHPMailer\Exception
* @return void
* @throws Exception
*/
public function action(Message $message, Registry $register, Log $log): void
{
Runtime::setHookFlags(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_TCP);
$payload = $message->getPayload() ?? [];
if (empty($payload)) {
throw new Exception('Missing payload');
}
$smtp = $payload['smtp'];
if (empty($smtp) && empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception('Skipped mail processing. No SMTP configuration has been set.');
}
$log->addTag('type', empty($smtp) ? 'cloud' : 'smtp');
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$recipient = $payload['recipient'];
$subject = $payload['subject'];
$variables = $payload['variables'];
$variables['host'] = $protocol . '://' . $hostname;
$name = $payload['name'];
$body = $payload['body'];
$preview = $payload['preview'] ?? '';
$variables['subject'] = $subject;
$variables['year'] = date("Y");
$attachment = $payload['attachment'] ?? [];
$bodyTemplate = $payload['bodyTemplate'];
if (empty($bodyTemplate)) {
$bodyTemplate = __DIR__ . '/../../../../app/config/locale/templates/email-base.tpl';
}
$bodyTemplate = Template::fromFile($bodyTemplate);
$bodyTemplate->setParam('{{body}}', $body, escapeHtml: false);
foreach ($variables as $key => $value) {
// TODO: hotfix for redirect param
$bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: $key !== 'redirect');
}
foreach ($this->richTextParams as $key => $value) {
$bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: false);
}
$previewWhitespace = '';
if (!empty($preview)) {
$previewTemplate = Template::fromString($preview);
foreach ($variables as $key => $value) {
$previewTemplate->setParam('{{' . $key . '}}', $value);
}
// render() will return the subject in <p> tags, so use strip_tags() to remove them
$preview = \strip_tags($previewTemplate->render());
$previewLen = strlen($preview);
if ($previewLen < $this->previewMaxLen) {
$previewWhitespace = str_repeat($this->whitespaceCodes, $this->previewMaxLen - $previewLen);
}
}
$bodyTemplate->setParam('{{preview}}', $preview);
$bodyTemplate->setParam('{{previewWhitespace}}', $previewWhitespace, false);
$body = $bodyTemplate->render();
$subjectTemplate = Template::fromString($subject);
foreach ($variables as $key => $value) {
$subjectTemplate->setParam('{{' . $key . '}}', $value);
}
// render() will return the subject in <p> tags, so use strip_tags() to remove them
$subject = \strip_tags($subjectTemplate->render());
/** @var PHPMailer $mail */
$mail = empty($smtp)
? $register->get('smtp')
: $this->getMailer($smtp);
$mail->clearAddresses();
$mail->clearAllRecipients();
$mail->clearReplyTos();
$mail->clearAttachments();
$mail->clearBCCs();
$mail->clearCCs();
$mail->addAddress($recipient, $name);
$mail->Subject = $subject;
$mail->Body = $body;
$mail->AltBody = $body;
$mail->AltBody = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $mail->AltBody);
$mail->AltBody = \strip_tags($mail->AltBody);
$mail->AltBody = \trim($mail->AltBody);
$replyTo = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$replyToName = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
if (!empty($smtp)) {
$replyTo = !empty($smtp['replyTo']) ? $smtp['replyTo'] : $smtp['senderEmail'];
$replyToName = $smtp['senderName'];
}
$mail->addReplyTo($replyTo, $replyToName);
if (!empty($attachment['content'] ?? '')) {
$mail->AddStringAttachment(
base64_decode($attachment['content']),
$attachment['filename'] ?? 'unknown.file',
$attachment['encoding'] ?? PHPMailer::ENCODING_BASE64,
$attachment['type'] ?? 'plain/text'
);
}
try {
$mail->send();
} catch (\Throwable $error) {
throw new Exception('Error sending mail: ' . $error->getMessage(), 500);
}
}
/**
* @param array $smtp
* @return PHPMailer
* @throws \PHPMailer\PHPMailer\Exception
*/
protected function getMailer(array $smtp): PHPMailer
{
$mail = new PHPMailer(true);
$mail->isSMTP();
$username = $smtp['username'];
$password = $smtp['password'];
$mail->XMailer = 'Appwrite Mailer';
$mail->Host = $smtp['host'];
$mail->Port = $smtp['port'];
$mail->SMTPAuth = (!empty($username) && !empty($password));
$mail->Username = $username;
$mail->Password = $password;
$mail->SMTPSecure = $smtp['secure'];
$mail->SMTPAutoTLS = false;
$mail->CharSet = 'UTF-8';
$mail->setFrom($smtp['senderEmail'], $smtp['senderName']);
$mail->isHTML();
return $mail;
}
}