Path: blob/master/PHPMailer/test/PHPMailerTest.php
738 views
<?php1/**2* PHPMailer - PHP email transport unit tests.3* PHP version 5.5.4*5* @author Marcus Bointon <[email protected]>6* @author Andy Prevost7* @copyright 2012 - 2020 Marcus Bointon8* @copyright 2004 - 2009 Andy Prevost9* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License10*/1112namespace PHPMailer\Test;1314use PHPMailer\PHPMailer\Exception;15use PHPMailer\PHPMailer\OAuth;16use PHPMailer\PHPMailer\PHPMailer;17use PHPMailer\PHPMailer\POP3;18use PHPMailer\PHPMailer\SMTP;19use PHPUnit\Framework\TestCase;2021/**22* PHPMailer - PHP email transport unit test class.23*/24final class PHPMailerTest extends TestCase25{26/**27* Holds the PHPMailer instance.28*29* @var PHPMailer30*/31private $Mail;3233/**34* Holds the SMTP mail host.35*36* @var string37*/38private $Host = '';3940/**41* Holds the change log.42*43* @var string[]44*/45private $ChangeLog = [];4647/**48* Holds the note log.49*50* @var string[]51*/52private $NoteLog = [];5354/**55* Default include path.56*57* @var string58*/59private $INCLUDE_DIR = '..';6061/**62* PIDs of any processes we need to kill.63*64* @var array65*/66private $pids = [];6768/**69* Run before each test is started.70*/71protected function setUp()72{73$this->INCLUDE_DIR = dirname(__DIR__); //Default to the dir above the test dir, i.e. the project home dir74if (file_exists($this->INCLUDE_DIR . '/test/testbootstrap.php')) {75include $this->INCLUDE_DIR . '/test/testbootstrap.php'; //Overrides go in here76}77$this->Mail = new PHPMailer();78$this->Mail->SMTPDebug = SMTP::DEBUG_CONNECTION; //Full debug output79$this->Mail->Debugoutput = ['PHPMailer\Test\DebugLogTestListener', 'debugLog'];80$this->Mail->Priority = 3;81$this->Mail->Encoding = '8bit';82$this->Mail->CharSet = PHPMailer::CHARSET_ISO88591;83if (array_key_exists('mail_from', $_REQUEST)) {84$this->Mail->From = $_REQUEST['mail_from'];85} else {86$this->Mail->From = '[email protected]';87}88$this->Mail->FromName = 'Unit Tester';89$this->Mail->Sender = '';90$this->Mail->Subject = 'Unit Test';91$this->Mail->Body = '';92$this->Mail->AltBody = '';93$this->Mail->WordWrap = 0;94if (array_key_exists('mail_host', $_REQUEST)) {95$this->Mail->Host = $_REQUEST['mail_host'];96} else {97$this->Mail->Host = 'mail.example.com';98}99if (array_key_exists('mail_port', $_REQUEST)) {100$this->Mail->Port = $_REQUEST['mail_port'];101} else {102$this->Mail->Port = 25;103}104$this->Mail->Helo = 'localhost.localdomain';105$this->Mail->SMTPAuth = false;106$this->Mail->Username = '';107$this->Mail->Password = '';108if (array_key_exists('mail_useauth', $_REQUEST)) {109$this->Mail->SMTPAuth = $_REQUEST['mail_useauth'];110}111if (array_key_exists('mail_username', $_REQUEST)) {112$this->Mail->Username = $_REQUEST['mail_username'];113}114if (array_key_exists('mail_userpass', $_REQUEST)) {115$this->Mail->Password = $_REQUEST['mail_userpass'];116}117$this->Mail->addReplyTo('[email protected]', 'Reply Guy');118$this->Mail->Sender = '[email protected]';119if ($this->Mail->Host != '') {120$this->Mail->isSMTP();121} else {122$this->Mail->isMail();123}124if (array_key_exists('mail_to', $_REQUEST)) {125$this->setAddress($_REQUEST['mail_to'], 'Test User', 'to');126}127if (array_key_exists('mail_cc', $_REQUEST) && $_REQUEST['mail_cc'] !== '') {128$this->setAddress($_REQUEST['mail_cc'], 'Carbon User', 'cc');129}130}131132/**133* Run after each test is completed.134*/135protected function tearDown()136{137// Clean global variables138$this->Mail = null;139$this->ChangeLog = [];140$this->NoteLog = [];141142foreach ($this->pids as $pid) {143$p = escapeshellarg($pid);144shell_exec("ps $p && kill -TERM $p");145}146}147148/**149* Build the body of the message in the appropriate format.150*/151private function buildBody()152{153$this->checkChanges();154155// Determine line endings for message156if ('text/html' === $this->Mail->ContentType || $this->Mail->AltBody !== '') {157$eol = "<br>\r\n";158$bullet_start = '<li>';159$bullet_end = "</li>\r\n";160$list_start = "<ul>\r\n";161$list_end = "</ul>\r\n";162} else {163$eol = "\r\n";164$bullet_start = ' - ';165$bullet_end = "\r\n";166$list_start = '';167$list_end = '';168}169170$ReportBody = '';171172$ReportBody .= '---------------------' . $eol;173$ReportBody .= 'Unit Test Information' . $eol;174$ReportBody .= '---------------------' . $eol;175$ReportBody .= 'phpmailer version: ' . PHPMailer::VERSION . $eol;176$ReportBody .= 'Content Type: ' . $this->Mail->ContentType . $eol;177$ReportBody .= 'CharSet: ' . $this->Mail->CharSet . $eol;178179if ($this->Mail->Host !== '') {180$ReportBody .= 'Host: ' . $this->Mail->Host . $eol;181}182183// If attachments then create an attachment list184$attachments = $this->Mail->getAttachments();185if (count($attachments) > 0) {186$ReportBody .= 'Attachments:' . $eol;187$ReportBody .= $list_start;188foreach ($attachments as $attachment) {189$ReportBody .= $bullet_start . 'Name: ' . $attachment[1] . ', ';190$ReportBody .= 'Encoding: ' . $attachment[3] . ', ';191$ReportBody .= 'Type: ' . $attachment[4] . $bullet_end;192}193$ReportBody .= $list_end . $eol;194}195196// If there are changes then list them197if (count($this->ChangeLog) > 0) {198$ReportBody .= 'Changes' . $eol;199$ReportBody .= '-------' . $eol;200201$ReportBody .= $list_start;202foreach ($this->ChangeLog as $iValue) {203$ReportBody .= $bullet_start . $iValue[0] . ' was changed to [' .204$iValue[1] . ']' . $bullet_end;205}206$ReportBody .= $list_end . $eol . $eol;207}208209// If there are notes then list them210if (count($this->NoteLog) > 0) {211$ReportBody .= 'Notes' . $eol;212$ReportBody .= '-----' . $eol;213214$ReportBody .= $list_start;215foreach ($this->NoteLog as $iValue) {216$ReportBody .= $bullet_start . $iValue . $bullet_end;217}218$ReportBody .= $list_end;219}220221// Re-attach the original body222$this->Mail->Body .= $eol . $ReportBody;223}224225/**226* Check which default settings have been changed for the report.227*/228private function checkChanges()229{230if (3 != $this->Mail->Priority) {231$this->addChange('Priority', $this->Mail->Priority);232}233if (PHPMailer::ENCODING_8BIT !== $this->Mail->Encoding) {234$this->addChange('Encoding', $this->Mail->Encoding);235}236if (PHPMailer::CHARSET_ISO88591 !== $this->Mail->CharSet) {237$this->addChange('CharSet', $this->Mail->CharSet);238}239if ('' != $this->Mail->Sender) {240$this->addChange('Sender', $this->Mail->Sender);241}242if (0 != $this->Mail->WordWrap) {243$this->addChange('WordWrap', $this->Mail->WordWrap);244}245if ('mail' !== $this->Mail->Mailer) {246$this->addChange('Mailer', $this->Mail->Mailer);247}248if (25 != $this->Mail->Port) {249$this->addChange('Port', $this->Mail->Port);250}251if ('localhost.localdomain' !== $this->Mail->Helo) {252$this->addChange('Helo', $this->Mail->Helo);253}254if ($this->Mail->SMTPAuth) {255$this->addChange('SMTPAuth', 'true');256}257}258259/**260* Add a changelog entry.261*262* @param string $sName263* @param string $sNewValue264*/265private function addChange($sName, $sNewValue)266{267$this->ChangeLog[] = [$sName, $sNewValue];268}269270/**271* Adds a simple note to the message.272*273* @param string $sValue274*/275private function addNote($sValue)276{277$this->NoteLog[] = $sValue;278}279280/**281* Adds all of the addresses.282*283* @param string $sAddress284* @param string $sName285* @param string $sType286*287* @return bool288*/289private function setAddress($sAddress, $sName = '', $sType = 'to')290{291switch ($sType) {292case 'to':293return $this->Mail->addAddress($sAddress, $sName);294case 'cc':295return $this->Mail->addCC($sAddress, $sName);296case 'bcc':297return $this->Mail->addBCC($sAddress, $sName);298}299300return false;301}302303/**304* Check that we have loaded default test params.305* Pretty much everything will fail due to unset recipient if this is not done.306*/307public function testBootstrap()308{309self::assertFileExists(310$this->INCLUDE_DIR . '/test/testbootstrap.php',311'Test config params missing - copy testbootstrap.php to testbootstrap-dist.php and change as appropriate'312);313}314315/**316* Test CRAM-MD5 authentication.317* Needs a connection to a server that supports this auth mechanism, so commented out by default.318*/319public function testAuthCRAMMD5()320{321$this->Mail->Host = 'hostname';322$this->Mail->Port = 587;323$this->Mail->SMTPAuth = true;324$this->Mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;325$this->Mail->AuthType = 'CRAM-MD5';326$this->Mail->Username = 'username';327$this->Mail->Password = 'password';328$this->Mail->Body = 'Test body';329$this->Mail->Subject .= ': Auth CRAM-MD5';330$this->Mail->From = '[email protected]';331$this->Mail->Sender = '[email protected]';332$this->Mail->clearAllRecipients();333$this->Mail->addAddress('[email protected]');334//self::assertTrue($this->mail->send(), $this->mail->ErrorInfo);335}336337/**338* Test email address validation.339* Test addresses obtained from http://isemail.info340* Some failing cases commented out that are apparently up for debate!341*/342public function testValidate()343{344$validaddresses = [345'[email protected]',346'[email protected]',347'1234567890123456789012345678901234567890123456789012345678901234@example.org',348'"first\"last"@example.org',349'"first@last"@example.org',350'"first\last"@example.org',351'first.last@[12.34.56.78]',352'first.last@x23456789012345678901234567890123456789012345678901234567890123.example.org',353'[email protected]',354'"first\last"@example.org',355'"Abc\@def"@example.org',356'"Fred\ Bloggs"@example.org',357'"Joe.\Blow"@example.org',358'"Abc@def"@example.org',359'[email protected]',360'customer/[email protected]',361'[email protected]',362'!def!xyz%[email protected]',363'[email protected]',364'[email protected]',365'[email protected]',366'[email protected]',367'[email protected]',368'[email protected]',369'[email protected]',370'[email protected]',371't*[email protected]',372'[email protected]',373'{_test_}@example.org',374'[email protected]',375'"test.test"@example.org',376'test."test"@example.org',377'"test@test"@example.org',378'[email protected]',379'test@[123.123.123.123]',380'[email protected]',381'[email protected]',382'"test\test"@example.org',383'"test\blah"@example.org',384'"test\blah"@example.org',385'"test\"blah"@example.org',386'customer/[email protected]',387'[email protected]',388'[email protected]',389'"Austin@Powers"@example.org',390'[email protected]',391'"Ima.Fool"@example.org',392'"first"."last"@example.org',393'"first".middle."last"@example.org',394'"first"[email protected]',395'first."last"@example.org',396'"first"."middle"."last"@example.org',397'"first.middle"."last"@example.org',398'"first.middle.last"@example.org',399'"first..last"@example.org',400'"first\"last"@example.org',401'first."mid\dle"."last"@example.org',402'[email protected]',403'[email protected]',404'aaa@[123.123.123.123]',405'[email protected]',406'[email protected]',407'[email protected]',408'[email protected]',409'[email protected]',410'[email protected]',411'"Joe\Blow"@example.org',412'user%[email protected]',413'cdburgess+!#$%&\'*-/=?+_{}|[email protected]',414'[email protected]',415'[email protected]',416'[email protected]',417];418//These are invalid according to PHP's filter_var419//which doesn't allow dotless domains, numeric TLDs or unbracketed IPv4 literals420$invalidphp = [421'a@b',422'a@bar',423'first.last@com',424'[email protected]',425'[email protected]',426'[email protected]',427];428//Valid RFC 5322 addresses using quoting and comments429//Note that these are *not* all valid for RFC5321430$validqandc = [431'HM2Kinsists@(that comments are allowed)this.is.ok',432'"Doug \"Ace\" L."@example.org',433'"[[ test ]]"@example.org',434'"Ima Fool"@example.org',435'"test blah"@example.org',436'(foo)cal(bar)@(baz)example.com(quux)',437'cal@example(woo).(yay)com',438'cal(woo(yay)hoopla)@example.com',439'cal(foo\@bar)@example.com',440'cal(foo\)bar)@example.com',441'first()[email protected]',442'pete(his account)@silly.test(his host)',443'c@(Chris\'s host.)public.example',444'jdoe@machine(comment). example',445'1234 @ local(blah) .machine .example',446'first(abc.def)[email protected]',447'first(a"bc.def)[email protected]',448'first.(")middle.last(")@example.org',449'first(abc\(def)@example.org',450'first.last@x(1234567890123456789012345678901234567890123456789012345678901234567890).com',451'a(a(b(c)d(e(f))g)h(i)j)@example.org',452'"hello my name is"@example.com',453'"Test \"Fail\" Ing"@example.org',454'first.last @example.org',455];456//Valid explicit IPv6 numeric addresses457$validipv6 = [458'first.last@[IPv6:::a2:a3:a4:b1:b2:b3:b4]',459'first.last@[IPv6:a1:a2:a3:a4:b1:b2:b3::]',460'first.last@[IPv6:::]',461'first.last@[IPv6:::b4]',462'first.last@[IPv6:::b3:b4]',463'first.last@[IPv6:a1::b4]',464'first.last@[IPv6:a1::]',465'first.last@[IPv6:a1:a2::]',466'first.last@[IPv6:0123:4567:89ab:cdef::]',467'first.last@[IPv6:0123:4567:89ab:CDEF::]',468'first.last@[IPv6:::a3:a4:b1:ffff:11.22.33.44]',469'first.last@[IPv6:::a2:a3:a4:b1:ffff:11.22.33.44]',470'first.last@[IPv6:a1:a2:a3:a4::11.22.33.44]',471'first.last@[IPv6:a1:a2:a3:a4:b1::11.22.33.44]',472'first.last@[IPv6:a1::11.22.33.44]',473'first.last@[IPv6:a1:a2::11.22.33.44]',474'first.last@[IPv6:0123:4567:89ab:cdef::11.22.33.44]',475'first.last@[IPv6:0123:4567:89ab:CDEF::11.22.33.44]',476'first.last@[IPv6:a1::b2:11.22.33.44]',477'first.last@[IPv6:::12.34.56.78]',478'first.last@[IPv6:1111:2222:3333::4444:12.34.56.78]',479'first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.56.78]',480'first.last@[IPv6:::1111:2222:3333:4444:5555:6666]',481'first.last@[IPv6:1111:2222:3333::4444:5555:6666]',482'first.last@[IPv6:1111:2222:3333:4444:5555:6666::]',483'first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]',484'first.last@[IPv6:1111:2222:3333::4444:5555:12.34.56.78]',485'first.last@[IPv6:1111:2222:3333::4444:5555:6666:7777]',486];487$invalidaddresses = [488'[email protected],com',489'first\@[email protected]',490'123456789012345678901234567890123456789012345678901234567890' .491'@12345678901234567890123456789012345678901234 [...]',492'first.last',493'12345678901234567890123456789012345678901234567890123456789012345@iana.org',494'[email protected]',495'[email protected]',496'[email protected]',497'"first"last"@iana.org',498'"""@iana.org',499'"\"@iana.org',500//'""@iana.org',501'first\@[email protected]',502'first.last@',503'x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.' .504'x23456789.x23456789.x23456789.x23 [...]',505'first.last@[.12.34.56.78]',506'first.last@[12.34.56.789]',507'first.last@[::12.34.56.78]',508'first.last@[IPv5:::12.34.56.78]',509'first.last@[IPv6:1111:2222:3333:4444:5555:12.34.56.78]',510'first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:12.34.56.78]',511'first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777]',512'first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]',513'first.last@[IPv6:1111:2222::3333::4444:5555:6666]',514'first.last@[IPv6:1111:2222:333x::4444:5555]',515'first.last@[IPv6:1111:2222:33333::4444:5555]',516'[email protected]',517'[email protected]',518'first.last@x234567890123456789012345678901234567890123456789012345678901234.iana.org',519'abc\@[email protected]',520'abc\@iana.org',521'Doug\ \"Ace\"\ [email protected]',522'abc@[email protected]',523'abc\@[email protected]',524'abc\@iana.org',525'@iana.org',526'doug@',527'"[email protected]',528'ote"@iana.org',529'[email protected]',530'[email protected]',531'[email protected]',532'"Doug "Ace" L."@iana.org',533'Doug\ \"Ace\"\ L\[email protected]',534'hello [email protected]',535//'helloworld@iana .org',536'[email protected].',537'test.iana.org',538'[email protected]',539'[email protected]',540'[email protected]',541'test@[email protected]',542'test@@iana.org',543'-- test [email protected]',544'[test]@iana.org',545'"test"test"@iana.org',546'()[]\;:,><@iana.org',547'test@.',548'test@example.',549'[email protected]',550'test@12345678901234567890123456789012345678901234567890123456789012345678901234567890' .551'12345678901234567890 [...]',552'test@[123.123.123.123',553'[email protected]]',554'NotAnEmail',555'@NotAnEmail',556'"test"blah"@iana.org',557'[email protected]',558'[email protected]',559'[email protected]',560'[email protected]',561'Ima [email protected]',562'phil.h\@\@[email protected]',563'foo@[\1.2.3.4]',564//'first.""[email protected]',565'first\[email protected]',566'Abc\@[email protected]',567'Fred\ [email protected]',568'Joe.\[email protected]',569'first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.567.89]',570'{^c\@**Dog^}@cartoon.com',571//'"foo"(yay)@(hoopla)[1.2.3.4]',572'cal(foo(bar)@iamcal.com',573'cal(foo)bar)@iamcal.com',574'cal(foo\)@iamcal.com',575'first(12345678901234567890123456789012345678901234567890)last@(1234567890123456789' .576'01234567890123456789012 [...]',577'first(middle)[email protected]',578'first(abc("def".ghi).mno)middle(abc("def".ghi).mno).last@(abc("def".ghi).mno)example' .579'(abc("def".ghi).mno). [...]',580'a(a(b(c)d(e(f))g)(h(i)j)@iana.org',581'.@',582'@bar.com',583'@@bar.com',584'aaa.com',585'[email protected]',586'[email protected]',587'aaa@[123.123.123.123]a',588'aaa@[123.123.123.333]',589'[email protected].',590'[email protected]',591'[email protected]',592'[email protected]',593'[email protected]',594'[email protected]',595'[email protected]',596'"Unicode NULL' . chr(0) . '"@char.com',597'Unicode NULL' . chr(0) . '@char.com',598'first.last@[IPv6::]',599'first.last@[IPv6::::]',600'first.last@[IPv6::b4]',601'first.last@[IPv6::::b4]',602'first.last@[IPv6::b3:b4]',603'first.last@[IPv6::::b3:b4]',604'first.last@[IPv6:a1:::b4]',605'first.last@[IPv6:a1:]',606'first.last@[IPv6:a1:::]',607'first.last@[IPv6:a1:a2:]',608'first.last@[IPv6:a1:a2:::]',609'first.last@[IPv6::11.22.33.44]',610'first.last@[IPv6::::11.22.33.44]',611'first.last@[IPv6:a1:11.22.33.44]',612'first.last@[IPv6:a1:::11.22.33.44]',613'first.last@[IPv6:a1:a2:::11.22.33.44]',614'first.last@[IPv6:0123:4567:89ab:cdef::11.22.33.xx]',615'first.last@[IPv6:0123:4567:89ab:CDEFF::11.22.33.44]',616'first.last@[IPv6:a1::a4:b1::b4:11.22.33.44]',617'first.last@[IPv6:a1::11.22.33]',618'first.last@[IPv6:a1::11.22.33.44.55]',619'first.last@[IPv6:a1::b211.22.33.44]',620'first.last@[IPv6:a1::b2::11.22.33.44]',621'first.last@[IPv6:a1::b3:]',622'first.last@[IPv6::a2::b4]',623'first.last@[IPv6:a1:a2:a3:a4:b1:b2:b3:]',624'first.last@[IPv6::a2:a3:a4:b1:b2:b3:b4]',625'first.last@[IPv6:a1:a2:a3:a4::b1:b2:b3:b4]',626//This is a valid RFC5322 address, but we don't want to allow it for obvious reasons!627"(\r\n RCPT TO:[email protected]\r\n DATA \\\nSubject: spam10\\\n\r\n Hello," .628"\r\n this is a spam mail.\\\n.\r\n QUIT\r\n ) [email protected]",629];630// IDNs in Unicode and ASCII forms.631$unicodeaddresses = [632'first.last@bücher.ch',633'first.last@кто.рф',634'first.last@phplíst.com',635];636$asciiaddresses = [637'[email protected]',638'[email protected]',639'[email protected]',640];641$goodfails = [];642foreach (array_merge($validaddresses, $asciiaddresses) as $address) {643if (!PHPMailer::validateAddress($address)) {644$goodfails[] = $address;645}646}647$badpasses = [];648foreach (array_merge($invalidaddresses, $unicodeaddresses) as $address) {649if (PHPMailer::validateAddress($address)) {650$badpasses[] = $address;651}652}653$err = '';654if (count($goodfails) > 0) {655$err .= "Good addresses that failed validation:\n";656$err .= implode("\n", $goodfails);657}658if (count($badpasses) > 0) {659if (!empty($err)) {660$err .= "\n\n";661}662$err .= "Bad addresses that passed validation:\n";663$err .= implode("\n", $badpasses);664}665self::assertEmpty($err, $err);666//For coverage667self::assertTrue(PHPMailer::validateAddress('[email protected]', 'auto'));668self::assertFalse(PHPMailer::validateAddress('[email protected].', 'auto'));669self::assertTrue(PHPMailer::validateAddress('[email protected]', 'pcre'));670self::assertFalse(PHPMailer::validateAddress('[email protected].', 'pcre'));671self::assertTrue(PHPMailer::validateAddress('[email protected]', 'pcre8'));672self::assertFalse(PHPMailer::validateAddress('[email protected].', 'pcre8'));673self::assertTrue(PHPMailer::validateAddress('[email protected]', 'html5'));674self::assertFalse(PHPMailer::validateAddress('[email protected].', 'html5'));675self::assertTrue(PHPMailer::validateAddress('[email protected]', 'php'));676self::assertFalse(PHPMailer::validateAddress('[email protected].', 'php'));677self::assertTrue(PHPMailer::validateAddress('[email protected]', 'noregex'));678self::assertFalse(PHPMailer::validateAddress('bad', 'noregex'));679}680681/**682* Test injecting a custom validator.683*/684public function testCustomValidator()685{686//Inject a one-off custom validator687self::assertTrue(688PHPMailer::validateAddress(689'[email protected]',690function ($address) {691return strpos($address, '@') !== false;692}693),694'Custom validator false negative'695);696self::assertFalse(697PHPMailer::validateAddress(698'userexample.com',699function ($address) {700return strpos($address, '@') !== false;701}702),703'Custom validator false positive'704);705//Set the default validator to an injected function706PHPMailer::$validator = function ($address) {707return '[email protected]' === $address;708};709self::assertTrue(710$this->Mail->addAddress('[email protected]'),711'Custom default validator false negative'712);713self::assertFalse(714//Need to pick a failing value which would pass all other validators715//to be sure we're using our custom one716$this->Mail->addAddress('[email protected]'),717'Custom default validator false positive'718);719//Set default validator to PHP built-in720PHPMailer::$validator = 'php';721self::assertFalse(722//This is a valid address that FILTER_VALIDATE_EMAIL thinks is invalid723$this->Mail->addAddress('[email protected]'),724'PHP validator not behaving as expected'725);726}727728/**729* Word-wrap an ASCII message.730*/731public function testWordWrap()732{733$this->Mail->WordWrap = 40;734$my_body = str_repeat(735'Here is the main body of this message. It should ' .736'be quite a few lines. It should be wrapped at ' .737'40 characters. Make sure that it is. ',73810739);740$nBodyLen = strlen($my_body);741$my_body .= "\n\nThis is the above body length: " . $nBodyLen;742743$this->Mail->Body = $my_body;744$this->Mail->Subject .= ': Wordwrap';745746$this->buildBody();747self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);748}749750/**751* Word-wrap a multibyte message.752*/753public function testWordWrapMultibyte()754{755$this->Mail->WordWrap = 40;756$my_body = str_repeat(757'飛兒樂 團光茫 飛兒樂 團光茫 飛兒樂 團光茫 飛兒樂 團光茫 ' .758'飛飛兒樂 團光茫兒樂 團光茫飛兒樂 團光飛兒樂 團光茫飛兒樂 團光茫兒樂 團光茫 ' .759'飛兒樂 團光茫飛兒樂 團飛兒樂 團光茫光茫飛兒樂 團光茫. ',76010761);762$nBodyLen = strlen($my_body);763$my_body .= "\n\nThis is the above body length: " . $nBodyLen;764765$this->Mail->Body = $my_body;766$this->Mail->Subject .= ': Wordwrap multibyte';767768$this->buildBody();769self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);770}771772/**773* Test low priority.774*/775public function testLowPriority()776{777$this->Mail->Priority = 5;778$this->Mail->Body = 'Here is the main body. There should be ' .779'a reply to address in this message.';780$this->Mail->Subject .= ': Low Priority';781$this->Mail->addReplyTo('[email protected]', 'Nobody (Unit Test)');782783$this->buildBody();784self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);785}786787/**788* Simple plain file attachment test.789*/790public function testMultiplePlainFileAttachment()791{792$this->Mail->Body = 'Here is the text body';793$this->Mail->Subject .= ': Plain + Multiple FileAttachments';794795if (!$this->Mail->addAttachment(realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'))) {796self::assertTrue(false, $this->Mail->ErrorInfo);797798return;799}800801if (!$this->Mail->addAttachment(__FILE__, 'test.txt')) {802self::assertTrue(false, $this->Mail->ErrorInfo);803804return;805}806807$this->buildBody();808self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);809}810811/**812* Rejection of non-local file attachments test.813*/814public function testRejectNonLocalFileAttachment()815{816self::assertFalse(817$this->Mail->addAttachment('https://github.com/PHPMailer/PHPMailer/raw/master/README.md'),818'addAttachment should reject remote URLs'819);820821self::assertFalse(822$this->Mail->addAttachment('phar://phar.php'),823'addAttachment should reject phar resources'824);825}826827/**828* Simple plain string attachment test.829*/830public function testPlainStringAttachment()831{832$this->Mail->Body = 'Here is the text body';833$this->Mail->Subject .= ': Plain + StringAttachment';834835$sAttachment = 'These characters are the content of the ' .836"string attachment.\nThis might be taken from a " .837'database or some other such thing. ';838839$this->Mail->addStringAttachment($sAttachment, 'string_attach.txt');840841$this->buildBody();842self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);843}844845/**846* Plain quoted-printable message.847*/848public function testQuotedPrintable()849{850$this->Mail->Body = 'Here is the main body';851$this->Mail->Subject .= ': Plain + Quoted-printable';852$this->Mail->Encoding = 'quoted-printable';853854$this->buildBody();855self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);856857//Check that a quoted printable encode and decode results in the same as went in858$t = file_get_contents(__FILE__); //Use this file as test content859//Force line breaks to UNIX-style860$t = str_replace(["\r\n", "\r"], "\n", $t);861self::assertEquals(862$t,863quoted_printable_decode($this->Mail->encodeQP($t)),864'Quoted-Printable encoding round-trip failed'865);866//Force line breaks to Windows-style867$t = str_replace("\n", "\r\n", $t);868self::assertEquals(869$t,870quoted_printable_decode($this->Mail->encodeQP($t)),871'Quoted-Printable encoding round-trip failed (Windows line breaks)'872);873}874875/**876* Test header encoding & folding.877*/878public function testHeaderEncoding()879{880$this->Mail->CharSet = 'UTF-8';881//This should select B-encoding automatically and should fold882$bencode = str_repeat('é', PHPMailer::STD_LINE_LENGTH + 1);883//This should select Q-encoding automatically and should fold884$qencode = str_repeat('e', PHPMailer::STD_LINE_LENGTH) . 'é';885//This should select B-encoding automatically and should not fold886$bencodenofold = str_repeat('é', 10);887//This should select Q-encoding automatically and should not fold888$qencodenofold = str_repeat('e', 9) . 'é';889//This should Q-encode as ASCII and fold (previously, this did not encode)890$longheader = str_repeat('e', PHPMailer::STD_LINE_LENGTH + 10);891//This should Q-encode as UTF-8 and fold892$longutf8 = str_repeat('é', PHPMailer::STD_LINE_LENGTH + 10);893//This should not change894$noencode = 'eeeeeeeeee';895$this->Mail->isMail();896//Expected results897898$bencoderes = '=?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' .899PHPMailer::getLE() .900' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' .901PHPMailer::getLE() .902' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' .903PHPMailer::getLE() .904' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqQ==?=';905$qencoderes = '=?UTF-8?Q?eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee?=' .906PHPMailer::getLE() .907' =?UTF-8?Q?eeeeeeeeeeeeeeeeeeeeeeeeee=C3=A9?=';908$bencodenofoldres = '=?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6k=?=';909$qencodenofoldres = '=?UTF-8?Q?eeeeeeeee=C3=A9?=';910$longheaderres = '=?us-ascii?Q?eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee?=' .911PHPMailer::getLE() . ' =?us-ascii?Q?eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee?=';912$longutf8res = '=?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' .913PHPMailer::getLE() . ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' .914PHPMailer::getLE() . ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' .915PHPMailer::getLE() . ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqQ==?=';916$noencoderes = 'eeeeeeeeee';917self::assertEquals(918$bencoderes,919$this->Mail->encodeHeader($bencode),920'Folded B-encoded header value incorrect'921);922self::assertEquals(923$qencoderes,924$this->Mail->encodeHeader($qencode),925'Folded Q-encoded header value incorrect'926);927self::assertEquals(928$bencodenofoldres,929$this->Mail->encodeHeader($bencodenofold),930'B-encoded header value incorrect'931);932self::assertEquals(933$qencodenofoldres,934$this->Mail->encodeHeader($qencodenofold),935'Q-encoded header value incorrect'936);937self::assertEquals(938$longheaderres,939$this->Mail->encodeHeader($longheader),940'Long header value incorrect'941);942self::assertEquals(943$longutf8res,944$this->Mail->encodeHeader($longutf8),945'Long UTF-8 header value incorrect'946);947self::assertEquals(948$noencoderes,949$this->Mail->encodeHeader($noencode),950'Unencoded header value incorrect'951);952}953954/**955* Send an HTML message.956*/957public function testHtml()958{959$this->Mail->isHTML(true);960$this->Mail->Subject .= ': HTML only';961962$this->Mail->Body = <<<'EOT'963<!DOCTYPE html>964<html lang="en">965<head>966<title>HTML email test</title>967</head>968<body>969<h1>PHPMailer does HTML!</h1>970<p>This is a <strong>test message</strong> written in HTML.<br>971Go to <a href="https://github.com/PHPMailer/PHPMailer/">https://github.com/PHPMailer/PHPMailer/</a>972for new versions of PHPMailer.</p>973<p>Thank you!</p>974</body>975</html>976EOT;977$this->buildBody();978self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);979$msg = $this->Mail->getSentMIMEMessage();980self::assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers');981}982983/**984* Send an HTML message specifying the DSN notifications we expect.985*/986public function testDsn()987{988$this->Mail->isHTML(true);989$this->Mail->Subject .= ': HTML only';990991$this->Mail->Body = <<<'EOT'992<!DOCTYPE html>993<html lang="en">994<head>995<title>HTML email test</title>996</head>997<body>998<p>PHPMailer</p>999</body>1000</html>1001EOT;1002$this->buildBody();1003$this->Mail->dsn = 'SUCCESS,FAILURE';1004self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1005//Sends the same mail, but sets the DSN notification to NEVER1006$this->Mail->dsn = 'NEVER';1007self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1008}10091010/**1011* createBody test of switch case1012*/1013public function testCreateBody()1014{1015$PHPMailer = new PHPMailer();1016$reflection = new \ReflectionClass($PHPMailer);1017$property = $reflection->getProperty('message_type');1018$property->setAccessible(true);1019$property->setValue($PHPMailer, 'inline');1020self::assertInternalType('string', $PHPMailer->createBody());10211022$property->setValue($PHPMailer, 'attach');1023self::assertInternalType('string', $PHPMailer->createBody());10241025$property->setValue($PHPMailer, 'inline_attach');1026self::assertInternalType('string', $PHPMailer->createBody());10271028$property->setValue($PHPMailer, 'alt');1029self::assertInternalType('string', $PHPMailer->createBody());10301031$property->setValue($PHPMailer, 'alt_inline');1032self::assertInternalType('string', $PHPMailer->createBody());10331034$property->setValue($PHPMailer, 'alt_attach');1035self::assertInternalType('string', $PHPMailer->createBody());10361037$property->setValue($PHPMailer, 'alt_inline_attach');1038self::assertInternalType('string', $PHPMailer->createBody());1039}10401041/**1042* Send a message containing ISO-8859-1 text.1043*/1044public function testHtmlIso8859()1045{1046$this->Mail->isHTML(true);1047$this->Mail->Subject .= ': ISO-8859-1 HTML';1048$this->Mail->CharSet = PHPMailer::CHARSET_ISO88591;10491050//This file is in ISO-8859-1 charset1051//Needs to be external because this file is in UTF-81052$content = file_get_contents(realpath($this->INCLUDE_DIR . '/examples/contents.html'));1053// This is the string 'éèîüçÅñæß' in ISO-8859-1, base-64 encoded1054$check = base64_decode('6eju/OfF8ebf');1055//Make sure it really is in ISO-8859-1!1056$this->Mail->msgHTML(1057mb_convert_encoding(1058$content,1059'ISO-8859-1',1060mb_detect_encoding($content, 'UTF-8, ISO-8859-1, ISO-8859-15', true)1061),1062realpath($this->INCLUDE_DIR . '/examples')1063);1064$this->buildBody();1065self::assertContains($check, $this->Mail->Body, 'ISO message body does not contain expected text');1066self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1067}10681069/**1070* Send a message containing multilingual UTF-8 text.1071*/1072public function testHtmlUtf8()1073{1074$this->Mail->isHTML(true);1075$this->Mail->Subject .= ': UTF-8 HTML Пустое тело сообщения';1076$this->Mail->CharSet = 'UTF-8';10771078$this->Mail->Body = <<<'EOT'1079<!DOCTYPE html>1080<html lang="en">1081<head>1082<meta http-equiv="Content-Type" content="text/html; charset=utf-8">1083<title>HTML email test</title>1084</head>1085<body>1086<p>Chinese text: 郵件內容為空</p>1087<p>Russian text: Пустое тело сообщения</p>1088<p>Armenian text: Հաղորդագրությունը դատարկ է</p>1089<p>Czech text: Prázdné tělo zprávy</p>1090</body>1091</html>1092EOT;1093$this->buildBody();1094self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1095$msg = $this->Mail->getSentMIMEMessage();1096self::assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers');1097}10981099/**1100* Send a message containing multilingual UTF-8 text with an embedded image.1101*/1102public function testUtf8WithEmbeddedImage()1103{1104$this->Mail->isHTML(true);1105$this->Mail->Subject .= ': UTF-8 with embedded image';1106$this->Mail->CharSet = 'UTF-8';11071108$this->Mail->Body = <<<'EOT'1109<!DOCTYPE html>1110<html lang="en">1111<head>1112<meta http-equiv="Content-Type" content="text/html; charset=utf-8">1113<title>HTML email test</title>1114</head>1115<body>1116<p>Chinese text: 郵件內容為空</p>1117<p>Russian text: Пустое тело сообщения</p>1118<p>Armenian text: Հաղորդագրությունը դատարկ է</p>1119<p>Czech text: Prázdné tělo zprávy</p>1120Embedded Image: <img alt="phpmailer" src="cid:bäck">1121</body>1122</html>1123EOT;1124$this->Mail->addEmbeddedImage(1125realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'),1126'bäck',1127'phpmailer.png',1128'base64',1129'image/png'1130);1131$this->buildBody();1132self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1133}11341135/**1136* Send a message containing multilingual UTF-8 text.1137*/1138public function testPlainUtf8()1139{1140$this->Mail->isHTML(false);1141$this->Mail->Subject .= ': UTF-8 plain text';1142$this->Mail->CharSet = 'UTF-8';11431144$this->Mail->Body = <<<'EOT'1145Chinese text: 郵件內容為空1146Russian text: Пустое тело сообщения1147Armenian text: Հաղորդագրությունը դատարկ է1148Czech text: Prázdné tělo zprávy1149EOT;1150$this->buildBody();1151self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1152$msg = $this->Mail->getSentMIMEMessage();1153self::assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers');1154}11551156/**1157* Test simple message builder and html2text converters.1158*/1159public function testMsgHTML()1160{1161$message = file_get_contents(realpath($this->INCLUDE_DIR . '/examples/contentsutf8.html'));1162$this->Mail->CharSet = PHPMailer::CHARSET_UTF8;1163$this->Mail->Body = '';1164$this->Mail->AltBody = '';1165//Uses internal HTML to text conversion1166$this->Mail->msgHTML($message, realpath($this->INCLUDE_DIR . '/examples'));1167$sub = $this->Mail->Subject . ': msgHTML';1168$this->Mail->Subject .= $sub;11691170self::assertNotEmpty($this->Mail->Body, 'Body not set by msgHTML');1171self::assertNotEmpty($this->Mail->AltBody, 'AltBody not set by msgHTML');1172self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);11731174//Again, using a custom HTML to text converter1175$this->Mail->AltBody = '';1176$this->Mail->msgHTML(1177$message,1178realpath($this->INCLUDE_DIR . '/examples'),1179function ($html) {1180return strtoupper(strip_tags($html));1181}1182);1183$this->Mail->Subject = $sub . ' + custom html2text';1184self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);11851186//Test that local paths without a basedir are ignored1187$this->Mail->msgHTML('<img src="/etc/hostname">test');1188self::assertContains('src="/etc/hostname"', $this->Mail->Body);1189//Test that local paths with a basedir are not ignored1190$this->Mail->msgHTML('<img src="composer.json">test', realpath($this->INCLUDE_DIR));1191self::assertNotContains('src="composer.json"', $this->Mail->Body);1192//Test that local paths with parent traversal are ignored1193$this->Mail->msgHTML('<img src="../composer.json">test', realpath($this->INCLUDE_DIR));1194self::assertNotContains('src="composer.json"', $this->Mail->Body);1195//Test that existing embedded URLs are ignored1196$this->Mail->msgHTML('<img src="cid:5d41402abc4b2a76b9719d911017c592">test');1197self::assertContains('src="cid:5d41402abc4b2a76b9719d911017c592"', $this->Mail->Body);1198//Test that absolute URLs are ignored1199$this->Mail->msgHTML('<img src="https://github.com/PHPMailer/PHPMailer/blob/master/composer.json">test');1200self::assertContains(1201'src="https://github.com/PHPMailer/PHPMailer/blob/master/composer.json"',1202$this->Mail->Body1203);1204//Test that absolute URLs with anonymous/relative protocol are ignored1205//Note that such URLs will not work in email anyway because they have no protocol to be relative to1206$this->Mail->msgHTML('<img src="//github.com/PHPMailer/PHPMailer/blob/master/composer.json">test');1207self::assertContains('src="//github.com/PHPMailer/PHPMailer/blob/master/composer.json"', $this->Mail->Body);1208}12091210/**1211* Simple HTML and attachment test.1212*/1213public function testHTMLAttachment()1214{1215$this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.';1216$this->Mail->Subject .= ': HTML + Attachment';1217$this->Mail->isHTML(true);1218$this->Mail->CharSet = 'UTF-8';12191220if (!$this->Mail->addAttachment(1221realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'),1222'phpmailer_mini.png'1223)1224) {1225self::assertTrue(false, $this->Mail->ErrorInfo);12261227return;1228}12291230//Make sure that trying to attach a nonexistent file fails1231$filename = __FILE__ . md5(microtime()) . 'nonexistent_file.txt';1232self::assertFalse($this->Mail->addAttachment($filename));1233//Make sure that trying to attach an existing but unreadable file fails1234touch($filename);1235chmod($filename, 0200);1236self::assertFalse($this->Mail->addAttachment($filename));1237chmod($filename, 0644);1238unlink($filename);12391240$this->buildBody();1241self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1242}12431244/**1245* Attachment naming test.1246*/1247public function testAttachmentNaming()1248{1249$this->Mail->Body = 'Attachments.';1250$this->Mail->Subject .= ': Attachments';1251$this->Mail->isHTML(true);1252$this->Mail->CharSet = 'UTF-8';1253$this->Mail->addAttachment(1254realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'),1255'phpmailer_mini.png";.jpg'1256);1257$this->Mail->addAttachment(1258realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'),1259'phpmailer.png'1260);1261$this->Mail->addAttachment(1262realpath($this->INCLUDE_DIR . '/examples/images/PHPMailer card logo.png'),1263'PHPMailer card logo.png'1264);1265$this->Mail->addAttachment(1266realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'),1267'phpmailer_mini.png\\\";.jpg'1268);1269$this->buildBody();1270$this->Mail->preSend();1271$message = $this->Mail->getSentMIMEMessage();1272self::assertContains(1273'Content-Type: image/png; name="phpmailer_mini.png\";.jpg"',1274$message,1275'Name containing double quote should be escaped in Content-Type'1276);1277self::assertContains(1278'Content-Disposition: attachment; filename="phpmailer_mini.png\";.jpg"',1279$message,1280'Filename containing double quote should be escaped in Content-Disposition'1281);1282self::assertContains(1283'Content-Type: image/png; name=phpmailer.png',1284$message,1285'Name without special chars should not be quoted in Content-Type'1286);1287self::assertContains(1288'Content-Disposition: attachment; filename=phpmailer.png',1289$message,1290'Filename without special chars should not be quoted in Content-Disposition'1291);1292self::assertContains(1293'Content-Type: image/png; name="PHPMailer card logo.png"',1294$message,1295'Name with spaces should be quoted in Content-Type'1296);1297self::assertContains(1298'Content-Disposition: attachment; filename="PHPMailer card logo.png"',1299$message,1300'Filename with spaces should be quoted in Content-Disposition'1301);1302}13031304/**1305* Test embedded image without a name.1306*/1307public function testHTMLStringEmbedNoName()1308{1309$this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.';1310$this->Mail->Subject .= ': HTML + unnamed embedded image';1311$this->Mail->isHTML(true);13121313if (!$this->Mail->addStringEmbeddedImage(1314file_get_contents(realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png')),1315hash('sha256', 'phpmailer_mini.png') . '@phpmailer.0',1316'', //Intentionally empty name1317'base64',1318'', //Intentionally empty MIME type1319'inline'1320)) {1321self::assertTrue(false, $this->Mail->ErrorInfo);13221323return;1324}13251326$this->buildBody();1327self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1328}13291330/**1331* Simple HTML and multiple attachment test.1332*/1333public function testHTMLMultiAttachment()1334{1335$this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.';1336$this->Mail->Subject .= ': HTML + multiple Attachment';1337$this->Mail->isHTML(true);13381339if (!$this->Mail->addAttachment(1340realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'),1341'phpmailer_mini.png'1342)1343) {1344self::assertTrue(false, $this->Mail->ErrorInfo);13451346return;1347}13481349if (!$this->Mail->addAttachment(1350realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'),1351'phpmailer.png'1352)1353) {1354self::assertTrue(false, $this->Mail->ErrorInfo);13551356return;1357}13581359$this->buildBody();1360self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1361}13621363/**1364* An embedded attachment test.1365*/1366public function testEmbeddedImage()1367{1368$this->Mail->Body = 'Embedded Image: <img alt="phpmailer" src="' .1369'cid:my-attach">' .1370'Here is an image!';1371$this->Mail->Subject .= ': Embedded Image';1372$this->Mail->isHTML(true);13731374if (!$this->Mail->addEmbeddedImage(1375realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'),1376'my-attach',1377'phpmailer.png',1378'base64',1379'image/png'1380)1381) {1382self::assertTrue(false, $this->Mail->ErrorInfo);13831384return;1385}13861387$this->buildBody();1388self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1389$this->Mail->clearAttachments();1390$this->Mail->msgHTML('<!DOCTYPE html>1391<html lang="en">1392<head>1393<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />1394<title>E-Mail Inline Image Test</title>1395</head>1396<body>1397<p><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="></p>1398</body>1399</html>');1400$this->Mail->preSend();1401self::assertContains(1402'Content-ID: <[email protected]>',1403$this->Mail->getSentMIMEMessage(),1404'Embedded image header encoding incorrect.'1405);1406//For code coverage1407$this->Mail->addEmbeddedImage('thisfiledoesntexist', 'xyz'); //Non-existent file1408$this->Mail->addEmbeddedImage(__FILE__, '123'); //Missing name1409}14101411/**1412* An embedded attachment test.1413*/1414public function testMultiEmbeddedImage()1415{1416$this->Mail->Body = 'Embedded Image: <img alt="phpmailer" src="' .1417'cid:my-attach">' .1418'Here is an image!</a>';1419$this->Mail->Subject .= ': Embedded Image + Attachment';1420$this->Mail->isHTML(true);14211422if (!$this->Mail->addEmbeddedImage(1423realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'),1424'my-attach',1425'phpmailer.png',1426'base64',1427'image/png'1428)1429) {1430self::assertTrue(false, $this->Mail->ErrorInfo);14311432return;1433}14341435if (!$this->Mail->addAttachment(__FILE__, 'test.txt')) {1436self::assertTrue(false, $this->Mail->ErrorInfo);14371438return;1439}14401441$this->buildBody();1442self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1443}14441445/**1446* Simple multipart/alternative test.1447*/1448public function testAltBody()1449{1450$this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.';1451$this->Mail->AltBody = 'Here is the plain text body of this message. ' .1452'It should be quite a few lines. It should be wrapped at ' .1453'40 characters. Make sure that it is.';1454$this->Mail->WordWrap = 40;1455$this->addNote('This is a multipart/alternative email');1456$this->Mail->Subject .= ': AltBody + Word Wrap';14571458$this->buildBody();1459self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1460}14611462/**1463* Simple HTML and attachment test.1464*/1465public function testAltBodyAttachment()1466{1467$this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.';1468$this->Mail->AltBody = 'This is the text part of the email.';1469$this->Mail->Subject .= ': AltBody + Attachment';1470$this->Mail->isHTML(true);14711472if (!$this->Mail->addAttachment(__FILE__, 'test_attach.txt')) {1473self::assertTrue(false, $this->Mail->ErrorInfo);14741475return;1476}14771478//Test using non-existent UNC path1479self::assertFalse($this->Mail->addAttachment('\\\\nowhere\nothing'));14801481$this->buildBody();1482self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1483}14841485/**1486* Test sending multiple messages with separate connections.1487*/1488public function testMultipleSend()1489{1490$this->Mail->Body = 'Sending two messages without keepalive';1491$this->buildBody();1492$subject = $this->Mail->Subject;14931494$this->Mail->Subject = $subject . ': SMTP 1';1495self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);14961497$this->Mail->Subject = $subject . ': SMTP 2';1498$this->Mail->Sender = '[email protected]';1499self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1500}15011502/**1503* Test sending using SendMail.1504*/1505public function testSendmailSend()1506{1507$this->Mail->Body = 'Sending via sendmail';1508$this->buildBody();1509$subject = $this->Mail->Subject;15101511$this->Mail->Subject = $subject . ': sendmail';1512$this->Mail->isSendmail();15131514self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1515}15161517/**1518* Test sending using Qmail.1519*/1520public function testQmailSend()1521{1522//Only run if we have qmail installed1523if (file_exists('/var/qmail/bin/qmail-inject')) {1524$this->Mail->Body = 'Sending via qmail';1525$this->buildBody();1526$subject = $this->Mail->Subject;15271528$this->Mail->Subject = $subject . ': qmail';1529$this->Mail->isQmail();1530self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1531} else {1532self::markTestSkipped('Qmail is not installed');1533}1534}15351536/**1537* Test sending using PHP mail() function.1538*/1539public function testMailSend()1540{1541$sendmail = ini_get('sendmail_path');1542//No path in sendmail_path1543if (strpos($sendmail, '/') === false) {1544ini_set('sendmail_path', '/usr/sbin/sendmail -t -i ');1545}1546$this->Mail->Body = 'Sending via mail()';1547$this->buildBody();1548$this->Mail->Subject = $this->Mail->Subject . ': mail()';1549$this->Mail->clearAddresses();1550$this->Mail->clearCCs();1551$this->Mail->clearBCCs();1552$this->setAddress('[email protected]', 'totest');1553$this->setAddress('[email protected]', 'cctest', $sType = 'cc');1554$this->setAddress('[email protected]', 'bcctest', $sType = 'bcc');1555$this->Mail->addReplyTo('[email protected]', 'replytotest');1556self::assertContains('[email protected]', $this->Mail->getToAddresses()[0]);1557self::assertContains('[email protected]', $this->Mail->getCcAddresses()[0]);1558self::assertContains('[email protected]', $this->Mail->getBccAddresses()[0]);1559self::assertContains(1560'[email protected]',1561$this->Mail->getReplyToAddresses()['[email protected]']1562);1563self::assertTrue($this->Mail->getAllRecipientAddresses()['[email protected]']);1564self::assertTrue($this->Mail->getAllRecipientAddresses()['[email protected]']);1565self::assertTrue($this->Mail->getAllRecipientAddresses()['[email protected]']);15661567$this->Mail->createHeader();1568$this->Mail->isMail();1569self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1570$msg = $this->Mail->getSentMIMEMessage();1571self::assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers');1572}15731574/**1575* Test sending an empty body.1576*/1577public function testEmptyBody()1578{1579$this->buildBody();1580$this->Mail->Body = '';1581$this->Mail->Subject = $this->Mail->Subject . ': Empty Body';1582$this->Mail->isMail();1583$this->Mail->AllowEmpty = true;1584self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1585$this->Mail->AllowEmpty = false;1586self::assertFalse($this->Mail->send(), $this->Mail->ErrorInfo);1587}15881589/**1590* Test constructing a multipart message that contains lines that are too long for RFC compliance.1591*/1592public function testLongBody()1593{1594$oklen = str_repeat(str_repeat('0', PHPMailer::MAX_LINE_LENGTH) . PHPMailer::getLE(), 2);1595//Use +2 to ensure line length is over limit - LE may only be 1 char1596$badlen = str_repeat(str_repeat('1', PHPMailer::MAX_LINE_LENGTH + 2) . PHPMailer::getLE(), 2);15971598$this->Mail->Body = 'This message contains lines that are too long.' .1599PHPMailer::getLE() . $oklen . $badlen . $oklen;1600self::assertTrue(1601PHPMailer::hasLineLongerThanMax($this->Mail->Body),1602'Test content does not contain long lines!'1603);1604$this->Mail->isHTML();1605$this->buildBody();1606$this->Mail->AltBody = $this->Mail->Body;1607$this->Mail->Encoding = '8bit';1608$this->Mail->preSend();1609$message = $this->Mail->getSentMIMEMessage();1610self::assertFalse(1611PHPMailer::hasLineLongerThanMax($message),1612'Long line not corrected (Max: ' . (PHPMailer::MAX_LINE_LENGTH + strlen(PHPMailer::getLE())) . ' chars)'1613);1614self::assertContains(1615'Content-Transfer-Encoding: quoted-printable',1616$message,1617'Long line did not cause transfer encoding switch.'1618);1619}16201621/**1622* Test constructing a message that does NOT contain lines that are too long for RFC compliance.1623*/1624public function testShortBody()1625{1626$oklen = str_repeat(str_repeat('0', PHPMailer::MAX_LINE_LENGTH) . PHPMailer::getLE(), 10);16271628$this->Mail->Body = 'This message does not contain lines that are too long.' .1629PHPMailer::getLE() . $oklen;1630self::assertFalse(1631PHPMailer::hasLineLongerThanMax($this->Mail->Body),1632'Test content contains long lines!'1633);1634$this->buildBody();1635$this->Mail->Encoding = '8bit';1636$this->Mail->preSend();1637$message = $this->Mail->getSentMIMEMessage();1638self::assertFalse(PHPMailer::hasLineLongerThanMax($message), 'Long line not corrected.');1639self::assertNotContains(1640'Content-Transfer-Encoding: quoted-printable',1641$message,1642'Short line caused transfer encoding switch.'1643);1644}16451646/**1647* Test keepalive (sending multiple messages in a single connection).1648*/1649public function testSmtpKeepAlive()1650{1651$this->Mail->Body = 'SMTP keep-alive test.';1652$this->buildBody();1653$subject = $this->Mail->Subject;16541655$this->Mail->SMTPKeepAlive = true;1656$this->Mail->Subject = $subject . ': SMTP keep-alive 1';1657self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);16581659$this->Mail->Subject = $subject . ': SMTP keep-alive 2';1660self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1661$this->Mail->smtpClose();1662}16631664/**1665* Test this denial of service attack.1666*1667* @see http://www.cybsec.com/vuln/PHPMailer-DOS.pdf1668*/1669public function testDenialOfServiceAttack()1670{1671$this->Mail->Body = 'This should no longer cause a denial of service.';1672$this->buildBody();16731674$this->Mail->Subject = substr(str_repeat('0123456789', 100), 0, 998);1675self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);1676}16771678/**1679* Tests this denial of service attack.1680*1681* @see https://sourceforge.net/p/phpmailer/bugs/383/1682* According to the ticket, this should get stuck in a loop, though I can't make it happen.1683*/1684public function testDenialOfServiceAttack2()1685{1686//Encoding name longer than 68 chars1687$this->Mail->Encoding = '1234567890123456789012345678901234567890123456789012345678901234567890';1688//Call wrapText with a zero length value1689$this->Mail->wrapText(str_repeat('This should no longer cause a denial of service. ', 30), 0);1690}16911692/**1693* Test error handling.1694*/1695public function testError()1696{1697$this->Mail->Subject .= ': Error handling test - this should be sent ok';1698$this->buildBody();1699$this->Mail->clearAllRecipients(); // no addresses should cause an error1700self::assertTrue($this->Mail->isError() == false, 'Error found');1701self::assertTrue($this->Mail->send() == false, 'send succeeded');1702self::assertTrue($this->Mail->isError(), 'No error found');1703self::assertEquals('You must provide at least one recipient email address.', $this->Mail->ErrorInfo);1704$this->Mail->addAddress($_REQUEST['mail_to']);1705self::assertTrue($this->Mail->send(), 'send failed');1706}17071708/**1709* Test addressing.1710*/1711public function testAddressing()1712{1713self::assertFalse($this->Mail->addAddress(''), 'Empty address accepted');1714self::assertFalse($this->Mail->addAddress('', 'Nobody'), 'Empty address with name accepted');1715self::assertFalse($this->Mail->addAddress('[email protected]'), 'Invalid address accepted');1716self::assertTrue($this->Mail->addAddress('[email protected]'), 'Addressing failed');1717self::assertFalse($this->Mail->addAddress('[email protected]'), 'Duplicate addressing failed');1718self::assertTrue($this->Mail->addCC('[email protected]'), 'CC addressing failed');1719self::assertFalse($this->Mail->addCC('[email protected]'), 'CC duplicate addressing failed');1720self::assertFalse($this->Mail->addCC('[email protected]'), 'CC duplicate addressing failed (2)');1721self::assertTrue($this->Mail->addBCC('[email protected]'), 'BCC addressing failed');1722self::assertFalse($this->Mail->addBCC('[email protected]'), 'BCC duplicate addressing failed');1723self::assertFalse($this->Mail->addBCC('[email protected]'), 'BCC duplicate addressing failed (2)');1724self::assertTrue($this->Mail->addReplyTo('[email protected]'), 'Replyto Addressing failed');1725self::assertFalse($this->Mail->addReplyTo('[email protected]'), 'Invalid Replyto address accepted');1726self::assertTrue($this->Mail->setFrom('[email protected]', 'some name'), 'setFrom failed');1727self::assertFalse($this->Mail->setFrom('[email protected].', 'some name'), 'setFrom accepted invalid address');1728$this->Mail->Sender = '';1729$this->Mail->setFrom('[email protected]', 'some name', true);1730self::assertEquals($this->Mail->Sender, '[email protected]', 'setFrom failed to set sender');1731$this->Mail->Sender = '';1732$this->Mail->setFrom('[email protected]', 'some name', false);1733self::assertEquals($this->Mail->Sender, '', 'setFrom should not have set sender');1734$this->Mail->clearCCs();1735$this->Mail->clearBCCs();1736$this->Mail->clearReplyTos();1737}17381739/**1740* Test addressing.1741*/1742public function testAddressing2()1743{1744$this->buildBody();1745$this->Mail->setFrom('[email protected]', '"Bob\'s Burgers" (Bob\'s "Burgers")', true);1746$this->Mail->isSMTP();1747$this->Mail->Subject .= ': quotes in from name';1748self::assertTrue($this->Mail->send(), 'send failed');1749}17501751/**1752* Test RFC822 address splitting.1753*/1754public function testAddressSplitting()1755{1756//Test built-in address parser1757self::assertCount(17582,1759PHPMailer::parseAddresses(1760'Joe User <[email protected]>, Jill User <[email protected]>'1761),1762'Failed to recognise address list (IMAP parser)'1763);1764self::assertEquals(1765[1766['name' => 'Joe User', 'address' => '[email protected]'],1767['name' => 'Jill User', 'address' => '[email protected]'],1768['name' => '', 'address' => '[email protected]'],1769],1770PHPMailer::parseAddresses(1771'Joe User <[email protected]>,'1772. 'Jill User <[email protected]>,'1773. '[email protected],'1774),1775'Parsed addresses'1776);1777//Test simple address parser1778self::assertCount(17792,1780PHPMailer::parseAddresses(1781'Joe User <[email protected]>, Jill User <[email protected]>',1782false1783),1784'Failed to recognise address list'1785);1786//Test single address1787self::assertNotEmpty(1788PHPMailer::parseAddresses(1789'Joe User <[email protected]>',1790false1791),1792'Failed to recognise single address'1793);1794//Test quoted name IMAP1795self::assertNotEmpty(1796PHPMailer::parseAddresses(1797'Tim "The Book" O\'Reilly <[email protected]>'1798),1799'Failed to recognise quoted name (IMAP)'1800);1801//Test quoted name1802self::assertNotEmpty(1803PHPMailer::parseAddresses(1804'Tim "The Book" O\'Reilly <[email protected]>',1805false1806),1807'Failed to recognise quoted name'1808);1809//Test single address IMAP1810self::assertNotEmpty(1811PHPMailer::parseAddresses(1812'Joe User <[email protected]>'1813),1814'Failed to recognise single address (IMAP)'1815);1816//Test unnamed address1817self::assertNotEmpty(1818PHPMailer::parseAddresses(1819'[email protected]',1820false1821),1822'Failed to recognise unnamed address'1823);1824//Test unnamed address IMAP1825self::assertNotEmpty(1826PHPMailer::parseAddresses(1827'[email protected]'1828),1829'Failed to recognise unnamed address (IMAP)'1830);1831//Test invalid addresses1832self::assertEmpty(1833PHPMailer::parseAddresses(1834'Joe User <[email protected].>, Jill User <[email protected]>'1835),1836'Failed to recognise invalid addresses (IMAP)'1837);1838//Test invalid addresses1839self::assertEmpty(1840PHPMailer::parseAddresses(1841'Joe User <[email protected].>, Jill User <[email protected]>',1842false1843),1844'Failed to recognise invalid addresses'1845);1846}18471848/**1849* Test address escaping.1850*/1851public function testAddressEscaping()1852{1853$this->Mail->Subject .= ': Address escaping';1854$this->Mail->clearAddresses();1855$this->Mail->addAddress('[email protected]', 'Tim "The Book" O\'Reilly');1856$this->Mail->Body = 'Test correct escaping of quotes in addresses.';1857$this->buildBody();1858$this->Mail->preSend();1859$b = $this->Mail->getSentMIMEMessage();1860self::assertContains('To: "Tim \"The Book\" O\'Reilly" <[email protected]>', $b);18611862$this->Mail->Subject .= ': Address escaping invalid';1863$this->Mail->clearAddresses();1864$this->Mail->addAddress('[email protected]', 'Tim "The Book" O\'Reilly');1865$this->Mail->addAddress('invalidaddressexample.com', 'invalidaddress');1866$this->Mail->Body = 'invalid address';1867$this->buildBody();1868$this->Mail->preSend();1869self::assertEquals('Invalid address: (to): invalidaddressexample.com', $this->Mail->ErrorInfo);18701871$this->Mail->addAttachment(1872realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'),1873'phpmailer_mini.png'1874);1875self::assertTrue($this->Mail->attachmentExists());1876}18771878/**1879* Test MIME structure assembly.1880*/1881public function testMIMEStructure()1882{1883$this->Mail->Subject .= ': MIME structure';1884$this->Mail->Body = '<h3>MIME structure test.</h3>';1885$this->Mail->AltBody = 'MIME structure test.';1886$this->buildBody();1887$this->Mail->preSend();1888self::assertRegExp(1889"/Content-Transfer-Encoding: 8bit\r\n\r\n" .1890'This is a multi-part message in MIME format./',1891$this->Mail->getSentMIMEMessage(),1892'MIME structure broken'1893);1894}18951896/**1897* Test BCC-only addressing.1898*/1899public function testBCCAddressing()1900{1901$this->Mail->isSMTP();1902$this->Mail->Subject .= ': BCC-only addressing';1903$this->buildBody();1904$this->Mail->clearAllRecipients();1905$this->Mail->addAddress('[email protected]', 'Foo');1906$this->Mail->preSend();1907$b = $this->Mail->getSentMIMEMessage();1908self::assertTrue($this->Mail->addBCC('[email protected]'), 'BCC addressing failed');1909self::assertContains('To: Foo <[email protected]>', $b);1910self::assertTrue($this->Mail->send(), 'send failed');1911}19121913/**1914* Encoding and charset tests.1915*/1916public function testEncodings()1917{1918$this->Mail->CharSet = PHPMailer::CHARSET_ISO88591;1919self::assertEquals(1920'=A1Hola!_Se=F1or!',1921$this->Mail->encodeQ("\xa1Hola! Se\xf1or!", 'text'),1922'Q Encoding (text) failed'1923);1924self::assertEquals(1925'=A1Hola!_Se=F1or!',1926$this->Mail->encodeQ("\xa1Hola! Se\xf1or!", 'comment'),1927'Q Encoding (comment) failed'1928);1929self::assertEquals(1930'=A1Hola!_Se=F1or!',1931$this->Mail->encodeQ("\xa1Hola! Se\xf1or!", 'phrase'),1932'Q Encoding (phrase) failed'1933);1934$this->Mail->CharSet = 'UTF-8';1935self::assertEquals(1936'=C2=A1Hola!_Se=C3=B1or!',1937$this->Mail->encodeQ("\xc2\xa1Hola! Se\xc3\xb1or!", 'text'),1938'Q Encoding (text) failed'1939);1940//Strings containing '=' are a special case1941self::assertEquals(1942'Nov=C3=A1=3D',1943$this->Mail->encodeQ("Nov\xc3\xa1=", 'text'),1944'Q Encoding (text) failed 2'1945);19461947self::assertEquals(1948'hello',1949$this->Mail->encodeString('hello', 'binary'),1950'Binary encoding changed input'1951);1952$this->Mail->ErrorInfo = '';1953$this->Mail->encodeString('hello', 'asdfghjkl');1954self::assertNotEmpty($this->Mail->ErrorInfo, 'Invalid encoding not detected');1955self::assertRegExp('/' . base64_encode('hello') . '/', $this->Mail->encodeString('hello'));1956}19571958/**1959* Expect exceptions on bad encoding1960*1961* @expectedException PHPMailer\PHPMailer\Exception1962*/1963public function testAddAttachmentEncodingException()1964{1965$mail = new PHPMailer(true);1966$mail->addAttachment(__FILE__, 'test.txt', 'invalidencoding');1967}19681969/**1970* Expect exceptions on sending after deleting a previously successfully attached file1971*1972* @expectedException PHPMailer\PHPMailer\Exception1973*/1974public function testDeletedAttachmentException()1975{1976$filename = __FILE__ . md5(microtime()) . 'test.txt';1977touch($filename);1978$this->Mail = new PHPMailer(true);1979$this->Mail->addAttachment($filename);1980unlink($filename);1981$this->Mail->send();1982}19831984/**1985* Expect error on sending after deleting a previously successfully attached file1986*/1987public function testDeletedAttachmentError()1988{1989$filename = __FILE__ . md5(microtime()) . 'test.txt';1990touch($filename);1991$this->Mail = new PHPMailer();1992$this->Mail->addAttachment($filename);1993unlink($filename);1994self::assertFalse($this->Mail->send());1995}19961997/**1998* Expect exceptions on bad encoding1999*2000* @expectedException PHPMailer\PHPMailer\Exception2001*/2002public function testStringAttachmentEncodingException()2003{2004$mail = new PHPMailer(true);2005$mail->addStringAttachment('hello', 'test.txt', 'invalidencoding');2006}20072008/**2009* Expect exceptions on bad encoding2010*2011* @expectedException PHPMailer\PHPMailer\Exception2012*/2013public function testEmbeddedImageEncodingException()2014{2015$mail = new PHPMailer(true);2016$mail->addEmbeddedImage(__FILE__, 'cid', 'test.png', 'invalidencoding');2017}20182019/**2020* Expect exceptions on bad encoding2021*2022* @expectedException PHPMailer\PHPMailer\Exception2023*/2024public function testStringEmbeddedImageEncodingException()2025{2026$mail = new PHPMailer(true);2027$mail->addStringEmbeddedImage('hello', 'cid', 'test.png', 'invalidencoding');2028}20292030/**2031* Test base-64 encoding.2032*/2033public function testBase64()2034{2035$this->Mail->Subject .= ': Base-64 encoding';2036$this->Mail->Encoding = 'base64';2037$this->buildBody();2038self::assertTrue($this->Mail->send(), 'Base64 encoding failed');2039}20402041/**2042* S/MIME Signing tests (self-signed).2043*2044* @requires extension openssl2045*/2046public function testSigning()2047{2048$this->Mail->Subject .= ': S/MIME signing';2049$this->Mail->Body = 'This message is S/MIME signed.';2050$this->buildBody();20512052$dn = [2053'countryName' => 'UK',2054'stateOrProvinceName' => 'Here',2055'localityName' => 'There',2056'organizationName' => 'PHP',2057'organizationalUnitName' => 'PHPMailer',2058'commonName' => 'PHPMailer Test',2059'emailAddress' => '[email protected]',2060];2061$keyconfig = [2062'digest_alg' => 'sha256',2063'private_key_bits' => 2048,2064'private_key_type' => OPENSSL_KEYTYPE_RSA,2065];2066$password = 'password';2067$certfile = 'certfile.pem';2068$keyfile = 'keyfile.pem';20692070//Make a new key pair2071$pk = openssl_pkey_new($keyconfig);2072//Create a certificate signing request2073$csr = openssl_csr_new($dn, $pk);2074//Create a self-signed cert2075$cert = openssl_csr_sign($csr, null, $pk, 1);2076//Save the cert2077openssl_x509_export($cert, $certout);2078file_put_contents($certfile, $certout);2079//Save the key2080openssl_pkey_export($pk, $pkeyout, $password);2081file_put_contents($keyfile, $pkeyout);20822083$this->Mail->sign(2084$certfile,2085$keyfile,2086$password2087);2088self::assertTrue($this->Mail->send(), 'S/MIME signing failed');20892090$msg = $this->Mail->getSentMIMEMessage();2091self::assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers');2092unlink($certfile);2093unlink($keyfile);2094}20952096/**2097* S/MIME Signing tests using a CA chain cert.2098* To test that a generated message is signed correctly, save the message in a file called `signed.eml`2099* and use openssl along with the certs generated by this script:2100* `openssl smime -verify -in signed.eml -signer certfile.pem -CAfile cacertfile.pem`.2101*2102* @requires extension openssl2103*/2104public function testSigningWithCA()2105{2106$this->Mail->Subject .= ': S/MIME signing with CA';2107$this->Mail->Body = 'This message is S/MIME signed with an extra CA cert.';2108$this->buildBody();21092110$certprops = [2111'countryName' => 'UK',2112'stateOrProvinceName' => 'Here',2113'localityName' => 'There',2114'organizationName' => 'PHP',2115'organizationalUnitName' => 'PHPMailer',2116'commonName' => 'PHPMailer Test',2117'emailAddress' => '[email protected]',2118];2119$cacertprops = [2120'countryName' => 'UK',2121'stateOrProvinceName' => 'Here',2122'localityName' => 'There',2123'organizationName' => 'PHP',2124'organizationalUnitName' => 'PHPMailer CA',2125'commonName' => 'PHPMailer Test CA',2126'emailAddress' => '[email protected]',2127];2128$keyconfig = [2129'digest_alg' => 'sha256',2130'private_key_bits' => 2048,2131'private_key_type' => OPENSSL_KEYTYPE_RSA,2132];2133$password = 'password';2134$cacertfile = 'cacertfile.pem';2135$cakeyfile = 'cakeyfile.pem';2136$certfile = 'certfile.pem';2137$keyfile = 'keyfile.pem';21382139//Create a CA cert2140//Make a new key pair2141$capk = openssl_pkey_new($keyconfig);2142//Create a certificate signing request2143$csr = openssl_csr_new($cacertprops, $capk);2144//Create a self-signed cert2145$cert = openssl_csr_sign($csr, null, $capk, 1);2146//Save the CA cert2147openssl_x509_export($cert, $certout);2148file_put_contents($cacertfile, $certout);2149//Save the CA key2150openssl_pkey_export($capk, $pkeyout, $password);2151file_put_contents($cakeyfile, $pkeyout);21522153//Create a cert signed by our CA2154//Make a new key pair2155$pk = openssl_pkey_new($keyconfig);2156//Create a certificate signing request2157$csr = openssl_csr_new($certprops, $pk);2158//Create a self-signed cert2159$cacert = file_get_contents($cacertfile);2160$cert = openssl_csr_sign($csr, $cacert, $capk, 1);2161//Save the cert2162openssl_x509_export($cert, $certout);2163file_put_contents($certfile, $certout);2164//Save the key2165openssl_pkey_export($pk, $pkeyout, $password);2166file_put_contents($keyfile, $pkeyout);21672168$this->Mail->sign(2169$certfile,2170$keyfile,2171$password,2172$cacertfile2173);2174self::assertTrue($this->Mail->send(), 'S/MIME signing with CA failed');2175unlink($cacertfile);2176unlink($cakeyfile);2177unlink($certfile);2178unlink($keyfile);2179}21802181/**2182* DKIM body canonicalization tests.2183*2184* @see https://tools.ietf.org/html/rfc6376#section-3.4.42185*/2186public function testDKIMBodyCanonicalization()2187{2188//Example from https://tools.ietf.org/html/rfc6376#section-3.4.52189$prebody = " C \r\nD \t E\r\n\r\n\r\n";2190$postbody = " C \r\nD \t E\r\n";2191self::assertEquals($this->Mail->DKIM_BodyC(''), "\r\n", 'DKIM empty body canonicalization incorrect');2192self::assertEquals(2193'frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN/XKdLCPjaYaY=',2194base64_encode(hash('sha256', $this->Mail->DKIM_BodyC(''), true)),2195'DKIM canonicalized empty body hash mismatch'2196);2197self::assertEquals($this->Mail->DKIM_BodyC($prebody), $postbody, 'DKIM body canonicalization incorrect');2198}21992200/**2201* DKIM header canonicalization tests.2202*2203* @see https://tools.ietf.org/html/rfc6376#section-3.4.22204*/2205public function testDKIMHeaderCanonicalization()2206{2207//Example from https://tools.ietf.org/html/rfc6376#section-3.4.52208$preheaders = "A: X\r\nB : Y\t\r\n\tZ \r\n";2209$postheaders = "a:X\r\nb:Y Z\r\n";2210self::assertEquals(2211$postheaders,2212$this->Mail->DKIM_HeaderC($preheaders),2213'DKIM header canonicalization incorrect'2214);2215//Check that long folded lines with runs of spaces are canonicalized properly2216$preheaders = 'Long-Header-1: <https://example.com/somescript.php?' .2217"id=1234567890&name=Abcdefghijklmnopquestuvwxyz&hash=\r\n abc1234\r\n" .2218"Long-Header-2: This is a long header value that contains runs of spaces and trailing \r\n" .2219' and is folded onto 2 lines';2220$postheaders = 'long-header-1:<https://example.com/somescript.php?id=1234567890&' .2221"name=Abcdefghijklmnopquestuvwxyz&hash= abc1234\r\nlong-header-2:This is a long" .2222' header value that contains runs of spaces and trailing and is folded onto 2 lines';2223self::assertEquals(2224$postheaders,2225$this->Mail->DKIM_HeaderC($preheaders),2226'DKIM header canonicalization of long lines incorrect'2227);2228}22292230/**2231* DKIM copied header fields tests.2232*2233* @group dkim2234*2235* @see https://tools.ietf.org/html/rfc6376#section-3.52236*/2237public function testDKIMOptionalHeaderFieldsCopy()2238{2239$privatekeyfile = 'dkim_private.pem';2240$pk = openssl_pkey_new(2241[2242'private_key_bits' => 2048,2243'private_key_type' => OPENSSL_KEYTYPE_RSA,2244]2245);2246openssl_pkey_export_to_file($pk, $privatekeyfile);2247$this->Mail->DKIM_private = 'dkim_private.pem';22482249//Example from https://tools.ietf.org/html/rfc6376#section-3.52250$from = '[email protected]';2251$to = '[email protected]';2252$date = 'date';2253$subject = 'example';22542255$headerLines = "From:$from\r\nTo:$to\r\nDate:$date\r\n";2256$copyHeaderFields = " z=From:$from\r\n |To:$to\r\n |Date:$date\r\n |Subject:$subject;\r\n";22572258$this->Mail->DKIM_copyHeaderFields = true;2259self::assertContains(2260$copyHeaderFields,2261$this->Mail->DKIM_Add($headerLines, $subject, ''),2262'DKIM header with copied header fields incorrect'2263);22642265$this->Mail->DKIM_copyHeaderFields = false;2266self::assertNotContains(2267$copyHeaderFields,2268$this->Mail->DKIM_Add($headerLines, $subject, ''),2269'DKIM header without copied header fields incorrect'2270);22712272unlink($privatekeyfile);2273}22742275/**2276* DKIM signing extra headers tests.2277*2278* @group dkim2279*/2280public function testDKIMExtraHeaders()2281{2282$privatekeyfile = 'dkim_private.pem';2283$pk = openssl_pkey_new(2284[2285'private_key_bits' => 2048,2286'private_key_type' => OPENSSL_KEYTYPE_RSA,2287]2288);2289openssl_pkey_export_to_file($pk, $privatekeyfile);2290$this->Mail->DKIM_private = 'dkim_private.pem';22912292//Example from https://tools.ietf.org/html/rfc6376#section-3.52293$from = '[email protected]';2294$to = '[email protected]';2295$date = 'date';2296$subject = 'example';2297$anyHeader = 'foo';2298$unsubscribeUrl = '<https://www.example.com/unsubscribe/?newsletterId=anytoken&actionToken=anyToken' .2299'&otherParam=otherValue&anotherParam=anotherVeryVeryVeryLongValue>';23002301$this->Mail->addCustomHeader('X-AnyHeader', $anyHeader);2302$this->Mail->addCustomHeader('Baz', 'bar');2303$this->Mail->addCustomHeader('List-Unsubscribe', $unsubscribeUrl);23042305$this->Mail->DKIM_extraHeaders = ['Baz', 'List-Unsubscribe'];23062307$headerLines = "From:$from\r\nTo:$to\r\nDate:$date\r\n";2308$headerLines .= "X-AnyHeader:$anyHeader\r\nBaz:bar\r\n";2309$headerLines .= 'List-Unsubscribe:' . $this->Mail->encodeHeader($unsubscribeUrl) . "\r\n";23102311$headerFields = 'h=From:To:Date:Baz:List-Unsubscribe:Subject';23122313$result = $this->Mail->DKIM_Add($headerLines, $subject, '');23142315self::assertContains($headerFields, $result, 'DKIM header with extra headers incorrect');23162317unlink($privatekeyfile);2318}23192320/**2321* DKIM Signing tests.2322*2323* @requires extension openssl2324*/2325public function testDKIM()2326{2327$this->Mail->Subject .= ': DKIM signing';2328$this->Mail->Body = 'This message is DKIM signed.';2329$this->buildBody();2330$privatekeyfile = 'dkim_private.pem';2331//Make a new key pair2332//(2048 bits is the recommended minimum key length -2333//gmail won't accept less than 1024 bits)2334$pk = openssl_pkey_new(2335[2336'private_key_bits' => 2048,2337'private_key_type' => OPENSSL_KEYTYPE_RSA,2338]2339);2340openssl_pkey_export_to_file($pk, $privatekeyfile);2341$this->Mail->DKIM_domain = 'example.com';2342$this->Mail->DKIM_private = $privatekeyfile;2343$this->Mail->DKIM_selector = 'phpmailer';2344$this->Mail->DKIM_passphrase = ''; //key is not encrypted2345self::assertTrue($this->Mail->send(), 'DKIM signed mail failed');2346$this->Mail->isMail();2347self::assertTrue($this->Mail->send(), 'DKIM signed mail via mail() failed');2348unlink($privatekeyfile);2349}23502351/**2352* Test line break reformatting.2353*/2354public function testLineBreaks()2355{2356//May have been altered by earlier tests, can interfere with line break format2357$this->Mail->isSMTP();2358$this->Mail->preSend();2359$unixsrc = "hello\nWorld\nAgain\n";2360$macsrc = "hello\rWorld\rAgain\r";2361$windowssrc = "hello\r\nWorld\r\nAgain\r\n";2362$mixedsrc = "hello\nWorld\rAgain\r\n";2363$target = "hello\r\nWorld\r\nAgain\r\n";2364self::assertEquals($target, PHPMailer::normalizeBreaks($unixsrc), 'UNIX break reformatting failed');2365self::assertEquals($target, PHPMailer::normalizeBreaks($macsrc), 'Mac break reformatting failed');2366self::assertEquals($target, PHPMailer::normalizeBreaks($windowssrc), 'Windows break reformatting failed');2367self::assertEquals($target, PHPMailer::normalizeBreaks($mixedsrc), 'Mixed break reformatting failed');23682369//To see accurate results when using postfix, set `sendmail_fix_line_endings = never` in main.cf2370$this->Mail->Subject = 'PHPMailer DOS line breaks';2371$this->Mail->Body = "This message\r\ncontains\r\nDOS-format\r\nCRLF line breaks.";2372self::assertTrue($this->Mail->send());23732374$this->Mail->Subject = 'PHPMailer UNIX line breaks';2375$this->Mail->Body = "This message\ncontains\nUNIX-format\nLF line breaks.";2376self::assertTrue($this->Mail->send());23772378$this->Mail->Encoding = 'quoted-printable';2379$this->Mail->Subject = 'PHPMailer DOS line breaks, QP';2380$this->Mail->Body = "This message\r\ncontains\r\nDOS-format\r\nCRLF line breaks.";2381self::assertTrue($this->Mail->send());23822383$this->Mail->Subject = 'PHPMailer UNIX line breaks, QP';2384$this->Mail->Body = "This message\ncontains\nUNIX-format\nLF line breaks.";2385self::assertTrue($this->Mail->send());2386}23872388/**2389* Test line length detection.2390*/2391public function testLineLength()2392{2393//May have been altered by earlier tests, can interfere with line break format2394$this->Mail->isSMTP();2395$this->Mail->preSend();2396$oklen = str_repeat(str_repeat('0', PHPMailer::MAX_LINE_LENGTH) . "\r\n", 2);2397$badlen = str_repeat(str_repeat('1', PHPMailer::MAX_LINE_LENGTH + 1) . "\r\n", 2);2398self::assertTrue(PHPMailer::hasLineLongerThanMax($badlen), 'Long line not detected (only)');2399self::assertTrue(PHPMailer::hasLineLongerThanMax($oklen . $badlen), 'Long line not detected (first)');2400self::assertTrue(PHPMailer::hasLineLongerThanMax($badlen . $oklen), 'Long line not detected (last)');2401self::assertTrue(2402PHPMailer::hasLineLongerThanMax($oklen . $badlen . $oklen),2403'Long line not detected (middle)'2404);2405self::assertFalse(PHPMailer::hasLineLongerThanMax($oklen), 'Long line false positive');2406$this->Mail->isHTML(false);2407$this->Mail->Subject .= ': Line length test';2408$this->Mail->CharSet = 'UTF-8';2409$this->Mail->Encoding = '8bit';2410$this->Mail->Body = $oklen . $badlen . $oklen . $badlen;2411$this->buildBody();2412self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);2413self::assertEquals('quoted-printable', $this->Mail->Encoding, 'Long line did not override transfer encoding');2414}24152416/**2417* Test setting and retrieving message ID.2418*/2419public function testMessageID()2420{2421$this->Mail->Body = 'Test message ID.';2422$id = hash('sha256', 12345);2423$this->Mail->MessageID = $id;2424$this->buildBody();2425$this->Mail->preSend();2426$lastid = $this->Mail->getLastMessageID();2427self::assertNotEquals($lastid, $id, 'Invalid Message ID allowed');2428$id = '<' . hash('sha256', 12345) . '@example.com>';2429$this->Mail->MessageID = $id;2430$this->buildBody();2431$this->Mail->preSend();2432$lastid = $this->Mail->getLastMessageID();2433self::assertEquals($lastid, $id, 'Custom Message ID not used');2434$this->Mail->MessageID = '';2435$this->buildBody();2436$this->Mail->preSend();2437$lastid = $this->Mail->getLastMessageID();2438self::assertRegExp('/^<.*@.*>$/', $lastid, 'Invalid default Message ID');2439}24402441/**2442* Check whether setting a bad custom header throws exceptions.2443*2444* @throws Exception2445*/2446public function testHeaderException()2447{2448$this->expectException(Exception::class);2449$mail = new PHPMailer(true);2450$mail->addCustomHeader('SomeHeader', "Some\n Value");2451}24522453/**2454* Miscellaneous calls to improve test coverage and some small tests.2455*/2456public function testMiscellaneous()2457{2458self::assertEquals('application/pdf', PHPMailer::_mime_types('pdf'), 'MIME TYPE lookup failed');2459$this->Mail->clearAttachments();2460$this->Mail->isHTML(false);2461$this->Mail->isSMTP();2462$this->Mail->isMail();2463$this->Mail->isSendmail();2464$this->Mail->isQmail();2465$this->Mail->setLanguage('fr');2466$this->Mail->Sender = '';2467$this->Mail->createHeader();2468self::assertFalse($this->Mail->set('x', 'y'), 'Invalid property set succeeded');2469self::assertTrue($this->Mail->set('Timeout', 11), 'Valid property set failed');2470self::assertTrue($this->Mail->set('AllowEmpty', null), 'Null property set failed');2471self::assertTrue($this->Mail->set('AllowEmpty', false), 'Valid property set of null property failed');2472//Test pathinfo2473$a = '/mnt/files/飛兒樂 團光茫.mp3';2474$q = PHPMailer::mb_pathinfo($a);2475self::assertEquals($q['dirname'], '/mnt/files', 'UNIX dirname not matched');2476self::assertEquals($q['basename'], '飛兒樂 團光茫.mp3', 'UNIX basename not matched');2477self::assertEquals($q['extension'], 'mp3', 'UNIX extension not matched');2478self::assertEquals($q['filename'], '飛兒樂 團光茫', 'UNIX filename not matched');2479self::assertEquals(2480PHPMailer::mb_pathinfo($a, PATHINFO_DIRNAME),2481'/mnt/files',2482'Dirname path element not matched'2483);2484self::assertEquals(2485PHPMailer::mb_pathinfo($a, PATHINFO_BASENAME),2486'飛兒樂 團光茫.mp3',2487'Basename path element not matched'2488);2489self::assertEquals(PHPMailer::mb_pathinfo($a, 'filename'), '飛兒樂 團光茫', 'Filename path element not matched');2490$a = 'c:\mnt\files\飛兒樂 團光茫.mp3';2491$q = PHPMailer::mb_pathinfo($a);2492self::assertEquals($q['dirname'], 'c:\mnt\files', 'Windows dirname not matched');2493self::assertEquals($q['basename'], '飛兒樂 團光茫.mp3', 'Windows basename not matched');2494self::assertEquals($q['extension'], 'mp3', 'Windows extension not matched');2495self::assertEquals($q['filename'], '飛兒樂 團光茫', 'Windows filename not matched');24962497self::assertEquals(2498PHPMailer::filenameToType('abc.jpg?xyz=1'),2499'image/jpeg',2500'Query string not ignored in filename'2501);2502self::assertEquals(2503PHPMailer::filenameToType('abc.xyzpdq'),2504'application/octet-stream',2505'Default MIME type not applied to unknown extension'2506);25072508//Line break normalization2509$eol = PHPMailer::getLE();2510$b1 = "1\r2\r3\r";2511$b2 = "1\n2\n3\n";2512$b3 = "1\r\n2\r3\n";2513$t1 = "1{$eol}2{$eol}3{$eol}";2514self::assertEquals(PHPMailer::normalizeBreaks($b1), $t1, 'Failed to normalize line breaks (1)');2515self::assertEquals(PHPMailer::normalizeBreaks($b2), $t1, 'Failed to normalize line breaks (2)');2516self::assertEquals(PHPMailer::normalizeBreaks($b3), $t1, 'Failed to normalize line breaks (3)');2517}25182519public function testBadSMTP()2520{2521$this->Mail->smtpConnect();2522$smtp = $this->Mail->getSMTPInstance();2523self::assertFalse($smtp->mail("somewhere\nbad"), 'Bad SMTP command containing breaks accepted');2524}25252526public function testHostValidation()2527{2528$good = [2529'localhost',2530'example.com',2531'smtp.gmail.com',2532'127.0.0.1',2533trim(str_repeat('a0123456789.', 21), '.'),2534'[::1]',2535'[0:1234:dc0:41:216:3eff:fe67:3e01]',2536];2537$bad = [2538null,2539123,25401.5,2541new \stdClass(),2542[],2543'',2544'999.0.0.0',2545'[1234]',2546'[1234:::1]',2547trim(str_repeat('a0123456789.', 22), '.'),2548'0:1234:dc0:41:216:3eff:fe67:3e01',2549'[012q:1234:dc0:41:216:3eff:fe67:3e01]',2550'[[::1]]',2551];2552foreach ($good as $h) {2553self::assertTrue(PHPMailer::isValidHost($h), 'Good hostname denied: ' . $h);2554}2555foreach ($bad as $h) {2556self::assertFalse(PHPMailer::isValidHost($h), 'Bad hostname accepted: ' . var_export($h, true));2557}2558}25592560/**2561* Tests the Custom header getter.2562*/2563public function testCustomHeaderGetter()2564{2565$this->Mail->addCustomHeader('foo', 'bar');2566self::assertEquals([['foo', 'bar']], $this->Mail->getCustomHeaders());25672568$this->Mail->addCustomHeader('foo', 'baz');2569self::assertEquals(2570[2571['foo', 'bar'],2572['foo', 'baz'],2573],2574$this->Mail->getCustomHeaders()2575);25762577$this->Mail->clearCustomHeaders();2578self::assertEmpty($this->Mail->getCustomHeaders());25792580$this->Mail->addCustomHeader('yux');2581self::assertEquals([['yux', '']], $this->Mail->getCustomHeaders());25822583$this->Mail->addCustomHeader('Content-Type: application/json');2584self::assertEquals(2585[2586['yux', ''],2587['Content-Type', 'application/json'],2588],2589$this->Mail->getCustomHeaders()2590);2591$this->Mail->clearCustomHeaders();2592$this->Mail->addCustomHeader('SomeHeader: Some Value');2593$headers = $this->Mail->getCustomHeaders();2594self::assertEquals($headers[0], ['SomeHeader', 'Some Value']);2595$this->Mail->clearCustomHeaders();2596$this->Mail->addCustomHeader('SomeHeader', 'Some Value');2597$headers = $this->Mail->getCustomHeaders();2598self::assertEquals($headers[0], ['SomeHeader', 'Some Value']);2599$this->Mail->clearCustomHeaders();2600self::assertFalse($this->Mail->addCustomHeader('SomeHeader', "Some\n Value"));2601self::assertFalse($this->Mail->addCustomHeader("Some\nHeader", 'Some Value'));2602}26032604/**2605* Tests setting and retrieving ConfirmReadingTo address, also known as "read receipt" address.2606*/2607public function testConfirmReadingTo()2608{2609$this->Mail->CharSet = PHPMailer::CHARSET_UTF8;2610$this->buildBody();26112612$this->Mail->ConfirmReadingTo = '[email protected]'; //Invalid address2613self::assertFalse($this->Mail->send(), $this->Mail->ErrorInfo);26142615$this->Mail->ConfirmReadingTo = ' [email protected]'; //Extra space to trim2616self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);2617self::assertEquals(2618'[email protected]',2619$this->Mail->ConfirmReadingTo,2620'Unexpected read receipt address'2621);26222623$this->Mail->ConfirmReadingTo = 'test@françois.ch'; //Address with IDN2624if (PHPMailer::idnSupported()) {2625self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);2626self::assertEquals(2627'[email protected]',2628$this->Mail->ConfirmReadingTo,2629'IDN address not converted to punycode'2630);2631} else {2632self::assertFalse($this->Mail->send(), $this->Mail->ErrorInfo);2633}2634}26352636/**2637* Tests CharSet and Unicode -> ASCII conversions for addresses with IDN.2638*/2639public function testConvertEncoding()2640{2641if (!PHPMailer::idnSupported()) {2642self::markTestSkipped('intl and/or mbstring extensions are not available');2643}26442645$this->Mail->clearAllRecipients();2646$this->Mail->clearReplyTos();26472648// This file is UTF-8 encoded. Create a domain encoded in "iso-8859-1".2649$domain = '@' . mb_convert_encoding('françois.ch', 'ISO-8859-1', 'UTF-8');2650$this->Mail->addAddress('test' . $domain);2651$this->Mail->addCC('test+cc' . $domain);2652$this->Mail->addBCC('test+bcc' . $domain);2653$this->Mail->addReplyTo('test+replyto' . $domain);26542655// Queued addresses are not returned by get*Addresses() before send() call.2656self::assertEmpty($this->Mail->getToAddresses(), 'Bad "to" recipients');2657self::assertEmpty($this->Mail->getCcAddresses(), 'Bad "cc" recipients');2658self::assertEmpty($this->Mail->getBccAddresses(), 'Bad "bcc" recipients');2659self::assertEmpty($this->Mail->getReplyToAddresses(), 'Bad "reply-to" recipients');26602661// Clear queued BCC recipient.2662$this->Mail->clearBCCs();26632664$this->buildBody();2665self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);26662667// Addresses with IDN are returned by get*Addresses() after send() call.2668$domain = $this->Mail->punyencodeAddress($domain);2669self::assertEquals(2670[['test' . $domain, '']],2671$this->Mail->getToAddresses(),2672'Bad "to" recipients'2673);2674self::assertEquals(2675[['test+cc' . $domain, '']],2676$this->Mail->getCcAddresses(),2677'Bad "cc" recipients'2678);2679self::assertEmpty($this->Mail->getBccAddresses(), 'Bad "bcc" recipients');2680self::assertEquals(2681['test+replyto' . $domain => ['test+replyto' . $domain, '']],2682$this->Mail->getReplyToAddresses(),2683'Bad "reply-to" addresses'2684);2685}26862687/**2688* Tests removal of duplicate recipients and reply-tos.2689*/2690public function testDuplicateIDNRemoved()2691{2692if (!PHPMailer::idnSupported()) {2693self::markTestSkipped('intl and/or mbstring extensions are not available');2694}26952696$this->Mail->clearAllRecipients();2697$this->Mail->clearReplyTos();26982699$this->Mail->CharSet = PHPMailer::CHARSET_UTF8;27002701self::assertTrue($this->Mail->addAddress('test@françois.ch'));2702self::assertFalse($this->Mail->addAddress('test@françois.ch'));2703self::assertTrue($this->Mail->addAddress('test@FRANÇOIS.CH'));2704self::assertFalse($this->Mail->addAddress('test@FRANÇOIS.CH'));2705self::assertTrue($this->Mail->addAddress('[email protected]'));2706self::assertFalse($this->Mail->addAddress('[email protected]'));2707self::assertFalse($this->Mail->addAddress('[email protected]'));27082709self::assertTrue($this->Mail->addReplyTo('test+replyto@françois.ch'));2710self::assertFalse($this->Mail->addReplyTo('test+replyto@françois.ch'));2711self::assertTrue($this->Mail->addReplyTo('test+replyto@FRANÇOIS.CH'));2712self::assertFalse($this->Mail->addReplyTo('test+replyto@FRANÇOIS.CH'));2713self::assertTrue($this->Mail->addReplyTo('[email protected]'));2714self::assertFalse($this->Mail->addReplyTo('[email protected]'));2715self::assertFalse($this->Mail->addReplyTo('[email protected]'));27162717$this->buildBody();2718self::assertTrue($this->Mail->send(), $this->Mail->ErrorInfo);27192720// There should be only one "To" address and one "Reply-To" address.2721self::assertCount(27221,2723$this->Mail->getToAddresses(),2724'Bad count of "to" recipients'2725);2726self::assertCount(27271,2728$this->Mail->getReplyToAddresses(),2729'Bad count of "reply-to" addresses'2730);2731}27322733/**2734* Use a fake POP3 server to test POP-before-SMTP auth with a known-good login.2735*2736* @group pop32737*/2738public function testPopBeforeSmtpGood()2739{2740//Start a fake POP server2741$pid = shell_exec(2742'/usr/bin/nohup ' .2743$this->INCLUDE_DIR .2744'/test/runfakepopserver.sh 1100 >/dev/null 2>/dev/null & printf "%u" $!'2745);2746$this->pids[] = $pid;27472748sleep(1);2749//Test a known-good login2750self::assertTrue(2751POP3::popBeforeSmtp('localhost', 1100, 10, 'user', 'test', $this->Mail->SMTPDebug),2752'POP before SMTP failed'2753);2754//Kill the fake server, don't care if it fails2755@shell_exec('kill -TERM ' . escapeshellarg($pid));2756sleep(2);2757}27582759/**2760* Use a fake POP3 server to test POP-before-SMTP auth2761* with a known-bad login.2762*2763* @group pop32764*/2765public function testPopBeforeSmtpBad()2766{2767//Start a fake POP server on a different port2768//so we don't inadvertently connect to the previous instance2769$pid = shell_exec(2770'/usr/bin/nohup ' .2771$this->INCLUDE_DIR .2772'/test/runfakepopserver.sh 1101 >/dev/null 2>/dev/null & printf "%u" $!'2773);2774$this->pids[] = $pid;27752776sleep(2);2777//Test a known-bad login2778self::assertFalse(2779POP3::popBeforeSmtp('localhost', 1101, 10, 'user', 'xxx', $this->Mail->SMTPDebug),2780'POP before SMTP should have failed'2781);2782//Kill the fake server, don't care if it fails2783@shell_exec('kill -TERM ' . escapeshellarg($pid));2784sleep(2);2785}27862787/**2788* Test SMTP host connections.2789* This test can take a long time, so run it last.2790*2791* @group slow2792*/2793public function testSmtpConnect()2794{2795$this->Mail->SMTPDebug = SMTP::DEBUG_LOWLEVEL; //Show connection-level errors2796self::assertTrue($this->Mail->smtpConnect(), 'SMTP single connect failed');2797$this->Mail->smtpClose();27982799// $this->Mail->Host = 'localhost:12345;10.10.10.10:54321;' . $_REQUEST['mail_host'];2800// self::assertTrue($this->Mail->smtpConnect(), 'SMTP multi-connect failed');2801// $this->Mail->smtpClose();2802// $this->Mail->Host = '[::1]:' . $this->Mail->Port . ';' . $_REQUEST['mail_host'];2803// self::assertTrue($this->Mail->smtpConnect(), 'SMTP IPv6 literal multi-connect failed');2804// $this->Mail->smtpClose();28052806// All these hosts are expected to fail2807// $this->Mail->Host = 'xyz://bogus:25;tls://[bogus]:25;ssl://localhost:12345;tls://localhost:587;10.10.10.10:54321;localhost:12345;10.10.10.10'. $_REQUEST['mail_host'].' ';2808// self::assertFalse($this->Mail->smtpConnect());2809// $this->Mail->smtpClose();28102811$this->Mail->Host = ' localhost:12345 ; ' . $_REQUEST['mail_host'] . ' ';2812self::assertTrue($this->Mail->smtpConnect(), 'SMTP hosts with stray spaces failed');2813$this->Mail->smtpClose();28142815// Need to pick a harmless option so as not cause problems of its own! socket:bind doesn't work with Travis-CI2816$this->Mail->Host = $_REQUEST['mail_host'];2817self::assertTrue($this->Mail->smtpConnect(['ssl' => ['verify_depth' => 10]]));28182819$this->Smtp = $this->Mail->getSMTPInstance();2820self::assertInstanceOf(\get_class($this->Smtp), $this->Mail->setSMTPInstance($this->Smtp));2821self::assertFalse($this->Smtp->startTLS(), 'SMTP connect with options failed');2822self::assertFalse($this->Mail->SMTPAuth);2823$this->Mail->smtpClose();2824}28252826/**2827* Test OAuth method2828*/2829public function testOAuth()2830{2831$PHPMailer = new PHPMailer();2832$reflection = new \ReflectionClass($PHPMailer);2833$property = $reflection->getProperty('oauth');2834$property->setAccessible(true);2835$property->setValue($PHPMailer, true);2836self::assertTrue($PHPMailer->getOAuth());28372838$options =[2839'provider' => 'dummyprovider',2840'userName' => 'dummyusername',2841'clientSecret' => 'dummyclientsecret',2842'clientId' => 'dummyclientid',2843'refreshToken' => 'dummyrefreshtoken',2844];28452846$oauth = new OAuth($options);2847self::assertInstanceOf(OAuth::class, $oauth);2848$subject = $PHPMailer->setOAuth($oauth);2849self::assertNull($subject);2850self::assertInstanceOf(OAuth::class, $PHPMailer->getOAuth());2851}28522853/**2854* Test ICal method2855*/2856public function testICalMethod()2857{2858$this->Mail->Subject .= ': ICal method';2859$this->Mail->Body = '<h3>ICal method test.</h3>';2860$this->Mail->AltBody = 'ICal method test.';2861$this->Mail->Ical = 'BEGIN:VCALENDAR'2862. "\r\nVERSION:2.0"2863. "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN"2864. "\r\nMETHOD:CANCEL"2865. "\r\nCALSCALE:GREGORIAN"2866. "\r\nX-MICROSOFT-CALSCALE:GREGORIAN"2867. "\r\nBEGIN:VEVENT"2868. "\r\nUID:201909250755-42825@test"2869. "\r\nDTSTART;20190930T080000Z"2870. "\r\nSEQUENCE:2"2871. "\r\nTRANSP:OPAQUE"2872. "\r\nSTATUS:CONFIRMED"2873. "\r\nDTEND:20190930T084500Z"2874. "\r\nLOCATION:[London] London Eye"2875. "\r\nSUMMARY:Test ICal method"2876. "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP="2877. "\r\n TRUE:MAILTO:[email protected]"2878. "\r\nCLASS:PUBLIC"2879. "\r\nDESCRIPTION:Some plain text"2880. "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:[email protected]"2881. "\r\nDTSTAMP:20190925T075546Z"2882. "\r\nCREATED:20190925T075709Z"2883. "\r\nLAST-MODIFIED:20190925T075546Z"2884. "\r\nEND:VEVENT"2885. "\r\nEND:VCALENDAR";2886$this->buildBody();2887$this->Mail->preSend();2888self::assertRegExp(2889'/Content-Type: text\/calendar; method=CANCEL;/',2890$this->Mail->getSentMIMEMessage(),2891'Wrong ICal method in Content-Type header'2892);2893}28942895/**2896* Test ICal missing method to use default (REQUEST)2897*/2898public function testICalInvalidMethod()2899{2900$this->Mail->Subject .= ': ICal method';2901$this->Mail->Body = '<h3>ICal method test.</h3>';2902$this->Mail->AltBody = 'ICal method test.';2903$this->Mail->Ical = 'BEGIN:VCALENDAR'2904. "\r\nVERSION:2.0"2905. "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN"2906. "\r\nMETHOD:INVALID"2907. "\r\nCALSCALE:GREGORIAN"2908. "\r\nX-MICROSOFT-CALSCALE:GREGORIAN"2909. "\r\nBEGIN:VEVENT"2910. "\r\nUID:201909250755-42825@test"2911. "\r\nDTSTART;20190930T080000Z"2912. "\r\nSEQUENCE:2"2913. "\r\nTRANSP:OPAQUE"2914. "\r\nSTATUS:CONFIRMED"2915. "\r\nDTEND:20190930T084500Z"2916. "\r\nLOCATION:[London] London Eye"2917. "\r\nSUMMARY:Test ICal method"2918. "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP="2919. "\r\n TRUE:MAILTO:[email protected]"2920. "\r\nCLASS:PUBLIC"2921. "\r\nDESCRIPTION:Some plain text"2922. "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:[email protected]"2923. "\r\nDTSTAMP:20190925T075546Z"2924. "\r\nCREATED:20190925T075709Z"2925. "\r\nLAST-MODIFIED:20190925T075546Z"2926. "\r\nEND:VEVENT"2927. "\r\nEND:VCALENDAR";2928$this->buildBody();2929$this->Mail->preSend();2930self::assertRegExp(2931'/Content-Type: text\/calendar; method=REQUEST;/',2932$this->Mail->getSentMIMEMessage(),2933'Wrong ICal method in Content-Type header'2934);2935}29362937/**2938* Test ICal invalid method to use default (REQUEST)2939*/2940public function testICalDefaultMethod()2941{2942$this->Mail->Subject .= ': ICal method';2943$this->Mail->Body = '<h3>ICal method test.</h3>';2944$this->Mail->AltBody = 'ICal method test.';2945$this->Mail->Ical = 'BEGIN:VCALENDAR'2946. "\r\nVERSION:2.0"2947. "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN"2948. "\r\nCALSCALE:GREGORIAN"2949. "\r\nX-MICROSOFT-CALSCALE:GREGORIAN"2950. "\r\nBEGIN:VEVENT"2951. "\r\nUID:201909250755-42825@test"2952. "\r\nDTSTART;20190930T080000Z"2953. "\r\nSEQUENCE:2"2954. "\r\nTRANSP:OPAQUE"2955. "\r\nSTATUS:CONFIRMED"2956. "\r\nDTEND:20190930T084500Z"2957. "\r\nLOCATION:[London] London Eye"2958. "\r\nSUMMARY:Test ICal method"2959. "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP="2960. "\r\n TRUE:MAILTO:[email protected]"2961. "\r\nCLASS:PUBLIC"2962. "\r\nDESCRIPTION:Some plain text"2963. "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:[email protected]"2964. "\r\nDTSTAMP:20190925T075546Z"2965. "\r\nCREATED:20190925T075709Z"2966. "\r\nLAST-MODIFIED:20190925T075546Z"2967. "\r\nEND:VEVENT"2968. "\r\nEND:VCALENDAR";2969$this->buildBody();2970$this->Mail->preSend();2971self::assertRegExp(2972'/Content-Type: text\/calendar; method=REQUEST;/',2973$this->Mail->getSentMIMEMessage(),2974'Wrong ICal method in Content-Type header'2975);2976}29772978/**2979* @test2980*/2981public function givenIdnAddress_addAddress_returns_true()2982{2983if (file_exists($this->INCLUDE_DIR . '/test/fakefunctions.php')) {2984include $this->INCLUDE_DIR . '/test/fakefunctions.php';2985$this->assertTrue($this->Mail->addAddress('test@françois.ch'));2986}2987}29882989/**2990* @test2991*/2992public function givenIdnAddress_addReplyTo_returns_true()2993{2994if (file_exists($this->INCLUDE_DIR . '/test/fakefunctions.php')) {2995include $this->INCLUDE_DIR . '/test/fakefunctions.php';2996$this->assertTrue($this->Mail->addReplyTo('test@françois.ch'));2997}2998}29993000/**3001* @test3002*/3003public function erroneousAddress_addAddress_returns_false()3004{3005$this->assertFalse($this->Mail->addAddress('mehome.com'));3006}30073008/**3009* @test3010*/3011public function imapParsedAddressList_parseAddress_returnsAddressArray()3012{3013$expected = [3014[3015'name' => 'joe',3016'address' => '[email protected]',3017],3018[3019'name' => 'me',3020'address' => '[email protected]',3021],3022];3023if (file_exists($this->INCLUDE_DIR . '/test/fakefunctions.php')) {3024include $this->INCLUDE_DIR . '/test/fakefunctions.php';3025$addresses = PHPMailer::parseAddresses('[email protected], [email protected]');3026$this->assertEquals(asort($expected), asort($addresses));3027}3028}30293030/**3031* @test3032*/3033public function givenIdnAddress_punyencodeAddress_returnsCorrectCode()3034{3035if (file_exists($this->INCLUDE_DIR . '/test/fakefunctions.php')) {3036include $this->INCLUDE_DIR . '/test/fakefunctions.php';3037$result = $this->Mail->punyencodeAddress('test@françois.ch');3038$this->assertEquals('test@1', $result);3039}3040}30413042/**3043* @test3044*/3045public function veryLongWordInMessage_wrapText_returnsWrappedText()3046{3047$expected = 'Lorem ipsumdolorsitametconsetetursadipscingelitrs=3048eddiamnonumy3049';3050$encodedMessage = 'Lorem ipsumdolorsitametconsetetursadipscingelitrseddiamnonumy';3051$result = $this->Mail->wrapText($encodedMessage, 50, true);3052$this->assertEquals($result, $expected);3053}30543055/**3056* @test3057*/3058public function encodedText_utf8CharBoundary_returnsCorrectMaxLength()3059{3060$encodedWordWithMultiByteCharFirstByte = 'H=E4tten';3061$encodedSingleByteCharacter = '=0C';3062$encodedWordWithMultiByteCharMiddletByte = 'L=C3=B6rem';30633064$this->assertEquals(1, $this->Mail->utf8CharBoundary($encodedWordWithMultiByteCharFirstByte, 3));3065$this->assertEquals(3, $this->Mail->utf8CharBoundary($encodedSingleByteCharacter, 3));3066$this->assertEquals(1, $this->Mail->utf8CharBoundary($encodedWordWithMultiByteCharMiddletByte, 6));3067}3068}3069/*3070* This is a sample form for setting appropriate test values through a browser3071* These values can also be set using a file called testbootstrap.php (not in repo) in the same folder as this script3072* which is probably more useful if you run these tests a lot3073* <html>3074* <body>3075* <h3>PHPMailer Unit Test</h3>3076* By entering a SMTP hostname it will automatically perform tests with SMTP.3077*3078* <form name="phpmailer_unit" action=__FILE__ method="get">3079* <input type="hidden" name="submitted" value="1"/>3080* From Address: <input type="text" size="50" name="mail_from" value="<?php echo get("mail_from"); ?>"/>3081* <br/>3082* To Address: <input type="text" size="50" name="mail_to" value="<?php echo get("mail_to"); ?>"/>3083* <br/>3084* Cc Address: <input type="text" size="50" name="mail_cc" value="<?php echo get("mail_cc"); ?>"/>3085* <br/>3086* SMTP Hostname: <input type="text" size="50" name="mail_host" value="<?php echo get("mail_host"); ?>"/>3087* <p/>3088* <input type="submit" value="Run Test"/>3089*3090* </form>3091* </body>3092* </html>3093*/309430953096