1
0

提交代码

This commit is contained in:
2020-08-06 14:50:07 +08:00
parent 9d0d5f4be9
commit d7a848c824
11299 changed files with 1321854 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
abstract class AbstractMimeTypeGuesserTest extends TestCase
{
public static function tearDownAfterClass(): void
{
$path = __DIR__.'/Fixtures/mimetypes/to_delete';
if (file_exists($path)) {
@chmod($path, 0666);
@unlink($path);
}
}
abstract protected function getGuesser(): MimeTypeGuesserInterface;
public function testGuessImageWithoutExtension()
{
if (!$this->getGuesser()->isGuesserSupported()) {
$this->markTestSkipped('Guesser is not supported');
}
$this->assertEquals('image/gif', $this->getGuesser()->guessMimeType(__DIR__.'/Fixtures/mimetypes/test'));
}
public function testGuessImageWithDirectory()
{
if (!$this->getGuesser()->isGuesserSupported()) {
$this->markTestSkipped('Guesser is not supported');
}
$this->expectException('\InvalidArgumentException');
$this->getGuesser()->guessMimeType(__DIR__.'/Fixtures/mimetypes/directory');
}
public function testGuessImageWithKnownExtension()
{
if (!$this->getGuesser()->isGuesserSupported()) {
$this->markTestSkipped('Guesser is not supported');
}
$this->assertEquals('image/gif', $this->getGuesser()->guessMimeType(__DIR__.'/Fixtures/mimetypes/test.gif'));
}
public function testGuessFileWithUnknownExtension()
{
if (!$this->getGuesser()->isGuesserSupported()) {
$this->markTestSkipped('Guesser is not supported');
}
$this->assertEquals('application/octet-stream', $this->getGuesser()->guessMimeType(__DIR__.'/Fixtures/mimetypes/.unknownextension'));
}
public function testGuessWithIncorrectPath()
{
if (!$this->getGuesser()->isGuesserSupported()) {
$this->markTestSkipped('Guesser is not supported');
}
$this->expectException('\InvalidArgumentException');
$this->getGuesser()->guessMimeType(__DIR__.'/Fixtures/mimetypes/not_here');
}
public function testGuessWithNonReadablePath()
{
if (!$this->getGuesser()->isGuesserSupported()) {
$this->markTestSkipped('Guesser is not supported');
}
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Can not verify chmod operations on Windows');
}
if (!getenv('USER') || 'root' === getenv('USER')) {
$this->markTestSkipped('This test will fail if run under superuser');
}
$path = __DIR__.'/Fixtures/mimetypes/to_delete';
touch($path);
@chmod($path, 0333);
if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) {
$this->expectException('\InvalidArgumentException');
$this->getGuesser()->guessMimeType($path);
} else {
$this->markTestSkipped('Can not verify chmod operations, change of file permissions failed');
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\NamedAddress;
class AddressTest extends TestCase
{
public function testConstructor()
{
$a = new Address('fabien@symfonï.com');
$this->assertEquals('fabien@symfonï.com', $a->getAddress());
$this->assertEquals('fabien@xn--symfon-nwa.com', $a->toString());
$this->assertEquals('fabien@xn--symfon-nwa.com', $a->getEncodedAddress());
}
public function testConstructorWithInvalidAddress()
{
$this->expectException(\InvalidArgumentException::class);
new Address('fab pot@symfony.com');
}
public function testCreate()
{
$this->assertSame($a = new Address('fabien@symfony.com'), Address::create($a));
$this->assertSame($b = new NamedAddress('helene@symfony.com', 'Helene'), Address::create($b));
$this->assertEquals($a, Address::create('fabien@symfony.com'));
}
public function testCreateWrongArg()
{
$this->expectException(\InvalidArgumentException::class);
Address::create(new \stdClass());
}
public function testCreateArray()
{
$fabien = new Address('fabien@symfony.com');
$helene = new NamedAddress('helene@symfony.com', 'Helene');
$this->assertSame([$fabien, $helene], Address::createArray([$fabien, $helene]));
$this->assertEquals([$fabien], Address::createArray(['fabien@symfony.com']));
}
public function testCreateArrayWrongArg()
{
$this->expectException(\InvalidArgumentException::class);
Address::createArray([new \stdClass()]);
}
}

View File

@@ -0,0 +1,87 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\CharacterStream;
class CharacterStreamTest extends TestCase
{
public function testReadCharactersAreInTact()
{
$stream = new CharacterStream(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE));
$stream->write(pack('C*',
0xD0, 0xBB,
0xD1, 0x8E,
0xD0, 0xB1,
0xD1, 0x8B,
0xD1, 0x85
));
$this->assertSame(pack('C*', 0xD0, 0x94), $stream->read(1));
$this->assertSame(pack('C*', 0xD0, 0xB6, 0xD0, 0xBE), $stream->read(2));
$this->assertSame(pack('C*', 0xD0, 0xBB), $stream->read(1));
$this->assertSame(pack('C*', 0xD1, 0x8E, 0xD0, 0xB1, 0xD1, 0x8B), $stream->read(3));
$this->assertSame(pack('C*', 0xD1, 0x85), $stream->read(1));
$this->assertNull($stream->read(1));
}
public function testCharactersCanBeReadAsByteArrays()
{
$stream = new CharacterStream(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE));
$stream->write(pack('C*',
0xD0, 0xBB,
0xD1, 0x8E,
0xD0, 0xB1,
0xD1, 0x8B,
0xD1, 0x85
));
$this->assertEquals([0xD0, 0x94], $stream->readBytes(1));
$this->assertEquals([0xD0, 0xB6, 0xD0, 0xBE], $stream->readBytes(2));
$this->assertEquals([0xD0, 0xBB], $stream->readBytes(1));
$this->assertEquals([0xD1, 0x8E, 0xD0, 0xB1, 0xD1, 0x8B], $stream->readBytes(3));
$this->assertEquals([0xD1, 0x85], $stream->readBytes(1));
$this->assertNull($stream->readBytes(1));
}
public function testRequestingLargeCharCountPastEndOfStream()
{
$stream = new CharacterStream(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE));
$this->assertSame(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE), $stream->read(100));
$this->assertNull($stream->read(1));
}
public function testRequestingByteArrayCountPastEndOfStream()
{
$stream = new CharacterStream(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE));
$this->assertEquals([0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE], $stream->readBytes(100));
$this->assertNull($stream->readBytes(1));
}
public function testPointerOffsetCanBeSet()
{
$stream = new CharacterStream(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE));
$this->assertSame(pack('C*', 0xD0, 0x94), $stream->read(1));
$stream->setPointer(0);
$this->assertSame(pack('C*', 0xD0, 0x94), $stream->read(1));
$stream->setPointer(2);
$this->assertSame(pack('C*', 0xD0, 0xBE), $stream->read(1));
}
public function testAlgorithmWithFixedWidthCharsets()
{
$stream = new CharacterStream(pack('C*', 0xD1, 0x8D, 0xD0, 0xBB, 0xD0, 0xB0));
$this->assertSame(pack('C*', 0xD1, 0x8D), $stream->read(1));
$this->assertSame(pack('C*', 0xD0, 0xBB), $stream->read(1));
$this->assertSame(pack('C*', 0xD0, 0xB0), $stream->read(1));
$this->assertNull($stream->read(1));
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Mime\DependencyInjection\AddMimeTypeGuesserPass;
use Symfony\Component\Mime\FileinfoMimeTypeGuesser;
use Symfony\Component\Mime\MimeTypes;
class AddMimeTypeGuesserPassTest extends TestCase
{
public function testTags()
{
$container = new ContainerBuilder();
$container->addCompilerPass(new AddMimeTypeGuesserPass());
$definition = new Definition(FileinfoMimeTypeGuesser::class);
$definition->addArgument('/path/to/magic/file');
$definition->addTag('mime.mime_type_guesser');
$container->setDefinition('some_mime_type_guesser', $definition->setPublic(true));
$container->register('mime_types', MimeTypes::class)->setPublic(true);
$container->compile();
$router = $container->getDefinition('mime_types');
$calls = $router->getMethodCalls();
$this->assertCount(1, $calls);
$this->assertEquals('registerGuesser', $calls[0][0]);
$this->assertEquals(new Reference('some_mime_type_guesser'), $calls[0][1][0]);
}
}

389
vendor/symfony/mime/Tests/EmailTest.php vendored Normal file
View File

@@ -0,0 +1,389 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\NamedAddress;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\AlternativePart;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
use Symfony\Component\Mime\Part\Multipart\RelatedPart;
use Symfony\Component\Mime\Part\TextPart;
class EmailTest extends TestCase
{
public function testSubject()
{
$e = new Email();
$e->subject('Subject');
$this->assertEquals('Subject', $e->getSubject());
}
public function testDate()
{
$e = new Email();
$e->date($d = new \DateTimeImmutable());
$this->assertSame($d, $e->getDate());
}
public function testReturnPath()
{
$e = new Email();
$e->returnPath('fabien@symfony.com');
$this->assertEquals(new Address('fabien@symfony.com'), $e->getReturnPath());
}
public function testSender()
{
$e = new Email();
$e->sender('fabien@symfony.com');
$this->assertEquals(new Address('fabien@symfony.com'), $e->getSender());
$e->sender($fabien = new Address('fabien@symfony.com'));
$this->assertSame($fabien, $e->getSender());
}
public function testFrom()
{
$e = new Email();
$helene = new Address('helene@symfony.com');
$thomas = new NamedAddress('thomas@symfony.com', 'Thomas');
$caramel = new Address('caramel@symfony.com');
$this->assertSame($e, $e->from('fabien@symfony.com', $helene, $thomas));
$v = $e->getFrom();
$this->assertCount(3, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertSame($e, $e->addFrom('lucas@symfony.com', $caramel));
$v = $e->getFrom();
$this->assertCount(5, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertEquals(new Address('lucas@symfony.com'), $v[3]);
$this->assertSame($caramel, $v[4]);
$e = new Email();
$e->addFrom('lucas@symfony.com', $caramel);
$this->assertCount(2, $e->getFrom());
$e = new Email();
$e->from('lucas@symfony.com');
$e->from($caramel);
$this->assertSame([$caramel], $e->getFrom());
}
public function testReplyTo()
{
$e = new Email();
$helene = new Address('helene@symfony.com');
$thomas = new NamedAddress('thomas@symfony.com', 'Thomas');
$caramel = new Address('caramel@symfony.com');
$this->assertSame($e, $e->replyTo('fabien@symfony.com', $helene, $thomas));
$v = $e->getReplyTo();
$this->assertCount(3, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertSame($e, $e->addReplyTo('lucas@symfony.com', $caramel));
$v = $e->getReplyTo();
$this->assertCount(5, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertEquals(new Address('lucas@symfony.com'), $v[3]);
$this->assertSame($caramel, $v[4]);
$e = new Email();
$e->addReplyTo('lucas@symfony.com', $caramel);
$this->assertCount(2, $e->getReplyTo());
$e = new Email();
$e->replyTo('lucas@symfony.com');
$e->replyTo($caramel);
$this->assertSame([$caramel], $e->getReplyTo());
}
public function testTo()
{
$e = new Email();
$helene = new Address('helene@symfony.com');
$thomas = new NamedAddress('thomas@symfony.com', 'Thomas');
$caramel = new Address('caramel@symfony.com');
$this->assertSame($e, $e->to('fabien@symfony.com', $helene, $thomas));
$v = $e->getTo();
$this->assertCount(3, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertSame($e, $e->addTo('lucas@symfony.com', $caramel));
$v = $e->getTo();
$this->assertCount(5, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertEquals(new Address('lucas@symfony.com'), $v[3]);
$this->assertSame($caramel, $v[4]);
$e = new Email();
$e->addTo('lucas@symfony.com', $caramel);
$this->assertCount(2, $e->getTo());
$e = new Email();
$e->to('lucas@symfony.com');
$e->to($caramel);
$this->assertSame([$caramel], $e->getTo());
}
public function testCc()
{
$e = new Email();
$helene = new Address('helene@symfony.com');
$thomas = new NamedAddress('thomas@symfony.com', 'Thomas');
$caramel = new Address('caramel@symfony.com');
$this->assertSame($e, $e->cc('fabien@symfony.com', $helene, $thomas));
$v = $e->getCc();
$this->assertCount(3, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertSame($e, $e->addCc('lucas@symfony.com', $caramel));
$v = $e->getCc();
$this->assertCount(5, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertEquals(new Address('lucas@symfony.com'), $v[3]);
$this->assertSame($caramel, $v[4]);
$e = new Email();
$e->addCc('lucas@symfony.com', $caramel);
$this->assertCount(2, $e->getCc());
$e = new Email();
$e->cc('lucas@symfony.com');
$e->cc($caramel);
$this->assertSame([$caramel], $e->getCc());
}
public function testBcc()
{
$e = new Email();
$helene = new Address('helene@symfony.com');
$thomas = new NamedAddress('thomas@symfony.com', 'Thomas');
$caramel = new Address('caramel@symfony.com');
$this->assertSame($e, $e->bcc('fabien@symfony.com', $helene, $thomas));
$v = $e->getBcc();
$this->assertCount(3, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertSame($e, $e->addBcc('lucas@symfony.com', $caramel));
$v = $e->getBcc();
$this->assertCount(5, $v);
$this->assertEquals(new Address('fabien@symfony.com'), $v[0]);
$this->assertSame($helene, $v[1]);
$this->assertSame($thomas, $v[2]);
$this->assertEquals(new Address('lucas@symfony.com'), $v[3]);
$this->assertSame($caramel, $v[4]);
$e = new Email();
$e->addBcc('lucas@symfony.com', $caramel);
$this->assertCount(2, $e->getBcc());
$e = new Email();
$e->bcc('lucas@symfony.com');
$e->bcc($caramel);
$this->assertSame([$caramel], $e->getBcc());
}
public function testPriority()
{
$e = new Email();
$this->assertEquals(3, $e->getPriority());
$e->priority(1);
$this->assertEquals(1, $e->getPriority());
$e->priority(10);
$this->assertEquals(5, $e->getPriority());
$e->priority(-10);
$this->assertEquals(1, $e->getPriority());
}
public function testGenerateBodyThrowsWhenEmptyBody()
{
$this->expectException(\LogicException::class);
(new Email())->getBody();
}
public function testGetBody()
{
$e = new Email();
$e->setBody($text = new TextPart('text content'));
$this->assertEquals($text, $e->getBody());
}
public function testGenerateBody()
{
$text = new TextPart('text content');
$html = new TextPart('html content', 'utf-8', 'html');
$att = new DataPart($file = fopen(__DIR__.'/Fixtures/mimetypes/test', 'r'));
$img = new DataPart($image = fopen(__DIR__.'/Fixtures/mimetypes/test.gif', 'r'), 'test.gif');
$e = new Email();
$e->text('text content');
$this->assertEquals($text, $e->getBody());
$this->assertEquals('text content', $e->getTextBody());
$e = new Email();
$e->html('html content');
$this->assertEquals($html, $e->getBody());
$this->assertEquals('html content', $e->getHtmlBody());
$e = new Email();
$e->html('html content');
$e->text('text content');
$this->assertEquals(new AlternativePart($text, $html), $e->getBody());
$e = new Email();
$e->html('html content', 'iso-8859-1');
$e->text('text content', 'iso-8859-1');
$this->assertEquals('iso-8859-1', $e->getTextCharset());
$this->assertEquals('iso-8859-1', $e->getHtmlCharset());
$this->assertEquals(new AlternativePart(new TextPart('text content', 'iso-8859-1'), new TextPart('html content', 'iso-8859-1', 'html')), $e->getBody());
$e = new Email();
$e->attach($file);
$e->text('text content');
$this->assertEquals(new MixedPart($text, $att), $e->getBody());
$e = new Email();
$e->attach($file);
$e->html('html content');
$this->assertEquals(new MixedPart($html, $att), $e->getBody());
$e = new Email();
$e->attach($file);
$this->assertEquals(new MixedPart($att), $e->getBody());
$e = new Email();
$e->html('html content');
$e->text('text content');
$e->attach($file);
$this->assertEquals(new MixedPart(new AlternativePart($text, $html), $att), $e->getBody());
$e = new Email();
$e->html('html content');
$e->text('text content');
$e->attach($file);
$e->attach($image, 'test.gif');
$this->assertEquals(new MixedPart(new AlternativePart($text, $html), $att, $img), $e->getBody());
$e = new Email();
$e->text('text content');
$e->attach($file);
$e->attach($image, 'test.gif');
$this->assertEquals(new MixedPart($text, $att, $img), $e->getBody());
$e = new Email();
$e->html($content = 'html content <img src="test.gif">');
$e->text('text content');
$e->attach($file);
$e->attach($image, 'test.gif');
$fullhtml = new TextPart($content, 'utf-8', 'html');
$this->assertEquals(new MixedPart(new AlternativePart($text, $fullhtml), $att, $img), $e->getBody());
$e = new Email();
$e->html($content = 'html content <img src="cid:test.gif">');
$e->text('text content');
$e->attach($file);
$e->attach($image, 'test.gif');
$fullhtml = new TextPart($content, 'utf-8', 'html');
$inlinedimg = (new DataPart($image, 'test.gif'))->asInline();
$body = $e->getBody();
$this->assertInstanceOf(MixedPart::class, $body);
$this->assertCount(2, $related = $body->getParts());
$this->assertInstanceOf(RelatedPart::class, $related[0]);
$this->assertEquals($att, $related[1]);
$this->assertCount(2, $parts = $related[0]->getParts());
$this->assertInstanceOf(AlternativePart::class, $parts[0]);
$generatedHtml = $parts[0]->getParts()[1];
$this->assertStringContainsString('cid:'.$parts[1]->getContentId(), $generatedHtml->getBody());
$content = 'html content <img src="cid:test.gif">';
$r = fopen('php://memory', 'r+', false);
fwrite($r, $content);
rewind($r);
$e = new Email();
$e->html($r);
// embedding the same image twice results in one image only in the email
$e->embed($image, 'test.gif');
$e->embed($image, 'test.gif');
$body = $e->getBody();
$this->assertInstanceOf(RelatedPart::class, $body);
// 2 parts only, not 3 (text + embedded image once)
$this->assertCount(2, $parts = $body->getParts());
$this->assertStringMatchesFormat('html content <img src=3D"cid:%s@symfony">', $parts[0]->bodyToString());
}
public function testAttachments()
{
$contents = file_get_contents($name = __DIR__.'/Fixtures/mimetypes/test', 'r');
$att = new DataPart($file = fopen($name, 'r'), 'test');
$inline = (new DataPart($contents, 'test'))->asInline();
$e = new Email();
$e->attach($file, 'test');
$e->embed($contents, 'test');
$this->assertEquals([$att, $inline], $e->getAttachments());
$att = DataPart::fromPath($name, 'test');
$inline = DataPart::fromPath($name, 'test')->asInline();
$e = new Email();
$e->attachFromPath($name);
$e->embedFromPath($name);
$this->assertEquals([$att->bodyToString(), $inline->bodyToString()], array_map(function (DataPart $a) { return $a->bodyToString(); }, $e->getAttachments()));
$this->assertEquals([$att->getPreparedHeaders(), $inline->getPreparedHeaders()], array_map(function (DataPart $a) { return $a->getPreparedHeaders(); }, $e->getAttachments()));
}
public function testSerialize()
{
$r = fopen('php://memory', 'r+', false);
fwrite($r, 'Text content');
rewind($r);
$e = new Email();
$e->from('fabien@symfony.com');
$e->text($r);
$e->html($r);
$contents = file_get_contents($name = __DIR__.'/Fixtures/mimetypes/test', 'r');
$file = fopen($name, 'r');
$e->attach($file, 'test');
$expected = clone $e;
$n = unserialize(serialize($e));
$this->assertEquals($expected->getHeaders(), $n->getHeaders());
$this->assertEquals($e->getBody(), $n->getBody());
}
}

View File

@@ -0,0 +1,158 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Encoder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Encoder\Base64Encoder;
class Base64EncoderTest extends TestCase
{
/*
There's really no point in testing the entire base64 encoding to the
level QP encoding has been tested. base64_encode() has been in PHP for
years.
*/
public function testInputOutputRatioIs3to4Bytes()
{
/*
RFC 2045, 6.8
The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters. Proceeding from left to right, a
24-bit input group is formed by concatenating 3 8bit input groups.
These 24 bits are then treated as 4 concatenated 6-bit groups, each
of which is translated into a single digit in the base64 alphabet.
*/
$encoder = new Base64Encoder();
$this->assertEquals('MTIz', $encoder->encodeString('123'), '3 bytes of input should yield 4 bytes of output');
$this->assertEquals('MTIzNDU2', $encoder->encodeString('123456'), '6 bytes in input should yield 8 bytes of output');
$this->assertEquals('MTIzNDU2Nzg5', $encoder->encodeString('123456789'), '%s: 9 bytes in input should yield 12 bytes of output');
}
public function testPadLength()
{
/*
RFC 2045, 6.8
Special processing is performed if fewer than 24 bits are available
at the end of the data being encoded. A full encoding quantum is
always completed at the end of a body. When fewer than 24 input bits
are available in an input group, zero bits are added (on the right)
to form an integral number of 6-bit groups. Padding at the end of
the data is performed using the "=" character. Since all base64
input is an integral number of octets, only the following cases can
arise: (1) the final quantum of encoding input is an integral
multiple of 24 bits; here, the final unit of encoded output will be
an integral multiple of 4 characters with no "=" padding, (2) the
final quantum of encoding input is exactly 8 bits; here, the final
unit of encoded output will be two characters followed by two "="
padding characters, or (3) the final quantum of encoding input is
exactly 16 bits; here, the final unit of encoded output will be three
characters followed by one "=" padding character.
*/
$encoder = new Base64Encoder();
for ($i = 0; $i < 30; ++$i) {
$input = pack('C', random_int(0, 255));
$this->assertRegExp('~^[a-zA-Z0-9/\+]{2}==$~', $encoder->encodeString($input), 'A single byte should have 2 bytes of padding');
}
for ($i = 0; $i < 30; ++$i) {
$input = pack('C*', random_int(0, 255), random_int(0, 255));
$this->assertRegExp('~^[a-zA-Z0-9/\+]{3}=$~', $encoder->encodeString($input), 'Two bytes should have 1 byte of padding');
}
for ($i = 0; $i < 30; ++$i) {
$input = pack('C*', random_int(0, 255), random_int(0, 255), random_int(0, 255));
$this->assertRegExp('~^[a-zA-Z0-9/\+]{4}$~', $encoder->encodeString($input), 'Three bytes should have no padding');
}
}
public function testMaximumLineLengthIs76Characters()
{
/*
The encoded output stream must be represented in lines of no more
than 76 characters each. All line breaks or other characters not
found in Table 1 must be ignored by decoding software.
*/
$input =
'abcdefghijklmnopqrstuvwxyz'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'1234567890'.
'abcdefghijklmnopqrstuvwxyz'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'1234567890'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$output =
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38
'NERUZHSElKS0xNTk9QUVJTVFVWV1hZWjEyMzQ1'."\r\n".//76 *
'Njc4OTBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3'.//38
'h5ekFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFla'."\r\n".//76 *
'MTIzNDU2Nzg5MEFCQ0RFRkdISUpLTE1OT1BRUl'.//38
'NUVVZXWFla'; //48
$encoder = new Base64Encoder();
$this->assertEquals($output, $encoder->encodeString($input), 'Lines should be no more than 76 characters');
}
public function testMaximumLineLengthCanBeSpecified()
{
$input =
'abcdefghijklmnopqrstuvwxyz'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'1234567890'.
'abcdefghijklmnopqrstuvwxyz'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'1234567890'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$output =
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38
'NERUZHSElKS0'."\r\n".//50 *
'xNTk9QUVJTVFVWV1hZWjEyMzQ1Njc4OTBhYmNk'.//38
'ZWZnaGlqa2xt'."\r\n".//50 *
'bm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLTE1OT1'.//38
'BRUlNUVVZXWF'."\r\n".//50 *
'laMTIzNDU2Nzg5MEFCQ0RFRkdISUpLTE1OT1BR'.//38
'UlNUVVZXWFla'; //50 *
$encoder = new Base64Encoder();
$this->assertEquals($output, $encoder->encodeString($input, 'utf-8', 0, 50), 'Lines should be no more than 100 characters');
}
public function testFirstLineLengthCanBeDifferent()
{
$input =
'abcdefghijklmnopqrstuvwxyz'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'1234567890'.
'abcdefghijklmnopqrstuvwxyz'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'1234567890'.
'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$output =
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38
'NERUZHSElKS0xNTk9QU'."\r\n".//57 *
'VJTVFVWV1hZWjEyMzQ1Njc4OTBhYmNkZWZnaGl'.//38
'qa2xtbm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLT'."\r\n".//76 *
'E1OT1BRUlNUVVZXWFlaMTIzNDU2Nzg5MEFCQ0R'.//38
'FRkdISUpLTE1OT1BRUlNUVVZXWFla'; //67
$encoder = new Base64Encoder();
$this->assertEquals($output, $encoder->encodeString($input, 'utf-8', 19), 'First line offset is 19 so first line should be 57 chars long');
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Encoder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Encoder\Base64MimeHeaderEncoder;
class Base64MimeHeaderEncoderTest extends TestCase
{
public function testNameIsB()
{
$this->assertEquals('B', (new Base64MimeHeaderEncoder())->getName());
}
}

View File

@@ -0,0 +1,213 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Encoder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Encoder\QpEncoder;
class QpEncoderTest extends TestCase
{
/* -- RFC 2045, 6.7 --
(1) (General 8bit representation) Any octet, except a CR or
LF that is part of a CRLF line break of the canonical
(standard) form of the data being encoded, may be
represented by an "=" followed by a two digit
hexadecimal representation of the octet's value. The
digits of the hexadecimal alphabet, for this purpose,
are "0123456789ABCDEF". Uppercase letters must be
used; lowercase letters are not allowed. Thus, for
example, the decimal value 12 (US-ASCII form feed) can
be represented by "=0C", and the decimal value 61 (US-
ASCII EQUAL SIGN) can be represented by "=3D". This
rule must be followed except when the following rules
allow an alternative encoding.
*/
public function testPermittedCharactersAreNotEncoded()
{
/* -- RFC 2045, 6.7 --
(2) (Literal representation) Octets with decimal values of
33 through 60 inclusive, and 62 through 126, inclusive,
MAY be represented as the US-ASCII characters which
correspond to those octets (EXCLAMATION POINT through
LESS THAN, and GREATER THAN through TILDE,
respectively).
*/
$encoder = new QpEncoder();
foreach (array_merge(range(33, 60), range(62, 126)) as $ordinal) {
$char = \chr($ordinal);
$this->assertSame($char, $encoder->encodeString($char));
}
}
public function testWhiteSpaceAtLineEndingIsEncoded()
{
/* -- RFC 2045, 6.7 --
(3) (White Space) Octets with values of 9 and 32 MAY be
represented as US-ASCII TAB (HT) and SPACE characters,
respectively, but MUST NOT be so represented at the end
of an encoded line. Any TAB (HT) or SPACE characters
on an encoded line MUST thus be followed on that line
by a printable character. In particular, an "=" at the
end of an encoded line, indicating a soft line break
(see rule #5) may follow one or more TAB (HT) or SPACE
characters. It follows that an octet with decimal
value 9 or 32 appearing at the end of an encoded line
must be represented according to Rule #1. This rule is
necessary because some MTAs (Message Transport Agents,
programs which transport messages from one user to
another, or perform a portion of such transfers) are
known to pad lines of text with SPACEs, and others are
known to remove "white space" characters from the end
of a line. Therefore, when decoding a Quoted-Printable
body, any trailing white space on a line must be
deleted, as it will necessarily have been added by
intermediate transport agents.
*/
$encoder = new QpEncoder();
$HT = \chr(0x09); // 9
$SPACE = \chr(0x20); // 32
// HT
$string = 'a'.$HT.$HT."\r\n".'b';
$this->assertEquals('a'.$HT.'=09'."\r\n".'b', $encoder->encodeString($string));
// SPACE
$string = 'a'.$SPACE.$SPACE."\r\n".'b';
$this->assertEquals('a'.$SPACE.'=20'."\r\n".'b', $encoder->encodeString($string));
}
public function testCRLFIsLeftAlone()
{
/*
(4) (Line Breaks) A line break in a text body, represented
as a CRLF sequence in the text canonical form, must be
represented by a (RFC 822) line break, which is also a
CRLF sequence, in the Quoted-Printable encoding. Since
the canonical representation of media types other than
text do not generally include the representation of
line breaks as CRLF sequences, no hard line breaks
(i.e. line breaks that are intended to be meaningful
and to be displayed to the user) can occur in the
quoted-printable encoding of such types. Sequences
like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely
appear in non-text data represented in quoted-
printable, of course.
Note that many implementations may elect to encode the
local representation of various content types directly
rather than converting to canonical form first,
encoding, and then converting back to local
representation. In particular, this may apply to plain
text material on systems that use newline conventions
other than a CRLF terminator sequence. Such an
implementation optimization is permissible, but only
when the combined canonicalization-encoding step is
equivalent to performing the three steps separately.
*/
$encoder = new QpEncoder();
$string = 'a'."\r\n".'b'."\r\n".'c'."\r\n";
$this->assertEquals($string, $encoder->encodeString($string));
}
public function testLinesLongerThan76CharactersAreSoftBroken()
{
/*
(5) (Soft Line Breaks) The Quoted-Printable encoding
REQUIRES that encoded lines be no more than 76
characters long. If longer lines are to be encoded
with the Quoted-Printable encoding, "soft" line breaks
must be used. An equal sign as the last character on a
encoded line indicates such a non-significant ("soft")
line break in the encoded text.
*/
$encoder = new QpEncoder();
$input = str_repeat('a', 140);
$output = '';
for ($i = 0; $i < 140; ++$i) {
// we read 4 chars at a time (max is 75)
if (18 * 4 /* 72 */ == $i) {
$output .= "=\r\n";
}
$output .= 'a';
}
$this->assertEquals($output, $encoder->encodeString($input));
}
public function testMaxLineLengthCanBeSpecified()
{
$encoder = new QpEncoder();
$input = str_repeat('a', 100);
$output = '';
for ($i = 0; $i < 100; ++$i) {
// we read 4 chars at a time (max is 53)
if (13 * 4 /* 52 */ == $i) {
$output .= "=\r\n";
}
$output .= 'a';
}
$this->assertEquals($output, $encoder->encodeString($input, 'utf-8', 0, 54));
}
public function testBytesBelowPermittedRangeAreEncoded()
{
// According to Rule (1 & 2)
$encoder = new QpEncoder();
foreach (range(0, 32) as $ordinal) {
$char = \chr($ordinal);
$this->assertEquals(sprintf('=%02X', $ordinal), $encoder->encodeString($char));
}
}
public function testDecimalByte61IsEncoded()
{
// According to Rule (1 & 2)
$encoder = new QpEncoder();
$this->assertEquals('=3D', $encoder->encodeString('='));
}
public function testBytesAbovePermittedRangeAreEncoded()
{
// According to Rule (1 & 2)
$encoder = new QpEncoder();
foreach (range(127, 255) as $ordinal) {
$this->assertSame(sprintf('=%02X', $ordinal), $encoder->encodeString(\chr($ordinal), 'iso-8859-1'));
}
}
public function testFirstLineLengthCanBeDifferent()
{
$encoder = new QpEncoder();
$input = str_repeat('a', 140);
$output = '';
for ($i = 0; $i < 140; ++$i) {
// we read 4 chars at a time (max is 54 for the first line and 75 for the second one)
if (13 * 4 == $i || 13 * 4 + 18 * 4 == $i) {
$output .= "=\r\n";
}
$output .= 'a';
}
$this->assertEquals($output, $encoder->encodeString($input, 'utf-8', 22), 'First line should start at offset 22 so can only have max length 54');
}
public function testTextIsPreWrapped()
{
$encoder = new QpEncoder();
$input = str_repeat('a', 70)."\r\n".str_repeat('a', 70)."\r\n".str_repeat('a', 70);
$this->assertEquals($input, $encoder->encodeString($input));
}
}

View File

@@ -0,0 +1,139 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Encoder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Encoder\QpMimeHeaderEncoder;
class QpMimeHeaderEncoderTest extends TestCase
{
public function testNameIsQ()
{
$encoder = new QpMimeHeaderEncoder();
$this->assertEquals('Q', $encoder->getName());
}
public function testSpaceAndTabNeverAppear()
{
/* -- RFC 2047, 4.
Only a subset of the printable ASCII characters may be used in
'encoded-text'. Space and tab characters are not allowed, so that
the beginning and end of an 'encoded-word' are obvious.
*/
$encoder = new QpMimeHeaderEncoder();
$this->assertNotRegExp('~[ \t]~', $encoder->encodeString("a \t b"), 'encoded-words in headers cannot contain LWSP as per RFC 2047.');
}
public function testSpaceIsRepresentedByUnderscore()
{
/* -- RFC 2047, 4.2.
(2) The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be
represented as "_" (underscore, ASCII 95.). (This character may
not pass through some internetwork mail gateways, but its use
will greatly enhance readability of "Q" encoded data with mail
readers that do not support this encoding.) Note that the "_"
always represents hexadecimal 20, even if the SPACE character
occupies a different code position in the character set in use.
*/
$encoder = new QpMimeHeaderEncoder();
$this->assertEquals('a_b', $encoder->encodeString('a b'), 'Spaces can be represented by more readable underscores as per RFC 2047.');
}
public function testEqualsAndQuestionAndUnderscoreAreEncoded()
{
/* -- RFC 2047, 4.2.
(3) 8-bit values which correspond to printable ASCII characters other
than "=", "?", and "_" (underscore), MAY be represented as those
characters. (But see section 5 for restrictions.) In
particular, SPACE and TAB MUST NOT be represented as themselves
within encoded words.
*/
$encoder = new QpMimeHeaderEncoder();
$this->assertEquals('=3D=3F=5F', $encoder->encodeString('=?_'), 'Chars =, ? and _ (underscore) may not appear as per RFC 2047.');
}
public function testParensAndQuotesAreEncoded()
{
/* -- RFC 2047, 5 (2).
A "Q"-encoded 'encoded-word' which appears in a 'comment' MUST NOT
contain the characters "(", ")" or "
*/
$encoder = new QpMimeHeaderEncoder();
$this->assertEquals('=28=22=29', $encoder->encodeString('(")'), 'Chars (, " (DQUOTE) and ) may not appear as per RFC 2047.');
}
public function testOnlyCharactersAllowedInPhrasesAreUsed()
{
/* -- RFC 2047, 5.
(3) As a replacement for a 'word' entity within a 'phrase', for example,
one that precedes an address in a From, To, or Cc header. The ABNF
definition for 'phrase' from RFC 822 thus becomes:
phrase = 1*( encoded-word / word )
In this case the set of characters that may be used in a "Q"-encoded
'encoded-word' is restricted to: <upper and lower case ASCII
letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_"
(underscore, ASCII 95.)>. An 'encoded-word' that appears within a
'phrase' MUST be separated from any adjacent 'word', 'text' or
'special' by 'linear-white-space'.
*/
$allowedBytes = array_merge(
range(\ord('a'), \ord('z')), range(\ord('A'), \ord('Z')),
range(\ord('0'), \ord('9')),
[\ord('!'), \ord('*'), \ord('+'), \ord('-'), \ord('/')]
);
$encoder = new QpMimeHeaderEncoder();
foreach (range(0x00, 0xFF) as $byte) {
$char = pack('C', $byte);
$encodedChar = $encoder->encodeString($char, 'iso-8859-1');
if (\in_array($byte, $allowedBytes)) {
$this->assertEquals($char, $encodedChar, 'Character '.$char.' should not be encoded.');
} elseif (0x20 == $byte) {
// special case
$this->assertEquals('_', $encodedChar, 'Space character should be replaced.');
} else {
$this->assertEquals(sprintf('=%02X', $byte), $encodedChar, 'Byte '.$byte.' should be encoded.');
}
}
}
public function testEqualsNeverAppearsAtEndOfLine()
{
/* -- RFC 2047, 5 (3).
The 'encoded-text' in an 'encoded-word' must be self-contained;
'encoded-text' MUST NOT be continued from one 'encoded-word' to
another. This implies that the 'encoded-text' portion of a "B"
'encoded-word' will be a multiple of 4 characters long; for a "Q"
'encoded-word', any "=" character that appears in the 'encoded-text'
portion will be followed by two hexadecimal characters.
*/
$input = str_repeat('a', 140);
$output = '';
$seq = 0;
for (; $seq < 140; ++$seq) {
// compute the end of line (multiple of 4 chars)
if (18 * 4 === $seq) {
$output .= "\r\n"; // =\r\n
}
$output .= 'a';
}
$encoder = new QpMimeHeaderEncoder();
$this->assertEquals($output, $encoder->encodeString($input));
}
}

View File

@@ -0,0 +1,129 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Encoder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Encoder\Rfc2231Encoder;
class Rfc2231EncoderTest extends TestCase
{
private $rfc2045Token = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+$/D';
/* --
This algorithm is described in RFC 2231, but is barely touched upon except
for mentioning bytes can be represented as their octet values (e.g. %20 for
the SPACE character).
The tests here focus on how to use that representation to always generate text
which matches RFC 2045's definition of "token".
*/
public function testEncodingAsciiCharactersProducesValidToken()
{
$string = '';
foreach (range(0x00, 0x7F) as $octet) {
$char = pack('C', $octet);
$string .= $char;
}
$encoder = new Rfc2231Encoder();
$encoded = $encoder->encodeString($string);
foreach (explode("\r\n", $encoded) as $line) {
$this->assertRegExp($this->rfc2045Token, $line, 'Encoder should always return a valid RFC 2045 token.');
}
}
public function testEncodingNonAsciiCharactersProducesValidToken()
{
$string = '';
foreach (range(0x80, 0xFF) as $octet) {
$char = pack('C', $octet);
$string .= $char;
}
$encoder = new Rfc2231Encoder();
$encoded = $encoder->encodeString($string);
foreach (explode("\r\n", $encoded) as $line) {
$this->assertRegExp($this->rfc2045Token, $line, 'Encoder should always return a valid RFC 2045 token.');
}
}
public function testMaximumLineLengthCanBeSet()
{
$string = '';
for ($x = 0; $x < 200; ++$x) {
$char = 'a';
$string .= $char;
}
$encoder = new Rfc2231Encoder();
$encoded = $encoder->encodeString($string, 'utf-8', 0, 75);
// 72 here and not 75 as we read 4 chars at a time
$this->assertEquals(
str_repeat('a', 72)."\r\n".
str_repeat('a', 72)."\r\n".
str_repeat('a', 56),
$encoded,
'Lines should be wrapped at each 72 characters'
);
}
public function testFirstLineCanHaveShorterLength()
{
$string = '';
for ($x = 0; $x < 200; ++$x) {
$char = 'a';
$string .= $char;
}
$encoder = new Rfc2231Encoder();
$encoded = $encoder->encodeString($string, 'utf-8', 24, 72);
$this->assertEquals(
str_repeat('a', 48)."\r\n".
str_repeat('a', 72)."\r\n".
str_repeat('a', 72)."\r\n".
str_repeat('a', 8),
$encoded,
'First line should be 24 bytes shorter than the others.'
);
}
public function testEncodingAndDecodingSamples()
{
$dir = realpath(__DIR__.'/../Fixtures/samples/charsets');
$sampleFp = opendir($dir);
while (false !== $encoding = readdir($sampleFp)) {
if ('.' == substr($encoding, 0, 1)) {
continue;
}
$encoder = new Rfc2231Encoder();
if (is_dir($dir.'/'.$encoding)) {
$fileFp = opendir($dir.'/'.$encoding);
while (false !== $sampleFile = readdir($fileFp)) {
if ('.' == substr($sampleFile, 0, 1)) {
continue;
}
$text = file_get_contents($dir.'/'.$encoding.'/'.$sampleFile);
$encodedText = $encoder->encodeString($text, $encoding);
$this->assertEquals(
urldecode(implode('', explode("\r\n", $encodedText))), $text,
'Encoded string should decode back to original string for sample '.$dir.'/'.$encoding.'/'.$sampleFile
);
}
closedir($fileFp);
}
}
closedir($sampleFp);
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use Symfony\Component\Mime\FileBinaryMimeTypeGuesser;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
class FileBinaryMimeTypeGuesserTest extends AbstractMimeTypeGuesserTest
{
protected function getGuesser(): MimeTypeGuesserInterface
{
return new FileBinaryMimeTypeGuesser();
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use Symfony\Component\Mime\FileinfoMimeTypeGuesser;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
/**
* @requires extension fileinfo
*/
class FileinfoMimeTypeGuesserTest extends AbstractMimeTypeGuesserTest
{
protected function getGuesser(): MimeTypeGuesserInterface
{
return new FileinfoMimeTypeGuesser();
}
}

View File

@@ -0,0 +1 @@
f

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 B

View File

@@ -0,0 +1,11 @@
ISO-2022-JPは、インターネット上(特に電子メール)などで使われる日本の文字用の文字符号化方式。ISO/IEC 2022のエスケープシーケンスを利用して文字集合を切り替える7ビットのコードであることを特徴とする (アナウンス機能のエスケープシーケンスは省略される)。俗に「JISコード」と呼ばれることもある。
概要
日本語表記への利用が想定されている文字コードであり、日本語の利用されるネットワークにおいて、日本の規格を応用したものである。また文字集合としては、日本語で用いられる漢字、ひらがな、カタカナはもちろん、ラテン文字、ギリシア文字、キリル文字なども含んでおり、学術や産業の分野での利用も考慮たものとなっている。規格名に、ISOの日本語の言語コードであるjaではなく、国・地域名コードのJPが示されているゆえんである。
文字集合としてJIS X 0201のC0集合制御文字、JIS X 0201のラテン文字集合、ISO 646の国際基準版図形文字、JIS X 0208の1978年版JIS C 6226-1978と1983年および1990年版が利用できる。JIS X 0201の片仮名文字集合は利用できない。1986年以降、日本の電子メールで用いられてきたJUNETコードを、村井純・Mark Crispin・Erik van der Poelが1993年にRFC化したもの(RFC 1468)。後にJIS X 0208:1997の附属書2としてJISに規定された。MIMEにおける文字符号化方式の識別用の名前として IANA に登録されている。
なお、符号化の仕様についてはISO/IEC 2022#ISO-2022-JPも参照。
ISO-2022-JPと非標準的拡張使用
「JISコード」または「ISO-2022-JP」というコード名の規定下では、その仕様通りの使用が求められる。しかし、Windows OS上では、実際にはCP932コード (MicrosoftによるShift JISを拡張した亜種。ISO-2022-JP規定外文字が追加されている。による独自拡張の文字を断りなく使うアプリケーションが多い。この例としてInternet ExplorerやOutlook Expressがある。また、EmEditor、秀丸エディタやThunderbirdのようなMicrosoft社以外のWindowsアプリケーションでも同様の場合がある。この場合、ISO-2022-JPの範囲外の文字を使ってしまうと、異なる製品間では未定義不明文字として認識されるか、もしくは文字化けを起こす原因となる。そのため、Windows用の電子メールクライアントであっても独自拡張の文字を使用すると警告を出したり、あえて使えないように制限しているものも存在する。さらにはISO-2022-JPの範囲内であってもCP932は非標準文字FULLWIDTH TILDE等を持つので文字化けの原因になり得る。
また、符号化方式名をISO-2022-JPとしているのに、文字集合としてはJIS X 0212 (いわゆる補助漢字) やJIS X 0201の片仮名文字集合 (いわゆる半角カナ) をも符号化している例があるが、ISO-2022-JPではこれらの文字を許容していない。これらの符号化は独自拡張の実装であり、中にはISO/IEC 2022の仕様に準拠すらしていないものもある[2]。従って受信側の電子メールクライアントがこれらの独自拡張に対応していない場合、その文字あるいはその文字を含む行、時にはテキスト全体が文字化けすることがある。

View File

@@ -0,0 +1,19 @@
Op mat eraus hinnen beschte, rou zënne schaddreg ké. Ké sin Eisen Kaffi prächteg, den haut esou Fielse wa, Well zielen d'Welt am dir. Aus grousse rëschten d'Stroos do, as dat Kléder gewëss d'Kàchen. Schied gehéiert d'Vioule net hu, rou ke zënter Säiten d'Hierz. Ze eise Fletschen mat, gei as gréng d'Lëtzebuerger. Wäit räich no mat.
Säiten d'Liewen aus en. Un gëtt bléit lossen wee, da wéi alle weisen Kolrettchen. Et deser d'Pan d'Kirmes vun, en wuel Benn rëschten méi. En get drem ménger beschte, da wär Stad welle. Nun Dach d'Pied do, mä gét ruffen gehéiert. Ze onser ugedon fir, d'Liewen Plett'len ech no, si Räis wielen bereet wat. Iwer spilt fir jo.
An hin däischter Margréitchen, eng ke Frot brommt, vu den Räis néierens. Da hir Hunn Frot nozegon, rout Fläiß Himmel zum si, net gutt Kaffi Gesträich fu. Vill lait Gaart sou wa, Land Mamm Schuebersonndeg rei do. Gei geet Minutt en, gei d'Leit beschte Kolrettchen et, Mamm fergiess un hun.
Et gutt Heck kommen oft, Lann rëscht rei um, Hunn rëscht schéinste ke der. En lait zielen schnéiwäiss hir, fu rou botze éiweg Minutt, rem fest gudden schaddreg en. Noper bereet Margréitchen mat op, dem denkt d'Leit d'Vioule no, oft ké Himmel Hämmel. En denkt blénken Fréijor net, Gart Schiet d'Natur no wou. No hin Ierd Frot d'Kirmes. Hire aremt un rou, ké den éiweg wielen Milliounen.
Mir si Hunn Blénkeg. Ké get ston derfir d'Kàchen. Haut d'Pan fu ons, dé frou löschteg d'Meereische rei. Sou op wuel Léift. Stret schlon grousse gin hu. Mä denkt d'Leit hinnen net, ké gét haut fort rëscht.
Koum d'Pan hannendrun ass ké, ké den brét Kaffi geplot. Schéi Hären d'Pied fu gét, do d'Mier néierens bei. Rëm päift Hämmel am, wee Engel beschéngt mä. Brommt klinzecht der ke, wa rout jeitzt dén. Get Zalot d'Vioule däischter da, jo fir Bänk päift duerch, bei d'Beem schéinen Plett'len jo. Den haut Faarwen ze, eng en Biereg Kirmesdag, um sin alles Faarwen d'Vioule.
Eng Hunn Schied et, wat wa Frot fest gebotzt. Bei jo bleiwe ruffen Klarinett. Un Feld klinzecht gét, rifft Margréitchen rem ke. Mir dé Noper duurch gewëss, ston sech kille sin en. Gei Stret d'Wise um, Haus Gart wee as. Monn ménger an blo, wat da Gart gefällt Hämmelsbrot.
Brommt geplot och ze, dat wa Räis Well Kaffi. Do get spilt prächteg, as wär kille bleiwe gewalteg. Onser frësch Margréitchen rem ke, blo en huet ugedon. Onser Hemecht wär de, hu eraus d'Sonn dat, eise deser hannendrun da och.
As durch Himmel hun, no fest iw'rem schéinste mir, Hunn séngt Hierz ke zum. Séngt iw'rem d'Natur zum an. Ke wär gutt Grénge. Kënnt gudden prächteg mä rei. Dé dir Blénkeg Klarinett Kolrettchen, da fort muerges d'Kanner wou, main Feld ruffen vu wéi. Da gin esou Zalot gewalteg, gét vill Hemecht blénken dé.
Haut gréng nun et, nei vu Bass gréng d'Gaassen. Fest d'Beem uechter si gin. Oft vu sinn wellen kréien. Et ass lait Zalot schéinen.

View File

@@ -0,0 +1,22 @@
Код одно гринспана руководишь на. Его вы знания движение. Ты две начать
одиночку, сказать основатель удовольствием но миф. Бы какие система тем.
Полностью использует три мы, человек клоунов те нас, бы давать творческую
эзотерическая шеф.
Мог не помнить никакого сэкономленного, две либо какие пишите бы. Должен
компанию кто те, этот заключалась проектировщик не ты. Глупые периоды ты
для. Вам который хороший он. Те любых кремния концентрируются мог,
собирать принадлежите без вы.
Джоэла меньше хорошего вы миф, за тем году разработки. Даже управляющим
руководители был не. Три коде выпускать заботиться ну. То его система
удовольствием безостановочно, или ты главной процессорах. Мы без джоэл
знания получат, статьи остальные мы ещё.
Них русском касается поскольку по, образование должником
систематизированный ну мои. Прийти кандидата университет но нас, для бы
должны никакого, биг многие причин интервьюирования за.
Тем до плиту почему. Вот учёт такие одного бы, об биг разным внешних
промежуток. Вас до какому возможностей безответственный, были погодите бы
его, по них глупые долгий количества.

View File

@@ -0,0 +1,45 @@
Αν ήδη διάβασε γλιτώσει μεταγλωτίσει, αυτήν θυμάμαι μου μα. Την κατάσταση χρησιμοποίησέ να! Τα διαφορά φαινόμενο διολισθήσεις πες, υψηλότερη προκαλείς περισσότερες όχι κι. Με ελέγχου γίνεται σας, μικρής δημιουργούν τη του. Τις τα γράψει εικόνες απαράδεκτη?
Να ότι πρώτοι απαραίτητο. Άμεση πετάνε κακόκεφος τον ώς, να χώρου πιθανότητες του. Το μέχρι ορίστε λιγότερους σας. Πω ναί φυσικά εικόνες.
Μου οι κώδικα αποκλειστικούς, λες το μάλλον συνεχώς. Νέου σημεία απίστευτα σας μα. Χρόνου μεταγλωτιστής σε νέα, τη τις πιάνει μπορούσες προγραμματιστές. Των κάνε βγαίνει εντυπωσιακό τα? Κρατάει τεσσαρών δυστυχώς της κι, ήδη υψηλότερη εξακολουθεί τα?
Ώρα πετάνε μπορούσε λιγότερους αν, τα απαράδεκτη συγχωνευτεί ροή. Τη έγραψες συνηθίζουν σαν. Όλα με υλικό στήλες χειρότερα. Ανώδυνη δουλέψει επί ως, αν διαδίκτυο εσωτερικών παράγοντες από. Κεντρικό επιτυχία πες το.
Πω ναι λέει τελειώσει, έξι ως έργων τελειώσει. Με αρχεία βουτήξουν ανταγωνιστής ώρα, πολύ γραφικά σελίδων τα στη. Όρο οέλεγχος δημιουργούν δε, ας θέλεις ελέγχου συντακτικό όρο! Της θυμάμαι επιδιόρθωση τα. Για μπορούσε περισσότερο αν, μέγιστη σημαίνει αποφάσισε τα του, άτομο αποτελέσει τι στα.
Τι στην αφήσεις διοίκηση στη. Τα εσφαλμένη δημιουργια επιχείριση έξι! Βήμα μαγικά εκτελέσει ανά τη. Όλη αφήσεις συνεχώς εμπορικά αν, το λες κόλπα επιτυχία. Ότι οι ζώνη κειμένων. Όρο κι ρωτάει γραμμής πελάτες, τελειώσει διολισθήσεις καθυστερούσε αν εγώ? Τι πετούν διοίκηση προβλήματα ήδη.
Τη γλιτώσει αποθηκευτικού μια. Πω έξι δημιουργια πιθανότητες, ως πέντε ελέγχους εκτελείται λες. Πως ερωτήσεις διοικητικό συγκεντρωμένοι οι, ας συνεχώς διοικητικό αποστηθίσει σαν. Δε πρώτες συνεχώς διολισθήσεις έχω, από τι κανένας βουτήξουν, γειτονιάς προσεκτικά ανταγωνιστής κι σαν.
Δημιουργια συνηθίζουν κλπ τι? Όχι ποσοστό διακοπής κι. Κλπ φακέλους δεδομένη εξοργιστικά θα? Υποψήφιο καθορίζουν με όλη, στα πήρε προσοχή εταιρείες πω, ώς τον συνάδελφος διοικητικό δημιουργήσεις! Δούλευε επιτίθενται σας θα, με ένας παραγωγικής ένα, να ναι σημεία μέγιστη απαράδεκτη?
Σας τεσσαρών συνεντεύξης τη, αρπάζεις σίγουρος μη για', επί τοπικές εντολές ακούσει θα? Ως δυστυχής μεταγλωτιστής όλη, να την είχαν σφάλμα απαραίτητο! Μην ώς άτομο διορθώσει χρησιμοποιούνταν. Δεν τα κόλπα πετάξαμε, μη που άγχος υόρκη άμεση, αφού δυστυχώς διακόψουμε όρο αν! Όλη μαγικά πετάνε επιδιορθώσεις δε, ροή φυσικά αποτελέσει πω.
Άπειρα παραπάνω φαινόμενο πω ώρα, σαν πόρτες κρατήσουν συνηθίζουν ως. Κι ώρα τρέξει είχαμε εφαρμογή. Απλό σχεδιαστής μεταγλωτιστής ας επί, τις τα όταν έγραψες γραμμής? Όλα κάνεις συνάδελφος εργαζόμενοι θα, χαρτιού χαμηλός τα ροή. Ως ναι όροφο έρθει, μην πελάτες αποφάσισε μεταφραστής με, να βιαστικά εκδόσεις αναζήτησης λες. Των φταίει εκθέσεις προσπαθήσεις οι, σπίτι αποστηθίσει ας λες?
Ώς που υπηρεσία απαραίτητο δημιουργείς. Μη άρα χαρά καθώς νύχτας, πω ματ μπουν είχαν. Άμεση δημιουργείς ώς ροή, γράψει γραμμής σίγουρος στα τι! Αν αφού πρώτοι εργαζόμενων ναί.
Άμεση διορθώσεις με δύο? Έχουν παράδειγμα των θα, μου έρθει θυμάμαι περισσότερο το. Ότι θα αφού χρειάζονται περισσότερες. Σαν συνεχώς περίπου οι.
Ώς πρώτης πετάξαμε λες, όρο κι πρώτες ζητήσεις δυστυχής. Ανά χρόνου διακοπή επιχειρηματίες ας, ώς μόλις άτομο χειρότερα όρο, κρατάει σχεδιαστής προσπαθήσεις νέο το. Πουλάς προσθέσει όλη πω, τύπου χαρακτηριστικό εγώ σε, πω πιο δούλευε αναζήτησης? Αναφορά δίνοντας σαν μη, μάθε δεδομένη εσωτερικών με ναι, αναφέρονται περιβάλλοντος ώρα αν. Και λέει απόλαυσε τα, που το όροφο προσπαθούν?
Πάντα χρόνου χρήματα ναι το, σαν σωστά θυμάμαι σκεφτείς τα. Μα αποτελέσει ανεπιθύμητη την, πιο το τέτοιο ατόμου, τη των τρόπο εργαλείων επιδιόρθωσης. Περιβάλλον παραγωγικής σου κι, κλπ οι τύπου κακόκεφους αποστηθίσει, δε των πλέον τρόποι. Πιθανότητες χαρακτηριστικών σας κι, γραφικά δημιουργήσεις μια οι, πω πολλοί εξαρτάται προσεκτικά εδώ. Σταματάς παράγοντες για' ώς, στις ρωτάει το ναι! Καρέκλα ζητήσεις συνδυασμούς τη ήδη!
Για μαγικά συνεχώς ακούσει το. Σταματάς προϊόντα βουτήξουν ώς ροή. Είχαν πρώτες οι ναι, μα λες αποστηθίσει ανακαλύπτεις. Όροφο άλγεβρα παραπάνω εδώ τη, πρόσληψη λαμβάνουν καταλάθος ήδη ας? Ως και εισαγωγή κρατήσουν, ένας κακόκεφους κι μας, όχι κώδικάς παίξουν πω. Πω νέα κρατάει εκφράσουν, τότε τελικών τη όχι, ας της τρέξει αλλάζοντας αποκλειστικούς.
Ένας βιβλίο σε άρα, ναι ως γράψει ταξινομεί διορθώσεις! Εδώ να γεγονός συγγραφείς, ώς ήδη διακόψουμε επιχειρηματίες? Ότι πακέτων εσφαλμένη κι, θα όρο κόλπα παραγωγικής? Αν έχω κεντρικό υψηλότερη, κι δεν ίδιο πετάνε παρατηρούμενη! Που λοιπόν σημαντικό μα, προκαλείς χειροκροτήματα ως όλα, μα επί κόλπα άγχος γραμμές! Δε σου κάνεις βουτήξουν, μη έργων επενδυτής χρησιμοποίησέ στα, ως του πρώτες διάσημα σημαντικό.
Βιβλίο τεράστιο προκύπτουν σαν το, σαν τρόπο επιδιόρθωση ας. Είχαν προσοχή προσπάθεια κι ματ, εδώ ως έτσι σελίδων συζήτηση. Και στην βγαίνει εσφαλμένη με, δυστυχής παράδειγμα δε μας, από σε υόρκη επιδιόρθωσης. Νέα πω νέου πιθανό, στήλες συγγραφείς μπαίνοντας μα για', το ρωτήσει κακόκεφους της? Μου σε αρέσει συγγραφής συγχωνευτεί, μη μου υόρκη ξέχασε διακοπής! Ώς επί αποφάσισε αποκλειστικούς χρησιμοποιώντας, χρήματα σελίδων ταξινομεί ναι με.
Μη ανά γραμμή απόλαυσε, πω ναι μάτσο διασφαλίζεται. Τη έξι μόλις εργάστηκε δημιουργούν, έκδοση αναφορά δυσκολότερο οι νέο. Σας ως μπορούσε παράδειγμα, αν ότι δούλευε μπορούσε αποκλειστικούς, πιο λέει βουτήξουν διορθώσει ως. Έχω τελευταία κακόκεφους ας, όσο εργαζόμενων δημιουργήσεις τα.
Του αν δουλέψει μπορούσε, πετούν χαμηλός εδώ ας? Κύκλο τύπους με που, δεν σε έχουν συνεχώς χειρότερα, τις τι απαράδεκτη συνηθίζουν? Θα μην τους αυτήν, τη ένα πήρε πακέτων, κι προκύπτουν περιβάλλον πως. Μα για δουλέψει απόλαυσε εφαμοργής, ώς εδώ σημαίνει μπορούσες, άμεση ακούσει προσοχή τη εδώ?
Στα δώσε αθόρυβες λιγότερους οι, δε αναγκάζονται αποκλειστικούς όλα! Ας μπουν διοικητικό μια, πάντα ελέγχου διορθώσεις ώς τον. Ότι πήρε κανόνα μα. Που άτομα κάνεις δημιουργίες τα, οι μας αφού κόλπα προγραμματιστής, αφού ωραίο προκύπτουν στα ως. Θέμα χρησιμοποιήσει αν όλα, του τα άλγεβρα σελίδων. Τα ότι ανώδυνη δυστυχώς συνδυασμούς, μας οι πάντα γνωρίζουμε ανταγωνιστής, όχι τα δοκιμάσεις σχεδιαστής! Στην συνεντεύξης επιδιόρθωση πιο τα, μα από πουλάς περιβάλλον παραγωγικής.
Έχουν μεταγλωτίσει σε σας, σε πάντα πρώτης μειώσει των, γράψει ρουτίνα δυσκολότερο ήδη μα? Ταξινομεί διορθώσεις να μας. Θα της προσπαθούν περιεχόμενα, δε έχω τοπικές στέλνοντάς. Ανά δε αλφα άμεση, κάποιο ρωτάει γνωρίζουμε πω στη, φράση μαγικά συνέχεια δε δύο! Αν είχαμε μειώσει ροή, μας μετράει καθυστερούσε επιδιορθώσεις μη. Χάος υόρκη κεντρικό έχω σε, ανά περίπου αναγκάζονται πω.
Όσο επιστρέφουν χρονοδιαγράμματα μη. Πως ωραίο κακόκεφος διαχειριστής ως, τις να διακοπής αναζήτησης. Κάποιο ποσοστό ταξινομεί επί τη? Μάθε άμεση αλλάζοντας δύο με, μου νέου πάντα να.
Πω του δυστυχώς πιθανότητες. Κι ρωτάει υψηλότερη δημιουργια ότι, πω εισαγωγή τελευταία απομόνωση ναι. Των ζητήσεις γνωρίζουμε ώς? Για' μη παραδοτέου αναφέρονται! Ύψος παραγωγικά ροή ως, φυσικά διάβασε εικόνες όσο σε? Δεν υόρκη διορθώσεις επεξεργασία θα, ως μέση σύστημα χρησιμοποιήσει τις.

View File

@@ -0,0 +1,3 @@
रखति आवश्यकत प्रेरना मुख्यतह हिंदी किएलोग असक्षम कार्यलय करते विवरण किके मानसिक दिनांक पुर्व संसाध एवम् कुशलता अमितकुमार प्रोत्साहित जनित देखने उदेशीत विकसित बलवान ब्रौशर किएलोग विश्लेषण लोगो कैसे जागरुक प्रव्रुति प्रोत्साहित सदस्य आवश्यकत प्रसारन उपलब्धता अथवा हिंदी जनित दर्शाता यन्त्रालय बलवान अतित सहयोग शुरुआत सभीकुछ माहितीवानीज्य लिये खरिदे है।अभी एकत्रित सम्पर्क रिती मुश्किल प्राथमिक भेदनक्षमता विश्व उन्हे गटको द्वारा तकरीबन
विश्व द्वारा व्याख्या सके। आजपर वातावरण व्याख्यान पहोच। हमारी कीसे प्राथमिक विचारशिलता पुर्व करती कम्प्युटर भेदनक्षमता लिये बलवान और्४५० यायेका वार्तालाप सुचना भारत शुरुआत लाभान्वित पढाए संस्था वर्णित मार्गदर्शन चुनने

View File

@@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Header;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Header\DateHeader;
class DateHeaderTest extends TestCase
{
/* --
The following tests refer to RFC 2822, section 3.6.1 and 3.3.
*/
public function testGetDateTime()
{
$header = new DateHeader('Date', $dateTime = new \DateTimeImmutable());
$this->assertSame($dateTime, $header->getDateTime());
}
public function testDateTimeCanBeSetBySetter()
{
$header = new DateHeader('Date', new \DateTimeImmutable());
$header->setDateTime($dateTime = new \DateTimeImmutable());
$this->assertSame($dateTime, $header->getDateTime());
}
public function testDateTimeIsConvertedToImmutable()
{
$dateTime = new \DateTime();
$header = new DateHeader('Date', $dateTime);
$this->assertInstanceOf('DateTimeImmutable', $header->getDateTime());
$this->assertEquals($dateTime->getTimestamp(), $header->getDateTime()->getTimestamp());
$this->assertEquals($dateTime->getTimezone(), $header->getDateTime()->getTimezone());
}
public function testDateTimeIsImmutable()
{
$header = new DateHeader('Date', $dateTime = new \DateTime('2000-01-01 12:00:00 Europe/Berlin'));
$dateTime->setDate(2002, 2, 2);
$this->assertEquals('Sat, 01 Jan 2000 12:00:00 +0100', $header->getDateTime()->format('r'));
$this->assertEquals('Sat, 01 Jan 2000 12:00:00 +0100', $header->getBodyAsString());
}
public function testDateTimeIsConvertedToRfc2822Date()
{
$header = new DateHeader('Date', $dateTime = new \DateTimeImmutable('2000-01-01 12:00:00 Europe/Berlin'));
$header->setDateTime($dateTime);
$this->assertEquals('Sat, 01 Jan 2000 12:00:00 +0100', $header->getBodyAsString());
}
public function testSetBody()
{
$header = new DateHeader('Date', $dateTime = new \DateTimeImmutable());
$header->setBody($dateTime);
$this->assertEquals($dateTime->format('r'), $header->getBodyAsString());
}
public function testGetBody()
{
$header = new DateHeader('Date', $dateTime = new \DateTimeImmutable());
$header->setDateTime($dateTime);
$this->assertEquals($dateTime, $header->getBody());
}
public function testToString()
{
$header = new DateHeader('Date', $dateTime = new \DateTimeImmutable('2000-01-01 12:00:00 Europe/Berlin'));
$header->setDateTime($dateTime);
$this->assertEquals('Date: Sat, 01 Jan 2000 12:00:00 +0100', $header->toString());
}
}

View File

@@ -0,0 +1,245 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Header;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\IdentificationHeader;
use Symfony\Component\Mime\Header\MailboxListHeader;
use Symfony\Component\Mime\Header\UnstructuredHeader;
class HeadersTest extends TestCase
{
public function testAddMailboxListHeaderDelegatesToFactory()
{
$headers = new Headers();
$headers->addMailboxListHeader('From', ['person@domain']);
$this->assertNotNull($headers->get('From'));
}
public function testAddDateHeaderDelegatesToFactory()
{
$dateTime = new \DateTimeImmutable();
$headers = new Headers();
$headers->addDateHeader('Date', $dateTime);
$this->assertNotNull($headers->get('Date'));
}
public function testAddTextHeaderDelegatesToFactory()
{
$headers = new Headers();
$headers->addTextHeader('Subject', 'some text');
$this->assertNotNull($headers->get('Subject'));
}
public function testAddParameterizedHeaderDelegatesToFactory()
{
$headers = new Headers();
$headers->addParameterizedHeader('Content-Type', 'text/plain', ['charset' => 'utf-8']);
$this->assertNotNull($headers->get('Content-Type'));
}
public function testAddIdHeaderDelegatesToFactory()
{
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$this->assertNotNull($headers->get('Message-ID'));
}
public function testAddPathHeaderDelegatesToFactory()
{
$headers = new Headers();
$headers->addPathHeader('Return-Path', 'some@path');
$this->assertNotNull($headers->get('Return-Path'));
}
public function testHasReturnsFalseWhenNoHeaders()
{
$headers = new Headers();
$this->assertFalse($headers->has('Some-Header'));
}
public function testAddedMailboxListHeaderIsSeenByHas()
{
$headers = new Headers();
$headers->addMailboxListHeader('From', ['person@domain']);
$this->assertTrue($headers->has('From'));
}
public function testAddedDateHeaderIsSeenByHas()
{
$dateTime = new \DateTimeImmutable();
$headers = new Headers();
$headers->addDateHeader('Date', $dateTime);
$this->assertTrue($headers->has('Date'));
}
public function testAddedTextHeaderIsSeenByHas()
{
$headers = new Headers();
$headers->addTextHeader('Subject', 'some text');
$this->assertTrue($headers->has('Subject'));
}
public function testAddedParameterizedHeaderIsSeenByHas()
{
$headers = new Headers();
$headers->addParameterizedHeader('Content-Type', 'text/plain', ['charset' => 'utf-8']);
$this->assertTrue($headers->has('Content-Type'));
}
public function testAddedIdHeaderIsSeenByHas()
{
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$this->assertTrue($headers->has('Message-ID'));
}
public function testAddedPathHeaderIsSeenByHas()
{
$headers = new Headers();
$headers->addPathHeader('Return-Path', 'some@path');
$this->assertTrue($headers->has('Return-Path'));
}
public function testNewlySetHeaderIsSeenByHas()
{
$headers = new Headers();
$headers->add(new UnstructuredHeader('X-Foo', 'bar'));
$this->assertTrue($headers->has('X-Foo'));
}
public function testHasCanDistinguishMultipleHeaders()
{
$headers = new Headers();
$headers->addTextHeader('X-Test', 'some@id');
$headers->addTextHeader('X-Test', 'other@id');
$this->assertTrue($headers->has('X-Test'));
}
public function testGet()
{
$header = new IdentificationHeader('Message-ID', 'some@id');
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$this->assertEquals($header->toString(), $headers->get('Message-ID')->toString());
}
public function testGetReturnsNullIfHeaderNotSet()
{
$headers = new Headers();
$this->assertNull($headers->get('Message-ID'));
}
public function testAllReturnsAllHeadersMatchingName()
{
$header0 = new UnstructuredHeader('X-Test', 'some@id');
$header1 = new UnstructuredHeader('X-Test', 'other@id');
$header2 = new UnstructuredHeader('X-Test', 'more@id');
$headers = new Headers();
$headers->addTextHeader('X-Test', 'some@id');
$headers->addTextHeader('X-Test', 'other@id');
$headers->addTextHeader('X-Test', 'more@id');
$this->assertEquals([$header0, $header1, $header2], iterator_to_array($headers->all('X-Test')));
}
public function testAllReturnsAllHeadersIfNoArguments()
{
$header0 = new IdentificationHeader('Message-ID', 'some@id');
$header1 = new UnstructuredHeader('Subject', 'thing');
$header2 = new MailboxListHeader('To', [new Address('person@example.org')]);
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$headers->addTextHeader('Subject', 'thing');
$headers->addMailboxListHeader('To', [new Address('person@example.org')]);
$this->assertEquals(['message-id' => $header0, 'subject' => $header1, 'to' => $header2], iterator_to_array($headers->all()));
}
public function testAllReturnsEmptyArrayIfNoneSet()
{
$headers = new Headers();
$this->assertEquals([], iterator_to_array($headers->all('Received')));
}
public function testRemoveRemovesAllHeadersWithName()
{
$header0 = new UnstructuredHeader('X-Test', 'some@id');
$header1 = new UnstructuredHeader('X-Test', 'other@id');
$headers = new Headers();
$headers->addIdHeader('X-Test', 'some@id');
$headers->addIdHeader('X-Test', 'other@id');
$headers->remove('X-Test');
$this->assertFalse($headers->has('X-Test'));
$this->assertFalse($headers->has('X-Test'));
}
public function testHasIsNotCaseSensitive()
{
$header = new IdentificationHeader('Message-ID', 'some@id');
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$this->assertTrue($headers->has('message-id'));
}
public function testGetIsNotCaseSensitive()
{
$header = new IdentificationHeader('Message-ID', 'some@id');
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$this->assertEquals($header, $headers->get('message-id'));
}
public function testAllIsNotCaseSensitive()
{
$header = new IdentificationHeader('Message-ID', 'some@id');
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$this->assertEquals([$header], iterator_to_array($headers->all('message-id')));
}
public function testRemoveIsNotCaseSensitive()
{
$header = new IdentificationHeader('Message-ID', 'some@id');
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$headers->remove('message-id');
$this->assertFalse($headers->has('Message-ID'));
}
public function testToStringJoinsHeadersTogether()
{
$headers = new Headers();
$headers->addTextHeader('Foo', 'bar');
$headers->addTextHeader('Zip', 'buttons');
$this->assertEquals("Foo: bar\r\nZip: buttons\r\n", $headers->toString());
}
public function testHeadersWithoutBodiesAreNotDisplayed()
{
$headers = new Headers();
$headers->addTextHeader('Foo', 'bar');
$headers->addTextHeader('Zip', '');
$this->assertEquals("Foo: bar\r\n", $headers->toString());
}
public function testToArray()
{
$headers = new Headers();
$headers->addIdHeader('Message-ID', 'some@id');
$headers->addTextHeader('Foo', str_repeat('a', 60).pack('C', 0x8F));
$this->assertEquals([
'Message-ID: <some@id>',
"Foo: =?utf-8?Q?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?=\r\n =?utf-8?Q?aaaa?=",
], $headers->toArray());
}
}

View File

@@ -0,0 +1,173 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Header;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Header\IdentificationHeader;
class IdentificationHeaderTest extends TestCase
{
public function testValueMatchesMsgIdSpec()
{
/* -- RFC 2822, 3.6.4.
message-id = "Message-ID:" msg-id CRLF
in-reply-to = "In-Reply-To:" 1*msg-id CRLF
references = "References:" 1*msg-id CRLF
msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]
id-left = dot-atom-text / no-fold-quote / obs-id-left
id-right = dot-atom-text / no-fold-literal / obs-id-right
no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE
no-fold-literal = "[" *(dtext / quoted-pair) "]"
*/
$header = new IdentificationHeader('Message-ID', 'id-left@id-right');
$this->assertEquals('<id-left@id-right>', $header->getBodyAsString());
}
public function testIdCanBeRetrievedVerbatim()
{
$header = new IdentificationHeader('Message-ID', 'id-left@id-right');
$this->assertEquals('id-left@id-right', $header->getId());
}
public function testMultipleIdsCanBeSet()
{
$header = new IdentificationHeader('References', 'c@d');
$header->setIds(['a@b', 'x@y']);
$this->assertEquals(['a@b', 'x@y'], $header->getIds());
}
public function testSettingMultipleIdsProducesAListValue()
{
/* -- RFC 2822, 3.6.4.
The "References:" and "In-Reply-To:" field each contain one or more
unique message identifiers, optionally separated by CFWS.
.. SNIP ..
in-reply-to = "In-Reply-To:" 1*msg-id CRLF
references = "References:" 1*msg-id CRLF
*/
$header = new IdentificationHeader('References', ['a@b', 'x@y']);
$this->assertEquals('<a@b> <x@y>', $header->getBodyAsString());
}
public function testIdLeftCanBeQuoted()
{
/* -- RFC 2822, 3.6.4.
id-left = dot-atom-text / no-fold-quote / obs-id-left
*/
$header = new IdentificationHeader('References', '"ab"@c');
$this->assertEquals('"ab"@c', $header->getId());
$this->assertEquals('<"ab"@c>', $header->getBodyAsString());
}
public function testIdLeftCanContainAnglesAsQuotedPairs()
{
/* -- RFC 2822, 3.6.4.
no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE
*/
$header = new IdentificationHeader('References', '"a\\<\\>b"@c');
$this->assertEquals('"a\\<\\>b"@c', $header->getId());
$this->assertEquals('<"a\\<\\>b"@c>', $header->getBodyAsString());
}
public function testIdLeftCanBeDotAtom()
{
$header = new IdentificationHeader('References', 'a.b+&%$.c@d');
$this->assertEquals('a.b+&%$.c@d', $header->getId());
$this->assertEquals('<a.b+&%$.c@d>', $header->getBodyAsString());
}
public function testInvalidIdLeftThrowsException()
{
$this->expectException('Exception');
$this->expectExceptionMessage('Email "a b c@d" does not comply with addr-spec of RFC 2822.');
$header = new IdentificationHeader('References', 'a b c@d');
}
public function testIdRightCanBeDotAtom()
{
/* -- RFC 2822, 3.6.4.
id-right = dot-atom-text / no-fold-literal / obs-id-right
*/
$header = new IdentificationHeader('References', 'a@b.c+&%$.d');
$this->assertEquals('a@b.c+&%$.d', $header->getId());
$this->assertEquals('<a@b.c+&%$.d>', $header->getBodyAsString());
}
public function testIdRightCanBeLiteral()
{
/* -- RFC 2822, 3.6.4.
no-fold-literal = "[" *(dtext / quoted-pair) "]"
*/
$header = new IdentificationHeader('References', 'a@[1.2.3.4]');
$this->assertEquals('a@[1.2.3.4]', $header->getId());
$this->assertEquals('<a@[1.2.3.4]>', $header->getBodyAsString());
}
public function testIdRigthIsIdnEncoded()
{
$header = new IdentificationHeader('References', 'a@ä');
$this->assertEquals('a@ä', $header->getId());
$this->assertEquals('<a@xn--4ca>', $header->getBodyAsString());
}
public function testInvalidIdRightThrowsException()
{
$this->expectException('Exception');
$this->expectExceptionMessage('Email "a@b c d" does not comply with addr-spec of RFC 2822.');
$header = new IdentificationHeader('References', 'a@b c d');
}
public function testMissingAtSignThrowsException()
{
$this->expectException('Exception');
$this->expectExceptionMessage('Email "abc" does not comply with addr-spec of RFC 2822.');
/* -- RFC 2822, 3.6.4.
msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]
*/
$header = new IdentificationHeader('References', 'abc');
}
public function testSetBody()
{
$header = new IdentificationHeader('Message-ID', 'c@d');
$header->setBody('a@b');
$this->assertEquals(['a@b'], $header->getIds());
}
public function testGetBody()
{
$header = new IdentificationHeader('Message-ID', 'a@b');
$this->assertEquals(['a@b'], $header->getBody());
}
public function testStringValue()
{
$header = new IdentificationHeader('References', ['a@b', 'x@y']);
$this->assertEquals('References: <a@b> <x@y>', $header->toString());
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Header;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Header\MailboxHeader;
use Symfony\Component\Mime\NamedAddress;
class MailboxHeaderTest extends TestCase
{
public function testConstructor()
{
$header = new MailboxHeader('Sender', $address = new Address('fabien@symfony.com'));
$this->assertEquals($address, $header->getAddress());
$this->assertEquals($address, $header->getBody());
}
public function testAddress()
{
$header = new MailboxHeader('Sender', new Address('fabien@symfony.com'));
$header->setBody($address = new Address('helene@symfony.com'));
$this->assertEquals($address, $header->getAddress());
$this->assertEquals($address, $header->getBody());
$header->setAddress($address = new Address('thomas@symfony.com'));
$this->assertEquals($address, $header->getAddress());
$this->assertEquals($address, $header->getBody());
}
public function testgetBodyAsString()
{
$header = new MailboxHeader('Sender', new Address('fabien@symfony.com'));
$this->assertEquals('fabien@symfony.com', $header->getBodyAsString());
$header->setAddress(new Address('fabien@sïmfony.com'));
$this->assertEquals('fabien@xn--smfony-iwa.com', $header->getBodyAsString());
$header = new MailboxHeader('Sender', new NamedAddress('fabien@symfony.com', 'Fabien Potencier'));
$this->assertEquals('Fabien Potencier <fabien@symfony.com>', $header->getBodyAsString());
$header = new MailboxHeader('Sender', new NamedAddress('fabien@symfony.com', 'Fabien Potencier, "from Symfony"'));
$this->assertEquals('"Fabien Potencier, \"from Symfony\"" <fabien@symfony.com>', $header->getBodyAsString());
$header = new MailboxHeader('From', new NamedAddress('fabien@symfony.com', 'Fabien Potencier, \\escaped\\'));
$this->assertEquals('"Fabien Potencier, \\\\escaped\\\\" <fabien@symfony.com>', $header->getBodyAsString());
$name = 'P'.pack('C', 0x8F).'tencier';
$header = new MailboxHeader('Sender', new NamedAddress('fabien@symfony.com', 'Fabien '.$name));
$header->setCharset('iso-8859-1');
$this->assertEquals('Fabien =?'.$header->getCharset().'?Q?P=8Ftencier?= <fabien@symfony.com>', $header->getBodyAsString());
}
public function testUtf8CharsInLocalPartThrows()
{
$this->expectException('Symfony\Component\Mime\Exception\AddressEncoderException');
$header = new MailboxHeader('Sender', new Address('fabïen@symfony.com'));
$header->getBodyAsString();
}
public function testToString()
{
$header = new MailboxHeader('Sender', new Address('fabien@symfony.com'));
$this->assertEquals('Sender: fabien@symfony.com', $header->toString());
$header = new MailboxHeader('Sender', new NamedAddress('fabien@symfony.com', 'Fabien Potencier'));
$this->assertEquals('Sender: Fabien Potencier <fabien@symfony.com>', $header->toString());
}
}

View File

@@ -0,0 +1,131 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Header;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Header\MailboxListHeader;
use Symfony\Component\Mime\NamedAddress;
class MailboxListHeaderTest extends TestCase
{
// RFC 2822, 3.6.2 for all tests
public function testMailboxIsSetForAddress()
{
$header = new MailboxListHeader('From', [new Address('chris@swiftmailer.org')]);
$this->assertEquals(['chris@swiftmailer.org'], $header->getAddressStrings());
}
public function testMailboxIsRenderedForNameAddress()
{
$header = new MailboxListHeader('From', [new NamedAddress('chris@swiftmailer.org', 'Chris Corbyn')]);
$this->assertEquals(['Chris Corbyn <chris@swiftmailer.org>'], $header->getAddressStrings());
}
public function testAddressCanBeReturnedForAddress()
{
$header = new MailboxListHeader('From', $addresses = [new Address('chris@swiftmailer.org')]);
$this->assertEquals($addresses, $header->getAddresses());
}
public function testQuotesInNameAreQuoted()
{
$header = new MailboxListHeader('From', [new NamedAddress('chris@swiftmailer.org', 'Chris Corbyn, "DHE"')]);
$this->assertEquals(['"Chris Corbyn, \"DHE\"" <chris@swiftmailer.org>'], $header->getAddressStrings());
}
public function testEscapeCharsInNameAreQuoted()
{
$header = new MailboxListHeader('From', [new NamedAddress('chris@swiftmailer.org', 'Chris Corbyn, \\escaped\\')]);
$this->assertEquals(['"Chris Corbyn, \\\\escaped\\\\" <chris@swiftmailer.org>'], $header->getAddressStrings());
}
public function testUtf8CharsInDomainAreIdnEncoded()
{
$header = new MailboxListHeader('From', [new NamedAddress('chris@swïftmailer.org', 'Chris Corbyn')]);
$this->assertEquals(['Chris Corbyn <chris@xn--swftmailer-78a.org>'], $header->getAddressStrings());
}
public function testUtf8CharsInLocalPartThrows()
{
$this->expectException('Symfony\Component\Mime\Exception\AddressEncoderException');
$header = new MailboxListHeader('From', [new NamedAddress('chrïs@swiftmailer.org', 'Chris Corbyn')]);
$header->getAddressStrings();
}
public function testGetMailboxesReturnsNameValuePairs()
{
$header = new MailboxListHeader('From', $addresses = [new NamedAddress('chris@swiftmailer.org', 'Chris Corbyn, DHE')]);
$this->assertEquals($addresses, $header->getAddresses());
}
public function testMultipleAddressesAsMailboxStrings()
{
$header = new MailboxListHeader('From', [new Address('chris@swiftmailer.org'), new Address('mark@swiftmailer.org')]);
$this->assertEquals(['chris@swiftmailer.org', 'mark@swiftmailer.org'], $header->getAddressStrings());
}
public function testNameIsEncodedIfNonAscii()
{
$name = 'C'.pack('C', 0x8F).'rbyn';
$header = new MailboxListHeader('From', [new NamedAddress('chris@swiftmailer.org', 'Chris '.$name)]);
$header->setCharset('iso-8859-1');
$addresses = $header->getAddressStrings();
$this->assertEquals('Chris =?'.$header->getCharset().'?Q?C=8Frbyn?= <chris@swiftmailer.org>', array_shift($addresses));
}
public function testEncodingLineLengthCalculations()
{
/* -- RFC 2047, 2.
An 'encoded-word' may not be more than 75 characters long, including
'charset', 'encoding', 'encoded-text', and delimiters.
*/
$name = 'C'.pack('C', 0x8F).'rbyn';
$header = new MailboxListHeader('From', [new NamedAddress('chris@swiftmailer.org', 'Chris '.$name)]);
$header->setCharset('iso-8859-1');
$addresses = $header->getAddressStrings();
$this->assertEquals('Chris =?'.$header->getCharset().'?Q?C=8Frbyn?= <chris@swiftmailer.org>', array_shift($addresses));
}
public function testGetValueReturnsMailboxStringValue()
{
$header = new MailboxListHeader('From', [new NamedAddress('chris@swiftmailer.org', 'Chris Corbyn')]);
$this->assertEquals('Chris Corbyn <chris@swiftmailer.org>', $header->getBodyAsString());
}
public function testGetValueReturnsMailboxStringValueForMultipleMailboxes()
{
$header = new MailboxListHeader('From', [new NamedAddress('chris@swiftmailer.org', 'Chris Corbyn'), new NamedAddress('mark@swiftmailer.org', 'Mark Corbyn')]);
$this->assertEquals('Chris Corbyn <chris@swiftmailer.org>, Mark Corbyn <mark@swiftmailer.org>', $header->getBodyAsString());
}
public function testSetBody()
{
$header = new MailboxListHeader('From', []);
$header->setBody($addresses = [new Address('chris@swiftmailer.org')]);
$this->assertEquals($addresses, $header->getAddresses());
}
public function testGetBody()
{
$header = new MailboxListHeader('From', $addresses = [new Address('chris@swiftmailer.org')]);
$this->assertEquals($addresses, $header->getBody());
}
public function testToString()
{
$header = new MailboxListHeader('From', [new NamedAddress('chris@example.org', 'Chris Corbyn'), new NamedAddress('mark@example.org', 'Mark Corbyn')]);
$this->assertEquals('From: Chris Corbyn <chris@example.org>, Mark Corbyn <mark@example.org>', $header->toString());
}
}

View File

@@ -0,0 +1,295 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Header;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Header\ParameterizedHeader;
class ParameterizedHeaderTest extends TestCase
{
private $charset = 'utf-8';
private $lang = 'en-us';
public function testValueIsReturnedVerbatim()
{
$header = new ParameterizedHeader('Content-Type', 'text/plain');
$this->assertEquals('text/plain', $header->getValue());
}
public function testParametersAreAppended()
{
/* -- RFC 2045, 5.1
parameter := attribute "=" value
attribute := token
; Matching of attributes
; is ALWAYS case-insensitive.
value := token / quoted-string
token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
or tspecials>
tspecials := "(" / ")" / "<" / ">" / "@" /
"," / ";" / ":" / "\" / <">
"/" / "[" / "]" / "?" / "="
; Must be in quoted-string,
; to use within parameter values
*/
$header = new ParameterizedHeader('Content-Type', 'text/plain');
$header->setParameters(['charset' => 'utf-8']);
$this->assertEquals('text/plain; charset=utf-8', $header->getBodyAsString());
}
public function testSpaceInParamResultsInQuotedString()
{
$header = new ParameterizedHeader('Content-Type', 'attachment');
$header->setParameters(['filename' => 'my file.txt']);
$this->assertEquals('attachment; filename="my file.txt"', $header->getBodyAsString());
}
public function testLongParamsAreBrokenIntoMultipleAttributeStrings()
{
/* -- RFC 2231, 3.
The asterisk character ("*") followed
by a decimal count is employed to indicate that multiple parameters
are being used to encapsulate a single parameter value. The count
starts at 0 and increments by 1 for each subsequent section of the
parameter value. Decimal values are used and neither leading zeroes
nor gaps in the sequence are allowed.
The original parameter value is recovered by concatenating the
various sections of the parameter, in order. For example, the
content-type field
Content-Type: message/external-body; access-type=URL;
URL*0="ftp://";
URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"
is semantically identical to
Content-Type: message/external-body; access-type=URL;
URL="ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"
Note that quotes around parameter values are part of the value
syntax; they are NOT part of the value itself. Furthermore, it is
explicitly permitted to have a mixture of quoted and unquoted
continuation fields.
*/
$value = str_repeat('a', 180);
$header = new ParameterizedHeader('Content-Disposition', 'attachment');
$header->setParameters(['filename' => $value]);
$this->assertEquals(
'attachment; '.
'filename*0*=utf-8\'\''.str_repeat('a', 60).";\r\n ".
'filename*1*='.str_repeat('a', 60).";\r\n ".
'filename*2*='.str_repeat('a', 60),
$header->getBodyAsString()
);
}
public function testEncodedParamDataIncludesCharsetAndLanguage()
{
/* -- RFC 2231, 4.
Asterisks ("*") are reused to provide the indicator that language and
character set information is present and encoding is being used. A
single quote ("'") is used to delimit the character set and language
information at the beginning of the parameter value. Percent signs
("%") are used as the encoding flag, which agrees with RFC 2047.
Specifically, an asterisk at the end of a parameter name acts as an
indicator that character set and language information may appear at
the beginning of the parameter value. A single quote is used to
separate the character set, language, and actual value information in
the parameter value string, and an percent sign is used to flag
octets encoded in hexadecimal. For example:
Content-Type: application/x-stuff;
title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A
Note that it is perfectly permissible to leave either the character
set or language field blank. Note also that the single quote
delimiters MUST be present even when one of the field values is
omitted.
*/
$value = str_repeat('a', 20).pack('C', 0x8F).str_repeat('a', 10);
$header = new ParameterizedHeader('Content-Disposition', 'attachment');
$header->setCharset('iso-8859-1');
$header->setValue('attachment');
$header->setParameters(['filename' => $value]);
$header->setLanguage($this->lang);
$this->assertEquals(
'attachment; filename*='.$header->getCharset()."'".$this->lang."'".
str_repeat('a', 20).'%8F'.str_repeat('a', 10),
$header->getBodyAsString()
);
}
public function testMultipleEncodedParamLinesAreFormattedCorrectly()
{
/* -- RFC 2231, 4.1.
Character set and language information may be combined with the
parameter continuation mechanism. For example:
Content-Type: application/x-stuff
title*0*=us-ascii'en'This%20is%20even%20more%20
title*1*=%2A%2A%2Afun%2A%2A%2A%20
title*2="isn't it!"
Note that:
(1) Language and character set information only appear at
the beginning of a given parameter value.
(2) Continuations do not provide a facility for using more
than one character set or language in the same
parameter value.
(3) A value presented using multiple continuations may
contain a mixture of encoded and unencoded segments.
(4) The first segment of a continuation MUST be encoded if
language and character set information are given.
(5) If the first segment of a continued parameter value is
encoded the language and character set field delimiters
MUST be present even when the fields are left blank.
*/
$value = str_repeat('a', 20).pack('C', 0x8F).str_repeat('a', 60);
$header = new ParameterizedHeader('Content-Disposition', 'attachment');
$header->setValue('attachment');
$header->setCharset('utf-6');
$header->setParameters(['filename' => $value]);
$header->setLanguage($this->lang);
$this->assertEquals(
'attachment; filename*0*='.$header->getCharset()."'".$this->lang."'".
str_repeat('a', 20).'%8F'.str_repeat('a', 23).";\r\n ".
'filename*1*='.str_repeat('a', 37),
$header->getBodyAsString()
);
}
public function testToString()
{
$header = new ParameterizedHeader('Content-Type', 'text/html');
$header->setParameters(['charset' => 'utf-8']);
$this->assertEquals('Content-Type: text/html; charset=utf-8', $header->toString());
}
public function testValueCanBeEncodedIfNonAscii()
{
$value = 'fo'.pack('C', 0x8F).'bar';
$header = new ParameterizedHeader('X-Foo', $value);
$header->setCharset('iso-8859-1');
$header->setParameters(['lookslike' => 'foobar']);
$this->assertEquals('X-Foo: =?'.$header->getCharset().'?Q?fo=8Fbar?=; lookslike=foobar', $header->toString());
}
public function testValueAndParamCanBeEncodedIfNonAscii()
{
$value = 'fo'.pack('C', 0x8F).'bar';
$header = new ParameterizedHeader('X-Foo', $value);
$header->setCharset('iso-8859-1');
$header->setParameters(['says' => $value]);
$this->assertEquals('X-Foo: =?'.$header->getCharset().'?Q?fo=8Fbar?=; says*='.$header->getCharset()."''fo%8Fbar", $header->toString());
}
public function testParamsAreEncodedIfNonAscii()
{
$value = 'fo'.pack('C', 0x8F).'bar';
$header = new ParameterizedHeader('X-Foo', 'bar');
$header->setCharset('iso-8859-1');
$header->setParameters(['says' => $value]);
$this->assertEquals('X-Foo: bar; says*='.$header->getCharset()."''fo%8Fbar", $header->toString());
}
public function testParamsAreEncodedWithLegacyEncodingEnabled()
{
$value = 'fo'.pack('C', 0x8F).'bar';
$header = new ParameterizedHeader('Content-Type', 'bar');
$header->setCharset('iso-8859-1');
$header->setParameters(['says' => $value]);
$this->assertEquals('Content-Type: bar; says="=?'.$header->getCharset().'?Q?fo=8Fbar?="', $header->toString());
}
public function testLanguageInformationAppearsInEncodedWords()
{
/* -- RFC 2231, 5.
5. Language specification in Encoded Words
RFC 2047 provides support for non-US-ASCII character sets in RFC 822
message header comments, phrases, and any unstructured text field.
This is done by defining an encoded word construct which can appear
in any of these places. Given that these are fields intended for
display, it is sometimes necessary to associate language information
with encoded words as well as just the character set. This
specification extends the definition of an encoded word to allow the
inclusion of such information. This is simply done by suffixing the
character set specification with an asterisk followed by the language
tag. For example:
From: =?US-ASCII*EN?Q?Keith_Moore?= <moore@cs.utk.edu>
-- RFC 2047, 5. Use of encoded-words in message headers
...
+ An 'encoded-word' MUST NOT be used in parameter of a MIME
Content-Type or Content-Disposition field, or in any structured
field body except within a 'comment' or 'phrase'.
-- RFC 2047, Appendix - changes since RFC 1522
...
+ clarify that encoded-words are allowed in '*text' fields in both
RFC822 headers and MIME body part headers, but NOT as parameter
values.
*/
$value = 'fo'.pack('C', 0x8F).'bar';
$header = new ParameterizedHeader('X-Foo', $value);
$header->setCharset('iso-8859-1');
$header->setLanguage('en');
$header->setParameters(['says' => $value]);
$this->assertEquals('X-Foo: =?'.$header->getCharset().'*en?Q?fo=8Fbar?=; says*='.$header->getCharset()."'en'fo%8Fbar", $header->toString());
}
public function testSetBody()
{
$header = new ParameterizedHeader('Content-Type', 'text/html');
$header->setBody('text/plain');
$this->assertEquals('text/plain', $header->getValue());
}
public function testGetBody()
{
$header = new ParameterizedHeader('Content-Type', 'text/plain');
$this->assertEquals('text/plain', $header->getBody());
}
public function testSetParameter()
{
$header = new ParameterizedHeader('Content-Type', 'text/html');
$header->setParameters(['charset' => 'utf-8', 'delsp' => 'yes']);
$header->setParameter('delsp', 'no');
$this->assertEquals(['charset' => 'utf-8', 'delsp' => 'no'], $header->getParameters());
}
public function testGetParameter()
{
$header = new ParameterizedHeader('Content-Type', 'text/html');
$header->setParameters(['charset' => 'utf-8', 'delsp' => 'yes']);
$this->assertEquals('utf-8', $header->getParameter('charset'));
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Header;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Header\PathHeader;
class PathHeaderTest extends TestCase
{
public function testSingleAddressCanBeSetAndFetched()
{
$header = new PathHeader('Return-Path', $address = new Address('chris@swiftmailer.org'));
$this->assertEquals($address, $header->getAddress());
}
public function testAddressMustComplyWithRfc2822()
{
$this->expectException('Exception');
$header = new PathHeader('Return-Path', new Address('chr is@swiftmailer.org'));
}
public function testValueIsAngleAddrWithValidAddress()
{
/* -- RFC 2822, 3.6.7.
return = "Return-Path:" path CRLF
path = ([CFWS] "<" ([CFWS] / addr-spec) ">" [CFWS]) /
obs-path
*/
$header = new PathHeader('Return-Path', new Address('chris@swiftmailer.org'));
$this->assertEquals('<chris@swiftmailer.org>', $header->getBodyAsString());
}
public function testAddressIsIdnEncoded()
{
$header = new PathHeader('Return-Path', new Address('chris@swïftmailer.org'));
$this->assertEquals('<chris@xn--swftmailer-78a.org>', $header->getBodyAsString());
}
public function testAddressMustBeEncodable()
{
$this->expectException('Symfony\Component\Mime\Exception\AddressEncoderException');
$header = new PathHeader('Return-Path', new Address('chrïs@swiftmailer.org'));
$header->getBodyAsString();
}
public function testSetBody()
{
$header = new PathHeader('Return-Path', new Address('foo@example.com'));
$header->setBody($address = new Address('foo@bar.tld'));
$this->assertEquals($address, $header->getAddress());
}
public function testGetBody()
{
$header = new PathHeader('Return-Path', $address = new Address('foo@bar.tld'));
$this->assertEquals($address, $header->getBody());
}
public function testToString()
{
$header = new PathHeader('Return-Path', new Address('chris@swiftmailer.org'));
$this->assertEquals('Return-Path: <chris@swiftmailer.org>', $header->toString());
}
}

View File

@@ -0,0 +1,247 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Header;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Header\UnstructuredHeader;
class UnstructuredHeaderTest extends TestCase
{
private $charset = 'utf-8';
public function testGetNameReturnsNameVerbatim()
{
$header = new UnstructuredHeader('Subject', '');
$this->assertEquals('Subject', $header->getName());
}
public function testGetValueReturnsValueVerbatim()
{
$header = new UnstructuredHeader('Subject', 'Test');
$this->assertEquals('Test', $header->getValue());
}
public function testBasicStructureIsKeyValuePair()
{
/* -- RFC 2822, 2.2
Header fields are lines composed of a field name, followed by a colon
(":"), followed by a field body, and terminated by CRLF.
*/
$header = new UnstructuredHeader('Subject', 'Test');
$this->assertEquals('Subject: Test', $header->toString());
}
public function testLongHeadersAreFoldedAtWordBoundary()
{
/* -- RFC 2822, 2.2.3
Each header field is logically a single line of characters comprising
the field name, the colon, and the field body. For convenience
however, and to deal with the 998/78 character limitations per line,
the field body portion of a header field can be split into a multiple
line representation; this is called "folding". The general rule is
that wherever this standard allows for folding white space (not
simply WSP characters), a CRLF may be inserted before any WSP.
*/
$value = 'The quick brown fox jumped over the fence, he was a very very '.
'scary brown fox with a bushy tail';
$header = new UnstructuredHeader('X-Custom-Header', $value);
/*
X-Custom-Header: The quick brown fox jumped over the fence, he was a very very
scary brown fox with a bushy tail
*/
$this->assertEquals(
'X-Custom-Header: The quick brown fox jumped over the fence, he was a'.
' very'."\r\n".//Folding
' very scary brown fox with a bushy tail',
$header->toString(), '%s: The header should have been folded at 76th char'
);
}
public function testPrintableAsciiOnlyAppearsInHeaders()
{
/* -- RFC 2822, 2.2.
A field name MUST be composed of printable US-ASCII characters (i.e.,
characters that have values between 33 and 126, inclusive), except
colon. A field body may be composed of any US-ASCII characters,
except for CR and LF.
*/
$nonAsciiChar = pack('C', 0x8F);
$header = new UnstructuredHeader('X-Test', $nonAsciiChar);
$this->assertRegExp('~^[^:\x00-\x20\x80-\xFF]+: [^\x80-\xFF\r\n]+$~s', $header->toString());
}
public function testEncodedWordsFollowGeneralStructure()
{
/* -- RFC 2047, 1.
Generally, an "encoded-word" is a sequence of printable ASCII
characters that begins with "=?", ends with "?=", and has two "?"s in
between.
*/
$nonAsciiChar = pack('C', 0x8F);
$header = new UnstructuredHeader('X-Test', $nonAsciiChar);
$this->assertRegExp('~^X-Test: \=?.*?\?.*?\?.*?\?=$~s', $header->toString());
}
public function testEncodedWordIncludesCharsetAndEncodingMethodAndText()
{
/* -- RFC 2047, 2.
An 'encoded-word' is defined by the following ABNF grammar. The
notation of RFC 822 is used, with the exception that white space
characters MUST NOT appear between components of an 'encoded-word'.
encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
*/
$nonAsciiChar = pack('C', 0x8F);
$header = new UnstructuredHeader('X-Test', $nonAsciiChar);
$header->setCharset('iso-8859-1');
$this->assertEquals('X-Test: =?'.$header->getCharset().'?Q?=8F?=', $header->toString());
}
public function testEncodedWordsAreUsedToEncodedNonPrintableAscii()
{
// SPACE and TAB permitted
$nonPrintableBytes = array_merge(range(0x00, 0x08), range(0x10, 0x19), [0x7F]);
foreach ($nonPrintableBytes as $byte) {
$char = pack('C', $byte);
$encodedChar = sprintf('=%02X', $byte);
$header = new UnstructuredHeader('X-A', $char);
$header->setCharset('iso-8859-1');
$this->assertEquals('X-A: =?'.$header->getCharset().'?Q?'.$encodedChar.'?=', $header->toString(), 'Non-printable ascii should be encoded');
}
}
public function testEncodedWordsAreUsedToEncode8BitOctets()
{
foreach (range(0x80, 0xFF) as $byte) {
$char = pack('C', $byte);
$encodedChar = sprintf('=%02X', $byte);
$header = new UnstructuredHeader('X-A', $char);
$header->setCharset('iso-8859-1');
$this->assertEquals('X-A: =?'.$header->getCharset().'?Q?'.$encodedChar.'?=', $header->toString(), '8-bit octets should be encoded');
}
}
public function testEncodedWordsAreNoMoreThan75CharsPerLine()
{
/* -- RFC 2047, 2.
An 'encoded-word' may not be more than 75 characters long, including
'charset', 'encoding', 'encoded-text', and delimiters.
... SNIP ...
While there is no limit to the length of a multiple-line header
field, each line of a header field that contains one or more
'encoded-word's is limited to 76 characters.
*/
$nonAsciiChar = pack('C', 0x8F);
//Note that multi-line headers begin with LWSP which makes 75 + 1 = 76
//Note also that =?utf-8?q??= is 12 chars which makes 75 - 12 = 63
//* X-Test: is 8 chars
$header = new UnstructuredHeader('X-Test', $nonAsciiChar);
$header->setCharset('iso-8859-1');
$this->assertEquals('X-Test: =?'.$header->getCharset().'?Q?=8F?=', $header->toString());
}
public function testFWSPIsUsedWhenEncoderReturnsMultipleLines()
{
/* --RFC 2047, 2.
If it is desirable to encode more text than will fit in an 'encoded-word' of
75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may
be used.
*/
// Note that multi-line headers begin with LWSP which makes 75 + 1 = 76
// Note also that =?utf-8?q??= is 12 chars which makes 75 - 12 = 63
//* X-Test: is 8 chars
$header = new UnstructuredHeader('X-Test', pack('C', 0x8F).'line_one_here'."\r\n".'line_two_here');
$header->setCharset('iso-8859-1');
$this->assertEquals('X-Test: =?'.$header->getCharset().'?Q?=8Fline=5Fone=5Fhere?='."\r\n".' =?'.$header->getCharset().'?Q?line=5Ftwo=5Fhere?=', $header->toString());
}
public function testAdjacentWordsAreEncodedTogether()
{
/* -- RFC 2047, 5 (1)
Ordinary ASCII text and 'encoded-word's may appear together in the
same header field. However, an 'encoded-word' that appears in a
header field defined as '*text' MUST be separated from any adjacent
'encoded-word' or 'text' by 'linear-white-space'.
-- RFC 2047, 2.
IMPORTANT: 'encoded-word's are designed to be recognized as 'atom's
by an RFC 822 parser. As a consequence, unencoded white space
characters (such as SPACE and HTAB) are FORBIDDEN within an
'encoded-word'.
*/
// It would be valid to encode all words needed, however it's probably
// easiest to encode the longest amount required at a time
$word = 'w'.pack('C', 0x8F).'rd';
$text = 'start '.$word.' '.$word.' then '.$word;
// 'start', ' word word', ' and end', ' word'
$header = new UnstructuredHeader('X-Test', $text);
$header->setCharset('iso-8859-1');
$this->assertEquals('X-Test: start =?'.$header->getCharset().'?Q?'.
'w=8Frd_w=8Frd?= then =?'.$header->getCharset().'?Q?'.
'w=8Frd?=', $header->toString(),
'Adjacent encoded words should appear grouped with WSP encoded'
);
}
public function testLanguageInformationAppearsInEncodedWords()
{
/* -- RFC 2231, 5.
5. Language specification in Encoded Words
RFC 2047 provides support for non-US-ASCII character sets in RFC 822
message header comments, phrases, and any unstructured text field.
This is done by defining an encoded word construct which can appear
in any of these places. Given that these are fields intended for
display, it is sometimes necessary to associate language information
with encoded words as well as just the character set. This
specification extends the definition of an encoded word to allow the
inclusion of such information. This is simply done by suffixing the
character set specification with an asterisk followed by the language
tag. For example:
From: =?US-ASCII*EN?Q?Keith_Moore?= <moore@cs.utk.edu>
*/
$value = 'fo'.pack('C', 0x8F).'bar';
$header = new UnstructuredHeader('Subject', $value);
$header->setLanguage('en');
$header->setCharset('iso-8859-1');
$this->assertEquals('Subject: =?iso-8859-1*en?Q?fo=8Fbar?=', $header->toString());
}
public function testSetBody()
{
$header = new UnstructuredHeader('X-Test', '');
$header->setBody('test');
$this->assertEquals('test', $header->getValue());
}
public function testGetBody()
{
$header = new UnstructuredHeader('Subject', 'test');
$this->assertEquals('test', $header->getBody());
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\MessageConverter;
class MessageConverterTest extends TestCase
{
public function testToEmail()
{
$file = file_get_contents(__DIR__.'/Fixtures/mimetypes/test.gif');
$email = (new Email())->from('fabien@symfony.com');
$this->assertSame($email, MessageConverter::toEmail($email));
$this->assertConversion((clone $email)->text('text content'));
$this->assertConversion((clone $email)->html('HTML content <img src="cid:test.jpg" />'));
$this->assertConversion((clone $email)
->text('text content')
->html('HTML content <img src="cid:test.jpg" />')
);
$this->assertConversion((clone $email)
->text('text content')
->html('HTML content <img src="cid:test.jpg" />')
->embed($file, 'test.jpg', 'image/gif')
);
$this->assertConversion((clone $email)
->text('text content')
->html('HTML content <img src="cid:test.jpg" />')
->attach($file, 'test_attached.jpg', 'image/gif')
);
$this->assertConversion((clone $email)
->text('text content')
->html('HTML content <img src="cid:test.jpg" />')
->embed($file, 'test.jpg', 'image/gif')
->attach($file, 'test_attached.jpg', 'image/gif')
);
$this->assertConversion((clone $email)
->text('text content')
->attach($file, 'test_attached.jpg', 'image/gif')
);
$this->assertConversion((clone $email)
->html('HTML content <img src="cid:test.jpg" />')
->attach($file, 'test_attached.jpg', 'image/gif')
);
$this->assertConversion((clone $email)
->html('HTML content <img src="cid:test.jpg" />')
->embed($file, 'test.jpg', 'image/gif')
);
$this->assertConversion((clone $email)
->text('text content')
->embed($file, 'test_attached.jpg', 'image/gif')
);
}
private function assertConversion(Email $expected)
{
$r = new \ReflectionMethod($expected, 'generateBody');
$r->setAccessible(true);
$message = new Message($expected->getHeaders(), $r->invoke($expected));
$converted = MessageConverter::toEmail($message);
if ($expected->getHtmlBody()) {
$this->assertStringMatchesFormat(str_replace('cid:test.jpg', 'cid:%s', $expected->getHtmlBody()), $converted->getHtmlBody());
$expected->html('HTML content');
$converted->html('HTML content');
}
$this->assertEquals($expected, $converted);
}
}

View File

@@ -0,0 +1,151 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\MailboxListHeader;
use Symfony\Component\Mime\Header\UnstructuredHeader;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\NamedAddress;
use Symfony\Component\Mime\Part\TextPart;
class MessageTest extends TestCase
{
public function testConstruct()
{
$m = new Message();
$this->assertNull($m->getBody());
$this->assertEquals(new Headers(), $m->getHeaders());
$m = new Message($h = (new Headers())->addDateHeader('Date', new \DateTime()), $b = new TextPart('content'));
$this->assertSame($b, $m->getBody());
$this->assertEquals($h, $m->getHeaders());
$m = new Message();
$m->setBody($b);
$m->setHeaders($h);
$this->assertSame($b, $m->getBody());
$this->assertSame($h, $m->getHeaders());
}
public function testGetPreparedHeadersThrowsWhenNoFrom()
{
$this->expectException(\LogicException::class);
(new Message())->getPreparedHeaders();
}
public function testGetPreparedHeadersCloneHeaders()
{
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com']);
$this->assertNotSame($message->getPreparedHeaders(), $message->getHeaders());
}
public function testGetPreparedHeadersSetRequiredHeaders()
{
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com']);
$headers = $message->getPreparedHeaders();
$this->assertTrue($headers->has('MIME-Version'));
$this->assertTrue($headers->has('Message-ID'));
$this->assertTrue($headers->has('Date'));
$this->assertFalse($headers->has('Bcc'));
}
public function testGetPreparedHeaders()
{
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com']);
$h = $message->getPreparedHeaders();
$this->assertCount(4, iterator_to_array($h->all()));
$this->assertEquals(new MailboxListHeader('From', [new Address('fabien@symfony.com')]), $h->get('From'));
$this->assertEquals(new UnstructuredHeader('MIME-Version', '1.0'), $h->get('mime-version'));
$this->assertTrue($h->has('Message-Id'));
$this->assertTrue($h->has('Date'));
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com']);
$message->getHeaders()->addDateHeader('Date', $n = new \DateTimeImmutable());
$this->assertEquals($n, $message->getPreparedHeaders()->get('Date')->getDateTime());
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com']);
$message->getHeaders()->addMailboxListHeader('Bcc', ['fabien@symfony.com']);
$this->assertNull($message->getPreparedHeaders()->get('Bcc'));
}
public function testGetPreparedHeadersWithNoFrom()
{
$this->expectException(\LogicException::class);
(new Message())->getPreparedHeaders();
}
public function testGetPreparedHeadersWithNamedFrom()
{
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', [new NamedAddress('fabien@symfony.com', 'Fabien')]);
$h = $message->getPreparedHeaders();
$this->assertEquals(new MailboxListHeader('From', [new NamedAddress('fabien@symfony.com', 'Fabien')]), $h->get('From'));
$this->assertTrue($h->has('Message-Id'));
}
public function testGetPreparedHeadersHasSenderWhenNeeded()
{
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com']);
$this->assertNull($message->getPreparedHeaders()->get('Sender'));
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com', 'lucas@symfony.com']);
$this->assertEquals('fabien@symfony.com', $message->getPreparedHeaders()->get('Sender')->getAddress()->getAddress());
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com', 'lucas@symfony.com']);
$message->getHeaders()->addMailboxHeader('Sender', 'thomas@symfony.com');
$this->assertEquals('thomas@symfony.com', $message->getPreparedHeaders()->get('Sender')->getAddress()->getAddress());
}
public function testToString()
{
$message = new Message();
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com']);
$expected = <<<EOF
From: fabien@symfony.com
MIME-Version: 1.0
Date: %s
Message-ID: <%s@symfony.com>
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
EOF;
$this->assertStringMatchesFormat($expected, str_replace("\r\n", "\n", $message->toString()));
$this->assertStringMatchesFormat($expected, str_replace("\r\n", "\n", implode('', iterator_to_array($message->toIterable(), false))));
$message = new Message(null, new TextPart('content'));
$message->getHeaders()->addMailboxListHeader('From', ['fabien@symfony.com']);
$expected = <<<EOF
From: fabien@symfony.com
MIME-Version: 1.0
Date: %s
Message-ID: <%s@symfony.com>
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
content
EOF;
$this->assertStringMatchesFormat($expected, str_replace("\r\n", "\n", $message->toString()));
$this->assertStringMatchesFormat($expected, str_replace("\r\n", "\n", implode('', iterator_to_array($message->toIterable(), false))));
}
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use Symfony\Component\Mime\Exception\RuntimeException;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
use Symfony\Component\Mime\MimeTypes;
/**
* @requires extension fileinfo
*/
class MimeTypesTest extends AbstractMimeTypeGuesserTest
{
protected function getGuesser(): MimeTypeGuesserInterface
{
return new MimeTypes();
}
public function testUnsupportedGuesser()
{
$guesser = $this->getGuesser();
$guesser->registerGuesser(new class() implements MimeTypeGuesserInterface {
public function isGuesserSupported(): bool
{
return false;
}
public function guessMimeType(string $mimeType): ?string
{
throw new RuntimeException('Should never be called.');
}
});
$this->assertEquals('image/gif', $guesser->guessMimeType(__DIR__.'/Fixtures/mimetypes/test'));
}
public function testGetExtensions()
{
$mt = new MimeTypes();
$this->assertSame(['mbox'], $mt->getExtensions('application/mbox'));
$this->assertSame(['ai', 'eps', 'ps'], $mt->getExtensions('application/postscript'));
$this->assertSame([], $mt->getExtensions('application/whatever-symfony'));
}
public function testGetMimeTypes()
{
$mt = new MimeTypes();
$this->assertSame(['application/mbox'], $mt->getMimeTypes('mbox'));
$this->assertContains('application/postscript', $mt->getMimeTypes('ai'));
$this->assertContains('application/postscript', $mt->getMimeTypes('ps'));
$this->assertSame([], $mt->getMimeTypes('symfony'));
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\NamedAddress;
class NamedAddressTest extends TestCase
{
public function testConstructor()
{
$a = new NamedAddress('fabien@symfonï.com', 'Fabien');
$this->assertEquals('Fabien', $a->getName());
$this->assertEquals('fabien@symfonï.com', $a->getAddress());
$this->assertEquals('Fabien <fabien@xn--symfon-nwa.com>', $a->toString());
$this->assertEquals('fabien@xn--symfon-nwa.com', $a->getEncodedAddress());
}
public function nameEmptyDataProvider(): array
{
return [[''], [' '], [" \r\n "]];
}
/**
* @dataProvider nameEmptyDataProvider
*/
public function testNameEmpty(string $name)
{
$mail = 'mail@example.org';
$this->assertSame($mail, (new NamedAddress($mail, $name))->getEncodedNamedAddress());
}
}

View File

@@ -0,0 +1,149 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Part;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\IdentificationHeader;
use Symfony\Component\Mime\Header\ParameterizedHeader;
use Symfony\Component\Mime\Header\UnstructuredHeader;
use Symfony\Component\Mime\Part\DataPart;
class DataPartTest extends TestCase
{
public function testConstructor()
{
$p = new DataPart('content');
$this->assertEquals('content', $p->getBody());
$this->assertEquals(base64_encode('content'), $p->bodyToString());
$this->assertEquals(base64_encode('content'), implode('', iterator_to_array($p->bodyToIterable())));
// bodyToIterable() can be called several times
$this->assertEquals(base64_encode('content'), implode('', iterator_to_array($p->bodyToIterable())));
$this->assertEquals('application', $p->getMediaType());
$this->assertEquals('octet-stream', $p->getMediaSubType());
$p = new DataPart('content', null, 'text/html');
$this->assertEquals('text', $p->getMediaType());
$this->assertEquals('html', $p->getMediaSubType());
}
public function testConstructorWithResource()
{
$f = fopen('php://memory', 'r+', false);
fwrite($f, 'content');
rewind($f);
$p = new DataPart($f);
$this->assertEquals('content', $p->getBody());
$this->assertEquals(base64_encode('content'), $p->bodyToString());
$this->assertEquals(base64_encode('content'), implode('', iterator_to_array($p->bodyToIterable())));
fclose($f);
}
public function testConstructorWithNonStringOrResource()
{
$this->expectException(\TypeError::class);
new DataPart(new \stdClass());
}
public function testHeaders()
{
$p = new DataPart('content');
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'application/octet-stream'),
new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
new ParameterizedHeader('Content-Disposition', 'attachment')
), $p->getPreparedHeaders());
$p = new DataPart('content', 'photo.jpg', 'text/html');
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'text/html', ['name' => 'photo.jpg']),
new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
new ParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'photo.jpg', 'filename' => 'photo.jpg'])
), $p->getPreparedHeaders());
}
public function testAsInline()
{
$p = new DataPart('content', 'photo.jpg', 'text/html');
$p->asInline();
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'text/html', ['name' => 'photo.jpg']),
new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
new ParameterizedHeader('Content-Disposition', 'inline', ['name' => 'photo.jpg', 'filename' => 'photo.jpg'])
), $p->getPreparedHeaders());
}
public function testAsInlineWithCID()
{
$p = new DataPart('content', 'photo.jpg', 'text/html');
$p->asInline();
$cid = $p->getContentId();
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'text/html', ['name' => 'photo.jpg']),
new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
new ParameterizedHeader('Content-Disposition', 'inline', ['name' => 'photo.jpg', 'filename' => 'photo.jpg']),
new IdentificationHeader('Content-ID', $cid)
), $p->getPreparedHeaders());
}
public function testFromPath()
{
$p = DataPart::fromPath($file = __DIR__.'/../Fixtures/mimetypes/test.gif');
$content = file_get_contents($file);
$this->assertEquals($content, $p->getBody());
$this->assertEquals(base64_encode($content), $p->bodyToString());
$this->assertEquals(base64_encode($content), implode('', iterator_to_array($p->bodyToIterable())));
$this->assertEquals('image', $p->getMediaType());
$this->assertEquals('gif', $p->getMediaSubType());
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'image/gif', ['name' => 'test.gif']),
new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
new ParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'test.gif', 'filename' => 'test.gif'])
), $p->getPreparedHeaders());
}
public function testFromPathWithMeta()
{
$p = DataPart::fromPath($file = __DIR__.'/../Fixtures/mimetypes/test.gif', 'photo.gif', 'image/jpeg');
$content = file_get_contents($file);
$this->assertEquals($content, $p->getBody());
$this->assertEquals(base64_encode($content), $p->bodyToString());
$this->assertEquals(base64_encode($content), implode('', iterator_to_array($p->bodyToIterable())));
$this->assertEquals('image', $p->getMediaType());
$this->assertEquals('jpeg', $p->getMediaSubType());
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'image/jpeg', ['name' => 'photo.gif']),
new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
new ParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'photo.gif', 'filename' => 'photo.gif'])
), $p->getPreparedHeaders());
}
public function testHasContentId()
{
$p = new DataPart('content');
$this->assertFalse($p->hasContentId());
$p->getContentId();
$this->assertTrue($p->hasContentId());
}
public function testSerialize()
{
$r = fopen('php://memory', 'r+', false);
fwrite($r, 'Text content');
rewind($r);
$p = new DataPart($r);
$p->getHeaders()->addTextHeader('foo', 'bar');
$expected = clone $p;
$this->assertEquals($expected->toString(), unserialize(serialize($p))->toString());
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Part;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\ParameterizedHeader;
use Symfony\Component\Mime\Header\UnstructuredHeader;
use Symfony\Component\Mime\Part\MessagePart;
class MessagePartTest extends TestCase
{
public function testConstructor()
{
$p = new MessagePart((new Email())->from('fabien@symfony.com')->text('content'));
$this->assertStringContainsString('content', $p->getBody());
$this->assertStringContainsString('content', $p->bodyToString());
$this->assertStringContainsString('content', implode('', iterator_to_array($p->bodyToIterable())));
$this->assertEquals('message', $p->getMediaType());
$this->assertEquals('rfc822', $p->getMediaSubType());
}
public function testHeaders()
{
$p = new MessagePart((new Email())->from('fabien@symfony.com')->text('content')->subject('Subject'));
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'message/rfc822', ['name' => 'Subject.eml']),
new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
new ParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'Subject.eml', 'filename' => 'Subject.eml'])
), $p->getPreparedHeaders());
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Part\Multipart;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Part\Multipart\AlternativePart;
class AlternativePartTest extends TestCase
{
public function testConstructor()
{
$a = new AlternativePart();
$this->assertEquals('multipart', $a->getMediaType());
$this->assertEquals('alternative', $a->getMediaSubtype());
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Part\Multipart;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\Part\MessagePart;
use Symfony\Component\Mime\Part\Multipart\DigestPart;
class DigestPartTest extends TestCase
{
public function testConstructor()
{
$r = new DigestPart($a = new MessagePart(new Message()), $b = new MessagePart(new Message()));
$this->assertEquals('multipart', $r->getMediaType());
$this->assertEquals('digest', $r->getMediaSubtype());
$this->assertEquals([$a, $b], $r->getParts());
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Part\Multipart;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\FormDataPart;
use Symfony\Component\Mime\Part\TextPart;
class FormDataPartTest extends TestCase
{
public function testConstructor()
{
$r = new \ReflectionProperty(TextPart::class, 'encoding');
$r->setAccessible(true);
$b = new TextPart('content');
$c = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif');
$f = new FormDataPart([
'foo' => $content = 'very very long content that will not be cut even if the length is way more than 76 characters, ok?',
'bar' => clone $b,
'baz' => clone $c,
]);
$this->assertEquals('multipart', $f->getMediaType());
$this->assertEquals('form-data', $f->getMediaSubtype());
$t = new TextPart($content, 'utf-8', 'plain', '8bit');
$t->setDisposition('form-data');
$t->setName('foo');
$t->getHeaders()->setMaxLineLength(PHP_INT_MAX);
$b->setDisposition('form-data');
$b->setName('bar');
$b->getHeaders()->setMaxLineLength(PHP_INT_MAX);
$r->setValue($b, '8bit');
$c->setDisposition('form-data');
$c->setName('baz');
$c->getHeaders()->setMaxLineLength(PHP_INT_MAX);
$r->setValue($c, '8bit');
$this->assertEquals([$t, $b, $c], $f->getParts());
}
public function testToString()
{
$p = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif');
$this->assertEquals(base64_encode(file_get_contents($file)), $p->bodyToString());
}
public function testContentLineLength()
{
$f = new FormDataPart([
'foo' => new DataPart($foo = str_repeat('foo', 1000), 'foo.txt', 'text/plain'),
'bar' => $bar = str_repeat('bar', 1000),
]);
$parts = $f->getParts();
$this->assertEquals($foo, $parts[0]->bodyToString());
$this->assertEquals($bar, $parts[1]->bodyToString());
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Part\Multipart;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
class MixedPartTest extends TestCase
{
public function testConstructor()
{
$a = new MixedPart();
$this->assertEquals('multipart', $a->getMediaType());
$this->assertEquals('mixed', $a->getMediaSubtype());
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Part\Multipart;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Part\Multipart\RelatedPart;
use Symfony\Component\Mime\Part\TextPart;
class RelatedPartTest extends TestCase
{
public function testConstructor()
{
$r = new RelatedPart($a = new TextPart('content'), $b = new TextPart('HTML content', 'utf-8', 'html'), $c = new TextPart('HTML content again', 'utf-8', 'html'));
$this->assertEquals('multipart', $r->getMediaType());
$this->assertEquals('related', $r->getMediaSubtype());
$this->assertEquals([$a, $b, $c], $r->getParts());
$this->assertFalse($a->getHeaders()->has('Content-ID'));
$this->assertTrue($b->getHeaders()->has('Content-ID'));
$this->assertTrue($c->getHeaders()->has('Content-ID'));
}
}

View File

@@ -0,0 +1,92 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests\Part;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\ParameterizedHeader;
use Symfony\Component\Mime\Header\UnstructuredHeader;
use Symfony\Component\Mime\Part\TextPart;
class TextPartTest extends TestCase
{
public function testConstructor()
{
$p = new TextPart('content');
$this->assertEquals('content', $p->getBody());
$this->assertEquals('content', $p->bodyToString());
$this->assertEquals('content', implode('', iterator_to_array($p->bodyToIterable())));
// bodyToIterable() can be called several times
$this->assertEquals('content', implode('', iterator_to_array($p->bodyToIterable())));
$this->assertEquals('text', $p->getMediaType());
$this->assertEquals('plain', $p->getMediaSubType());
$p = new TextPart('content', null, 'html');
$this->assertEquals('html', $p->getMediaSubType());
}
public function testConstructorWithResource()
{
$f = fopen('php://memory', 'r+', false);
fwrite($f, 'content');
rewind($f);
$p = new TextPart($f);
$this->assertEquals('content', $p->getBody());
$this->assertEquals('content', $p->bodyToString());
$this->assertEquals('content', implode('', iterator_to_array($p->bodyToIterable())));
fclose($f);
}
public function testConstructorWithNonStringOrResource()
{
$this->expectException(\TypeError::class);
new TextPart(new \stdClass());
}
public function testHeaders()
{
$p = new TextPart('content');
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'text/plain', ['charset' => 'utf-8']),
new UnstructuredHeader('Content-Transfer-Encoding', 'quoted-printable')
), $p->getPreparedHeaders());
$p = new TextPart('content', 'iso-8859-1');
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'text/plain', ['charset' => 'iso-8859-1']),
new UnstructuredHeader('Content-Transfer-Encoding', 'quoted-printable')
), $p->getPreparedHeaders());
}
public function testEncoding()
{
$p = new TextPart('content', 'utf-8', 'plain', 'base64');
$this->assertEquals(base64_encode('content'), $p->bodyToString());
$this->assertEquals(base64_encode('content'), implode('', iterator_to_array($p->bodyToIterable())));
$this->assertEquals(new Headers(
new ParameterizedHeader('Content-Type', 'text/plain', ['charset' => 'utf-8']),
new UnstructuredHeader('Content-Transfer-Encoding', 'base64')
), $p->getPreparedHeaders());
}
public function testSerialize()
{
$r = fopen('php://memory', 'r+', false);
fwrite($r, 'Text content');
rewind($r);
$p = new TextPart($r);
$p->getHeaders()->addTextHeader('foo', 'bar');
$expected = clone $p;
$this->assertEquals($expected->toString(), unserialize(serialize($p))->toString());
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\RawMessage;
class RawMessageTest extends TestCase
{
public function testToString()
{
$message = new RawMessage('string');
$this->assertEquals('string', $message->toString());
$this->assertEquals('string', implode('', iterator_to_array($message->toIterable())));
// calling methods more than once work
$this->assertEquals('string', $message->toString());
$this->assertEquals('string', implode('', iterator_to_array($message->toIterable())));
$message = new RawMessage(new \ArrayObject(['some', ' ', 'string']));
$this->assertEquals('some string', $message->toString());
$this->assertEquals('some string', implode('', iterator_to_array($message->toIterable())));
// calling methods more than once work
$this->assertEquals('some string', $message->toString());
$this->assertEquals('some string', implode('', iterator_to_array($message->toIterable())));
}
}