From c6d44fecb62631adf3f6cac6db5cfe6d1eb92eea Mon Sep 17 00:00:00 2001 From: Alex Tselegidis Date: Thu, 21 Nov 2024 16:51:31 +0100 Subject: [PATCH] Replace the internal email library with phpmailer for better smtp server support --- application/libraries/Email_messages.php | 110 +++++++++++++---------- composer.json | 3 +- composer.lock | 83 ++++++++++++++++- 3 files changed, 149 insertions(+), 47 deletions(-) diff --git a/application/libraries/Email_messages.php b/application/libraries/Email_messages.php index 6a005ee1..ebe40f3c 100644 --- a/application/libraries/Email_messages.php +++ b/application/libraries/Email_messages.php @@ -11,6 +11,10 @@ * @since v1.4.0 * ---------------------------------------------------------------------------- */ +use PHPMailer\PHPMailer\PHPMailer; +use PHPMailer\PHPMailer\SMTP; +use PHPMailer\PHPMailer\Exception; + /** * Email messages library. * @@ -59,6 +63,8 @@ class Email_messages * @param string $ics_stream ICS file contents. * @param string|null $timezone Custom timezone. * + * @throws DateInvalidTimeZoneException + * @throws DateMalformedStringException * @throws Exception */ public function send_appointment_saved( @@ -106,27 +112,18 @@ class Email_messages true, ); - $from_name = config('from_name') ?: $settings['company_name']; - $from_address = config('from_address') ?: $settings['company_email']; - $reply_to = config('reply_to') ?: $settings['company_email']; + $php_mailer = $this->get_php_mailer(); - $this->CI->email->from($from_address, $from_name); + $php_mailer->addAddress($recipient_email); - if ($reply_to) { - $this->CI->email->reply_to($reply_to); - } + $php_mailer->isHTML(); + $php_mailer->Subject = $subject; + $php_mailer->Body = $html; + $php_mailer->AltBody = $html; - $this->CI->email->to($recipient_email); + $php_mailer->addStringAttachment($ics_stream, 'invitation.ics', PHPMailer::ENCODING_BASE64, 'text/vcalendar'); - $this->CI->email->subject($subject); - - $this->CI->email->message($html); - - $this->CI->email->attach($ics_stream, 'attachment', 'invitation.ics', 'text/vcalendar'); - - if (!$this->CI->email->send(false)) { - throw new RuntimeException('Email was not sent: ' . $this->CI->email->print_debugger()); - } + $php_mailer->send(); } /** @@ -141,6 +138,8 @@ class Email_messages * @param string|null $reason Removal reason. * @param string|null $timezone Custom timezone. * + * @throws DateInvalidTimeZoneException + * @throws DateMalformedStringException * @throws Exception */ public function send_appointment_deleted( @@ -183,25 +182,18 @@ class Email_messages true, ); - $from_name = config('from_name') ?: $settings['company_name']; - $from_address = config('from_address') ?: $settings['company_email']; - $reply_to = config('reply_to') ?: $settings['company_email']; + $subject = lang('appointment_cancelled_title'); - $this->CI->email->from($from_address, $from_name); + $php_mailer = $this->get_php_mailer(); - if ($reply_to) { - $this->CI->email->reply_to($reply_to); - } + $php_mailer->addAddress($recipient_email); - $this->CI->email->to($recipient_email); + $php_mailer->isHTML(); + $php_mailer->Subject = $subject; + $php_mailer->Body = $html; + $php_mailer->AltBody = $html; - $this->CI->email->subject(lang('appointment_cancelled_title')); - - $this->CI->email->message($html); - - if (!$this->CI->email->send(false)) { - throw new RuntimeException('Email was not sent: ' . $this->CI->email->print_debugger()); - } + $php_mailer->send(); } /** @@ -210,6 +202,8 @@ class Email_messages * @param string $password New password. * @param string $recipient_email Recipient email address. * @param array $settings App settings. + * + * @throws Exception */ public function send_password(string $password, string $recipient_email, array $settings): void { @@ -223,24 +217,50 @@ class Email_messages true, ); - $from_name = config('from_name') ?: $settings['company_name']; - $from_address = config('from_address') ?: $settings['company_email']; - $reply_to = config('reply_to') ?: $settings['company_email']; + $subject = lang('new_account_password'); - $this->CI->email->from($from_address, $from_name); + $php_mailer = $this->get_php_mailer(); - if ($reply_to) { - $this->CI->email->reply_to($reply_to); + $php_mailer->addAddress($recipient_email); + + $php_mailer->isHTML(); + $php_mailer->Subject = $subject; + $php_mailer->Body = $html; + $php_mailer->AltBody = $html; + + $php_mailer->send(); + } + + /** + * Create PHP Mailer instance based on the email configuration. + * + * @return PHPMailer + * + * @throws Exception + */ + private function get_php_mailer(): PHPMailer + { + $php_mailer = new PHPMailer(true); + + $php_mailer->SMTPDebug = config('smtp_debug') ? SMTP::DEBUG_SERVER : null; + + if (config('protocol') === 'smtp') { + $php_mailer->isSMTP(); + $php_mailer->Host = config('smtp_host'); + $php_mailer->SMTPAuth = config('smtp_auth'); + $php_mailer->Username = config('smtp_user'); + $php_mailer->Password = config('smtp_pass'); + $php_mailer->SMTPSecure = config('smtp_crypto'); + $php_mailer->Port = config('smtp_port'); } - $this->CI->email->to($recipient_email); + $from_name = config('from_name') ?: setting('company_name'); + $from_address = config('from_address') ?: setting('company_email'); + $reply_to_address = config('reply_to') ?: setting('company_email'); - $this->CI->email->subject(lang('new_account_password')); + $php_mailer->setFrom($from_address, $from_name); + $php_mailer->addReplyTo($reply_to_address); - $this->CI->email->message($html); - - if (!$this->CI->email->send(false)) { - throw new RuntimeException('Email was not sent: ' . $this->CI->email->print_debugger()); - } + return $php_mailer; } } diff --git a/composer.json b/composer.json index b3671a9d..7025781c 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,8 @@ "guzzlehttp/guzzle": "^7.5.0", "sabre/vobject": "^4.5", "ezyang/htmlpurifier": "^4.17", - "symfony/finder": "^6.4" + "symfony/finder": "^6.4", + "phpmailer/phpmailer": "^6.9" }, "require-dev": { "roave/security-advisories": "dev-master", diff --git a/composer.lock b/composer.lock index 32ba04e8..96dbc869 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1600e731726d98eb3e9b5c299cdf108e", + "content-hash": "dbf5da5fb7080dd1067d78bcc8625e2d", "packages": [ { "name": "ezyang/htmlpurifier", @@ -956,6 +956,87 @@ }, "time": "2020-10-15T08:29:30+00:00" }, + { + "name": "phpmailer/phpmailer", + "version": "v6.9.2", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "a7b17b42fa4887c92146243f3d2f4ccb962af17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a7b17b42fa4887c92146243f3d2f4ccb962af17c", + "reference": "a7b17b42fa4887c92146243f3d2f4ccb962af17c", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.2", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.2" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2024-10-09T10:07:50+00:00" + }, { "name": "phpseclib/phpseclib", "version": "3.0.37",