1<?php 2/** 3 * Copyright 2010-2017 Horde LLC (http://www.horde.org/) 4 * 5 * @category Horde 6 * @copyright 2010-2016 Horde LLC 7 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 8 * @package Mime 9 * @subpackage UnitTests 10 */ 11 12/** 13 * Tests for the Horde_Mime_Headers class. 14 * 15 * @author Michael Slusarz <slusarz@horde.org> 16 * @category Horde 17 * @copyright 2010-2016 Horde LLC 18 * @internal 19 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 20 * @package Mime 21 * @subpackage UnitTests 22 */ 23class Horde_Mime_HeadersTest extends PHPUnit_Framework_TestCase 24{ 25 public function testClone() 26 { 27 $hdrs = new Horde_Mime_Headers(); 28 $hdrs->addHeader('To', 'foo@example.com'); 29 $hdrs->addHeader('Resent-To', 'foo2@example.com'); 30 $hdrs->addHeader('Resent-To', 'foo3@example.com'); 31 32 $ct = new Horde_Mime_Headers_ContentParam_ContentType( 33 null, 34 'text/plain; charset="iso-8859-1"' 35 ); 36 $hdrs->addHeaderOb($ct); 37 38 $cd = new Horde_Mime_Headers_ContentParam_ContentDisposition( 39 null, 40 'attachment; filename="foo"' 41 ); 42 $hdrs->addHeaderOb($cd); 43 44 $hdrs2 = clone $hdrs; 45 46 $hdrs->addHeader('To', 'bar@example.com'); 47 $hdrs->addHeader('Resent-To', 'bar2@example.com'); 48 $ct['charset'] = 'utf-8'; 49 $cd['filename'] = 'bar'; 50 51 $this->assertEquals( 52 'foo@example.com', 53 strval($hdrs2['To']) 54 ); 55 $this->assertEquals( 56 array('foo2@example.com', 'foo3@example.com'), 57 $hdrs2['Resent-To']->value 58 ); 59 $this->assertEquals( 60 array('charset' => 'iso-8859-1'), 61 $hdrs2['Content-Type']->params 62 ); 63 $this->assertEquals( 64 array('filename' => 'foo'), 65 $hdrs2['Content-Disposition']->params 66 ); 67 } 68 69 /** 70 * @dataProvider serializeProvider 71 */ 72 public function testSerialize($header, $value) 73 { 74 $hdrs = new Horde_Mime_Headers(); 75 $hdrs->addHeader($header, $value); 76 77 $hdrs2 = unserialize(serialize($hdrs)); 78 79 /* @deprecated */ 80 $this->assertEquals( 81 $value, 82 $hdrs2->getValue($header) 83 ); 84 85 $this->assertequals( 86 $value, 87 strval($hdrs2[$header]) 88 ); 89 } 90 91 public function serializeProvider() 92 { 93 return array( 94 array( 95 'Subject', 'My Subject' 96 ), 97 array( 98 'To', 'recipient@example.com' 99 ), 100 array( 101 'Cc', 'null@example.com' 102 ), 103 array( 104 'Bcc', 'invisible@example.com' 105 ), 106 array( 107 'From', 'sender@example.com' 108 ) 109 ); 110 } 111 112 /** 113 * @dataProvider normalHeaderDecodeProvider 114 */ 115 public function testNormalHeaderDecode($header, $value, $decoded) 116 { 117 $hdrs = new Horde_Mime_Headers(); 118 $hdrs->addHeader($header, $value); 119 120 /* @deprecated */ 121 $this->assertEquals( 122 $decoded, 123 $hdrs->getValue($header) 124 ); 125 126 $this->assertEquals( 127 $decoded, 128 $hdrs[$header]->value_single 129 ); 130 } 131 132 public function normalHeaderDecodeProvider() 133 { 134 return array( 135 array( 136 'Test', 137 '=?iso-8859-15?b?VmVyc2nzbg==?=', 138 'Versión' 139 ), 140 array( 141 'To', 142 '=?utf-8?B?IklsZ2EginVwbGluc2thIg==?= <foo@example.com>', 143 'Ilga Šuplinska <foo@example.com>' 144 ), 145 array( 146 'From', 147 'Firstname =?utf-8?b?U2Vjb25kw5Es?= Third <correct.email@host.com>', 148 '"Firstname SecondÑ, Third" <correct.email@host.com>' 149 ), 150 array( 151 'From', 152 '=?utf-8?B?TGFuZXNza29nLCBKw7hyZ2Vu?= <Jorgen.Lanesskog@marinit.no>', 153 '"Lanesskog, Jørgen" <Jorgen.Lanesskog@marinit.no>' 154 ) 155 ); 156 } 157 158 /** 159 * @dataProvider contentParamHeaderDecodeProvider 160 */ 161 public function testContentParamHeaderDecode( 162 $header, $value, $decode_value, $decode_params 163 ) 164 { 165 $hdrs = new Horde_Mime_Headers(); 166 $hdrs->addHeader($header, $value); 167 168 $this->assertEquals( 169 $decode_value, 170 $hdrs[$header]->value 171 ); 172 173 ksort($decode_params); 174 $params = $hdrs[$header]->params; 175 ksort($params); 176 177 $this->assertEquals( 178 $decode_params, 179 $params 180 ); 181 } 182 183 public function contentParamHeaderDecodeProvider() 184 { 185 return array( 186 array( 187 'Content-Type', 188 'text/plain; name="=?iso-8859-15?b?VmVyc2nzbg==?="', 189 'text/plain', 190 array( 191 'name' => 'Versión' 192 ) 193 ), 194 array( 195 'Content-Disposition', 196 "attachment; size=147502;\n filename*=utf-8''Factura%20n%C2%BA%2010.pdf", 197 'attachment', 198 array( 199 'size' => '147502', 200 'filename' => 'Factura nº 10.pdf' 201 ) 202 ) 203 ); 204 } 205 206 /** 207 * @dataProvider headerAutoDetectCharsetProvider 208 */ 209 public function testHeaderAutoDetectCharset($header, $value, $decoded) 210 { 211 $hdrs = Horde_Mime_Headers::parseHeaders($value); 212 213 /* @deprecated */ 214 $this->assertEquals( 215 $decoded, 216 $hdrs->getValue($header) 217 ); 218 219 $this->assertEquals( 220 $decoded, 221 $hdrs[$header]->value_single 222 ); 223 } 224 225 public function headerAutoDetectCharsetProvider() 226 { 227 return array( 228 array( 229 'Test', 230 // This string is in Windows-1252 231 'Test: ' . base64_decode('UnVubmVyc5IgQWxlcnQh='), 232 'Runners’ Alert!' 233 ) 234 ); 235 } 236 237 /** 238 * @dataProvider headerEncodeProvider 239 */ 240 public function testHeaderEncode( 241 $header, $values, $charset, $encoded 242 ) 243 { 244 $hdrs = new Horde_Mime_Headers(); 245 foreach ($values as $val) { 246 $hdrs->addHeader($header, $val); 247 } 248 249 $hdr_encode = $hdrs[$header]->sendEncode(array( 250 'charset' => $charset 251 )); 252 253 $this->assertEquals( 254 $encoded, 255 $hdr_encode 256 ); 257 258 $hdr_array = $hdrs->toArray(array( 259 'charset' => $charset 260 )); 261 262 $this->assertEquals( 263 (count($encoded) > 1) ? $encoded : reset($encoded), 264 $hdr_array[$header] 265 ); 266 } 267 268 public function headerEncodeProvider() 269 { 270 return array( 271 /* Single address header */ 272 array( 273 'To', 274 array( 275 'Empfänger <recipient@example.com>' 276 ), 277 'iso-8859-1', 278 array( 279 '=?iso-8859-1?b?RW1wZuRuZ2Vy?= <recipient@example.com>' 280 ) 281 ), 282 /* Multiple address header */ 283 array( 284 'Resent-To', 285 array( 286 'Empfänger <recipient@example.com>', 287 'Foo <foo@example.com>' 288 ), 289 'iso-8859-1', 290 array( 291 '=?iso-8859-1?b?RW1wZuRuZ2Vy?= <recipient@example.com>', 292 'Foo <foo@example.com>' 293 ) 294 ), 295 /* Bug #13814 */ 296 array( 297 'Content-Description', 298 array( 299 'AüA' 300 ), 301 'utf-8', 302 array( 303 '=?utf-8?b?QcO8QQ==?=' 304 ) 305 ) 306 ); 307 } 308 309 public function testMultipleContentType() 310 { 311 $hdrs = Horde_Mime_Headers::parseHeaders( 312 "Content-Type: multipart/mixed\n" . 313 "Content-Type: multipart/mixed\n" 314 ); 315 316 /* @deprecated */ 317 $this->assertInternalType( 318 'string', 319 $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_BASE) 320 ); 321 322 $this->assertInternalType( 323 'string', 324 $hdrs['content-type']->value 325 ); 326 } 327 328 /** 329 * @dataProvider multivalueHeadersProvider 330 */ 331 public function testMultivalueHeaders($header, $in, $expected) 332 { 333 $hdrs = Horde_Mime_Headers::parseHeaders($in); 334 335 /* @deprecated */ 336 $this->assertEquals( 337 $expected, 338 $hdrs->getValue($header) 339 ); 340 341 $this->assertEquals( 342 $expected, 343 $hdrs['to']->value 344 ); 345 } 346 347 public function multivalueHeadersProvider() 348 { 349 $expected = 'recipient1@example.com, recipient2@example.com'; 350 351 return array( 352 array( 353 'To', 354 'To: recipient1@example.com, recipient2@example.com', 355 $expected 356 ), 357 array( 358 'To', 359 "To: recipient1@example.com\nTo: recipient2@example.com", 360 $expected 361 ) 362 ); 363 } 364 365 /** 366 * @dataProvider addHeaderWithGroupProvider 367 */ 368 public function testAddHeaderWithGroup($header, $email) 369 { 370 $rfc822 = new Horde_Mail_Rfc822(); 371 $ob = $rfc822->parseAddressList($email); 372 373 $hdrs = new Horde_Mime_Headers(); 374 $hdrs->addHeader($header, $ob); 375 376 /* @deprecated */ 377 $this->assertEquals( 378 $email, 379 $hdrs->getValue($header) 380 ); 381 382 $this->assertEquals( 383 $email, 384 $hdrs[$header]->value 385 ); 386 } 387 388 public function addHeaderWithGroupProvider() 389 { 390 return array( 391 array( 392 'To', 393 'Test: foo@example.com, bar@example.com;' 394 ) 395 ); 396 } 397 398 /** 399 * @dataProvider unencodeMimeHeaderProvider 400 */ 401 public function testUnencodedMimeHeader($header, $in, $decoded) 402 { 403 $hdrs = Horde_Mime_Headers::parseHeaders($in); 404 405 /* @deprecated */ 406 $this->assertEquals( 407 $decoded, 408 $hdrs->getValue($header) 409 ); 410 411 $this->assertEquals( 412 $decoded, 413 $hdrs[$header]->value 414 ); 415 } 416 417 public function unencodeMimeHeaderProvider() 418 { 419 return array( 420 array( 421 'From', 422 // The header is base64 encoded to preserve charset data. 423 base64_decode('RnJvbTogwqkgVklBR1JBIMKuIE9mZmljaWFsIFNpdGUgPGZvb0BleGFtcGxlLmNvbT4='), 424 '© VIAGRA ® Official Site <foo@example.com>' 425 ) 426 ); 427 } 428 429 /** 430 * @dataProvider parseContentDispositionHeaderWithUtf8DataProvider 431 */ 432 public function testParseContentDispositionHeaderWithUtf8Data( 433 $header, $parameter, $msg, $value 434 ) 435 { 436 $hdrs = Horde_Mime_Headers::parseHeaders($msg); 437 438 /* @deprecated */ 439 $cd_params = $hdrs->getValue( 440 $header, 441 $hdrs::VALUE_PARAMS 442 ); 443 $this->assertEquals( 444 $value, 445 $cd_params[$parameter] 446 ); 447 448 $this->assertEquals( 449 $value, 450 $hdrs[$header]->params[$parameter] 451 ); 452 } 453 454 public function parseContentDispositionHeaderWithUtf8DataProvider() 455 { 456 return array( 457 array( 458 'content-disposition', 459 'filename', 460 file_get_contents(__DIR__ . '/fixtures/sample_msg_eai.txt'), 461 'blåbærsyltetøy' 462 ) 463 ); 464 } 465 466 public function testCaseInsensitiveContentParameters() 467 { 468 $hdr = 'Content-Type: multipart/mixed; BOUNDARY="foo"'; 469 $hdrs = Horde_Mime_Headers::parseHeaders($hdr); 470 471 /* @deprecated */ 472 $c_params = $hdrs->getValue( 473 'Content-Type', 474 $hdrs::VALUE_PARAMS 475 ); 476 $this->assertEquals( 477 'foo', 478 $c_params['boundary'] 479 ); 480 481 $this->assertEquals( 482 'foo', 483 $hdrs['content-type']['boundary'] 484 ); 485 } 486 487 public function testParseEaiAddresses() 488 { 489 /* Simple message. */ 490 $msg = file_get_contents(__DIR__ . '/fixtures/sample_msg_eai_2.txt'); 491 $hdrs = Horde_Mime_Headers::parseHeaders($msg); 492 493 /* @deprecated */ 494 $this->assertEquals( 495 'Jøran Øygårdvær <jøran@example.com>', 496 $hdrs->getValue('from') 497 ); 498 499 $this->assertEquals( 500 'Jøran Øygårdvær <jøran@example.com>', 501 $hdrs['from']->value 502 ); 503 504 /* Message with EAI addresses in 2 fields, and another header that 505 * contains a string (that looks like an address). */ 506 $msg = file_get_contents(__DIR__ . '/fixtures/sample_msg_eai_4.txt'); 507 $hdrs = Horde_Mime_Headers::parseHeaders($msg); 508 509 /* @deprecated */ 510 $this->assertEquals( 511 'Jøran Øygårdvær <jøran@example.com>', 512 $hdrs->getValue('from') 513 ); 514 $this->assertEquals( 515 'Jøran Øygårdvær <jøran@example.com>', 516 $hdrs->getValue('cc') 517 ); 518 $this->assertEquals( 519 'Jøran Øygårdvær <jøran@example.com>', 520 $hdrs->getValue('signed-off-by') 521 ); 522 523 $this->assertEquals( 524 'Jøran Øygårdvær <jøran@example.com>', 525 $hdrs['from']->value 526 ); 527 $this->assertEquals( 528 'Jøran Øygårdvær <jøran@example.com>', 529 $hdrs['cc']->value 530 ); 531 $this->assertEquals( 532 'Jøran Øygårdvær <jøran@example.com>', 533 $hdrs['signed-off-by']->value_single 534 ); 535 } 536 537 /** 538 * @dataProvider undisclosedHeaderParsingProvider 539 */ 540 public function testUndisclosedHeaderParsing($header, $value) 541 { 542 $hdrs = new Horde_Mime_Headers(); 543 $hdrs->addHeader($header, $value); 544 545 /* @deprecated */ 546 $this->assertEquals( 547 '', 548 $hdrs->getValue($header) 549 ); 550 551 $this->assertEquals( 552 '', 553 $hdrs[$header]->value 554 ); 555 } 556 557 public function undisclosedHeaderParsingProvider() 558 { 559 return array( 560 array('To', 'undisclosed-recipients'), 561 array('To', 'undisclosed-recipients:'), 562 array('To', 'undisclosed-recipients:;') 563 ); 564 } 565 566 public function testMultipleToAddresses() 567 { 568 $msg = file_get_contents(__DIR__ . '/fixtures/multiple_to.txt'); 569 $hdrs = Horde_Mime_Headers::parseHeaders($msg); 570 571 /* @deprecated */ 572 $this->assertNotEmpty($hdrs->getValue('To')); 573 574 $this->assertNotEmpty($hdrs['To']->value); 575 } 576 577 public function testBug12189() 578 { 579 $msg = file_get_contents(__DIR__ . '/fixtures/header_trailing_ws.txt'); 580 $hdrs = Horde_Mime_Headers::parseHeaders($msg); 581 582 /* @deprecated */ 583 $this->assertNotNull($hdrs->getValue('From')); 584 585 $this->assertNotNull($hdrs['From']->value); 586 } 587 588 public function testParseHeadersGivingStreamResource() 589 { 590 $fp = fopen(__DIR__ . '/fixtures/multiple_to.txt', 'r'); 591 $hdrs = Horde_Mime_Headers::parseHeaders($fp); 592 fclose($fp); 593 594 /* @deprecated */ 595 $this->assertNotEmpty($hdrs->getValue('To')); 596 597 $this->assertNotEmpty($hdrs['To']->value); 598 } 599 600 public function testParseHeadersGivingHordeStreamObject() 601 { 602 $stream = new Horde_Stream_Existing(array( 603 'stream' => fopen(__DIR__ . '/fixtures/multiple_to.txt', 'r') 604 )); 605 $hdrs = Horde_Mime_Headers::parseHeaders($stream); 606 607 /* @deprecated */ 608 $this->assertNotEmpty($hdrs->getValue('To')); 609 610 $this->assertNotEmpty($hdrs['To']); 611 } 612 613 public function testParseHeadersBlankSubject() 614 { 615 $stream = new Horde_Stream_Existing(array( 616 'stream' => fopen(__DIR__ . '/fixtures/blank_subject.txt', 'r') 617 )); 618 $hdrs = Horde_Mime_Headers::parseHeaders($stream); 619 620 /* @deprecated */ 621 $this->assertNotEmpty($hdrs->getValue('To')); 622 623 $this->assertNotEmpty($hdrs['To']); 624 } 625 626 /** 627 * @dataProvider multiplePriorityHeadersProvider 628 */ 629 public function testMultiplePriorityHeaders($header, $data, $value) 630 { 631 $hdrs = Horde_Mime_Headers::parseHeaders($data); 632 633 /* @deprecated */ 634 $this->assertInternalType( 635 'string', 636 $hdrs->getValue($header, Horde_Mime_Headers::VALUE_BASE) 637 ); 638 $this->assertEquals( 639 $value, 640 $hdrs->getValue($header) 641 ); 642 643 $this->assertInternalType( 644 'string', 645 $hdrs[$header]->value_single 646 ); 647 $this->assertEquals( 648 $value, 649 $hdrs[$header]->value_single 650 ); 651 652 } 653 654 public function multiplePriorityHeadersProvider() 655 { 656 return array( 657 array( 658 'Importance', 659 "Importance: High\nImportance: Low\n", 660 'High' 661 ), 662 array( 663 'X-priority', 664 "X-Priority: 1\nX-priority: 5\n", 665 '1' 666 ) 667 ); 668 } 669 670 public function testInvalidHeaderParsing() 671 { 672 $data = file_get_contents(__DIR__ . '/fixtures/invalid_hdr.txt'); 673 674 $hdrs = Horde_Mime_Headers::parseHeaders($data); 675 676 $this->assertEquals( 677 'foo@example.com', 678 strval($hdrs['to']) 679 ); 680 681 $this->assertNull($hdrs['From test2@example.com Sun Apr 5 09']); 682 } 683 684 /** 685 * @dataProvider addHeaderObProvider 686 */ 687 public function testAddHeaderOb($ob, $valid) 688 { 689 $hdrs = new Horde_Mime_Headers(); 690 691 try { 692 $hdrs->addHeaderOb($ob, true); 693 if (!$valid) { 694 $this->fail(); 695 } 696 } catch (InvalidArgumentException $e) { 697 if ($valid) { 698 $this->fail(); 699 } 700 return; 701 } 702 703 $this->assertEquals( 704 $ob, 705 $hdrs[$ob->name] 706 ); 707 } 708 709 public function addHeaderObProvider() 710 { 711 return array( 712 array( 713 new Horde_Mime_Headers_Addresses('To', 'foo@example.com'), 714 true 715 ), 716 array( 717 new Horde_Mime_Headers_Element_Single('To', 'foo@example.com'), 718 false 719 ) 720 ); 721 } 722 723 /** 724 * @dataProvider headerGenerationProvider 725 */ 726 public function testHeaderGeneration($label, $data, $class) 727 { 728 $hdrs = new Horde_Mime_Headers(); 729 730 $this->assertNull($hdrs[$label]); 731 732 $hdrs->addHeader($label, $data); 733 734 $ob = $hdrs[$label]; 735 736 $this->assertNotNull($ob); 737 $this->assertInstanceOf($class, $ob); 738 } 739 740 public function headerGenerationProvider() 741 { 742 return array( 743 array( 744 'content-disposition', 745 'inline', 746 'Horde_Mime_Headers_ContentParam_ContentDisposition' 747 ), 748 array( 749 'content-language', 750 'en', 751 'Horde_Mime_Headers_ContentLanguage' 752 ), 753 array( 754 'content-type', 755 'text/plain', 756 'Horde_Mime_Headers_ContentParam_ContentType' 757 ) 758 ); 759 } 760 761 public function testBug14381() 762 { 763 $hdrs = new Horde_Mime_Headers(); 764 $hdrs->addHeader('Date', 'Sat, 22 May 2016 01:41:04 0000'); 765 $this->assertEquals($hdrs->getValue('Date'), 'Sat, 22 May 2016 01:41:04 +0000'); 766 767 $hdrs = new Horde_Mime_Headers(); 768 $hdrs->addHeader('Date', 'Sat, 22 May 2016 01:41:04 +0000'); 769 $this->assertEquals($hdrs->getValue('Date'), 'Sat, 22 May 2016 01:41:04 +0000'); 770 } 771 772} 773