This commit is contained in:
Lonnie Ezell 2022-05-31 23:00:21 -05:00
parent 547fb221fc
commit 7ec7c1779e
No known key found for this signature in database
GPG Key ID: 71836D6EF250F1D1
9 changed files with 592 additions and 43 deletions

View File

@ -73,7 +73,7 @@ class Address
$element = reset($addresses);
if (is_string($element) && strpos($element, ',') !== false) {
$addresses = preg_split('/[\s,]/', $element, -1, PREG_SPLIT_NO_EMPTY);
$addresses = preg_split('/[\n\t\v,]/', $element, -1, PREG_SPLIT_NO_EMPTY);
}
}

View File

@ -21,6 +21,9 @@ use DateTimeInterface;
*
* Represents a single email message.
* This class extends HTTP\Message and uses headers for most storage and retrieval.
*
* @see https://datatracker.ietf.org/doc/html/rfc5322
* @see https://datatracker.ietf.org/doc/html/rfc2047
*/
class Email extends Message
{
@ -45,7 +48,7 @@ class Email extends Message
*
* @var Attachment[]
*/
protected $attachements = [];
protected $attachments = [];
/**
* This email's unique Message ID.
@ -104,8 +107,8 @@ class Email extends Message
$getter = 'get' . ucfirst($setter);
if (array_key_exists($setter, $data)) {
if ($overwrite || null === $this->{$getter}()) {
$this->{$method}($data[$method]);
if ($overwrite || $this->{$getter}() === null) {
$this->{$setter}($data[$setter]);
}
}
}
@ -130,7 +133,7 @@ class Email extends Message
if (isset($exclude) && in_array($name, $exclude, true)) {
continue;
}
// Check includion
// Check inclusion
if (isset($include) && ! in_array($name, $include, true)) {
continue;
}
@ -140,12 +143,14 @@ class Email extends Message
$string .= implode(',', array_map('__toString', $value));
} elseif (is_array($value)) {
foreach ($value as $header) {
$string .= $header . $this->newline;
$string .= $header . $newline;
}
} else {
$string .= $value . $this->newline; // Trailing newline is intentional
$string .= (string)$value->getValue() . $newline; // Trailing newline is intentional
}
}
return $string;
}
//--------------------------------------------------------------------
@ -183,9 +188,13 @@ class Email extends Message
/**
* @return $this
*/
public function from(string $address)
public function from(string $address, ?string $name=null)
{
return $this->setHeader('From', Address::create($address));
$addr = $name !== null
? new Address($address, $name)
: Address::create($address);
return $this->setHeader('From', $addr);
}
/**
@ -195,6 +204,12 @@ class Email extends Message
*/
public function to(...$addresses)
{
// If an array of address was pushed in
// break it out so we get only the actual emails
if($addresses[0] && is_array($addresses[0])) {
$addresses = $addresses[0];
}
return $this->setHeader('To', Address::createArray($addresses));
}
@ -205,6 +220,12 @@ class Email extends Message
*/
public function cc(...$addresses)
{
// If an array of address was pushed in
// break it out so we get only the actual emails
if($addresses[0] && is_array($addresses[0])) {
$addresses = $addresses[0];
}
return $this->setHeader('Cc', Address::createArray($addresses));
}
@ -215,15 +236,25 @@ class Email extends Message
*/
public function bcc(...$addresses)
{
// If an array of address was pushed in
// break it out so we get only the actual emails
if($addresses[0] && is_array($addresses[0])) {
$addresses = $addresses[0];
}
return $this->setHeader('Bcc', Address::createArray($addresses));
}
/**
* @return $this
*/
public function replyTo(string $address)
public function replyTo(string $address, ?string $name=null)
{
return $this->setHeader('From', Address::create($address));
$addr = $name !== null
? new Address($address, $name)
: Address::create($address);
return $this->setHeader('Reply-To', $addr);
}
/**
@ -266,12 +297,12 @@ class Email extends Message
public function getSubject(): ?string
{
return $this->hasHeader('Subject') ? $this->getHeader('Subject')->getValue() : null;
return $this->hasHeader('Subject') ? $this->header('Subject')->getValue() : null;
}
public function getFrom(): ?Address
{
return $this->hasHeader('From') ? Address::create($this->getHeader('From')->getValue()) : null;
return $this->hasHeader('From') ? Address::create($this->header('From')->getValue()) : null;
}
/**
@ -279,7 +310,7 @@ class Email extends Message
*/
public function getTo(): ?array
{
return $this->hasHeader('To') ? Address::createArray($this->getHeader('To')->getValue()) : null;
return $this->hasHeader('To') ? Address::createArray($this->header('To')->getValue()) : null;
}
/**
@ -287,7 +318,7 @@ class Email extends Message
*/
public function getCc(): ?array
{
return $this->hasHeader('Cc') ? Address::createArray($this->getHeader('Cc')->getValue()) : null;
return $this->hasHeader('Cc') ? Address::createArray($this->header('Cc')->getValue()) : null;
}
/**
@ -295,12 +326,12 @@ class Email extends Message
*/
public function getBcc(): ?array
{
return $this->hasHeader('Bcc') ? Address::createArray($this->getHeader('Bcc')->getValue()) : null;
return $this->hasHeader('Bcc') ? Address::createArray($this->header('Bcc')->getValue()) : null;
}
public function getReplyTo(): ?Address
{
return $this->hasHeader('Reply-To') ? Address::create($this->getHeader('Reply-To')->getValue()) : null;
return $this->hasHeader('Reply-To') ? Address::create($this->header('Reply-To')->getValue()) : null;
}
public function getReturnPath(): ?Address
@ -310,7 +341,7 @@ class Email extends Message
}
// Get just the email portion of the stored address
$address = $this->getHeader('Return-Path')->getValue();
$address = $this->header('Return-Path')->getValue();
$email = Address::split($address)['email'];
// Force Address to use angle braces by giving an empty display name
@ -326,7 +357,7 @@ class Email extends Message
public function getPriority(): ?int
{
if ($this->hasHeader('X-Priority')) {
return array_search($this->getHeader('X-Priority')->getValue(), self::PRIORITIES, true) ?: null;
return array_search($this->header('X-Priority')->getValue(), self::PRIORITIES, true) ?: null;
}
return null;
@ -334,7 +365,7 @@ class Email extends Message
public function getDate(): ?Time
{
return $this->hasHeader('Date') ? Time::parse($this->getHeader('Date')->getValue()) : null;
return $this->hasHeader('Date') ? Time::parse($this->header('Date')->getValue()) : null;
}
/**
@ -343,7 +374,7 @@ class Email extends Message
*/
public function getMessageId(): ?string
{
if (null === $this->messageId && $returnPath = $this->getReturnPath()) {
if ($this->messageId === null && $returnPath = $this->getReturnPath()) {
// Use a unique ID with the same domain as the Return-Path email
$this->messageId = '<' . uniqid('', true) . strstr($returnPath->getEmail(), '@') . '>';
}
@ -434,6 +465,20 @@ class Email extends Message
return $this->body($body);
}
/**
* Sets a header and it's value.
*
* Extends the default
*
* @param array|string|null $value
*
* @return $this
*/
public function setHeader(string $name, $value): self
{
}
/**
* Magic method to allow CI3-style methods (like "setReplyTo()") to
* forward to their equivalent setters.

View File

@ -159,33 +159,64 @@ final class Encode
/**
* Performs "Q Encoding" on a string for use in email headers.
* This is mostly straight from CodeIgniter 3.
* It's related but not identical to quoted-printable, so it has its
* own method.
*
* @see https://www.freesoft.org/CIE/RFC/1522/6.htm
* @param string $str
*
* @return string
*/
public function Q(string $string): string
protected function Q($str)
{
$string = str_replace(["\r", "\n"], '', $string);
$str = str_replace(["\r", "\n"], '', $str);
if ($this->charset === 'UTF-8') {
// Note: We used to have mb_encode_mimeheader() as the first choice
// here, but it turned out to be buggy and unreliable. DO NOT
// re-add it! -- Narf
if (extension_loaded('iconv')) {
$output = @iconv_mime_encode('', $str, [
'scheme' => 'Q',
'line-length' => 76,
'input-charset' => $this->charset,
'output-charset' => $this->charset,
'line-break-chars' => $this->CRLF,
]);
// There are reports that iconv_mime_encode() might fail and return FALSE
if ($output !== false) {
// iconv_mime_encode() will always put a header field name.
// We've passed it an empty one, but it still prepends our
// encoded string with ': ', so we need to strip it.
return static::substr($output, 2);
}
$chars = iconv_strlen($str, 'UTF-8');
} elseif (extension_loaded('mbstring')) {
$chars = mb_strlen($str, 'UTF-8');
}
}
// We might already have this set for UTF-8
if (! isset($chars)) {
$chars = static::strlen($str);
}
$output = '=?' . $this->charset . '?Q?';
$chars = $this->charset === 'UTF-8' ? mb_strlen($string, 'UTF-8') : mb_strlen($string, '8bit');
$length = mb_strlen($output, '8bit');
for ($i = 0; $i < $chars; $i++) {
$chr = ($this->charset === 'UTF-8' && extension_loaded('iconv'))
? '=' . implode('=', str_split(strtoupper(bin2hex(iconv_substr($string, $i, 1, $this->charset))), 2))
: '=' . strtoupper(bin2hex($string[$i]));
for ($i = 0, $length = static::strlen($output); $i < $chars; $i++) {
$chr = ($this->charset === 'UTF-8' && extension_loaded('iconv')) ? '=' . implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2)) : '=' . strtoupper(bin2hex($str[$i]));
$chrlen = mb_strlen($chr, '8bit');
// RFC 2045 sets a limit of 76 characters per line, but leave space for ?= at the end of each line
if ($length + $chrlen > 74) {
$output .= '?=' . $this->crlf // EOL
// RFC 2045 sets a limit of 76 characters per line.
// We'll append ?= to the end of each line though.
if ($length + ($l = static::strlen($chr)) > 74) {
$output .= '?=' . $this->CRLF // EOL
. ' =?' . $this->charset . '?Q?' . $chr; // New line
$length = 6 + mb_strlen($this->charset, '8bit') + $chrlen; // Reset the length for the new line
$length = 6 + static::strlen($this->charset) + $l; // Reset the length for the new line
} else {
$output .= $chr;
$length += $chrlen;
$length += $l;
}
}

View File

@ -16,6 +16,8 @@ use CodeIgniter\Mailer\Email;
use CodeIgniter\Mailer\Exceptions\MailerException;
use CodeIgniter\Mailer\MailerInterface;
use Config\Mailer;
use CodeIgniter\Mailer\Encode;
use CodeIgniter\Mailer\Format;
/**
* Mail Handler
@ -30,7 +32,7 @@ abstract class BaseHandler implements MailerInterface
*
* @var string
*/
protected $protocol;
protected string $protocol;
/**
* The Mailer config.

View File

@ -137,6 +137,39 @@ class AddressTest extends CIUnitTestCase
];
}
public function testCreateArray()
{
$addresses = [
'leia@alderaan.org',
'"Luke Skywalker" <luke@tatooine.org>',
];
$created = Address::createArray($addresses);
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $created);
$this->assertTrue(in_array('leia@alderaan.org', $emails));
$this->assertTrue(in_array('luke@tatooine.org', $emails));
}
public function testCreateArrayFromCSV()
{
$addresses = [
'leia@alderaan.org,"Luke Skywalker" <luke@tatooine.org>',
];
$created = Address::createArray($addresses);
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $created);
$this->assertTrue(in_array('leia@alderaan.org', $emails));
$this->assertTrue(in_array('luke@tatooine.org', $emails));
}
/**
* @dataProvider userProvider
*/

View File

@ -0,0 +1,33 @@
<?php
namespace CodeIgniter\Mailer;
use CodeIgniter\Test\CIUnitTestCase;
class AttachmentTest extends CIUnitTestCase
{
public function testConstructorSimple()
{
$attachment = new Attachment(__FILE__);
$this->assertSame('AttachmentTest.php', $attachment->getBasename());
}
public function testGetContentID()
{
$attachment = new Attachment(__FILE__);
$this->assertStringContainsString('AttachmentTest.php@', $attachment->getContentId());
}
public function testGetContent()
{
$attachment = new Attachment(__FILE__);
$body = $attachment->getContent();
$this->assertNotEmpty($body);
// Detect if string if base64_encoded
$this->assertTrue(preg_match('%^[a-zA-Z0-9/+]*={0,2}$%', $body) === 1);
}
}

View File

@ -2,17 +2,338 @@
namespace CodeIgniter\Mailer;
use CodeIgniter\I18n\Time;
use CodeIgniter\Test\CIUnitTestCase;
class EmailTest extends CIUnitTestCase
{
public function tearDown(): void
{
// Restore file permissions after unreadable attachment test
$thefile = SUPPORTPATH . 'Mailer/ci-logo-not-readable.png';
chmod($thefile, 0664);
}
//--------------------------------------------------------------------
// Test constructor & configs
public function testEmptyConstructor()
{
$email = new Email();
// Just to make sure it didn't crash
$this->assertInstanceOf(Email::class, $email);
$this->assertNull($email->getBody());
$this->assertNull($email->getSubject());
$this->assertNull($email->getFrom());
$this->assertNull($email->getTo());
$this->assertNull($email->getCc());
$this->assertNull($email->getBcc());
$this->assertNull($email->getReplyTo());
$this->assertNull($email->getReturnPath());
$this->assertNull($email->getPriority());
$this->assertNull($email->getDate());
}
public function testConstructorUsesData()
{
$address = 'leia@alderaan.org';
$email = new Email([
'from' => $address,
'body' => 'Email body',
'subject' => 'The Rebels need you!',
'from' => 'leia@alderaan.org',
'to' => 'lukeskywalker@tattoine.org',
'cc' => '"Obi Wan Kenobi" <obiwan@tattoine.org>',
'bcc' => ['one@example.com', 'two@example.com'],
'replyTo' => 'no-reply@alderaan.org',
'returnPath' => 'bounces@alderaan.org',
'priority' => 2,
'date' => Time::now(),
'truth' => 'out there',
]);
$this->assertSame($address, (string) $email->getFrom());
$this->assertSame('Email body', (string) $email->getBody());
$this->assertSame('The Rebels need you!', (string) $email->getSubject());
$this->assertSame('leia@alderaan.org', (string) $email->getFrom());
$this->assertSame('lukeskywalker@tattoine.org', (string)$email->getTo()[0]);
$this->assertSame('"Obi Wan Kenobi" <obiwan@tattoine.org>', (string)$email->getCc()[0]);
$this->assertSame('one@example.com', (string)$email->getBcc()[0]);
$this->assertSame('no-reply@alderaan.org', (string)$email->getReplyTo());
$this->assertSame('bounces@alderaan.org', (string)$email->getReturnPath());
$this->assertSame(2, $email->getPriority());
$this->assertInstanceOf(Time::class, $email->getDate());
}
public function testSetters()
{
$email = new Email();
$email->body('Email body');
$email->subject('The Rebels need you!');
$email->from('leia@alderaan.org');
$email->to('lukeskywalker@tattoine.org');
$email->cc('"Obi Wan Kenobi" <obiwan@tattoine.org>');
$email->bcc('one@example.com', 'two@example.com');
$email->replyTo('no-reply@alderaan.org');
$email->returnPath('bounces@alderaan.org');
$email->priority(2);
$email->date(Time::now());
$this->assertSame('Email body', (string) $email->getBody());
$this->assertSame('The Rebels need you!', (string) $email->getSubject());
$this->assertSame('leia@alderaan.org', (string) $email->getFrom());
$this->assertSame('lukeskywalker@tattoine.org', (string)$email->getTo()[0]);
$this->assertSame('"Obi Wan Kenobi" <obiwan@tattoine.org>', (string)$email->getCc()[0]);
$this->assertSame('one@example.com', (string)$email->getBcc()[0]);
$this->assertSame('no-reply@alderaan.org', (string)$email->getReplyTo());
$this->assertSame('bounces@alderaan.org', (string)$email->getReturnPath());
$this->assertSame(2, $email->getPriority());
$this->assertInstanceOf(Time::class, $email->getDate());
}
public function testMessageId()
{
$email = new Email([
'returnPath' => 'no-reply@alderaan.org'
]);
$this->assertMatchesRegularExpression("/^<(.*)@alderaan.org>/", $email->getMessageId());
}
public function testMessageIdReturnsNull()
{
$email = new Email();
$this->assertNull($email->getMessageId());
}
public function testGetBoundaryCreatesBoundary()
{
$email = new Email();
$this->assertStringContainsString('foo_', $email->getBoundary('foo'));
}
public function testCI3SetterMagic()
{
$email = new Email();
$email->setBody('Email body');
$email->setSubject('The Rebels need you!');
$email->setFrom('leia@alderaan.org');
$email->setTo('lukeskywalker@tattoine.org');
$email->setCc('"Obi Wan Kenobi" <obiwan@tattoine.org>');
$email->setBcc('one@example.com', 'two@example.com');
$email->setReplyTo('no-reply@alderaan.org');
$email->setReturnPath('bounces@alderaan.org');
$email->setPriority(2);
$email->setDate(Time::now());
$this->assertSame('Email body', (string) $email->getBody());
$this->assertSame('The Rebels need you!', (string) $email->getSubject());
$this->assertSame('leia@alderaan.org', (string) $email->getFrom());
$this->assertSame('lukeskywalker@tattoine.org', (string)$email->getTo()[0]);
$this->assertSame('"Obi Wan Kenobi" <obiwan@tattoine.org>', (string)$email->getCc()[0]);
$this->assertSame('one@example.com', (string)$email->getBcc()[0]);
$this->assertSame('no-reply@alderaan.org', (string)$email->getReplyTo());
$this->assertSame('bounces@alderaan.org', (string)$email->getReturnPath());
$this->assertSame(2, $email->getPriority());
$this->assertInstanceOf(Time::class, $email->getDate());
}
public function testSetMessage()
{
$email = new Email();
$email->setMessage('Email body');
$this->assertSame('Email body', $email->getBody());
}
//--------------------------------------------------------------------
// Test setting the "from" property
// NOTE: Return-Path should NOT be automatically set,
// see: https://www.postmastery.com/about-the-return-path-header/
public function testSetFromMailerOnly()
{
$email = new Email();
$email->from('leia@alderaan.org');
$this->assertEquals('leia@alderaan.org', (string)$email->getFrom());
$this->assertNull($email->header('Return-Path'));
}
public function testSetFromMailerAndName()
{
$email = new Email();
$email->from('leia@alderaan.org', 'Princess Leia');
$this->assertEquals('"Princess Leia" <leia@alderaan.org>', $email->getFrom());
}
public function testSetFromMailerAndNameString()
{
$email = new Email();
$email->from('"Princess Leia" <leia@alderaan.org>');
$this->assertEquals('"Princess Leia" <leia@alderaan.org>', (string)$email->getFrom());
}
//--------------------------------------------------------------------
// Test setting the "replyTo" property
public function testSetReplyToMailerOnly()
{
$mailer = new Email();
$mailer->setReplyTo('leia@alderaan.org');
$this->assertEquals('leia@alderaan.org', (string)$mailer->getReplyTo());
}
public function testSetReplyToMailerAndName()
{
$mailer = new Email();
$mailer->setReplyTo('leia@alderaan.org', 'Princess Leia');
$this->assertEquals('"Princess Leia" <leia@alderaan.org>', (string)$mailer->getReplyTo());
}
//--------------------------------------------------------------------
// Test setting the "to" property
public function testSetToBasic()
{
$mailer = new Email();
$mailer->setTo('Luke <luke@tatooine.org>');
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getTo());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
}
public function testSetToMultiple()
{
$mailer = new Email();
$mailer->setTo('Luke <luke@tatooine.org>', 'padme@naboo.org');
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getTo());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
$this->assertTrue(in_array('padme@naboo.org', $emails));
}
public function testSetToArray()
{
$mailer = new Email();
$mailer->setTo(['Luke <luke@tatooine.org>', 'padme@naboo.org']);
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getTo());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
$this->assertTrue(in_array('padme@naboo.org', $emails));
}
//--------------------------------------------------------------------
// Test setting the "cc" property (copied recipients)
public function testSetCCBasic()
{
$mailer = new Email();
$mailer->cc('Luke <luke@tatooine.org>');
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getCc());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
}
public function testSetCCMultiple()
{
$mailer = new Email();
$mailer->cc('Luke <luke@tatooine.org>', 'padme@naboo.org');
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getCc());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
$this->assertTrue(in_array('padme@naboo.org', $emails));
}
public function testSetCCArray()
{
$mailer = new Email();
$mailer->cc(['Luke <luke@tatooine.org>', 'padme@naboo.org']);
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getCc());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
$this->assertTrue(in_array('padme@naboo.org', $emails));
}
//--------------------------------------------------------------------
// Test setting the "bcc" property (blind-copied recipients)
public function testSetBCCBasic()
{
$mailer = new Email();
$mailer->bcc('Luke <luke@tatooine.org>');
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getBcc());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
}
public function testSetBCCMultiple()
{
$mailer = new Email();
$mailer->bcc('Luke <luke@tatooine.org>', 'padme@naboo.org');
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getBcc());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
$this->assertTrue(in_array('padme@naboo.org', $emails));
}
public function testSetBCCArray()
{
$mailer = new Email();
$mailer->bcc(['Luke <luke@tatooine.org>', 'padme@naboo.org']);
$emails = array_map(function($addy) {
return $addy->getEmail();
}, $mailer->getBcc());
$this->assertTrue(in_array('luke@tatooine.org', $emails));
$this->assertTrue(in_array('padme@naboo.org', $emails));
}
//--------------------------------------------------------------------
// Test setting the subject
public function testSetSubject()
{
$mailer = new Email();
$original = 'Just a silly love song';
$mailer->setSubject($original);
$this->assertEquals($original, $mailer->header('Subject')->getValue());
}
public function testSetEncodedSubject()
{
$mailer = new Email();
$original = 'Just a silly Leià song';
$expected = '=?UTF-8?Q?Just=20a=20silly=20Lei=C3=A0=20s?==?UTF-8?Q?ong?=';
$mailer->setSubject($original);
$this->assertEquals($expected, $mailer->header('Subject')->getValue());
}
}

