1<?php 2/** 3 * PHPMailer - PHP email transport unit tests. 4 * PHP version 5.5. 5 * 6 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> 7 * @author Andy Prevost 8 * @copyright 2012 - 2017 Marcus Bointon 9 * @copyright 2004 - 2009 Andy Prevost 10 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 11 */ 12 13namespace PHPMailer\Test; 14 15use PHPMailer\PHPMailer\Exception; 16use PHPMailer\PHPMailer\OAuth; 17use PHPMailer\PHPMailer\PHPMailer; 18use PHPMailer\PHPMailer\POP3; 19use PHPMailer\PHPMailer\SMTP; 20use PHPUnit\Framework\TestCase; 21 22/** 23 * PHPMailer - PHP email transport unit test class. 24 */ 25final class PHPMailerTest extends TestCase 26{ 27 /** 28 * Holds the PHPMailer instance. 29 * 30 * @var PHPMailer 31 */ 32 private $Mail; 33 34 /** 35 * Holds the SMTP mail host. 36 * 37 * @var string 38 */ 39 private $Host = ''; 40 41 /** 42 * Holds the change log. 43 * 44 * @var string[] 45 */ 46 private $ChangeLog = []; 47 48 /** 49 * Holds the note log. 50 * 51 * @var string[] 52 */ 53 private $NoteLog = []; 54 55 /** 56 * Default include path. 57 * 58 * @var string 59 */ 60 private $INCLUDE_DIR = '..'; 61 62 /** 63 * PIDs of any processes we need to kill. 64 * 65 * @var array 66 */ 67 private $pids = []; 68 69 /** 70 * Run before each test is started. 71 */ 72 protected function setUp() 73 { 74 $this->INCLUDE_DIR = dirname(__DIR__); //Default to the dir above the test dir, i.e. the project home dir 75 if (file_exists($this->INCLUDE_DIR . '/test/testbootstrap.php')) { 76 include $this->INCLUDE_DIR . '/test/testbootstrap.php'; //Overrides go in here 77 } 78 $this->Mail = new PHPMailer(); 79 $this->Mail->SMTPDebug = SMTP::DEBUG_CONNECTION; //Full debug output 80 $this->Mail->Debugoutput = ['PHPMailer\Test\DebugLogTestListener', 'debugLog']; 81 $this->Mail->Priority = 3; 82 $this->Mail->Encoding = '8bit'; 83 $this->Mail->CharSet = PHPMailer::CHARSET_ISO88591; 84 if (array_key_exists('mail_from', $_REQUEST)) { 85 $this->Mail->From = $_REQUEST['mail_from']; 86 } else { 87 $this->Mail->From = 'unit_test@phpmailer.example.com'; 88 } 89 $this->Mail->FromName = 'Unit Tester'; 90 $this->Mail->Sender = ''; 91 $this->Mail->Subject = 'Unit Test'; 92 $this->Mail->Body = ''; 93 $this->Mail->AltBody = ''; 94 $this->Mail->WordWrap = 0; 95 if (array_key_exists('mail_host', $_REQUEST)) { 96 $this->Mail->Host = $_REQUEST['mail_host']; 97 } else { 98 $this->Mail->Host = 'mail.example.com'; 99 } 100 if (array_key_exists('mail_port', $_REQUEST)) { 101 $this->Mail->Port = $_REQUEST['mail_port']; 102 } else { 103 $this->Mail->Port = 25; 104 } 105 $this->Mail->Helo = 'localhost.localdomain'; 106 $this->Mail->SMTPAuth = false; 107 $this->Mail->Username = ''; 108 $this->Mail->Password = ''; 109 $this->Mail->addReplyTo('no_reply@phpmailer.example.com', 'Reply Guy'); 110 $this->Mail->Sender = 'unit_test@phpmailer.example.com'; 111 if (strlen($this->Mail->Host) > 0) { 112 $this->Mail->isSMTP(); 113 } else { 114 $this->Mail->isMail(); 115 } 116 if (array_key_exists('mail_to', $_REQUEST)) { 117 $this->setAddress($_REQUEST['mail_to'], 'Test User', 'to'); 118 } 119 if (array_key_exists('mail_cc', $_REQUEST) and strlen($_REQUEST['mail_cc']) > 0) { 120 $this->setAddress($_REQUEST['mail_cc'], 'Carbon User', 'cc'); 121 } 122 } 123 124 /** 125 * Run after each test is completed. 126 */ 127 protected function tearDown() 128 { 129 // Clean global variables 130 $this->Mail = null; 131 $this->ChangeLog = []; 132 $this->NoteLog = []; 133 134 foreach ($this->pids as $pid) { 135 $p = escapeshellarg($pid); 136 shell_exec("ps $p && kill -TERM $p"); 137 } 138 } 139 140 /** 141 * Build the body of the message in the appropriate format. 142 */ 143 private function buildBody() 144 { 145 $this->checkChanges(); 146 147 // Determine line endings for message 148 if ('text/html' == $this->Mail->ContentType || strlen($this->Mail->AltBody) > 0) { 149 $eol = "<br>\r\n"; 150 $bullet_start = '<li>'; 151 $bullet_end = "</li>\r\n"; 152 $list_start = "<ul>\r\n"; 153 $list_end = "</ul>\r\n"; 154 } else { 155 $eol = "\r\n"; 156 $bullet_start = ' - '; 157 $bullet_end = "\r\n"; 158 $list_start = ''; 159 $list_end = ''; 160 } 161 162 $ReportBody = ''; 163 164 $ReportBody .= '---------------------' . $eol; 165 $ReportBody .= 'Unit Test Information' . $eol; 166 $ReportBody .= '---------------------' . $eol; 167 $ReportBody .= 'phpmailer version: ' . PHPMailer::VERSION . $eol; 168 $ReportBody .= 'Content Type: ' . $this->Mail->ContentType . $eol; 169 $ReportBody .= 'CharSet: ' . $this->Mail->CharSet . $eol; 170 171 if (strlen($this->Mail->Host) > 0) { 172 $ReportBody .= 'Host: ' . $this->Mail->Host . $eol; 173 } 174 175 // If attachments then create an attachment list 176 $attachments = $this->Mail->getAttachments(); 177 if (count($attachments) > 0) { 178 $ReportBody .= 'Attachments:' . $eol; 179 $ReportBody .= $list_start; 180 foreach ($attachments as $attachment) { 181 $ReportBody .= $bullet_start . 'Name: ' . $attachment[1] . ', '; 182 $ReportBody .= 'Encoding: ' . $attachment[3] . ', '; 183 $ReportBody .= 'Type: ' . $attachment[4] . $bullet_end; 184 } 185 $ReportBody .= $list_end . $eol; 186 } 187 188 // If there are changes then list them 189 if (count($this->ChangeLog) > 0) { 190 $ReportBody .= 'Changes' . $eol; 191 $ReportBody .= '-------' . $eol; 192 193 $ReportBody .= $list_start; 194 for ($i = 0; $i < count($this->ChangeLog); ++$i) { 195 $ReportBody .= $bullet_start . $this->ChangeLog[$i][0] . ' was changed to [' . 196 $this->ChangeLog[$i][1] . ']' . $bullet_end; 197 } 198 $ReportBody .= $list_end . $eol . $eol; 199 } 200 201 // If there are notes then list them 202 if (count($this->NoteLog) > 0) { 203 $ReportBody .= 'Notes' . $eol; 204 $ReportBody .= '-----' . $eol; 205 206 $ReportBody .= $list_start; 207 for ($i = 0; $i < count($this->NoteLog); ++$i) { 208 $ReportBody .= $bullet_start . $this->NoteLog[$i] . $bullet_end; 209 } 210 $ReportBody .= $list_end; 211 } 212 213 // Re-attach the original body 214 $this->Mail->Body .= $eol . $ReportBody; 215 } 216 217 /** 218 * Check which default settings have been changed for the report. 219 */ 220 private function checkChanges() 221 { 222 if (3 != $this->Mail->Priority) { 223 $this->addChange('Priority', $this->Mail->Priority); 224 } 225 if (PHPMailer::ENCODING_8BIT != $this->Mail->Encoding) { 226 $this->addChange('Encoding', $this->Mail->Encoding); 227 } 228 if (PHPMailer::CHARSET_ISO88591 != $this->Mail->CharSet) { 229 $this->addChange('CharSet', $this->Mail->CharSet); 230 } 231 if ('' != $this->Mail->Sender) { 232 $this->addChange('Sender', $this->Mail->Sender); 233 } 234 if (0 != $this->Mail->WordWrap) { 235 $this->addChange('WordWrap', $this->Mail->WordWrap); 236 } 237 if ('mail' != $this->Mail->Mailer) { 238 $this->addChange('Mailer', $this->Mail->Mailer); 239 } 240 if (25 != $this->Mail->Port) { 241 $this->addChange('Port', $this->Mail->Port); 242 } 243 if ('localhost.localdomain' != $this->Mail->Helo) { 244 $this->addChange('Helo', $this->Mail->Helo); 245 } 246 if ($this->Mail->SMTPAuth) { 247 $this->addChange('SMTPAuth', 'true'); 248 } 249 } 250 251 /** 252 * Add a changelog entry. 253 * 254 * @param string $sName 255 * @param string $sNewValue 256 */ 257 private function addChange($sName, $sNewValue) 258 { 259 $this->ChangeLog[] = [$sName, $sNewValue]; 260 } 261 262 /** 263 * Adds a simple note to the message. 264 * 265 * @param string $sValue 266 */ 267 private function addNote($sValue) 268 { 269 $this->NoteLog[] = $sValue; 270 } 271 272 /** 273 * Adds all of the addresses. 274 * 275 * @param string $sAddress 276 * @param string $sName 277 * @param string $sType 278 * 279 * @return bool 280 */ 281 private function setAddress($sAddress, $sName = '', $sType = 'to') 282 { 283 switch ($sType) { 284 case 'to': 285 return $this->Mail->addAddress($sAddress, $sName); 286 case 'cc': 287 return $this->Mail->addCC($sAddress, $sName); 288 case 'bcc': 289 return $this->Mail->addBCC($sAddress, $sName); 290 } 291 292 return false; 293 } 294 295 /** 296 * Check that we have loaded default test params. 297 * Pretty much everything will fail due to unset recipient if this is not done. 298 */ 299 public function testBootstrap() 300 { 301 $this->assertFileExists( 302 $this->INCLUDE_DIR . '/test/testbootstrap.php', 303 'Test config params missing - copy testbootstrap.php to testbootstrap-dist.php and change as appropriate' 304 ); 305 } 306 307 /** 308 * Test CRAM-MD5 authentication. 309 * Needs a connection to a server that supports this auth mechanism, so commented out by default. 310 */ 311 public function testAuthCRAMMD5() 312 { 313 $this->Mail->Host = 'hostname'; 314 $this->Mail->Port = 587; 315 $this->Mail->SMTPAuth = true; 316 $this->Mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; 317 $this->Mail->AuthType = 'CRAM-MD5'; 318 $this->Mail->Username = 'username'; 319 $this->Mail->Password = 'password'; 320 $this->Mail->Body = 'Test body'; 321 $this->Mail->Subject .= ': Auth CRAM-MD5'; 322 $this->Mail->From = 'from@example.com'; 323 $this->Mail->Sender = 'from@example.com'; 324 $this->Mail->clearAllRecipients(); 325 $this->Mail->addAddress('user@example.com'); 326 //$this->assertTrue($this->mail->send(), $this->mail->ErrorInfo); 327 } 328 329 /** 330 * Test email address validation. 331 * Test addresses obtained from http://isemail.info 332 * Some failing cases commented out that are apparently up for debate! 333 */ 334 public function testValidate() 335 { 336 $validaddresses = [ 337 'first@example.org', 338 'first.last@example.org', 339 '1234567890123456789012345678901234567890123456789012345678901234@example.org', 340 '"first\"last"@example.org', 341 '"first@last"@example.org', 342 '"first\last"@example.org', 343 'first.last@[12.34.56.78]', 344 'first.last@x23456789012345678901234567890123456789012345678901234567890123.example.org', 345 'first.last@123.example.org', 346 '"first\last"@example.org', 347 '"Abc\@def"@example.org', 348 '"Fred\ Bloggs"@example.org', 349 '"Joe.\Blow"@example.org', 350 '"Abc@def"@example.org', 351 'user+mailbox@example.org', 352 'customer/department=shipping@example.org', 353 '$A12345@example.org', 354 '!def!xyz%abc@example.org', 355 '_somename@example.org', 356 'dclo@us.example.com', 357 'peter.piper@example.org', 358 'test@example.org', 359 'TEST@example.org', 360 '1234567890@example.org', 361 'test+test@example.org', 362 'test-test@example.org', 363 't*est@example.org', 364 '+1~1+@example.org', 365 '{_test_}@example.org', 366 'test.test@example.org', 367 '"test.test"@example.org', 368 'test."test"@example.org', 369 '"test@test"@example.org', 370 'test@123.123.123.x123', 371 'test@[123.123.123.123]', 372 'test@example.example.org', 373 'test@example.example.example.org', 374 '"test\test"@example.org', 375 '"test\blah"@example.org', 376 '"test\blah"@example.org', 377 '"test\"blah"@example.org', 378 'customer/department@example.org', 379 '_Yosemite.Sam@example.org', 380 '~@example.org', 381 '"Austin@Powers"@example.org', 382 'Ima.Fool@example.org', 383 '"Ima.Fool"@example.org', 384 '"first"."last"@example.org', 385 '"first".middle."last"@example.org', 386 '"first".last@example.org', 387 'first."last"@example.org', 388 '"first"."middle"."last"@example.org', 389 '"first.middle"."last"@example.org', 390 '"first.middle.last"@example.org', 391 '"first..last"@example.org', 392 '"first\"last"@example.org', 393 'first."mid\dle"."last"@example.org', 394 'name.lastname@example.com', 395 'a@example.com', 396 'aaa@[123.123.123.123]', 397 'a-b@example.com', 398 '+@b.c', 399 '+@b.com', 400 'a@b.co-foo.uk', 401 'valid@about.museum', 402 'shaitan@my-domain.thisisminekthx', 403 '"Joe\Blow"@example.org', 404 'user%uucp!path@example.edu', 405 'cdburgess+!#$%&\'*-/=?+_{}|~test@example.com', 406 'test@test.com', 407 'test@xn--example.com', 408 'test@example.com', 409 ]; 410 //These are invalid according to PHP's filter_var 411 //which doesn't allow dotless domains, numeric TLDs or unbracketed IPv4 literals 412 $invalidphp = [ 413 'a@b', 414 'a@bar', 415 'first.last@com', 416 'test@123.123.123.123', 417 'foobar@192.168.0.1', 418 'first.last@example.123', 419 ]; 420 //Valid RFC 5322 addresses using quoting and comments 421 //Note that these are *not* all valid for RFC5321 422 $validqandc = [ 423 'HM2Kinsists@(that comments are allowed)this.is.ok', 424 '"Doug \"Ace\" L."@example.org', 425 '"[[ test ]]"@example.org', 426 '"Ima Fool"@example.org', 427 '"test blah"@example.org', 428 '(foo)cal(bar)@(baz)example.com(quux)', 429 'cal@example(woo).(yay)com', 430 'cal(woo(yay)hoopla)@example.com', 431 'cal(foo\@bar)@example.com', 432 'cal(foo\)bar)@example.com', 433 'first().last@example.org', 434 'pete(his account)@silly.test(his host)', 435 'c@(Chris\'s host.)public.example', 436 'jdoe@machine(comment). example', 437 '1234 @ local(blah) .machine .example', 438 'first(abc.def).last@example.org', 439 'first(a"bc.def).last@example.org', 440 'first.(")middle.last(")@example.org', 441 'first(abc\(def)@example.org', 442 'first.last@x(1234567890123456789012345678901234567890123456789012345678901234567890).com', 443 'a(a(b(c)d(e(f))g)h(i)j)@example.org', 444 '"hello my name is"@example.com', 445 '"Test \"Fail\" Ing"@example.org', 446 'first.last @example.org', 447 ]; 448 //Valid explicit IPv6 numeric addresses 449 $validipv6 = [ 450 'first.last@[IPv6:::a2:a3:a4:b1:b2:b3:b4]', 451 'first.last@[IPv6:a1:a2:a3:a4:b1:b2:b3::]', 452 'first.last@[IPv6:::]', 453 'first.last@[IPv6:::b4]', 454 'first.last@[IPv6:::b3:b4]', 455 'first.last@[IPv6:a1::b4]', 456 'first.last@[IPv6:a1::]', 457 'first.last@[IPv6:a1:a2::]', 458 'first.last@[IPv6:0123:4567:89ab:cdef::]', 459 'first.last@[IPv6:0123:4567:89ab:CDEF::]', 460 'first.last@[IPv6:::a3:a4:b1:ffff:11.22.33.44]', 461 'first.last@[IPv6:::a2:a3:a4:b1:ffff:11.22.33.44]', 462 'first.last@[IPv6:a1:a2:a3:a4::11.22.33.44]', 463 'first.last@[IPv6:a1:a2:a3:a4:b1::11.22.33.44]', 464 'first.last@[IPv6:a1::11.22.33.44]', 465 'first.last@[IPv6:a1:a2::11.22.33.44]', 466 'first.last@[IPv6:0123:4567:89ab:cdef::11.22.33.44]', 467 'first.last@[IPv6:0123:4567:89ab:CDEF::11.22.33.44]', 468 'first.last@[IPv6:a1::b2:11.22.33.44]', 469 'first.last@[IPv6:::12.34.56.78]', 470 'first.last@[IPv6:1111:2222:3333::4444:12.34.56.78]', 471 'first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.56.78]', 472 'first.last@[IPv6:::1111:2222:3333:4444:5555:6666]', 473 'first.last@[IPv6:1111:2222:3333::4444:5555:6666]', 474 'first.last@[IPv6:1111:2222:3333:4444:5555:6666::]', 475 'first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]', 476 'first.last@[IPv6:1111:2222:3333::4444:5555:12.34.56.78]', 477 'first.last@[IPv6:1111:2222:3333::4444:5555:6666:7777]', 478 ]; 479 $invalidaddresses = [ 480 'first.last@sub.do,com', 481 'first\@last@iana.org', 482 '123456789012345678901234567890123456789012345678901234567890' . 483 '@12345678901234567890123456789012345678901234 [...]', 484 'first.last', 485 '12345678901234567890123456789012345678901234567890123456789012345@iana.org', 486 '.first.last@iana.org', 487 'first.last.@iana.org', 488 'first..last@iana.org', 489 '"first"last"@iana.org', 490 '"""@iana.org', 491 '"\"@iana.org', 492 //'""@iana.org', 493 'first\@last@iana.org', 494 'first.last@', 495 'x@x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.x23456789.' . 496 'x23456789.x23456789.x23456789.x23 [...]', 497 'first.last@[.12.34.56.78]', 498 'first.last@[12.34.56.789]', 499 'first.last@[::12.34.56.78]', 500 'first.last@[IPv5:::12.34.56.78]', 501 'first.last@[IPv6:1111:2222:3333:4444:5555:12.34.56.78]', 502 'first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:12.34.56.78]', 503 'first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777]', 504 'first.last@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]', 505 'first.last@[IPv6:1111:2222::3333::4444:5555:6666]', 506 'first.last@[IPv6:1111:2222:333x::4444:5555]', 507 'first.last@[IPv6:1111:2222:33333::4444:5555]', 508 'first.last@-xample.com', 509 'first.last@exampl-.com', 510 'first.last@x234567890123456789012345678901234567890123456789012345678901234.iana.org', 511 'abc\@def@iana.org', 512 'abc\@iana.org', 513 'Doug\ \"Ace\"\ Lovell@iana.org', 514 'abc@def@iana.org', 515 'abc\@def@iana.org', 516 'abc\@iana.org', 517 '@iana.org', 518 'doug@', 519 '"qu@iana.org', 520 'ote"@iana.org', 521 '.dot@iana.org', 522 'dot.@iana.org', 523 'two..dot@iana.org', 524 '"Doug "Ace" L."@iana.org', 525 'Doug\ \"Ace\"\ L\.@iana.org', 526 'hello world@iana.org', 527 //'helloworld@iana .org', 528 'gatsby@f.sc.ot.t.f.i.tzg.era.l.d.', 529 'test.iana.org', 530 'test.@iana.org', 531 'test..test@iana.org', 532 '.test@iana.org', 533 'test@test@iana.org', 534 'test@@iana.org', 535 '-- test --@iana.org', 536 '[test]@iana.org', 537 '"test"test"@iana.org', 538 '()[]\;:,><@iana.org', 539 'test@.', 540 'test@example.', 541 'test@.org', 542 'test@12345678901234567890123456789012345678901234567890123456789012345678901234567890' . 543 '12345678901234567890 [...]', 544 'test@[123.123.123.123', 545 'test@123.123.123.123]', 546 'NotAnEmail', 547 '@NotAnEmail', 548 '"test"blah"@iana.org', 549 '.wooly@iana.org', 550 'wo..oly@iana.org', 551 'pootietang.@iana.org', 552 '.@iana.org', 553 'Ima Fool@iana.org', 554 'phil.h\@\@ck@haacked.com', 555 'foo@[\1.2.3.4]', 556 //'first."".last@iana.org', 557 'first\last@iana.org', 558 'Abc\@def@iana.org', 559 'Fred\ Bloggs@iana.org', 560 'Joe.\Blow@iana.org', 561 'first.last@[IPv6:1111:2222:3333:4444:5555:6666:12.34.567.89]', 562 '{^c\@**Dog^}@cartoon.com', 563 //'"foo"(yay)@(hoopla)[1.2.3.4]', 564 'cal(foo(bar)@iamcal.com', 565 'cal(foo)bar)@iamcal.com', 566 'cal(foo\)@iamcal.com', 567 'first(12345678901234567890123456789012345678901234567890)last@(1234567890123456789' . 568 '01234567890123456789012 [...]', 569 'first(middle)last@iana.org', 570 'first(abc("def".ghi).mno)middle(abc("def".ghi).mno).last@(abc("def".ghi).mno)example' . 571 '(abc("def".ghi).mno). [...]', 572 'a(a(b(c)d(e(f))g)(h(i)j)@iana.org', 573 '.@', 574 '@bar.com', 575 '@@bar.com', 576 'aaa.com', 577 'aaa@.com', 578 'aaa@.123', 579 'aaa@[123.123.123.123]a', 580 'aaa@[123.123.123.333]', 581 'a@bar.com.', 582 'a@-b.com', 583 'a@b-.com', 584 '-@..com', 585 '-@a..com', 586 'invalid@about.museum-', 587 'test@...........com', 588 '"Unicode NULL' . chr(0) . '"@char.com', 589 'Unicode NULL' . chr(0) . '@char.com', 590 'first.last@[IPv6::]', 591 'first.last@[IPv6::::]', 592 'first.last@[IPv6::b4]', 593 'first.last@[IPv6::::b4]', 594 'first.last@[IPv6::b3:b4]', 595 'first.last@[IPv6::::b3:b4]', 596 'first.last@[IPv6:a1:::b4]', 597 'first.last@[IPv6:a1:]', 598 'first.last@[IPv6:a1:::]', 599 'first.last@[IPv6:a1:a2:]', 600 'first.last@[IPv6:a1:a2:::]', 601 'first.last@[IPv6::11.22.33.44]', 602 'first.last@[IPv6::::11.22.33.44]', 603 'first.last@[IPv6:a1:11.22.33.44]', 604 'first.last@[IPv6:a1:::11.22.33.44]', 605 'first.last@[IPv6:a1:a2:::11.22.33.44]', 606 'first.last@[IPv6:0123:4567:89ab:cdef::11.22.33.xx]', 607 'first.last@[IPv6:0123:4567:89ab:CDEFF::11.22.33.44]', 608 'first.last@[IPv6:a1::a4:b1::b4:11.22.33.44]', 609 'first.last@[IPv6:a1::11.22.33]', 610 'first.last@[IPv6:a1::11.22.33.44.55]', 611 'first.last@[IPv6:a1::b211.22.33.44]', 612 'first.last@[IPv6:a1::b2::11.22.33.44]', 613 'first.last@[IPv6:a1::b3:]', 614 'first.last@[IPv6::a2::b4]', 615 'first.last@[IPv6:a1:a2:a3:a4:b1:b2:b3:]', 616 'first.last@[IPv6::a2:a3:a4:b1:b2:b3:b4]', 617 'first.last@[IPv6:a1:a2:a3:a4::b1:b2:b3:b4]', 618 //This is a valid RFC5322 address, but we don't want to allow it for obvious reasons! 619 "(\r\n RCPT TO:user@example.com\r\n DATA \\\nSubject: spam10\\\n\r\n Hello," . 620 "\r\n this is a spam mail.\\\n.\r\n QUIT\r\n ) a@example.net", 621 ]; 622 // IDNs in Unicode and ASCII forms. 623 $unicodeaddresses = [ 624 'first.last@bücher.ch', 625 'first.last@кто.рф', 626 'first.last@phplíst.com', 627 ]; 628 $asciiaddresses = [ 629 'first.last@xn--bcher-kva.ch', 630 'first.last@xn--j1ail.xn--p1ai', 631 'first.last@xn--phplst-6va.com', 632 ]; 633 $goodfails = []; 634 foreach (array_merge($validaddresses, $asciiaddresses) as $address) { 635 if (!PHPMailer::validateAddress($address)) { 636 $goodfails[] = $address; 637 } 638 } 639 $badpasses = []; 640 foreach (array_merge($invalidaddresses, $unicodeaddresses) as $address) { 641 if (PHPMailer::validateAddress($address)) { 642 $badpasses[] = $address; 643 } 644 } 645 $err = ''; 646 if (count($goodfails) > 0) { 647 $err .= "Good addresses that failed validation:\n"; 648 $err .= implode("\n", $goodfails); 649 } 650 if (count($badpasses) > 0) { 651 if (!empty($err)) { 652 $err .= "\n\n"; 653 } 654 $err .= "Bad addresses that passed validation:\n"; 655 $err .= implode("\n", $badpasses); 656 } 657 $this->assertEmpty($err, $err); 658 //For coverage 659 $this->assertTrue(PHPMailer::validateAddress('test@example.com', 'auto')); 660 $this->assertFalse(PHPMailer::validateAddress('test@example.com.', 'auto')); 661 $this->assertTrue(PHPMailer::validateAddress('test@example.com', 'pcre')); 662 $this->assertFalse(PHPMailer::validateAddress('test@example.com.', 'pcre')); 663 $this->assertTrue(PHPMailer::validateAddress('test@example.com', 'pcre8')); 664 $this->assertFalse(PHPMailer::validateAddress('test@example.com.', 'pcre8')); 665 $this->assertTrue(PHPMailer::validateAddress('test@example.com', 'html5')); 666 $this->assertFalse(PHPMailer::validateAddress('test@example.com.', 'html5')); 667 $this->assertTrue(PHPMailer::validateAddress('test@example.com', 'php')); 668 $this->assertFalse(PHPMailer::validateAddress('test@example.com.', 'php')); 669 $this->assertTrue(PHPMailer::validateAddress('test@example.com', 'noregex')); 670 $this->assertFalse(PHPMailer::validateAddress('bad', 'noregex')); 671 } 672 673 /** 674 * Test injecting a custom validator. 675 */ 676 public function testCustomValidator() 677 { 678 //Inject a one-off custom validator 679 $this->assertTrue( 680 PHPMailer::validateAddress( 681 'user@example.com', 682 function ($address) { 683 return strpos($address, '@') !== false; 684 } 685 ), 686 'Custom validator false negative' 687 ); 688 $this->assertFalse( 689 PHPMailer::validateAddress( 690 'userexample.com', 691 function ($address) { 692 return strpos($address, '@') !== false; 693 } 694 ), 695 'Custom validator false positive' 696 ); 697 //Set the default validator to an injected function 698 PHPMailer::$validator = function ($address) { 699 return 'user@example.com' === $address; 700 }; 701 $this->assertTrue( 702 $this->Mail->addAddress('user@example.com'), 703 'Custom default validator false negative' 704 ); 705 $this->assertFalse( 706 //Need to pick a failing value which would pass all other validators 707 //to be sure we're using our custom one 708 $this->Mail->addAddress('bananas@example.com'), 709 'Custom default validator false positive' 710 ); 711 //Set default validator to PHP built-in 712 PHPMailer::$validator = 'php'; 713 $this->assertFalse( 714 //This is a valid address that FILTER_VALIDATE_EMAIL thinks is invalid 715 $this->Mail->addAddress('first.last@example.123'), 716 'PHP validator not behaving as expected' 717 ); 718 } 719 720 /** 721 * Word-wrap an ASCII message. 722 */ 723 public function testWordWrap() 724 { 725 $this->Mail->WordWrap = 40; 726 $my_body = str_repeat( 727 'Here is the main body of this message. It should ' . 728 'be quite a few lines. It should be wrapped at ' . 729 '40 characters. Make sure that it is. ', 730 10 731 ); 732 $nBodyLen = strlen($my_body); 733 $my_body .= "\n\nThis is the above body length: " . $nBodyLen; 734 735 $this->Mail->Body = $my_body; 736 $this->Mail->Subject .= ': Wordwrap'; 737 738 $this->buildBody(); 739 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 740 } 741 742 /** 743 * Word-wrap a multibyte message. 744 */ 745 public function testWordWrapMultibyte() 746 { 747 $this->Mail->WordWrap = 40; 748 $my_body = str_repeat( 749 '飛兒樂 團光茫 飛兒樂 團光茫 飛兒樂 團光茫 飛兒樂 團光茫 ' . 750 '飛飛兒樂 團光茫兒樂 團光茫飛兒樂 團光飛兒樂 團光茫飛兒樂 團光茫兒樂 團光茫 ' . 751 '飛兒樂 團光茫飛兒樂 團飛兒樂 團光茫光茫飛兒樂 團光茫. ', 752 10 753 ); 754 $nBodyLen = strlen($my_body); 755 $my_body .= "\n\nThis is the above body length: " . $nBodyLen; 756 757 $this->Mail->Body = $my_body; 758 $this->Mail->Subject .= ': Wordwrap multibyte'; 759 760 $this->buildBody(); 761 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 762 } 763 764 /** 765 * Test low priority. 766 */ 767 public function testLowPriority() 768 { 769 $this->Mail->Priority = 5; 770 $this->Mail->Body = 'Here is the main body. There should be ' . 771 'a reply to address in this message.'; 772 $this->Mail->Subject .= ': Low Priority'; 773 $this->Mail->addReplyTo('nobody@nobody.com', 'Nobody (Unit Test)'); 774 775 $this->buildBody(); 776 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 777 } 778 779 /** 780 * Simple plain file attachment test. 781 */ 782 public function testMultiplePlainFileAttachment() 783 { 784 $this->Mail->Body = 'Here is the text body'; 785 $this->Mail->Subject .= ': Plain + Multiple FileAttachments'; 786 787 if (!$this->Mail->addAttachment(realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'))) { 788 $this->assertTrue(false, $this->Mail->ErrorInfo); 789 790 return; 791 } 792 793 if (!$this->Mail->addAttachment(__FILE__, 'test.txt')) { 794 $this->assertTrue(false, $this->Mail->ErrorInfo); 795 796 return; 797 } 798 799 $this->buildBody(); 800 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 801 } 802 803 /** 804 * Rejection of non-local file attachments test. 805 */ 806 public function testRejectNonLocalFileAttachment() 807 { 808 $this->assertFalse( 809 $this->Mail->addAttachment('https://github.com/PHPMailer/PHPMailer/raw/master/README.md'), 810 'addAttachment should reject remote URLs' 811 ); 812 813 $this->assertFalse( 814 $this->Mail->addAttachment('phar://phar.php'), 815 'addAttachment should reject phar resources' 816 ); 817 } 818 819 /** 820 * Simple plain string attachment test. 821 */ 822 public function testPlainStringAttachment() 823 { 824 $this->Mail->Body = 'Here is the text body'; 825 $this->Mail->Subject .= ': Plain + StringAttachment'; 826 827 $sAttachment = 'These characters are the content of the ' . 828 "string attachment.\nThis might be taken from a " . 829 'database or some other such thing. '; 830 831 $this->Mail->addStringAttachment($sAttachment, 'string_attach.txt'); 832 833 $this->buildBody(); 834 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 835 } 836 837 /** 838 * Plain quoted-printable message. 839 */ 840 public function testQuotedPrintable() 841 { 842 $this->Mail->Body = 'Here is the main body'; 843 $this->Mail->Subject .= ': Plain + Quoted-printable'; 844 $this->Mail->Encoding = 'quoted-printable'; 845 846 $this->buildBody(); 847 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 848 849 //Check that a quoted printable encode and decode results in the same as went in 850 $t = file_get_contents(__FILE__); //Use this file as test content 851 //Force line breaks to UNIX-style 852 $t = str_replace(["\r\n", "\r"], "\n", $t); 853 $this->assertEquals( 854 $t, 855 quoted_printable_decode($this->Mail->encodeQP($t)), 856 'Quoted-Printable encoding round-trip failed' 857 ); 858 //Force line breaks to Windows-style 859 $t = str_replace("\n", "\r\n", $t); 860 $this->assertEquals( 861 $t, 862 quoted_printable_decode($this->Mail->encodeQP($t)), 863 'Quoted-Printable encoding round-trip failed (Windows line breaks)' 864 ); 865 } 866 867 /** 868 * Test header encoding & folding. 869 */ 870 public function testHeaderEncoding() 871 { 872 $this->Mail->CharSet = 'UTF-8'; 873 //This should select B-encoding automatically and should fold 874 $bencode = str_repeat('é', PHPMailer::STD_LINE_LENGTH + 1); 875 //This should select Q-encoding automatically and should fold 876 $qencode = str_repeat('e', PHPMailer::STD_LINE_LENGTH) . 'é'; 877 //This should select B-encoding automatically and should not fold 878 $bencodenofold = str_repeat('é', 10); 879 //This should select Q-encoding automatically and should not fold 880 $qencodenofold = str_repeat('e', 9) . 'é'; 881 //This should Q-encode as ASCII and fold (previously, this did not encode) 882 $longheader = str_repeat('e', PHPMailer::STD_LINE_LENGTH + 10); 883 //This should Q-encode as UTF-8 and fold 884 $longutf8 = str_repeat('é', PHPMailer::STD_LINE_LENGTH + 10); 885 //This should not change 886 $noencode = 'eeeeeeeeee'; 887 $this->Mail->isMail(); 888 //Expected results 889 890 $bencoderes = '=?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' . 891 PHPMailer::getLE() . 892 ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' . 893 PHPMailer::getLE() . 894 ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' . 895 PHPMailer::getLE() . 896 ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqQ==?='; 897 $qencoderes = '=?UTF-8?Q?eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee?=' . 898 PHPMailer::getLE() . 899 ' =?UTF-8?Q?eeeeeeeeeeeeeeeeeeeeeeeeee=C3=A9?='; 900 $bencodenofoldres = '=?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6k=?='; 901 $qencodenofoldres = '=?UTF-8?Q?eeeeeeeee=C3=A9?='; 902 $longheaderres = '=?us-ascii?Q?eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee?=' . 903 PHPMailer::getLE() . ' =?us-ascii?Q?eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee?='; 904 $longutf8res = '=?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' . 905 PHPMailer::getLE() . ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' . 906 PHPMailer::getLE() . ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6k=?=' . 907 PHPMailer::getLE() . ' =?UTF-8?B?w6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqcOpw6nDqQ==?='; 908 $noencoderes = 'eeeeeeeeee'; 909 $this->assertEquals( 910 $bencoderes, 911 $this->Mail->encodeHeader($bencode), 912 'Folded B-encoded header value incorrect' 913 ); 914 $this->assertEquals( 915 $qencoderes, 916 $this->Mail->encodeHeader($qencode), 917 'Folded Q-encoded header value incorrect' 918 ); 919 $this->assertEquals( 920 $bencodenofoldres, 921 $this->Mail->encodeHeader($bencodenofold), 922 'B-encoded header value incorrect' 923 ); 924 $this->assertEquals( 925 $qencodenofoldres, 926 $this->Mail->encodeHeader($qencodenofold), 927 'Q-encoded header value incorrect' 928 ); 929 $this->assertEquals( 930 $longheaderres, 931 $this->Mail->encodeHeader($longheader), 932 'Long header value incorrect' 933 ); 934 $this->assertEquals( 935 $longutf8res, 936 $this->Mail->encodeHeader($longutf8), 937 'Long UTF-8 header value incorrect' 938 ); 939 $this->assertEquals( 940 $noencoderes, 941 $this->Mail->encodeHeader($noencode), 942 'Unencoded header value incorrect' 943 ); 944 } 945 946 /** 947 * Send an HTML message. 948 */ 949 public function testHtml() 950 { 951 $this->Mail->isHTML(true); 952 $this->Mail->Subject .= ': HTML only'; 953 954 $this->Mail->Body = <<<'EOT' 955<html> 956 <head> 957 <title>HTML email test</title> 958 </head> 959 <body> 960 <h1>PHPMailer does HTML!</h1> 961 <p>This is a <strong>test message</strong> written in HTML.<br> 962 Go to <a href="https://github.com/PHPMailer/PHPMailer/">https://github.com/PHPMailer/PHPMailer/</a> 963 for new versions of PHPMailer.</p> 964 <p>Thank you!</p> 965 </body> 966</html> 967EOT; 968 $this->buildBody(); 969 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 970 $msg = $this->Mail->getSentMIMEMessage(); 971 $this->assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers'); 972 } 973 974 /** 975 * Send an HTML message specifying the DSN notifications we expect. 976 */ 977 public function testDsn() 978 { 979 $this->Mail->isHTML(true); 980 $this->Mail->Subject .= ': HTML only'; 981 982 $this->Mail->Body = <<<'EOT' 983<html> 984 <head> 985 <title>HTML email test</title> 986 </head> 987 <body> 988 <p>PHPMailer</p> 989 </body> 990</html> 991EOT; 992 $this->buildBody(); 993 $this->Mail->dsn = 'SUCCESS,FAILURE'; 994 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 995 //Sends the same mail, but sets the DSN notification to NEVER 996 $this->Mail->dsn = 'NEVER'; 997 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 998 } 999 1000 /** 1001 * createBody test of switch case 1002 */ 1003 public function testCreateBody() 1004 { 1005 $PHPMailer = new PHPMailer(); 1006 $reflection = new \ReflectionClass($PHPMailer); 1007 $property = $reflection->getProperty('message_type'); 1008 $property->setAccessible(true); 1009 $property->setValue($PHPMailer, 'inline'); 1010 $this->assertInternalType('string', $PHPMailer->createBody()); 1011 1012 $property->setValue($PHPMailer, 'attach'); 1013 $this->assertInternalType('string', $PHPMailer->createBody()); 1014 1015 $property->setValue($PHPMailer, 'inline_attach'); 1016 $this->assertInternalType('string', $PHPMailer->createBody()); 1017 1018 $property->setValue($PHPMailer, 'alt'); 1019 $this->assertInternalType('string', $PHPMailer->createBody()); 1020 1021 $property->setValue($PHPMailer, 'alt_inline'); 1022 $this->assertInternalType('string', $PHPMailer->createBody()); 1023 1024 $property->setValue($PHPMailer, 'alt_attach'); 1025 $this->assertInternalType('string', $PHPMailer->createBody()); 1026 1027 $property->setValue($PHPMailer, 'alt_inline_attach'); 1028 $this->assertInternalType('string', $PHPMailer->createBody()); 1029 } 1030 1031 /** 1032 * Send a message containing ISO-8859-1 text. 1033 */ 1034 public function testHtmlIso8859() 1035 { 1036 $this->Mail->isHTML(true); 1037 $this->Mail->Subject .= ': ISO-8859-1 HTML'; 1038 $this->Mail->CharSet = PHPMailer::CHARSET_ISO88591; 1039 1040 //This file is in ISO-8859-1 charset 1041 //Needs to be external because this file is in UTF-8 1042 $content = file_get_contents(realpath($this->INCLUDE_DIR . '/examples/contents.html')); 1043 // This is the string 'éèîüçÅñæß' in ISO-8859-1, base-64 encoded 1044 $check = base64_decode('6eju/OfF8ebf'); 1045 //Make sure it really is in ISO-8859-1! 1046 $this->Mail->msgHTML( 1047 mb_convert_encoding( 1048 $content, 1049 'ISO-8859-1', 1050 mb_detect_encoding($content, 'UTF-8, ISO-8859-1, ISO-8859-15', true) 1051 ), 1052 realpath($this->INCLUDE_DIR . '/examples') 1053 ); 1054 $this->buildBody(); 1055 $this->assertContains($check, $this->Mail->Body, 'ISO message body does not contain expected text'); 1056 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1057 } 1058 1059 /** 1060 * Send a message containing multilingual UTF-8 text. 1061 */ 1062 public function testHtmlUtf8() 1063 { 1064 $this->Mail->isHTML(true); 1065 $this->Mail->Subject .= ': UTF-8 HTML Пустое тело сообщения'; 1066 $this->Mail->CharSet = 'UTF-8'; 1067 1068 $this->Mail->Body = <<<'EOT' 1069<html> 1070 <head> 1071 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 1072 <title>HTML email test</title> 1073 </head> 1074 <body> 1075 <p>Chinese text: 郵件內容為空</p> 1076 <p>Russian text: Пустое тело сообщения</p> 1077 <p>Armenian text: Հաղորդագրությունը դատարկ է</p> 1078 <p>Czech text: Prázdné tělo zprávy</p> 1079 </body> 1080</html> 1081EOT; 1082 $this->buildBody(); 1083 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1084 $msg = $this->Mail->getSentMIMEMessage(); 1085 $this->assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers'); 1086 } 1087 1088 /** 1089 * Send a message containing multilingual UTF-8 text with an embedded image. 1090 */ 1091 public function testUtf8WithEmbeddedImage() 1092 { 1093 $this->Mail->isHTML(true); 1094 $this->Mail->Subject .= ': UTF-8 with embedded image'; 1095 $this->Mail->CharSet = 'UTF-8'; 1096 1097 $this->Mail->Body = <<<'EOT' 1098<!DOCTYPE html> 1099<html lang="en"> 1100 <head> 1101 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 1102 <title>HTML email test</title> 1103 </head> 1104 <body> 1105 <p>Chinese text: 郵件內容為空</p> 1106 <p>Russian text: Пустое тело сообщения</p> 1107 <p>Armenian text: Հաղորդագրությունը դատարկ է</p> 1108 <p>Czech text: Prázdné tělo zprávy</p> 1109 Embedded Image: <img alt="phpmailer" src="cid:bäck"> 1110 </body> 1111</html> 1112EOT; 1113 $this->Mail->addEmbeddedImage( 1114 realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'), 1115 'bäck', 1116 'phpmailer.png', 1117 'base64', 1118 'image/png' 1119 ); 1120 $this->buildBody(); 1121 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1122 } 1123 1124 /** 1125 * Send a message containing multilingual UTF-8 text. 1126 */ 1127 public function testPlainUtf8() 1128 { 1129 $this->Mail->isHTML(false); 1130 $this->Mail->Subject .= ': UTF-8 plain text'; 1131 $this->Mail->CharSet = 'UTF-8'; 1132 1133 $this->Mail->Body = <<<'EOT' 1134Chinese text: 郵件內容為空 1135Russian text: Пустое тело сообщения 1136Armenian text: Հաղորդագրությունը դատարկ է 1137Czech text: Prázdné tělo zprávy 1138EOT; 1139 $this->buildBody(); 1140 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1141 $msg = $this->Mail->getSentMIMEMessage(); 1142 $this->assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers'); 1143 } 1144 1145 /** 1146 * Test simple message builder and html2text converters. 1147 */ 1148 public function testMsgHTML() 1149 { 1150 $message = file_get_contents(realpath($this->INCLUDE_DIR . '/examples/contentsutf8.html')); 1151 $this->Mail->CharSet = PHPMailer::CHARSET_UTF8; 1152 $this->Mail->Body = ''; 1153 $this->Mail->AltBody = ''; 1154 //Uses internal HTML to text conversion 1155 $this->Mail->msgHTML($message, realpath($this->INCLUDE_DIR . '/examples')); 1156 $sub = $this->Mail->Subject . ': msgHTML'; 1157 $this->Mail->Subject .= $sub; 1158 1159 $this->assertNotEmpty($this->Mail->Body, 'Body not set by msgHTML'); 1160 $this->assertNotEmpty($this->Mail->AltBody, 'AltBody not set by msgHTML'); 1161 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1162 1163 //Again, using a custom HTML to text converter 1164 $this->Mail->AltBody = ''; 1165 $this->Mail->msgHTML( 1166 $message, 1167 realpath($this->INCLUDE_DIR . '/examples'), 1168 function ($html) { 1169 return strtoupper(strip_tags($html)); 1170 } 1171 ); 1172 $this->Mail->Subject = $sub . ' + custom html2text'; 1173 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1174 1175 //Test that local paths without a basedir are ignored 1176 $this->Mail->msgHTML('<img src="/etc/hostname">test'); 1177 $this->assertContains('src="/etc/hostname"', $this->Mail->Body); 1178 //Test that local paths with a basedir are not ignored 1179 $this->Mail->msgHTML('<img src="composer.json">test', realpath($this->INCLUDE_DIR)); 1180 $this->assertNotContains('src="composer.json"', $this->Mail->Body); 1181 //Test that local paths with parent traversal are ignored 1182 $this->Mail->msgHTML('<img src="../composer.json">test', realpath($this->INCLUDE_DIR)); 1183 $this->assertNotContains('src="composer.json"', $this->Mail->Body); 1184 //Test that existing embedded URLs are ignored 1185 $this->Mail->msgHTML('<img src="cid:5d41402abc4b2a76b9719d911017c592">test'); 1186 $this->assertContains('src="cid:5d41402abc4b2a76b9719d911017c592"', $this->Mail->Body); 1187 //Test that absolute URLs are ignored 1188 $this->Mail->msgHTML('<img src="https://github.com/PHPMailer/PHPMailer/blob/master/composer.json">test'); 1189 $this->assertContains('src="https://github.com/PHPMailer/PHPMailer/blob/master/composer.json"', $this->Mail->Body); 1190 //Test that absolute URLs with anonymous/relative protocol are ignored 1191 //Note that such URLs will not work in email anyway because they have no protocol to be relative to 1192 $this->Mail->msgHTML('<img src="//github.com/PHPMailer/PHPMailer/blob/master/composer.json">test'); 1193 $this->assertContains('src="//github.com/PHPMailer/PHPMailer/blob/master/composer.json"', $this->Mail->Body); 1194 } 1195 1196 /** 1197 * Simple HTML and attachment test. 1198 */ 1199 public function testHTMLAttachment() 1200 { 1201 $this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.'; 1202 $this->Mail->Subject .= ': HTML + Attachment'; 1203 $this->Mail->isHTML(true); 1204 $this->Mail->CharSet = 'UTF-8'; 1205 1206 if (!$this->Mail->addAttachment( 1207 realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'), 1208 'phpmailer_mini.png' 1209 ) 1210 ) { 1211 $this->assertTrue(false, $this->Mail->ErrorInfo); 1212 1213 return; 1214 } 1215 1216 //Make sure that trying to attach a nonexistent file fails 1217 $filename = __FILE__ . md5(microtime()) . 'nonexistent_file.txt'; 1218 $this->assertFalse($this->Mail->addAttachment($filename)); 1219 //Make sure that trying to attach an existing but unreadable file fails 1220 touch($filename); 1221 chmod($filename, 0200); 1222 $this->assertFalse($this->Mail->addAttachment($filename)); 1223 chmod($filename, 0644); 1224 unlink($filename); 1225 1226 $this->buildBody(); 1227 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1228 } 1229 1230 /** 1231 * Attachment naming test. 1232 */ 1233 public function testAttachmentNaming() 1234 { 1235 $this->Mail->Body = 'Attachments.'; 1236 $this->Mail->Subject .= ': Attachments'; 1237 $this->Mail->isHTML(true); 1238 $this->Mail->CharSet = 'UTF-8'; 1239 $this->Mail->addAttachment( 1240 realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'), 1241 'phpmailer_mini.png";.jpg' 1242 ); 1243 $this->Mail->addAttachment( 1244 realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'), 1245 'phpmailer.png' 1246 ); 1247 $this->Mail->addAttachment( 1248 realpath($this->INCLUDE_DIR . '/examples/images/PHPMailer card logo.png'), 1249 'PHPMailer card logo.png' 1250 ); 1251 $this->buildBody(); 1252 $this->Mail->preSend(); 1253 $message = $this->Mail->getSentMIMEMessage(); 1254 $this->assertContains( 1255 'Content-Type: image/png; name="phpmailer_mini.png\";.jpg"', 1256 $message, 1257 'Name containing double quote should be escaped in Content-Type' 1258 ); 1259 $this->assertContains( 1260 'Content-Disposition: attachment; filename="phpmailer_mini.png\";.jpg"', 1261 $message, 1262 'Filename containing double quote should be escaped in Content-Disposition' 1263 ); 1264 $this->assertContains( 1265 'Content-Type: image/png; name=phpmailer.png', 1266 $message, 1267 'Name without special chars should not be quoted in Content-Type' 1268 ); 1269 $this->assertContains( 1270 'Content-Disposition: attachment; filename=phpmailer.png', 1271 $message, 1272 'Filename without special chars should not be quoted in Content-Disposition' 1273 ); 1274 $this->assertContains( 1275 'Content-Type: image/png; name="PHPMailer card logo.png"', 1276 $message, 1277 'Name with spaces should be quoted in Content-Type' 1278 ); 1279 $this->assertContains( 1280 'Content-Disposition: attachment; filename="PHPMailer card logo.png"', 1281 $message, 1282 'Filename with spaces should be quoted in Content-Disposition' 1283 ); 1284 } 1285 1286 /** 1287 * Test embedded image without a name. 1288 */ 1289 public function testHTMLStringEmbedNoName() 1290 { 1291 $this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.'; 1292 $this->Mail->Subject .= ': HTML + unnamed embedded image'; 1293 $this->Mail->isHTML(true); 1294 1295 if (!$this->Mail->addStringEmbeddedImage( 1296 file_get_contents(realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png')), 1297 hash('sha256', 'phpmailer_mini.png') . '@phpmailer.0', 1298 '', //Intentionally empty name 1299 'base64', 1300 '', //Intentionally empty MIME type 1301 'inline' 1302 )) { 1303 $this->assertTrue(false, $this->Mail->ErrorInfo); 1304 1305 return; 1306 } 1307 1308 $this->buildBody(); 1309 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1310 } 1311 1312 /** 1313 * Simple HTML and multiple attachment test. 1314 */ 1315 public function testHTMLMultiAttachment() 1316 { 1317 $this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.'; 1318 $this->Mail->Subject .= ': HTML + multiple Attachment'; 1319 $this->Mail->isHTML(true); 1320 1321 if (!$this->Mail->addAttachment( 1322 realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'), 1323 'phpmailer_mini.png' 1324 ) 1325 ) { 1326 $this->assertTrue(false, $this->Mail->ErrorInfo); 1327 1328 return; 1329 } 1330 1331 if (!$this->Mail->addAttachment( 1332 realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'), 1333 'phpmailer.png' 1334 ) 1335 ) { 1336 $this->assertTrue(false, $this->Mail->ErrorInfo); 1337 1338 return; 1339 } 1340 1341 $this->buildBody(); 1342 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1343 } 1344 1345 /** 1346 * An embedded attachment test. 1347 */ 1348 public function testEmbeddedImage() 1349 { 1350 $this->Mail->Body = 'Embedded Image: <img alt="phpmailer" src="' . 1351 'cid:my-attach">' . 1352 'Here is an image!'; 1353 $this->Mail->Subject .= ': Embedded Image'; 1354 $this->Mail->isHTML(true); 1355 1356 if (!$this->Mail->addEmbeddedImage( 1357 realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'), 1358 'my-attach', 1359 'phpmailer.png', 1360 'base64', 1361 'image/png' 1362 ) 1363 ) { 1364 $this->assertTrue(false, $this->Mail->ErrorInfo); 1365 1366 return; 1367 } 1368 1369 $this->buildBody(); 1370 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1371 $this->Mail->clearAttachments(); 1372 $this->Mail->msgHTML('<!DOCTYPE html> 1373<html lang="en"> 1374 <head> 1375 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 1376 <title>E-Mail Inline Image Test</title> 1377 </head> 1378 <body> 1379 <p><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="></p> 1380 </body> 1381</html>'); 1382 $this->Mail->preSend(); 1383 $this->assertContains( 1384 'Content-ID: <bb229a48bee31f5d54ca12dc9bd960c6@phpmailer.0>', 1385 $this->Mail->getSentMIMEMessage(), 1386 'Embedded image header encoding incorrect.' 1387 ); 1388 //For code coverage 1389 $this->Mail->addEmbeddedImage('thisfiledoesntexist', 'xyz'); //Non-existent file 1390 $this->Mail->addEmbeddedImage(__FILE__, '123'); //Missing name 1391 } 1392 1393 /** 1394 * An embedded attachment test. 1395 */ 1396 public function testMultiEmbeddedImage() 1397 { 1398 $this->Mail->Body = 'Embedded Image: <img alt="phpmailer" src="' . 1399 'cid:my-attach">' . 1400 'Here is an image!</a>'; 1401 $this->Mail->Subject .= ': Embedded Image + Attachment'; 1402 $this->Mail->isHTML(true); 1403 1404 if (!$this->Mail->addEmbeddedImage( 1405 realpath($this->INCLUDE_DIR . '/examples/images/phpmailer.png'), 1406 'my-attach', 1407 'phpmailer.png', 1408 'base64', 1409 'image/png' 1410 ) 1411 ) { 1412 $this->assertTrue(false, $this->Mail->ErrorInfo); 1413 1414 return; 1415 } 1416 1417 if (!$this->Mail->addAttachment(__FILE__, 'test.txt')) { 1418 $this->assertTrue(false, $this->Mail->ErrorInfo); 1419 1420 return; 1421 } 1422 1423 $this->buildBody(); 1424 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1425 } 1426 1427 /** 1428 * Simple multipart/alternative test. 1429 */ 1430 public function testAltBody() 1431 { 1432 $this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.'; 1433 $this->Mail->AltBody = 'Here is the plain text body of this message. ' . 1434 'It should be quite a few lines. It should be wrapped at ' . 1435 '40 characters. Make sure that it is.'; 1436 $this->Mail->WordWrap = 40; 1437 $this->addNote('This is a multipart/alternative email'); 1438 $this->Mail->Subject .= ': AltBody + Word Wrap'; 1439 1440 $this->buildBody(); 1441 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1442 } 1443 1444 /** 1445 * Simple HTML and attachment test. 1446 */ 1447 public function testAltBodyAttachment() 1448 { 1449 $this->Mail->Body = 'This is the <strong>HTML</strong> part of the email.'; 1450 $this->Mail->AltBody = 'This is the text part of the email.'; 1451 $this->Mail->Subject .= ': AltBody + Attachment'; 1452 $this->Mail->isHTML(true); 1453 1454 if (!$this->Mail->addAttachment(__FILE__, 'test_attach.txt')) { 1455 $this->assertTrue(false, $this->Mail->ErrorInfo); 1456 1457 return; 1458 } 1459 1460 $this->buildBody(); 1461 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1462 } 1463 1464 /** 1465 * Test sending multiple messages with separate connections. 1466 */ 1467 public function testMultipleSend() 1468 { 1469 $this->Mail->Body = 'Sending two messages without keepalive'; 1470 $this->buildBody(); 1471 $subject = $this->Mail->Subject; 1472 1473 $this->Mail->Subject = $subject . ': SMTP 1'; 1474 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1475 1476 $this->Mail->Subject = $subject . ': SMTP 2'; 1477 $this->Mail->Sender = 'blah@example.com'; 1478 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1479 } 1480 1481 /** 1482 * Test sending using SendMail. 1483 */ 1484 public function testSendmailSend() 1485 { 1486 $this->Mail->Body = 'Sending via sendmail'; 1487 $this->buildBody(); 1488 $subject = $this->Mail->Subject; 1489 1490 $this->Mail->Subject = $subject . ': sendmail'; 1491 $this->Mail->isSendmail(); 1492 1493 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1494 } 1495 1496 /** 1497 * Test sending using Qmail. 1498 */ 1499 public function testQmailSend() 1500 { 1501 //Only run if we have qmail installed 1502 if (file_exists('/var/qmail/bin/qmail-inject')) { 1503 $this->Mail->Body = 'Sending via qmail'; 1504 $this->buildBody(); 1505 $subject = $this->Mail->Subject; 1506 1507 $this->Mail->Subject = $subject . ': qmail'; 1508 $this->Mail->isQmail(); 1509 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1510 } else { 1511 $this->markTestSkipped('Qmail is not installed'); 1512 } 1513 } 1514 1515 /** 1516 * Test sending using PHP mail() function. 1517 */ 1518 public function testMailSend() 1519 { 1520 $sendmail = ini_get('sendmail_path'); 1521 //No path in sendmail_path 1522 if (strpos($sendmail, '/') === false) { 1523 ini_set('sendmail_path', '/usr/sbin/sendmail -t -i '); 1524 } 1525 $this->Mail->Body = 'Sending via mail()'; 1526 $this->buildBody(); 1527 $this->Mail->Subject = $this->Mail->Subject . ': mail()'; 1528 $this->Mail->clearAddresses(); 1529 $this->Mail->clearCCs(); 1530 $this->Mail->clearBCCs(); 1531 $this->setAddress('testmailsend@example.com', 'totest'); 1532 $this->setAddress('cctestmailsend@example.com', 'cctest', $sType = 'cc'); 1533 $this->setAddress('bcctestmailsend@example.com', 'bcctest', $sType = 'bcc'); 1534 $this->Mail->addReplyTo('replytotestmailsend@example.com', 'replytotest'); 1535 $this->assertContains('testmailsend@example.com', $this->Mail->getToAddresses()[0]); 1536 $this->assertContains('cctestmailsend@example.com', $this->Mail->getCcAddresses()[0]); 1537 $this->assertContains('bcctestmailsend@example.com', $this->Mail->getBccAddresses()[0]); 1538 $this->assertContains('replytotestmailsend@example.com', $this->Mail->getReplyToAddresses()['replytotestmailsend@example.com']); 1539 $this->assertTrue($this->Mail->getAllRecipientAddresses()['testmailsend@example.com']); 1540 $this->assertTrue($this->Mail->getAllRecipientAddresses()['cctestmailsend@example.com']); 1541 $this->assertTrue($this->Mail->getAllRecipientAddresses()['bcctestmailsend@example.com']); 1542 1543 $this->Mail->createHeader(); 1544 $this->Mail->isMail(); 1545 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1546 $msg = $this->Mail->getSentMIMEMessage(); 1547 $this->assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers'); 1548 } 1549 1550 /** 1551 * Test sending an empty body. 1552 */ 1553 public function testEmptyBody() 1554 { 1555 $this->buildBody(); 1556 $this->Mail->Body = ''; 1557 $this->Mail->Subject = $this->Mail->Subject . ': Empty Body'; 1558 $this->Mail->isMail(); 1559 $this->Mail->AllowEmpty = true; 1560 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1561 $this->Mail->AllowEmpty = false; 1562 $this->assertFalse($this->Mail->send(), $this->Mail->ErrorInfo); 1563 } 1564 1565 /** 1566 * Test constructing a multipart message that contains lines that are too long for RFC compliance. 1567 */ 1568 public function testLongBody() 1569 { 1570 $oklen = str_repeat(str_repeat('0', PHPMailer::MAX_LINE_LENGTH) . PHPMailer::getLE(), 2); 1571 //Use +2 to ensure line length is over limit - LE may only be 1 char 1572 $badlen = str_repeat(str_repeat('1', PHPMailer::MAX_LINE_LENGTH + 2) . PHPMailer::getLE(), 2); 1573 1574 $this->Mail->Body = 'This message contains lines that are too long.' . 1575 PHPMailer::getLE() . $oklen . $badlen . $oklen; 1576 $this->assertTrue( 1577 PHPMailer::hasLineLongerThanMax($this->Mail->Body), 1578 'Test content does not contain long lines!' 1579 ); 1580 $this->Mail->isHTML(); 1581 $this->buildBody(); 1582 $this->Mail->AltBody = $this->Mail->Body; 1583 $this->Mail->Encoding = '8bit'; 1584 $this->Mail->preSend(); 1585 $message = $this->Mail->getSentMIMEMessage(); 1586 $this->assertFalse( 1587 PHPMailer::hasLineLongerThanMax($message), 1588 'Long line not corrected (Max: ' . (PHPMailer::MAX_LINE_LENGTH + strlen(PHPMailer::getLE())) . ' chars)' 1589 ); 1590 $this->assertContains( 1591 'Content-Transfer-Encoding: quoted-printable', 1592 $message, 1593 'Long line did not cause transfer encoding switch.' 1594 ); 1595 } 1596 1597 /** 1598 * Test constructing a message that does NOT contain lines that are too long for RFC compliance. 1599 */ 1600 public function testShortBody() 1601 { 1602 $oklen = str_repeat(str_repeat('0', PHPMailer::MAX_LINE_LENGTH) . PHPMailer::getLE(), 10); 1603 1604 $this->Mail->Body = 'This message does not contain lines that are too long.' . 1605 PHPMailer::getLE() . $oklen; 1606 $this->assertFalse( 1607 PHPMailer::hasLineLongerThanMax($this->Mail->Body), 1608 'Test content contains long lines!' 1609 ); 1610 $this->buildBody(); 1611 $this->Mail->Encoding = '8bit'; 1612 $this->Mail->preSend(); 1613 $message = $this->Mail->getSentMIMEMessage(); 1614 $this->assertFalse(PHPMailer::hasLineLongerThanMax($message), 'Long line not corrected.'); 1615 $this->assertNotContains( 1616 'Content-Transfer-Encoding: quoted-printable', 1617 $message, 1618 'Short line caused transfer encoding switch.' 1619 ); 1620 } 1621 1622 /** 1623 * Test keepalive (sending multiple messages in a single connection). 1624 */ 1625 public function testSmtpKeepAlive() 1626 { 1627 $this->Mail->Body = 'SMTP keep-alive test.'; 1628 $this->buildBody(); 1629 $subject = $this->Mail->Subject; 1630 1631 $this->Mail->SMTPKeepAlive = true; 1632 $this->Mail->Subject = $subject . ': SMTP keep-alive 1'; 1633 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1634 1635 $this->Mail->Subject = $subject . ': SMTP keep-alive 2'; 1636 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1637 $this->Mail->smtpClose(); 1638 } 1639 1640 /** 1641 * Test this denial of service attack. 1642 * 1643 * @see http://www.cybsec.com/vuln/PHPMailer-DOS.pdf 1644 */ 1645 public function testDenialOfServiceAttack() 1646 { 1647 $this->Mail->Body = 'This should no longer cause a denial of service.'; 1648 $this->buildBody(); 1649 1650 $this->Mail->Subject = substr(str_repeat('0123456789', 100), 0, 998); 1651 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 1652 } 1653 1654 /** 1655 * Tests this denial of service attack. 1656 * 1657 * @see https://sourceforge.net/p/phpmailer/bugs/383/ 1658 * According to the ticket, this should get stuck in a loop, though I can't make it happen. 1659 */ 1660 public function testDenialOfServiceAttack2() 1661 { 1662 //Encoding name longer than 68 chars 1663 $this->Mail->Encoding = '1234567890123456789012345678901234567890123456789012345678901234567890'; 1664 //Call wrapText with a zero length value 1665 $this->Mail->wrapText(str_repeat('This should no longer cause a denial of service. ', 30), 0); 1666 } 1667 1668 /** 1669 * Test error handling. 1670 */ 1671 public function testError() 1672 { 1673 $this->Mail->Subject .= ': Error handling test - this should be sent ok'; 1674 $this->buildBody(); 1675 $this->Mail->clearAllRecipients(); // no addresses should cause an error 1676 $this->assertTrue($this->Mail->isError() == false, 'Error found'); 1677 $this->assertTrue($this->Mail->send() == false, 'send succeeded'); 1678 $this->assertTrue($this->Mail->isError(), 'No error found'); 1679 $this->assertEquals('You must provide at least one recipient email address.', $this->Mail->ErrorInfo); 1680 $this->Mail->addAddress($_REQUEST['mail_to']); 1681 $this->assertTrue($this->Mail->send(), 'send failed'); 1682 } 1683 1684 /** 1685 * Test addressing. 1686 */ 1687 public function testAddressing() 1688 { 1689 $this->assertFalse($this->Mail->addAddress(''), 'Empty address accepted'); 1690 $this->assertFalse($this->Mail->addAddress('', 'Nobody'), 'Empty address with name accepted'); 1691 $this->assertFalse($this->Mail->addAddress('a@example..com'), 'Invalid address accepted'); 1692 $this->assertTrue($this->Mail->addAddress('a@example.com'), 'Addressing failed'); 1693 $this->assertFalse($this->Mail->addAddress('a@example.com'), 'Duplicate addressing failed'); 1694 $this->assertTrue($this->Mail->addCC('b@example.com'), 'CC addressing failed'); 1695 $this->assertFalse($this->Mail->addCC('b@example.com'), 'CC duplicate addressing failed'); 1696 $this->assertFalse($this->Mail->addCC('a@example.com'), 'CC duplicate addressing failed (2)'); 1697 $this->assertTrue($this->Mail->addBCC('c@example.com'), 'BCC addressing failed'); 1698 $this->assertFalse($this->Mail->addBCC('c@example.com'), 'BCC duplicate addressing failed'); 1699 $this->assertFalse($this->Mail->addBCC('a@example.com'), 'BCC duplicate addressing failed (2)'); 1700 $this->assertTrue($this->Mail->addReplyTo('a@example.com'), 'Replyto Addressing failed'); 1701 $this->assertFalse($this->Mail->addReplyTo('a@example..com'), 'Invalid Replyto address accepted'); 1702 $this->assertTrue($this->Mail->setFrom('a@example.com', 'some name'), 'setFrom failed'); 1703 $this->assertFalse($this->Mail->setFrom('a@example.com.', 'some name'), 'setFrom accepted invalid address'); 1704 $this->Mail->Sender = ''; 1705 $this->Mail->setFrom('a@example.com', 'some name', true); 1706 $this->assertEquals($this->Mail->Sender, 'a@example.com', 'setFrom failed to set sender'); 1707 $this->Mail->Sender = ''; 1708 $this->Mail->setFrom('a@example.com', 'some name', false); 1709 $this->assertEquals($this->Mail->Sender, '', 'setFrom should not have set sender'); 1710 $this->Mail->clearCCs(); 1711 $this->Mail->clearBCCs(); 1712 $this->Mail->clearReplyTos(); 1713 } 1714 1715 /** 1716 * Test addressing. 1717 */ 1718 public function testAddressing2() 1719 { 1720 $this->buildBody(); 1721 $this->Mail->setFrom('bob@example.com', '"Bob\'s Burgers" (Bob\'s "Burgers")', true); 1722 $this->Mail->isSMTP(); 1723 $this->Mail->Subject .= ': quotes in from name'; 1724 $this->assertTrue($this->Mail->send(), 'send failed'); 1725 } 1726 1727 /** 1728 * Test RFC822 address splitting. 1729 */ 1730 public function testAddressSplitting() 1731 { 1732 //Test built-in address parser 1733 $this->assertCount( 1734 2, 1735 PHPMailer::parseAddresses( 1736 'Joe User <joe@example.com>, Jill User <jill@example.net>' 1737 ), 1738 'Failed to recognise address list (IMAP parser)' 1739 ); 1740 $this->assertEquals( 1741 [ 1742 ['name' => 'Joe User', 'address' => 'joe@example.com'], 1743 ['name' => 'Jill User', 'address' => 'jill@example.net'], 1744 ['name' => '', 'address' => 'frank@example.com'], 1745 ], 1746 PHPMailer::parseAddresses( 1747 'Joe User <joe@example.com>,' 1748 . 'Jill User <jill@example.net>,' 1749 . 'frank@example.com,' 1750 ), 1751 'Parsed addresses' 1752 ); 1753 //Test simple address parser 1754 $this->assertCount( 1755 2, 1756 PHPMailer::parseAddresses( 1757 'Joe User <joe@example.com>, Jill User <jill@example.net>', 1758 false 1759 ), 1760 'Failed to recognise address list' 1761 ); 1762 //Test single address 1763 $this->assertNotEmpty( 1764 PHPMailer::parseAddresses( 1765 'Joe User <joe@example.com>', 1766 false 1767 ), 1768 'Failed to recognise single address' 1769 ); 1770 //Test quoted name IMAP 1771 $this->assertNotEmpty( 1772 PHPMailer::parseAddresses( 1773 'Tim "The Book" O\'Reilly <foo@example.com>' 1774 ), 1775 'Failed to recognise quoted name (IMAP)' 1776 ); 1777 //Test quoted name 1778 $this->assertNotEmpty( 1779 PHPMailer::parseAddresses( 1780 'Tim "The Book" O\'Reilly <foo@example.com>', 1781 false 1782 ), 1783 'Failed to recognise quoted name' 1784 ); 1785 //Test single address IMAP 1786 $this->assertNotEmpty( 1787 PHPMailer::parseAddresses( 1788 'Joe User <joe@example.com>' 1789 ), 1790 'Failed to recognise single address (IMAP)' 1791 ); 1792 //Test unnamed address 1793 $this->assertNotEmpty( 1794 PHPMailer::parseAddresses( 1795 'joe@example.com', 1796 false 1797 ), 1798 'Failed to recognise unnamed address' 1799 ); 1800 //Test unnamed address IMAP 1801 $this->assertNotEmpty( 1802 PHPMailer::parseAddresses( 1803 'joe@example.com' 1804 ), 1805 'Failed to recognise unnamed address (IMAP)' 1806 ); 1807 //Test invalid addresses 1808 $this->assertEmpty( 1809 PHPMailer::parseAddresses( 1810 'Joe User <joe@example.com.>, Jill User <jill.@example.net>' 1811 ), 1812 'Failed to recognise invalid addresses (IMAP)' 1813 ); 1814 //Test invalid addresses 1815 $this->assertEmpty( 1816 PHPMailer::parseAddresses( 1817 'Joe User <joe@example.com.>, Jill User <jill.@example.net>', 1818 false 1819 ), 1820 'Failed to recognise invalid addresses' 1821 ); 1822 } 1823 1824 /** 1825 * Test address escaping. 1826 */ 1827 public function testAddressEscaping() 1828 { 1829 $this->Mail->Subject .= ': Address escaping'; 1830 $this->Mail->clearAddresses(); 1831 $this->Mail->addAddress('foo@example.com', 'Tim "The Book" O\'Reilly'); 1832 $this->Mail->Body = 'Test correct escaping of quotes in addresses.'; 1833 $this->buildBody(); 1834 $this->Mail->preSend(); 1835 $b = $this->Mail->getSentMIMEMessage(); 1836 $this->assertContains('To: "Tim \"The Book\" O\'Reilly" <foo@example.com>', $b); 1837 1838 $this->Mail->Subject .= ': Address escaping invalid'; 1839 $this->Mail->clearAddresses(); 1840 $this->Mail->addAddress('foo@example.com', 'Tim "The Book" O\'Reilly'); 1841 $this->Mail->addAddress('invalidaddressexample.com', 'invalidaddress'); 1842 $this->Mail->Body = 'invalid address'; 1843 $this->buildBody(); 1844 $this->Mail->preSend(); 1845 $this->assertEquals($this->Mail->ErrorInfo, 'Invalid address: (to): invalidaddressexample.com'); 1846 1847 $this->Mail->addAttachment(realpath($this->INCLUDE_DIR . '/examples/images/phpmailer_mini.png'), 'phpmailer_mini.png'); 1848 $this->assertTrue($this->Mail->attachmentExists()); 1849 } 1850 1851 /** 1852 * Test MIME structure assembly. 1853 */ 1854 public function testMIMEStructure() 1855 { 1856 $this->Mail->Subject .= ': MIME structure'; 1857 $this->Mail->Body = '<h3>MIME structure test.</h3>'; 1858 $this->Mail->AltBody = 'MIME structure test.'; 1859 $this->buildBody(); 1860 $this->Mail->preSend(); 1861 $this->assertRegExp( 1862 "/Content-Transfer-Encoding: 8bit\r\n\r\n" . 1863 'This is a multi-part message in MIME format./', 1864 $this->Mail->getSentMIMEMessage(), 1865 'MIME structure broken' 1866 ); 1867 } 1868 1869 /** 1870 * Test BCC-only addressing. 1871 */ 1872 public function testBCCAddressing() 1873 { 1874 $this->Mail->isSMTP(); 1875 $this->Mail->Subject .= ': BCC-only addressing'; 1876 $this->buildBody(); 1877 $this->Mail->clearAllRecipients(); 1878 $this->Mail->addAddress('foo@example.com', 'Foo'); 1879 $this->Mail->preSend(); 1880 $b = $this->Mail->getSentMIMEMessage(); 1881 $this->assertTrue($this->Mail->addBCC('a@example.com'), 'BCC addressing failed'); 1882 $this->assertContains('To: Foo <foo@example.com>', $b); 1883 $this->assertTrue($this->Mail->send(), 'send failed'); 1884 } 1885 1886 /** 1887 * Encoding and charset tests. 1888 */ 1889 public function testEncodings() 1890 { 1891 $this->Mail->CharSet = PHPMailer::CHARSET_ISO88591; 1892 $this->assertEquals( 1893 '=A1Hola!_Se=F1or!', 1894 $this->Mail->encodeQ("\xa1Hola! Se\xf1or!", 'text'), 1895 'Q Encoding (text) failed' 1896 ); 1897 $this->assertEquals( 1898 '=A1Hola!_Se=F1or!', 1899 $this->Mail->encodeQ("\xa1Hola! Se\xf1or!", 'comment'), 1900 'Q Encoding (comment) failed' 1901 ); 1902 $this->assertEquals( 1903 '=A1Hola!_Se=F1or!', 1904 $this->Mail->encodeQ("\xa1Hola! Se\xf1or!", 'phrase'), 1905 'Q Encoding (phrase) failed' 1906 ); 1907 $this->Mail->CharSet = 'UTF-8'; 1908 $this->assertEquals( 1909 '=C2=A1Hola!_Se=C3=B1or!', 1910 $this->Mail->encodeQ("\xc2\xa1Hola! Se\xc3\xb1or!", 'text'), 1911 'Q Encoding (text) failed' 1912 ); 1913 //Strings containing '=' are a special case 1914 $this->assertEquals( 1915 'Nov=C3=A1=3D', 1916 $this->Mail->encodeQ("Nov\xc3\xa1=", 'text'), 1917 'Q Encoding (text) failed 2' 1918 ); 1919 1920 $this->assertEquals($this->Mail->encodeString('hello', 'binary'), 'hello', 'Binary encoding changed input'); 1921 $this->Mail->ErrorInfo = ''; 1922 $this->Mail->encodeString('hello', 'asdfghjkl'); 1923 $this->assertNotEmpty($this->Mail->ErrorInfo, 'Invalid encoding not detected'); 1924 $this->assertRegExp('/' . base64_encode('hello') . '/', $this->Mail->encodeString('hello')); 1925 } 1926 1927 /** 1928 * Expect exceptions on bad encoding 1929 * 1930 * @expectedException PHPMailer\PHPMailer\Exception 1931 */ 1932 public function testAddAttachmentEncodingException() 1933 { 1934 $mail = new PHPMailer(true); 1935 $mail->addAttachment(__FILE__, 'test.txt', 'invalidencoding'); 1936 } 1937 1938 /** 1939 * Expect exceptions on sending after deleting a previously successfully attached file 1940 * 1941 * @expectedException PHPMailer\PHPMailer\Exception 1942 */ 1943 public function testDeletedAttachmentException() 1944 { 1945 $filename = __FILE__ . md5(microtime()) . 'test.txt'; 1946 touch($filename); 1947 $this->Mail = new PHPMailer(true); 1948 $this->Mail->addAttachment($filename); 1949 unlink($filename); 1950 $this->Mail->send(); 1951 } 1952 1953 /** 1954 * Expect error on sending after deleting a previously successfully attached file 1955 */ 1956 public function testDeletedAttachmentError() 1957 { 1958 $filename = __FILE__ . md5(microtime()) . 'test.txt'; 1959 touch($filename); 1960 $this->Mail = new PHPMailer(); 1961 $this->Mail->addAttachment($filename); 1962 unlink($filename); 1963 $this->assertFalse($this->Mail->send()); 1964 } 1965 1966 /** 1967 * Expect exceptions on bad encoding 1968 * 1969 * @expectedException PHPMailer\PHPMailer\Exception 1970 */ 1971 public function testStringAttachmentEncodingException() 1972 { 1973 $mail = new PHPMailer(true); 1974 $mail->addStringAttachment('hello', 'test.txt', 'invalidencoding'); 1975 } 1976 1977 /** 1978 * Expect exceptions on bad encoding 1979 * 1980 * @expectedException PHPMailer\PHPMailer\Exception 1981 */ 1982 public function testEmbeddedImageEncodingException() 1983 { 1984 $mail = new PHPMailer(true); 1985 $mail->addEmbeddedImage(__FILE__, 'cid', 'test.png', 'invalidencoding'); 1986 } 1987 1988 /** 1989 * Expect exceptions on bad encoding 1990 * 1991 * @expectedException PHPMailer\PHPMailer\Exception 1992 */ 1993 public function testStringEmbeddedImageEncodingException() 1994 { 1995 $mail = new PHPMailer(true); 1996 $mail->addStringEmbeddedImage('hello', 'cid', 'test.png', 'invalidencoding'); 1997 } 1998 1999 /** 2000 * Test base-64 encoding. 2001 */ 2002 public function testBase64() 2003 { 2004 $this->Mail->Subject .= ': Base-64 encoding'; 2005 $this->Mail->Encoding = 'base64'; 2006 $this->buildBody(); 2007 $this->assertTrue($this->Mail->send(), 'Base64 encoding failed'); 2008 } 2009 2010 /** 2011 * S/MIME Signing tests (self-signed). 2012 * 2013 * @requires extension openssl 2014 */ 2015 public function testSigning() 2016 { 2017 $this->Mail->Subject .= ': S/MIME signing'; 2018 $this->Mail->Body = 'This message is S/MIME signed.'; 2019 $this->buildBody(); 2020 2021 $dn = [ 2022 'countryName' => 'UK', 2023 'stateOrProvinceName' => 'Here', 2024 'localityName' => 'There', 2025 'organizationName' => 'PHP', 2026 'organizationalUnitName' => 'PHPMailer', 2027 'commonName' => 'PHPMailer Test', 2028 'emailAddress' => 'phpmailer@example.com', 2029 ]; 2030 $keyconfig = [ 2031 'digest_alg' => 'sha256', 2032 'private_key_bits' => 2048, 2033 'private_key_type' => OPENSSL_KEYTYPE_RSA, 2034 ]; 2035 $password = 'password'; 2036 $certfile = 'certfile.pem'; 2037 $keyfile = 'keyfile.pem'; 2038 2039 //Make a new key pair 2040 $pk = openssl_pkey_new($keyconfig); 2041 //Create a certificate signing request 2042 $csr = openssl_csr_new($dn, $pk); 2043 //Create a self-signed cert 2044 $cert = openssl_csr_sign($csr, null, $pk, 1); 2045 //Save the cert 2046 openssl_x509_export($cert, $certout); 2047 file_put_contents($certfile, $certout); 2048 //Save the key 2049 openssl_pkey_export($pk, $pkeyout, $password); 2050 file_put_contents($keyfile, $pkeyout); 2051 2052 $this->Mail->sign( 2053 $certfile, 2054 $keyfile, 2055 $password 2056 ); 2057 $this->assertTrue($this->Mail->send(), 'S/MIME signing failed'); 2058 2059 $msg = $this->Mail->getSentMIMEMessage(); 2060 $this->assertNotContains("\r\n\r\nMIME-Version:", $msg, 'Incorrect MIME headers'); 2061 unlink($certfile); 2062 unlink($keyfile); 2063 } 2064 2065 /** 2066 * S/MIME Signing tests using a CA chain cert. 2067 * To test that a generated message is signed correctly, save the message in a file called `signed.eml` 2068 * and use openssl along with the certs generated by this script: 2069 * `openssl smime -verify -in signed.eml -signer certfile.pem -CAfile cacertfile.pem`. 2070 * 2071 * @requires extension openssl 2072 */ 2073 public function testSigningWithCA() 2074 { 2075 $this->Mail->Subject .= ': S/MIME signing with CA'; 2076 $this->Mail->Body = 'This message is S/MIME signed with an extra CA cert.'; 2077 $this->buildBody(); 2078 2079 $certprops = [ 2080 'countryName' => 'UK', 2081 'stateOrProvinceName' => 'Here', 2082 'localityName' => 'There', 2083 'organizationName' => 'PHP', 2084 'organizationalUnitName' => 'PHPMailer', 2085 'commonName' => 'PHPMailer Test', 2086 'emailAddress' => 'phpmailer@example.com', 2087 ]; 2088 $cacertprops = [ 2089 'countryName' => 'UK', 2090 'stateOrProvinceName' => 'Here', 2091 'localityName' => 'There', 2092 'organizationName' => 'PHP', 2093 'organizationalUnitName' => 'PHPMailer CA', 2094 'commonName' => 'PHPMailer Test CA', 2095 'emailAddress' => 'phpmailer@example.com', 2096 ]; 2097 $keyconfig = [ 2098 'digest_alg' => 'sha256', 2099 'private_key_bits' => 2048, 2100 'private_key_type' => OPENSSL_KEYTYPE_RSA, 2101 ]; 2102 $password = 'password'; 2103 $cacertfile = 'cacertfile.pem'; 2104 $cakeyfile = 'cakeyfile.pem'; 2105 $certfile = 'certfile.pem'; 2106 $keyfile = 'keyfile.pem'; 2107 2108 //Create a CA cert 2109 //Make a new key pair 2110 $capk = openssl_pkey_new($keyconfig); 2111 //Create a certificate signing request 2112 $csr = openssl_csr_new($cacertprops, $capk); 2113 //Create a self-signed cert 2114 $cert = openssl_csr_sign($csr, null, $capk, 1); 2115 //Save the CA cert 2116 openssl_x509_export($cert, $certout); 2117 file_put_contents($cacertfile, $certout); 2118 //Save the CA key 2119 openssl_pkey_export($capk, $pkeyout, $password); 2120 file_put_contents($cakeyfile, $pkeyout); 2121 2122 //Create a cert signed by our CA 2123 //Make a new key pair 2124 $pk = openssl_pkey_new($keyconfig); 2125 //Create a certificate signing request 2126 $csr = openssl_csr_new($certprops, $pk); 2127 //Create a self-signed cert 2128 $cacert = file_get_contents($cacertfile); 2129 $cert = openssl_csr_sign($csr, $cacert, $capk, 1); 2130 //Save the cert 2131 openssl_x509_export($cert, $certout); 2132 file_put_contents($certfile, $certout); 2133 //Save the key 2134 openssl_pkey_export($pk, $pkeyout, $password); 2135 file_put_contents($keyfile, $pkeyout); 2136 2137 $this->Mail->sign( 2138 $certfile, 2139 $keyfile, 2140 $password, 2141 $cacertfile 2142 ); 2143 $this->assertTrue($this->Mail->send(), 'S/MIME signing with CA failed'); 2144 unlink($cacertfile); 2145 unlink($cakeyfile); 2146 unlink($certfile); 2147 unlink($keyfile); 2148 } 2149 2150 /** 2151 * DKIM body canonicalization tests. 2152 * 2153 * @see https://tools.ietf.org/html/rfc6376#section-3.4.4 2154 */ 2155 public function testDKIMBodyCanonicalization() 2156 { 2157 //Example from https://tools.ietf.org/html/rfc6376#section-3.4.5 2158 $prebody = " C \r\nD \t E\r\n\r\n\r\n"; 2159 $postbody = " C \r\nD \t E\r\n"; 2160 $this->assertEquals($this->Mail->DKIM_BodyC(''), "\r\n", 'DKIM empty body canonicalization incorrect'); 2161 $this->assertEquals( 2162 'frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN/XKdLCPjaYaY=', 2163 base64_encode(hash('sha256', $this->Mail->DKIM_BodyC(''), true)), 2164 'DKIM canonicalized empty body hash mismatch' 2165 ); 2166 $this->assertEquals($this->Mail->DKIM_BodyC($prebody), $postbody, 'DKIM body canonicalization incorrect'); 2167 } 2168 2169 /** 2170 * DKIM header canonicalization tests. 2171 * 2172 * @see https://tools.ietf.org/html/rfc6376#section-3.4.2 2173 */ 2174 public function testDKIMHeaderCanonicalization() 2175 { 2176 //Example from https://tools.ietf.org/html/rfc6376#section-3.4.5 2177 $preheaders = "A: X\r\nB : Y\t\r\n\tZ \r\n"; 2178 $postheaders = "a:X\r\nb:Y Z\r\n"; 2179 $this->assertEquals( 2180 $postheaders, 2181 $this->Mail->DKIM_HeaderC($preheaders), 2182 'DKIM header canonicalization incorrect' 2183 ); 2184 //Check that long folded lines with runs of spaces are canonicalized properly 2185 $preheaders = 'Long-Header-1: <https://example.com/somescript.php?' . 2186 "id=1234567890&name=Abcdefghijklmnopquestuvwxyz&hash=\r\n abc1234\r\n" . 2187 "Long-Header-2: This is a long header value that contains runs of spaces and trailing \r\n" . 2188 ' and is folded onto 2 lines'; 2189 $postheaders = 'long-header-1:<https://example.com/somescript.php?id=1234567890&' . 2190 "name=Abcdefghijklmnopquestuvwxyz&hash= abc1234\r\nlong-header-2:This is a long" . 2191 ' header value that contains runs of spaces and trailing and is folded onto 2 lines'; 2192 $this->assertEquals( 2193 $postheaders, 2194 $this->Mail->DKIM_HeaderC($preheaders), 2195 'DKIM header canonicalization of long lines incorrect' 2196 ); 2197 } 2198 2199 /** 2200 * DKIM copied header fields tests. 2201 * 2202 * @group dkim 2203 * 2204 * @see https://tools.ietf.org/html/rfc6376#section-3.5 2205 */ 2206 public function testDKIMOptionalHeaderFieldsCopy() 2207 { 2208 $privatekeyfile = 'dkim_private.pem'; 2209 $pk = openssl_pkey_new( 2210 [ 2211 'private_key_bits' => 2048, 2212 'private_key_type' => OPENSSL_KEYTYPE_RSA, 2213 ] 2214 ); 2215 openssl_pkey_export_to_file($pk, $privatekeyfile); 2216 $this->Mail->DKIM_private = 'dkim_private.pem'; 2217 2218 //Example from https://tools.ietf.org/html/rfc6376#section-3.5 2219 $from = 'from@example.com'; 2220 $to = 'to@example.com'; 2221 $date = 'date'; 2222 $subject = 'example'; 2223 2224 $headerLines = "From:$from\r\nTo:$to\r\nDate:$date\r\n"; 2225 $copyHeaderFields = " z=From:$from\r\n |To:$to\r\n |Date:$date\r\n |Subject:$subject;\r\n"; 2226 2227 $this->Mail->DKIM_copyHeaderFields = true; 2228 $this->assertContains( 2229 $copyHeaderFields, 2230 $this->Mail->DKIM_Add($headerLines, $subject, ''), 2231 'DKIM header with copied header fields incorrect' 2232 ); 2233 2234 $this->Mail->DKIM_copyHeaderFields = false; 2235 $this->assertNotContains( 2236 $copyHeaderFields, 2237 $this->Mail->DKIM_Add($headerLines, $subject, ''), 2238 'DKIM header without copied header fields incorrect' 2239 ); 2240 2241 unlink($privatekeyfile); 2242 } 2243 2244 /** 2245 * DKIM signing extra headers tests. 2246 * 2247 * @group dkim 2248 */ 2249 public function testDKIMExtraHeaders() 2250 { 2251 $privatekeyfile = 'dkim_private.pem'; 2252 $pk = openssl_pkey_new( 2253 [ 2254 'private_key_bits' => 2048, 2255 'private_key_type' => OPENSSL_KEYTYPE_RSA, 2256 ] 2257 ); 2258 openssl_pkey_export_to_file($pk, $privatekeyfile); 2259 $this->Mail->DKIM_private = 'dkim_private.pem'; 2260 2261 //Example from https://tools.ietf.org/html/rfc6376#section-3.5 2262 $from = 'from@example.com'; 2263 $to = 'to@example.com'; 2264 $date = 'date'; 2265 $subject = 'example'; 2266 $anyHeader = 'foo'; 2267 $unsubscribeUrl = '<https://www.example.com/unsubscribe/?newsletterId=anytoken&actionToken=anyToken' . 2268 '&otherParam=otherValue&anotherParam=anotherVeryVeryVeryLongValue>'; 2269 2270 $this->Mail->addCustomHeader('X-AnyHeader', $anyHeader); 2271 $this->Mail->addCustomHeader('Baz', 'bar'); 2272 $this->Mail->addCustomHeader('List-Unsubscribe', $unsubscribeUrl); 2273 2274 $this->Mail->DKIM_extraHeaders = ['Baz', 'List-Unsubscribe']; 2275 2276 $headerLines = "From:$from\r\nTo:$to\r\nDate:$date\r\n"; 2277 $headerLines .= "X-AnyHeader:$anyHeader\r\nBaz:bar\r\n"; 2278 $headerLines .= 'List-Unsubscribe:' . $this->Mail->encodeHeader($unsubscribeUrl) . "\r\n"; 2279 2280 $headerFields = 'h=From:To:Date:Baz:List-Unsubscribe:Subject'; 2281 2282 $result = $this->Mail->DKIM_Add($headerLines, $subject, ''); 2283 2284 $this->assertContains($headerFields, $result, 'DKIM header with extra headers incorrect'); 2285 2286 unlink($privatekeyfile); 2287 } 2288 2289 /** 2290 * DKIM Signing tests. 2291 * 2292 * @requires extension openssl 2293 */ 2294 public function testDKIM() 2295 { 2296 $this->Mail->Subject .= ': DKIM signing'; 2297 $this->Mail->Body = 'This message is DKIM signed.'; 2298 $this->buildBody(); 2299 $privatekeyfile = 'dkim_private.pem'; 2300 //Make a new key pair 2301 //(2048 bits is the recommended minimum key length - 2302 //gmail won't accept less than 1024 bits) 2303 $pk = openssl_pkey_new( 2304 [ 2305 'private_key_bits' => 2048, 2306 'private_key_type' => OPENSSL_KEYTYPE_RSA, 2307 ] 2308 ); 2309 openssl_pkey_export_to_file($pk, $privatekeyfile); 2310 $this->Mail->DKIM_domain = 'example.com'; 2311 $this->Mail->DKIM_private = $privatekeyfile; 2312 $this->Mail->DKIM_selector = 'phpmailer'; 2313 $this->Mail->DKIM_passphrase = ''; //key is not encrypted 2314 $this->assertTrue($this->Mail->send(), 'DKIM signed mail failed'); 2315 $this->Mail->isMail(); 2316 $this->assertTrue($this->Mail->send(), 'DKIM signed mail via mail() failed'); 2317 unlink($privatekeyfile); 2318 } 2319 2320 /** 2321 * Test line break reformatting. 2322 */ 2323 public function testLineBreaks() 2324 { 2325 //May have been altered by earlier tests, can interfere with line break format 2326 $this->Mail->isSMTP(); 2327 $this->Mail->preSend(); 2328 $unixsrc = "hello\nWorld\nAgain\n"; 2329 $macsrc = "hello\rWorld\rAgain\r"; 2330 $windowssrc = "hello\r\nWorld\r\nAgain\r\n"; 2331 $mixedsrc = "hello\nWorld\rAgain\r\n"; 2332 $target = "hello\r\nWorld\r\nAgain\r\n"; 2333 $this->assertEquals($target, PHPMailer::normalizeBreaks($unixsrc), 'UNIX break reformatting failed'); 2334 $this->assertEquals($target, PHPMailer::normalizeBreaks($macsrc), 'Mac break reformatting failed'); 2335 $this->assertEquals($target, PHPMailer::normalizeBreaks($windowssrc), 'Windows break reformatting failed'); 2336 $this->assertEquals($target, PHPMailer::normalizeBreaks($mixedsrc), 'Mixed break reformatting failed'); 2337 2338 //To see accurate results when using postfix, set `sendmail_fix_line_endings = never` in main.cf 2339 $this->Mail->Subject = 'PHPMailer DOS line breaks'; 2340 $this->Mail->Body = "This message\r\ncontains\r\nDOS-format\r\nCRLF line breaks."; 2341 $this->assertTrue($this->Mail->send()); 2342 2343 $this->Mail->Subject = 'PHPMailer UNIX line breaks'; 2344 $this->Mail->Body = "This message\ncontains\nUNIX-format\nLF line breaks."; 2345 $this->assertTrue($this->Mail->send()); 2346 2347 $this->Mail->Encoding = 'quoted-printable'; 2348 $this->Mail->Subject = 'PHPMailer DOS line breaks, QP'; 2349 $this->Mail->Body = "This message\r\ncontains\r\nDOS-format\r\nCRLF line breaks."; 2350 $this->assertTrue($this->Mail->send()); 2351 2352 $this->Mail->Subject = 'PHPMailer UNIX line breaks, QP'; 2353 $this->Mail->Body = "This message\ncontains\nUNIX-format\nLF line breaks."; 2354 $this->assertTrue($this->Mail->send()); 2355 } 2356 2357 /** 2358 * Test line length detection. 2359 */ 2360 public function testLineLength() 2361 { 2362 //May have been altered by earlier tests, can interfere with line break format 2363 $this->Mail->isSMTP(); 2364 $this->Mail->preSend(); 2365 $oklen = str_repeat(str_repeat('0', PHPMailer::MAX_LINE_LENGTH) . "\r\n", 2); 2366 $badlen = str_repeat(str_repeat('1', PHPMailer::MAX_LINE_LENGTH + 1) . "\r\n", 2); 2367 $this->assertTrue(PHPMailer::hasLineLongerThanMax($badlen), 'Long line not detected (only)'); 2368 $this->assertTrue(PHPMailer::hasLineLongerThanMax($oklen . $badlen), 'Long line not detected (first)'); 2369 $this->assertTrue(PHPMailer::hasLineLongerThanMax($badlen . $oklen), 'Long line not detected (last)'); 2370 $this->assertTrue( 2371 PHPMailer::hasLineLongerThanMax($oklen . $badlen . $oklen), 2372 'Long line not detected (middle)' 2373 ); 2374 $this->assertFalse(PHPMailer::hasLineLongerThanMax($oklen), 'Long line false positive'); 2375 $this->Mail->isHTML(false); 2376 $this->Mail->Subject .= ': Line length test'; 2377 $this->Mail->CharSet = 'UTF-8'; 2378 $this->Mail->Encoding = '8bit'; 2379 $this->Mail->Body = $oklen . $badlen . $oklen . $badlen; 2380 $this->buildBody(); 2381 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 2382 $this->assertEquals('quoted-printable', $this->Mail->Encoding, 'Long line did not override transfer encoding'); 2383 } 2384 2385 /** 2386 * Test setting and retrieving message ID. 2387 */ 2388 public function testMessageID() 2389 { 2390 $this->Mail->Body = 'Test message ID.'; 2391 $id = hash('sha256', 12345); 2392 $this->Mail->MessageID = $id; 2393 $this->buildBody(); 2394 $this->Mail->preSend(); 2395 $lastid = $this->Mail->getLastMessageID(); 2396 $this->assertNotEquals($lastid, $id, 'Invalid Message ID allowed'); 2397 $id = '<' . hash('sha256', 12345) . '@example.com>'; 2398 $this->Mail->MessageID = $id; 2399 $this->buildBody(); 2400 $this->Mail->preSend(); 2401 $lastid = $this->Mail->getLastMessageID(); 2402 $this->assertEquals($lastid, $id, 'Custom Message ID not used'); 2403 $this->Mail->MessageID = ''; 2404 $this->buildBody(); 2405 $this->Mail->preSend(); 2406 $lastid = $this->Mail->getLastMessageID(); 2407 $this->assertRegExp('/^<.*@.*>$/', $lastid, 'Invalid default Message ID'); 2408 } 2409 2410 /** 2411 * Check whether setting a bad custom header throws exceptions. 2412 * 2413 * @throws Exception 2414 */ 2415 public function testHeaderException() 2416 { 2417 $this->expectException(Exception::class); 2418 $mail = new PHPMailer(true); 2419 $mail->addCustomHeader('SomeHeader', "Some\n Value"); 2420 } 2421 2422 /** 2423 * Miscellaneous calls to improve test coverage and some small tests. 2424 */ 2425 public function testMiscellaneous() 2426 { 2427 $this->assertEquals('application/pdf', PHPMailer::_mime_types('pdf'), 'MIME TYPE lookup failed'); 2428 $this->Mail->clearAttachments(); 2429 $this->Mail->isHTML(false); 2430 $this->Mail->isSMTP(); 2431 $this->Mail->isMail(); 2432 $this->Mail->isSendmail(); 2433 $this->Mail->isQmail(); 2434 $this->Mail->setLanguage('fr'); 2435 $this->Mail->Sender = ''; 2436 $this->Mail->createHeader(); 2437 $this->assertFalse($this->Mail->set('x', 'y'), 'Invalid property set succeeded'); 2438 $this->assertTrue($this->Mail->set('Timeout', 11), 'Valid property set failed'); 2439 $this->assertTrue($this->Mail->set('AllowEmpty', null), 'Null property set failed'); 2440 $this->assertTrue($this->Mail->set('AllowEmpty', false), 'Valid property set of null property failed'); 2441 //Test pathinfo 2442 $a = '/mnt/files/飛兒樂 團光茫.mp3'; 2443 $q = PHPMailer::mb_pathinfo($a); 2444 $this->assertEquals($q['dirname'], '/mnt/files', 'UNIX dirname not matched'); 2445 $this->assertEquals($q['basename'], '飛兒樂 團光茫.mp3', 'UNIX basename not matched'); 2446 $this->assertEquals($q['extension'], 'mp3', 'UNIX extension not matched'); 2447 $this->assertEquals($q['filename'], '飛兒樂 團光茫', 'UNIX filename not matched'); 2448 $this->assertEquals( 2449 PHPMailer::mb_pathinfo($a, PATHINFO_DIRNAME), 2450 '/mnt/files', 2451 'Dirname path element not matched' 2452 ); 2453 $this->assertEquals( 2454 PHPMailer::mb_pathinfo($a, PATHINFO_BASENAME), 2455 '飛兒樂 團光茫.mp3', 2456 'Basename path element not matched' 2457 ); 2458 $this->assertEquals(PHPMailer::mb_pathinfo($a, 'filename'), '飛兒樂 團光茫', 'Filename path element not matched'); 2459 $a = 'c:\mnt\files\飛兒樂 團光茫.mp3'; 2460 $q = PHPMailer::mb_pathinfo($a); 2461 $this->assertEquals($q['dirname'], 'c:\mnt\files', 'Windows dirname not matched'); 2462 $this->assertEquals($q['basename'], '飛兒樂 團光茫.mp3', 'Windows basename not matched'); 2463 $this->assertEquals($q['extension'], 'mp3', 'Windows extension not matched'); 2464 $this->assertEquals($q['filename'], '飛兒樂 團光茫', 'Windows filename not matched'); 2465 2466 $this->assertEquals( 2467 PHPMailer::filenameToType('abc.jpg?xyz=1'), 2468 'image/jpeg', 2469 'Query string not ignored in filename' 2470 ); 2471 $this->assertEquals( 2472 PHPMailer::filenameToType('abc.xyzpdq'), 2473 'application/octet-stream', 2474 'Default MIME type not applied to unknown extension' 2475 ); 2476 2477 //Line break normalization 2478 $eol = PHPMailer::getLE(); 2479 $b1 = "1\r2\r3\r"; 2480 $b2 = "1\n2\n3\n"; 2481 $b3 = "1\r\n2\r3\n"; 2482 $t1 = "1{$eol}2{$eol}3{$eol}"; 2483 $this->assertEquals(PHPMailer::normalizeBreaks($b1), $t1, 'Failed to normalize line breaks (1)'); 2484 $this->assertEquals(PHPMailer::normalizeBreaks($b2), $t1, 'Failed to normalize line breaks (2)'); 2485 $this->assertEquals(PHPMailer::normalizeBreaks($b3), $t1, 'Failed to normalize line breaks (3)'); 2486 } 2487 2488 public function testBadSMTP() 2489 { 2490 $this->Mail->smtpConnect(); 2491 $smtp = $this->Mail->getSMTPInstance(); 2492 $this->assertFalse($smtp->mail("somewhere\nbad"), 'Bad SMTP command containing breaks accepted'); 2493 } 2494 2495 public function testHostValidation() 2496 { 2497 $good = [ 2498 'localhost', 2499 'example.com', 2500 'smtp.gmail.com', 2501 '127.0.0.1', 2502 trim(str_repeat('a0123456789.', 21), '.'), 2503 '[::1]', 2504 '[0:1234:dc0:41:216:3eff:fe67:3e01]', 2505 ]; 2506 $bad = [ 2507 null, 2508 123, 2509 1.5, 2510 new \stdClass(), 2511 [], 2512 '', 2513 '999.0.0.0', 2514 '[1234]', 2515 '[1234:::1]', 2516 trim(str_repeat('a0123456789.', 22), '.'), 2517 '0:1234:dc0:41:216:3eff:fe67:3e01', 2518 '[012q:1234:dc0:41:216:3eff:fe67:3e01]', 2519 '[[::1]]', 2520 ]; 2521 foreach ($good as $h) { 2522 $this->assertTrue(PHPMailer::isValidHost($h), 'Good hostname denied: ' . $h); 2523 } 2524 foreach ($bad as $h) { 2525 $this->assertFalse(PHPMailer::isValidHost($h), 'Bad hostname accepted: ' . var_export($h, true)); 2526 } 2527 } 2528 2529 /** 2530 * Tests the Custom header getter. 2531 */ 2532 public function testCustomHeaderGetter() 2533 { 2534 $this->Mail->addCustomHeader('foo', 'bar'); 2535 $this->assertEquals([['foo', 'bar']], $this->Mail->getCustomHeaders()); 2536 2537 $this->Mail->addCustomHeader('foo', 'baz'); 2538 $this->assertEquals( 2539 [ 2540 ['foo', 'bar'], 2541 ['foo', 'baz'], 2542 ], 2543 $this->Mail->getCustomHeaders() 2544 ); 2545 2546 $this->Mail->clearCustomHeaders(); 2547 $this->assertEmpty($this->Mail->getCustomHeaders()); 2548 2549 $this->Mail->addCustomHeader('yux'); 2550 $this->assertEquals([['yux', '']], $this->Mail->getCustomHeaders()); 2551 2552 $this->Mail->addCustomHeader('Content-Type: application/json'); 2553 $this->assertEquals( 2554 [ 2555 ['yux', ''], 2556 ['Content-Type', 'application/json'], 2557 ], 2558 $this->Mail->getCustomHeaders() 2559 ); 2560 $this->Mail->clearCustomHeaders(); 2561 $this->Mail->addCustomHeader('SomeHeader: Some Value'); 2562 $headers = $this->Mail->getCustomHeaders(); 2563 $this->assertEquals($headers[0], ['SomeHeader', 'Some Value']); 2564 $this->Mail->clearCustomHeaders(); 2565 $this->Mail->addCustomHeader('SomeHeader', 'Some Value'); 2566 $headers = $this->Mail->getCustomHeaders(); 2567 $this->assertEquals($headers[0], ['SomeHeader', 'Some Value']); 2568 $this->Mail->clearCustomHeaders(); 2569 $this->assertFalse($this->Mail->addCustomHeader('SomeHeader', "Some\n Value")); 2570 $this->assertFalse($this->Mail->addCustomHeader("Some\nHeader", 'Some Value')); 2571 } 2572 2573 /** 2574 * Tests setting and retrieving ConfirmReadingTo address, also known as "read receipt" address. 2575 */ 2576 public function testConfirmReadingTo() 2577 { 2578 $this->Mail->CharSet = PHPMailer::CHARSET_UTF8; 2579 $this->buildBody(); 2580 2581 $this->Mail->ConfirmReadingTo = 'test@example..com'; //Invalid address 2582 $this->assertFalse($this->Mail->send(), $this->Mail->ErrorInfo); 2583 2584 $this->Mail->ConfirmReadingTo = ' test@example.com'; //Extra space to trim 2585 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 2586 $this->assertEquals( 2587 'test@example.com', 2588 $this->Mail->ConfirmReadingTo, 2589 'Unexpected read receipt address' 2590 ); 2591 2592 $this->Mail->ConfirmReadingTo = 'test@françois.ch'; //Address with IDN 2593 if (PHPMailer::idnSupported()) { 2594 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 2595 $this->assertEquals( 2596 'test@xn--franois-xxa.ch', 2597 $this->Mail->ConfirmReadingTo, 2598 'IDN address not converted to punycode' 2599 ); 2600 } else { 2601 $this->assertFalse($this->Mail->send(), $this->Mail->ErrorInfo); 2602 } 2603 } 2604 2605 /** 2606 * Tests CharSet and Unicode -> ASCII conversions for addresses with IDN. 2607 */ 2608 public function testConvertEncoding() 2609 { 2610 if (!PHPMailer::idnSupported()) { 2611 $this->markTestSkipped('intl and/or mbstring extensions are not available'); 2612 } 2613 2614 $this->Mail->clearAllRecipients(); 2615 $this->Mail->clearReplyTos(); 2616 2617 // This file is UTF-8 encoded. Create a domain encoded in "iso-8859-1". 2618 $domain = '@' . mb_convert_encoding('françois.ch', 'ISO-8859-1', 'UTF-8'); 2619 $this->Mail->addAddress('test' . $domain); 2620 $this->Mail->addCC('test+cc' . $domain); 2621 $this->Mail->addBCC('test+bcc' . $domain); 2622 $this->Mail->addReplyTo('test+replyto' . $domain); 2623 2624 // Queued addresses are not returned by get*Addresses() before send() call. 2625 $this->assertEmpty($this->Mail->getToAddresses(), 'Bad "to" recipients'); 2626 $this->assertEmpty($this->Mail->getCcAddresses(), 'Bad "cc" recipients'); 2627 $this->assertEmpty($this->Mail->getBccAddresses(), 'Bad "bcc" recipients'); 2628 $this->assertEmpty($this->Mail->getReplyToAddresses(), 'Bad "reply-to" recipients'); 2629 2630 // Clear queued BCC recipient. 2631 $this->Mail->clearBCCs(); 2632 2633 $this->buildBody(); 2634 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 2635 2636 // Addresses with IDN are returned by get*Addresses() after send() call. 2637 $domain = $this->Mail->punyencodeAddress($domain); 2638 $this->assertEquals( 2639 [['test' . $domain, '']], 2640 $this->Mail->getToAddresses(), 2641 'Bad "to" recipients' 2642 ); 2643 $this->assertEquals( 2644 [['test+cc' . $domain, '']], 2645 $this->Mail->getCcAddresses(), 2646 'Bad "cc" recipients' 2647 ); 2648 $this->assertEmpty($this->Mail->getBccAddresses(), 'Bad "bcc" recipients'); 2649 $this->assertEquals( 2650 ['test+replyto' . $domain => ['test+replyto' . $domain, '']], 2651 $this->Mail->getReplyToAddresses(), 2652 'Bad "reply-to" addresses' 2653 ); 2654 } 2655 2656 /** 2657 * Tests removal of duplicate recipients and reply-tos. 2658 */ 2659 public function testDuplicateIDNRemoved() 2660 { 2661 if (!PHPMailer::idnSupported()) { 2662 $this->markTestSkipped('intl and/or mbstring extensions are not available'); 2663 } 2664 2665 $this->Mail->clearAllRecipients(); 2666 $this->Mail->clearReplyTos(); 2667 2668 $this->Mail->CharSet = PHPMailer::CHARSET_UTF8; 2669 2670 $this->assertTrue($this->Mail->addAddress('test@françois.ch')); 2671 $this->assertFalse($this->Mail->addAddress('test@françois.ch')); 2672 $this->assertTrue($this->Mail->addAddress('test@FRANÇOIS.CH')); 2673 $this->assertFalse($this->Mail->addAddress('test@FRANÇOIS.CH')); 2674 $this->assertTrue($this->Mail->addAddress('test@xn--franois-xxa.ch')); 2675 $this->assertFalse($this->Mail->addAddress('test@xn--franois-xxa.ch')); 2676 $this->assertFalse($this->Mail->addAddress('test@XN--FRANOIS-XXA.CH')); 2677 2678 $this->assertTrue($this->Mail->addReplyTo('test+replyto@françois.ch')); 2679 $this->assertFalse($this->Mail->addReplyTo('test+replyto@françois.ch')); 2680 $this->assertTrue($this->Mail->addReplyTo('test+replyto@FRANÇOIS.CH')); 2681 $this->assertFalse($this->Mail->addReplyTo('test+replyto@FRANÇOIS.CH')); 2682 $this->assertTrue($this->Mail->addReplyTo('test+replyto@xn--franois-xxa.ch')); 2683 $this->assertFalse($this->Mail->addReplyTo('test+replyto@xn--franois-xxa.ch')); 2684 $this->assertFalse($this->Mail->addReplyTo('test+replyto@XN--FRANOIS-XXA.CH')); 2685 2686 $this->buildBody(); 2687 $this->assertTrue($this->Mail->send(), $this->Mail->ErrorInfo); 2688 2689 // There should be only one "To" address and one "Reply-To" address. 2690 $this->assertEquals( 2691 1, 2692 \count($this->Mail->getToAddresses()), 2693 'Bad count of "to" recipients' 2694 ); 2695 $this->assertEquals( 2696 1, 2697 \count($this->Mail->getReplyToAddresses()), 2698 'Bad count of "reply-to" addresses' 2699 ); 2700 } 2701 2702 /** 2703 * Use a fake POP3 server to test POP-before-SMTP auth with a known-good login. 2704 * 2705 * @group pop3 2706 */ 2707 public function testPopBeforeSmtpGood() 2708 { 2709 //Start a fake POP server 2710 $pid = shell_exec( 2711 '/usr/bin/nohup ' . 2712 $this->INCLUDE_DIR . 2713 '/test/runfakepopserver.sh 1100 >/dev/null 2>/dev/null & printf "%u" $!' 2714 ); 2715 $this->pids[] = $pid; 2716 2717 sleep(1); 2718 //Test a known-good login 2719 $this->assertTrue( 2720 POP3::popBeforeSmtp('localhost', 1100, 10, 'user', 'test', $this->Mail->SMTPDebug), 2721 'POP before SMTP failed' 2722 ); 2723 //Kill the fake server, don't care if it fails 2724 @shell_exec('kill -TERM ' . escapeshellarg($pid)); 2725 sleep(2); 2726 } 2727 2728 /** 2729 * Use a fake POP3 server to test POP-before-SMTP auth 2730 * with a known-bad login. 2731 * 2732 * @group pop3 2733 */ 2734 public function testPopBeforeSmtpBad() 2735 { 2736 //Start a fake POP server on a different port 2737 //so we don't inadvertently connect to the previous instance 2738 $pid = shell_exec( 2739 '/usr/bin/nohup ' . 2740 $this->INCLUDE_DIR . 2741 '/test/runfakepopserver.sh 1101 >/dev/null 2>/dev/null & printf "%u" $!' 2742 ); 2743 $this->pids[] = $pid; 2744 2745 sleep(2); 2746 //Test a known-bad login 2747 $this->assertFalse( 2748 POP3::popBeforeSmtp('localhost', 1101, 10, 'user', 'xxx', $this->Mail->SMTPDebug), 2749 'POP before SMTP should have failed' 2750 ); 2751 //Kill the fake server, don't care if it fails 2752 @shell_exec('kill -TERM ' . escapeshellarg($pid)); 2753 sleep(2); 2754 } 2755 2756 /** 2757 * Test SMTP host connections. 2758 * This test can take a long time, so run it last. 2759 * 2760 * @group slow 2761 */ 2762 public function testSmtpConnect() 2763 { 2764 $this->Mail->SMTPDebug = SMTP::DEBUG_LOWLEVEL; //Show connection-level errors 2765 $this->assertTrue($this->Mail->smtpConnect(), 'SMTP single connect failed'); 2766 $this->Mail->smtpClose(); 2767 2768 // $this->Mail->Host = 'localhost:12345;10.10.10.10:54321;' . $_REQUEST['mail_host']; 2769 // $this->assertTrue($this->Mail->smtpConnect(), 'SMTP multi-connect failed'); 2770 // $this->Mail->smtpClose(); 2771 // $this->Mail->Host = '[::1]:' . $this->Mail->Port . ';' . $_REQUEST['mail_host']; 2772 // $this->assertTrue($this->Mail->smtpConnect(), 'SMTP IPv6 literal multi-connect failed'); 2773 // $this->Mail->smtpClose(); 2774 2775 // All these hosts are expected to fail 2776 // $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'].' '; 2777 // $this->assertFalse($this->Mail->smtpConnect()); 2778 // $this->Mail->smtpClose(); 2779 2780 $this->Mail->Host = ' localhost:12345 ; ' . $_REQUEST['mail_host'] . ' '; 2781 $this->assertTrue($this->Mail->smtpConnect(), 'SMTP hosts with stray spaces failed'); 2782 $this->Mail->smtpClose(); 2783 2784 // Need to pick a harmless option so as not cause problems of its own! socket:bind doesn't work with Travis-CI 2785 $this->Mail->Host = $_REQUEST['mail_host']; 2786 $this->assertTrue($this->Mail->smtpConnect(['ssl' => ['verify_depth' => 10]])); 2787 2788 $this->Smtp = $this->Mail->getSMTPInstance(); 2789 $this->assertInstanceOf(\get_class($this->Smtp), $this->Mail->setSMTPInstance($this->Smtp)); 2790 $this->assertFalse($this->Smtp->startTLS(), 'SMTP connect with options failed'); 2791 $this->assertFalse($this->Mail->SMTPAuth); 2792 $this->Mail->smtpClose(); 2793 } 2794 2795 /** 2796 * Test OAuth method 2797 */ 2798 public function testOAuth() 2799 { 2800 $PHPMailer = new PHPMailer(); 2801 $reflection = new \ReflectionClass($PHPMailer); 2802 $property = $reflection->getProperty('oauth'); 2803 $property->setAccessible(true); 2804 $property->setValue($PHPMailer, true); 2805 $this->assertTrue($PHPMailer->getOAuth()); 2806 2807 $options =[ 2808 'provider' => 'dummyprovider', 2809 'userName' => 'dummyusername', 2810 'clientSecret' => 'dummyclientsecret', 2811 'clientId' => 'dummyclientid', 2812 'refreshToken' => 'dummyrefreshtoken', 2813 ]; 2814 2815 $oauth = new OAuth($options); 2816 $this->assertInstanceOf(OAuth::class, $oauth); 2817 $subject = $PHPMailer->setOAuth($oauth); 2818 $this->assertNull($subject); 2819 $this->assertInstanceOf(OAuth::class, $PHPMailer->getOAuth()); 2820 } 2821 2822 /** 2823 * Test ICal method 2824 */ 2825 public function testICalMethod() 2826 { 2827 $this->Mail->Subject .= ': ICal method'; 2828 $this->Mail->Body = '<h3>ICal method test.</h3>'; 2829 $this->Mail->AltBody = 'ICal method test.'; 2830 $this->Mail->Ical = 'BEGIN:VCALENDAR' 2831 . "\r\nVERSION:2.0" 2832 . "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN" 2833 . "\r\nMETHOD:CANCEL" 2834 . "\r\nCALSCALE:GREGORIAN" 2835 . "\r\nX-MICROSOFT-CALSCALE:GREGORIAN" 2836 . "\r\nBEGIN:VEVENT" 2837 . "\r\nUID:201909250755-42825@test" 2838 . "\r\nDTSTART;20190930T080000Z" 2839 . "\r\nSEQUENCE:2" 2840 . "\r\nTRANSP:OPAQUE" 2841 . "\r\nSTATUS:CONFIRMED" 2842 . "\r\nDTEND:20190930T084500Z" 2843 . "\r\nLOCATION:[London] London Eye" 2844 . "\r\nSUMMARY:Test ICal method" 2845 . "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=" 2846 . "\r\n TRUE:MAILTO:attendee-test@example.com" 2847 . "\r\nCLASS:PUBLIC" 2848 . "\r\nDESCRIPTION:Some plain text" 2849 . "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:test@example.com" 2850 . "\r\nDTSTAMP:20190925T075546Z" 2851 . "\r\nCREATED:20190925T075709Z" 2852 . "\r\nLAST-MODIFIED:20190925T075546Z" 2853 . "\r\nEND:VEVENT" 2854 . "\r\nEND:VCALENDAR"; 2855 $this->buildBody(); 2856 $this->Mail->preSend(); 2857 $this->assertRegExp( 2858 '/Content-Type: text\/calendar; method=CANCEL;/', 2859 $this->Mail->getSentMIMEMessage(), 2860 'Wrong ICal method in Content-Type header' 2861 ); 2862 } 2863 2864 /** 2865 * Test ICal missing method to use default (REQUEST) 2866 */ 2867 public function testICalInvalidMethod() 2868 { 2869 $this->Mail->Subject .= ': ICal method'; 2870 $this->Mail->Body = '<h3>ICal method test.</h3>'; 2871 $this->Mail->AltBody = 'ICal method test.'; 2872 $this->Mail->Ical = 'BEGIN:VCALENDAR' 2873 . "\r\nVERSION:2.0" 2874 . "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN" 2875 . "\r\nMETHOD:INVALID" 2876 . "\r\nCALSCALE:GREGORIAN" 2877 . "\r\nX-MICROSOFT-CALSCALE:GREGORIAN" 2878 . "\r\nBEGIN:VEVENT" 2879 . "\r\nUID:201909250755-42825@test" 2880 . "\r\nDTSTART;20190930T080000Z" 2881 . "\r\nSEQUENCE:2" 2882 . "\r\nTRANSP:OPAQUE" 2883 . "\r\nSTATUS:CONFIRMED" 2884 . "\r\nDTEND:20190930T084500Z" 2885 . "\r\nLOCATION:[London] London Eye" 2886 . "\r\nSUMMARY:Test ICal method" 2887 . "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=" 2888 . "\r\n TRUE:MAILTO:attendee-test@example.com" 2889 . "\r\nCLASS:PUBLIC" 2890 . "\r\nDESCRIPTION:Some plain text" 2891 . "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:test@example.com" 2892 . "\r\nDTSTAMP:20190925T075546Z" 2893 . "\r\nCREATED:20190925T075709Z" 2894 . "\r\nLAST-MODIFIED:20190925T075546Z" 2895 . "\r\nEND:VEVENT" 2896 . "\r\nEND:VCALENDAR"; 2897 $this->buildBody(); 2898 $this->Mail->preSend(); 2899 $this->assertRegExp( 2900 '/Content-Type: text\/calendar; method=REQUEST;/', 2901 $this->Mail->getSentMIMEMessage(), 2902 'Wrong ICal method in Content-Type header' 2903 ); 2904 } 2905 2906 /** 2907 * Test ICal invalid method to use default (REQUEST) 2908 */ 2909 public function testICalDefaultMethod() 2910 { 2911 $this->Mail->Subject .= ': ICal method'; 2912 $this->Mail->Body = '<h3>ICal method test.</h3>'; 2913 $this->Mail->AltBody = 'ICal method test.'; 2914 $this->Mail->Ical = 'BEGIN:VCALENDAR' 2915 . "\r\nVERSION:2.0" 2916 . "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN" 2917 . "\r\nCALSCALE:GREGORIAN" 2918 . "\r\nX-MICROSOFT-CALSCALE:GREGORIAN" 2919 . "\r\nBEGIN:VEVENT" 2920 . "\r\nUID:201909250755-42825@test" 2921 . "\r\nDTSTART;20190930T080000Z" 2922 . "\r\nSEQUENCE:2" 2923 . "\r\nTRANSP:OPAQUE" 2924 . "\r\nSTATUS:CONFIRMED" 2925 . "\r\nDTEND:20190930T084500Z" 2926 . "\r\nLOCATION:[London] London Eye" 2927 . "\r\nSUMMARY:Test ICal method" 2928 . "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=" 2929 . "\r\n TRUE:MAILTO:attendee-test@example.com" 2930 . "\r\nCLASS:PUBLIC" 2931 . "\r\nDESCRIPTION:Some plain text" 2932 . "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:test@example.com" 2933 . "\r\nDTSTAMP:20190925T075546Z" 2934 . "\r\nCREATED:20190925T075709Z" 2935 . "\r\nLAST-MODIFIED:20190925T075546Z" 2936 . "\r\nEND:VEVENT" 2937 . "\r\nEND:VCALENDAR"; 2938 $this->buildBody(); 2939 $this->Mail->preSend(); 2940 $this->assertRegExp( 2941 '/Content-Type: text\/calendar; method=REQUEST;/', 2942 $this->Mail->getSentMIMEMessage(), 2943 'Wrong ICal method in Content-Type header' 2944 ); 2945 } 2946} 2947/* 2948 * This is a sample form for setting appropriate test values through a browser 2949 * These values can also be set using a file called testbootstrap.php (not in repo) in the same folder as this script 2950 * which is probably more useful if you run these tests a lot 2951 * <html> 2952 * <body> 2953 * <h3>PHPMailer Unit Test</h3> 2954 * By entering a SMTP hostname it will automatically perform tests with SMTP. 2955 * 2956 * <form name="phpmailer_unit" action=__FILE__ method="get"> 2957 * <input type="hidden" name="submitted" value="1"/> 2958 * From Address: <input type="text" size="50" name="mail_from" value="<?php echo get("mail_from"); ?>"/> 2959 * <br/> 2960 * To Address: <input type="text" size="50" name="mail_to" value="<?php echo get("mail_to"); ?>"/> 2961 * <br/> 2962 * Cc Address: <input type="text" size="50" name="mail_cc" value="<?php echo get("mail_cc"); ?>"/> 2963 * <br/> 2964 * SMTP Hostname: <input type="text" size="50" name="mail_host" value="<?php echo get("mail_host"); ?>"/> 2965 * <p/> 2966 * <input type="submit" value="Run Test"/> 2967 * 2968 * </form> 2969 * </body> 2970 * </html> 2971 */ 2972