<?php
/**
* PHPMailer - PHP email transport unit tests.
* PHP version 5.5.
*
* @author Marcus Bointon <phpmailer@synchromedia.co.uk>
* @author Andy Prevost
* @copyright 2012 - 2020 Marcus Bointon
* @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace PHPMailer\Test\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\Test\PreSendTestCase;
final class ReplyToGetSetClearTest extends PreSendTestCase
{
public function testAddReplyToValidAddressNonIdn($address, $name = null, $expected = null)
{
if (isset($name)) {
$result = $this->Mail->addReplyTo($address, $name);
} else {
$result = $this->Mail->addReplyTo($address);
$name = '';
}
if (isset($expected) === false) {
$expected = [
'key' => $address,
'address' => $address,
'name' => $name,
];
}
self::assertTrue($result, 'Replyto Addressing failed');
$retrieved = $this->Mail->getReplyToAddresses();
self::assertIsArray($retrieved, 'ReplyTo property is not an array');
self::assertCount(1, $retrieved, 'ReplyTo property does not contain exactly one address');
$key = $expected['key'];
self::assertArrayHasKey(
$key,
$retrieved,
'ReplyTo property does not contain an entry with this address as the key'
);
self::assertCount(
2,
$retrieved[$key],
'ReplyTo array for this address does not contain exactly two array items'
);
self::assertSame(
$expected['address'],
$retrieved[$key][0],
'ReplyTo array for this address does not contain added address'
);
self::assertSame(
$expected['name'],
$retrieved[$key][1],
'ReplyTo array for this address does not contain added name'
);
}
public function dataAddReplyToValidAddressNonIdn()
{
return [
'Valid address' => [
'address' => 'a@example.com',
],
'Valid address with surrounding whitespace and mixed case' => [
'address' => " \tMiXeD@Example.Com \r\n",
'name' => null,
'expected' => [
'key' => 'mixed@example.com',
'address' => 'MiXeD@Example.Com',
'name' => '',
],
],
'Valid address with name' => [
'address' => 'a@example.com',
'name' => 'ReplyTo name',
],
'Valid address with name; name with whitespace and line breaks' => [
'address' => 'a@example.com',
'name' => "\t\t ReplyTo\r\nname ",
'expected' => [
'key' => 'a@example.com',
'address' => 'a@example.com',
'name' => 'ReplyToname',
],
],
];
}
public function testAddReplyToInvalidAddressNonIdn($address)
{
$result = $this->Mail->addReplyTo($address);
self::assertFalse($result, 'Invalid Replyto address accepted');
$retrieved = $this->Mail->getReplyToAddresses();
self::assertIsArray($retrieved, 'ReplyTo property is not an array');
self::assertCount(0, $retrieved, 'ReplyTo property is not empty');
}
public function testAddReplyToInvalidAddressNonIdnException($address)
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('Invalid address: (Reply-To): ' . $address);
$mail = new PHPMailer(true);
$mail->addReplyTo($address);
}
public function dataAddReplyToInvalidAddressNonIdn()
{
return [
'Invalid domain' => ['a@example..com'],
'Missing @ sign' => ['example.com'],
];
}
public function testReplyToInMessageHeader($addresses, $expected)
{
$this->Mail->Body = 'Here is the main body. There should be ' .
'a reply to header in this message.';
$this->Mail->Subject .= ': Reply to header';
foreach ($addresses as $address) {
if (isset($address['name'])) {
$this->Mail->addReplyTo($address['address'], $address['name']);
} else {
$this->Mail->addReplyTo($address['address']);
}
}
$this->buildBody();
self::assertTrue($this->Mail->preSend(), $this->Mail->ErrorInfo);
$message = $this->Mail->getSentMIMEMessage();
self::assertStringContainsString($expected, $message, 'Message does not contain the expected reply-to header');
}
public function dataReplyToInMessageHeader()
{
$LE = PHPMailer::getLE();
return [
'Single address' => [
'addresses' => [
[
'address' => 'nobody@nobody.com',
],
],
'expected' => $LE . 'Reply-To: nobody@nobody.com' . $LE,
],
'Single address + name' => [
'addresses' => [
[
'address' => 'nobody@nobody.com',
'name' => 'Nobody (Unit Test)',
],
],
'expected' => $LE . 'Reply-To: "Nobody (Unit Test)" <nobody@nobody.com>' . $LE,
],
'Multiple addresses, including no name and mixed case email' => [
'addresses' => [
[
'address' => 'nobody@nobody.com',
'name' => 'Nobody (Unit Test)',
],
[
'address' => 'Somebody@SomeBody.com',
'name' => 'Somebody (Unit Test)',
],
[
'address' => 'noname@noname.com',
],
],
'expected' => $LE . 'Reply-To: "Nobody (Unit Test)" <nobody@nobody.com>,'
. ' "Somebody (Unit Test)" <Somebody@SomeBody.com>, noname@noname.com' . $LE,
],
];
}
public function testEnqueueAndAddIdnAddress()
{
$letter = html_entity_decode('ç', ENT_COMPAT, PHPMailer::CHARSET_ISO88591);
$domain = '@' . 'fran' . $letter . 'ois.ch';
$address = 'test+replyto' . $domain;
self::assertTrue($this->Mail->addReplyTo($address), 'Replyto Addressing failed');
self::assertEmpty($this->Mail->getReplyToAddresses(), 'Unexpected "reply-to" address added');
$queue = $this->getPropertyValue($this->Mail, 'ReplyToQueue');
self::assertCount(1, $queue, 'Queue does not contain exactly 1 entry');
self::assertArrayHasKey($address, $queue, 'Queue does not contain an entry for the IDN address');
$this->buildBody();
self::assertTrue($this->Mail->preSend(), $this->Mail->ErrorInfo);
$domain = $this->Mail->punyencodeAddress($domain);
self::assertSame(
['test+replyto' . $domain => ['test+replyto' . $domain, '']],
$this->Mail->getReplyToAddresses(),
'Bad "reply-to" addresses'
);
}
public function testNoDuplicateReplyToAddresses()
{
$this->Mail->CharSet = PHPMailer::CHARSET_UTF8;
self::assertTrue(
$this->Mail->addReplyTo('test+replyto@françois.ch', 'UTF8 domain'),
'Initial address + name not queued'
);
self::assertFalse(
$this->Mail->addReplyTo('test+replyto@françois.ch'),
'Duplicate address should not have been queued'
);
self::assertTrue(
$this->Mail->addReplyTo('test+replyto@FRANÇOIS.CH'),
'Duplicate address, different case address not queued'
);
self::assertFalse(
$this->Mail->addReplyTo('test+replyto@FRANÇOIS.CH'),
'Duplicate address, different case should not have been queued twice'
);
self::assertTrue(
$this->Mail->addReplyTo('test+replyto@xn--franois-xxa.ch'),
'Punycode address, no name not added'
);
self::assertFalse(
$this->Mail->addReplyTo('test+replyto@xn--franois-xxa.ch', 'Punycode domain'),
'Duplicate punycode address should not have been added, no matter that this one has a name'
);
self::assertFalse(
$this->Mail->addReplyTo('test+replyto@XN--FRANOIS-XXA.CH'),
'Duplicate punycode address, different case should not have been added'
);
self::assertCount(1, $this->Mail->getReplyToAddresses(), 'Addresses added did not match expected count of 1');
$queue = $this->getPropertyValue($this->Mail, 'ReplyToQueue');
self::assertIsArray($queue, 'Queue is not an array');
self::assertCount(2, $queue, 'Queue does not contain exactly 2 entries');
self::assertArrayHasKey(
'test+replyto@françois.ch',
$queue,
'Queue does not contain an entry for the lowercase address'
);
self::assertArrayHasKey(
'test+replyto@FRANÇOIS.CH',
$queue,
'Queue does not contain an entry for the uppercase address'
);
$this->buildBody();
self::assertTrue($this->Mail->preSend(), $this->Mail->ErrorInfo);
self::assertCount(
1,
$this->Mail->getReplyToAddresses(),
'Bad count of "reply-to" addresses'
);
$expectedAddress = 'test+replyto@xn--franois-xxa.ch';
$retrieved = $this->Mail->getReplyToAddresses();
self::assertCount(1, $retrieved, 'Stored addresses after preSend() is not 1');
self::assertArrayHasKey(
$expectedAddress,
$retrieved,
'ReplyTo property does not contain an entry with this address as the key'
);
self::assertCount(
2,
$retrieved[$expectedAddress],
'ReplyTo array for this address does not contain exactly two array items'
);
self::assertSame(
$expectedAddress,
$retrieved[$expectedAddress][0],
'ReplyTo array for this address does not contain added address'
);
self::assertSame(
'',
$retrieved[$expectedAddress][1],
'ReplyTo array for this address does not contain added name'
);
}
public function testAddReplyToFailsOn8BitCharInDomainWithoutOptionalExtensions()
{
if (extension_loaded('mbstring') && function_exists('idn_to_ascii')) {
$this->markTestSkipped('Test requires MbString and/or Intl *not* to be available');
}
self::assertFalse($this->Mail->addReplyTo('test@françois.ch'));
}
public function testClearReplyTos()
{
self::assertTrue($this->Mail->addReplyTo('example@example.com'), 'Address not added');
self::assertTrue($this->Mail->addReplyTo('test@françois.ch'), 'IDN Address not queued');
$retrieved = $this->Mail->getReplyToAddresses();
self::assertIsArray($retrieved, 'ReplyTo property is not an array (pre-clear)');
self::assertCount(1, $retrieved, 'ReplyTo property does not contain exactly one address');
$queue = $this->getPropertyValue($this->Mail, 'ReplyToQueue');
self::assertIsArray($queue, 'Queue is not an array (pre-clear)');
self::assertCount(1, $queue, 'Queue does not contain exactly one entry');
$this->Mail->clearReplyTos();
$retrieved = $this->Mail->getReplyToAddresses();
self::assertIsArray($retrieved, 'ReplyTo property is not an array (post-clear)');
self::assertCount(0, $retrieved, 'ReplyTo property still contains an address');
$queue = $this->getPropertyValue($this->Mail, 'ReplyToQueue');
self::assertIsArray($queue, 'Queue is not an array (post-clear)');
self::assertCount(0, $queue, 'Queue still contains an entry');
}
}