View File

@ -0,0 +1,84 @@
<?php
namespace CodeIgniter\Mailer;
use CodeIgniter\Test\CIUnitTestCase;
class EncodeTest extends CIUnitTestCase
{
public function qEncodingProvider()
{
return [
'Encode for text; char encoding default (iso88591)' => [
'input' => "\xa1Hola! Se\xf1or!",
'expected' => '=A1Hola!_Se=F1or!',
'position' => 'text',
],
'Encode for TEXT (uppercase); char encoding default (iso88591)' => [
'input' => "\xa1Hola! Se\xf1or!",
'expected' => '=A1Hola!_Se=F1or!',
'position' => 'TEXT',
],
'Encode for comment; char encoding default (iso88591)' => [
'input' => "\xa1Hola! Se\xf1or!",
'expected' => '=A1Hola!_Se=F1or!',
'position' => 'comment',
],
'Encode for Phrase (mixed case); char encoding default (iso88591)' => [
'input' => "\xa1Hola! Se\xf1or!",
'expected' => '=A1Hola!_Se=F1or!',
'position' => 'Phrase',
],
'Encode for text; char encoding explicit: utf-8' => [
'input' => "\xc2\xa1Hola! Se\xc3\xb1or!",
'expected' => '=C2=A1Hola!_Se=C3=B1or!',
'position' => 'text',
'charset' => 'utf-8',
],
'Encode for text; char encoding explicit: utf-8; string contains "=" character' => [
'input' => "Nov\xc3\xa1=",
'expected' => 'Nov=C3=A1=3D',
'position' => 'text',
'charset' => 'utf-8',
],
'Encode for text; char encoding default (iso88591); string containing new lines' => [
'input' => "\xa1Hola!\r\nSe\xf1or!\r\n",
'expected' => '=A1Hola!Se=F1or!',
'position' => 'text',
],
'Encode for text; char encoding explicit: utf-8; phrase vs text regex (text)' => [
'input' => "Hello?\xbdWorld\x5e\xa9",
'expected' => 'Hello=3F=BDWorld^=A9',
'position' => 'text',
'charset' => 'UTF-8',
],
];
}
/**
* @dataProvider qEncodingProvider
*
* @param string $input The text to encode.
* @param string $expected The expected function return value.
* @param string $charset Optional. The charset to use.
*/
public function testQEncode(string $input, string $expected, ?string $position = null, ?string $charset = null)
{
$config = config('Mailer');
$config->charset = ! empty($charset) ? $charset : $config->charset;
$encoder = new Encode($config);
$result = $encoder->Q($input);
self::assertSame($expected, $result);
}
public function testQuotedPrintable()
{
$encoder = new Encode(config('Mailer'));
$string = 'Möchten Sie ein paar Äpfel?';
$expected = "M=C3=B6chten Sie ein paar =C3=84pfel?";
$this->assertEquals($expected, $encoder->quotedPrintable($string));
}
}

View File

@ -4,7 +4,7 @@ use CodeIgniter\Test\CIUnitTestCase;
class MailerTest extends CIUnitTestCase
{
public function tearDown()
public function tearDown(): void
{
// Restore file permissions after unreadable attachment test
$thefile = SUPPORTPATH . 'Mailer/ci-logo-not-readable.png';