/*
 * 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\Test\Constraint;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Test\Constraint\EmailHasHeader;

clas

ublic function testFailureDescription()
    {
        $headers = new Headers();
        $headers->addMailboxHeader('headerName', 'test@example.com');

        $this->expectException(ExpectationFailedException::class);
        $this->expectExceptionMessage(

s EmailHasHeaderTest extends TestCase
{
    public function testToString()
    {
        $constraint = new EmailHasHeader('headerName');

        $this->assertSame('has header "headerName"', $constraint->toString());
    }

    p


/*
 * 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\Test\Constraint;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Test\Constraint\EmailHtmlBodyContains;

class EmailHtmlBodyContainsTest extends

tFailureDescription()
    {
        $expectedValue = 'expectedValue';
        $email = new Email();
        $email->html('actualValue')->text($expectedValue);

        $this->expectException(ExpectationFailedException::class);
        $this->expectExceptio

TestCase
{
    public function testToString()
    {
        $constraint = new EmailHtmlBodyContains('expectedValue');

        $this->assertSame('contains "expectedValue"', $constraint->toString());
    }

    public function tes


/*
 * 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\Test\Constraint;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Test\Constraint\EmailTextBodyContains;

class EmailTextBodyContainsTest extends

tFailureDescription()
    {
        $expectedValue = 'expectedValue';
        $email = new Email();
        $email->html($expectedValue)->text('actualValue');

        $this->expectException(ExpectationFailedException::class);
        $this->expectExceptio

TestCase
{
    public function testToString()
    {
        $constraint = new EmailTextBodyContains('expectedValue');

        $this->assertSame('contains "expectedValue"', $constraint->toString());
    }

    public function tes


buted with this source code.
 */

namespace Symfony\Component\Mime\Tests\Crypto;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ClockMock;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Crypto\DkimSigner;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Message;

#[RequiresPhpExtension('openssl')]
#[Group('time-sensitive')]
cla

n7PDdZ8EuuxlAa7oNo/38YYV+5Oki93m
        io6rGV8zLMGLLygAB1sJaJVP5W9wm0RLY776YFL4V/nekA5ZTnA4+KaIYwIDAQAB
        AoGAJLhjgoKkA8kI1omkxAjDWRlmqD1Ga4hKy2FYd/GxbnPVVZ+0atUG/Cvarw2d
        kWVZjkxcr8nFoPTrwHOJQgUyOXWLuIuirznoTtDKzC+4JlDsZJd8hkVohqwKfdPA


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Mime\Tests\Crypto;

use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Symfony\Component\Mime\Crypto\SMimeEncrypter;
use Symfony\Component\Mime\Crypto\SMimeSigner;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Message;

#[RequiresPhpExtension('openssl')]
class SMimeEncrypterTest extends SMimeTestCase
{
    public function t

')
            ->from('noreply@example.com')
            ->text('El Barto was not here');

        $message->getHeaders()->addIdHeader('Message-ID', 'some@id');

        $encrypter = new SMimeEncrypter($this->samplesDir.'encrypt.crt');
        $encryptedMe

estEncryptMessage()
    {
        $message = (new Email())
            ->date(new \DateTimeImmutable('2019-04-07 10:36:30', new \DateTimeZone('Europe/Paris')))
            ->to('fabien@symfony.com')
            ->subject('Testing


buted with this source code.
 */

namespace Symfony\Component\Mime\Tests\Crypto;

use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Symfony\Component\Mime\Crypto\SMimeEncrypter;
use Symfony\Component\Mime\Crypto\SMimeSigner;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\TextPart;

#[RequiresPhpExtension('openssl')]
class SMimeSignerTest extends SMimeTestCase
{

                ->addMailboxListHeader('From', ['fabien@symfony.com']),
            new TextPart('content')
        );

        $signer = new SMimeSigner($this->samplesDir.'sign.crt', $this->samplesDir.'sign.key');
        $signedMessage = $signer->sign($m

    public function testSignedMessage()
    {
        $message = new Message(
            (new Headers())
                ->addDateHeader('Date', new \DateTimeImmutable('2019-04-07 10:36:30', new \DateTimeZone('Europe/Paris')))


ge.
 *
 * (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\Crypto;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Exception\RuntimeException;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\RawMessage;

abstract class SMimeTestCase extends TestCase
{
    protected string $samplesDir;

    private array $tmpFiles = [

) {
            @unlink($file);
        }
        $this->tmpFiles = [];
    }

    protected function generateTmpFilename(): string
    {
        return $this->tmpFiles[] = tempnam(sys_get_temp_dir(), 'sf_mime_');
    }

    protected function normalizeFil

];

    protected function setUp(): void
    {
        $this->samplesDir = str_replace('\\', '/', realpath(__DIR__.'/../').'/_data/');
    }

    protected function tearDown(): void
    {
        foreach ($this->tmpFiles as $file


ge.
 *
 * (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 DateHead

imeImmutable());
        $header->setDateTime($dateTime = new \DateTimeImmutable());
        $this->assertEquals($dateTime, $header->getDateTime());
    }

    public function testDateTimeIsConvertedToImmutable()
    {
        $dateTime = new \DateTime();

er('Date', $dateTime = new \DateTimeImmutable());
        $this->assertEquals($dateTime, $header->getDateTime());
    }

    public function testDateTimeCanBeSetBySetter()
    {
        $header = new DateHeader('Date', new \DateT


ny\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\IdentificationHeader;
use Symfony\Component\Mime\Header\MailboxListHeader;
use Symfony\Component\Mime\Header\PathHeader;
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'));
    }

aders->get('Date'));
    }

    public function testAddTextHeaderDelegatesToFactory()
    {
        $headers = new Headers();
        $headers->addTextHeader('Subject', 'some text');
        $this->assertNotNull($headers->get('Subject'));
    }

    public

    public function testAddDateHeaderDelegatesToFactory()
    {
        $dateTime = new \DateTimeImmutable();
        $headers = new Headers();
        $headers->addDateHeader('Date', $dateTime);
        $this->assertNotNull($he


buted 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

ld-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-righ

    =       [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-fo


 *
 * 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;

class MailboxHeaderTest extends TestCase
{
    public function testConstructor()
    {
        $header = new MailboxHeader('Sender', $address = new Address('fabien@symfony.com'));
        $this->assertEquals($a

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')

ddress, $header->getAddress());
        $this->assertEquals($address, $header->getBody());
    }

    public function testAddress()
    {
        $header = new MailboxHeader('Sender', new Address('fabien@symfony.com'));
        $


buted 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;

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->getAddressStrin

ris@swiftmailer.org>'], $header->getAddressStrings());
    }

    public function testAddressCanBeReturnedForAddress()
    {
        $header = new MailboxListHeader('From', $addresses = [new Address('chris@swiftmailer.org')]);
        $this->assertEquals($

gs());
    }

    public function testMailboxIsRenderedForNameAddress()
    {
        $header = new MailboxListHeader('From', [new Address('chris@swiftmailer.org', 'Chris Corbyn')]);
        $this->assertEquals(['Chris Corbyn <ch


ds TestCase
{
    private string $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 c

" / ":" / "\" / <">
                   "/" / "[" / "]" / "?" / "="
                   ; Must be in quoted-string,
                   ; to use within parameter values
        */

        $header = new ParameterizedHeader('Content-Type', 'text/plain');

ase-insensitive.

        value := token / quoted-string

        token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
                 or tspecials>

        tspecials :=  "(" / ")" / "<" / ">" / "@" /
                   "," / ";


ge.
 *
 * (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 PathHeader('Return-Path', new Address('chr is@swiftmailer.org'));
    }

    public function testValueIsAngleAddrWithValidAddress()
    {
        /* -- RFC 2822, 3.6.7.

            return          =       "Return-Path:" path CRLF

            path

 new Address('chris@swiftmailer.org'));
        $this->assertEquals($address, $header->getAddress());
    }

    public function testAddressMustComplyWithRfc2822()
    {
        $this->expectException(\Exception::class);


 TestCase
{
    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

oString());
    }

    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 co

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->t


buted 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 repr

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 inp

esents 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


buted 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

  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 represe

            hexadecimal representation of the octet's value.  The
                    digits of the hexadecimal alphabet, for this purpose,
                    are "0123456789ABCDEF".  Uppercase letters must be


buted 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

DoesNotMatchRegularExpression('~[ \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

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->assert


 *
 * 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 string $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
    fo

oken".
    */

    public function testEncodingAsciiCharactersProducesValidToken()
    {
        $string = '';
        foreach (range(0x00, 0x7F) as $octet) {
            $char = pack('C', $octet);
            $string .= $char;
        }
        $encoder =

r 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 "t


ge.
 *
 * (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\AddMimeTypeGuesser

;
        $container->addCompilerPass(new AddMimeTypeGuesserPass());

        $definition = new Definition(FileinfoMimeTypeGuesser::class);
        $definition->addArgument('/path/to/magic/file');
        $definition->addTag('mime.mime_type_guesser');

Pass;
use Symfony\Component\Mime\FileinfoMimeTypeGuesser;
use Symfony\Component\Mime\MimeTypes;

class AddMimeTypeGuesserPassTest extends TestCase
{
    public function testTags()
    {
        $container = new ContainerBuilder()


 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\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Mime\Header\MailboxHeader;
use Symfony\Component\Mime\Header\MailboxListHeader;
use Symfony\Component\Mime\RawMessage;

final class EmailAddressContains extends Constraint
{
    pub

is->headerName, $this->expectedValue);
    }

    /**
     * @param RawMessage $message
     */
    protected function matches($message): bool
    {
        if (RawMessage::class === $message::class) {
            throw new \LogicException('Unable to test

lic function __construct(
        private string $headerName,
        private string $expectedValue,
    ) {
    }

    public function toString(): string
    {
        return \sprintf('contains address "%s" with value "%s"', $th


 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\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\RawMessage;

final class EmailAttachmentCount extends Constraint
{
    public function __construct(
        private int $expect

tches($message): bool
    {
        if (RawMessage::class === $message::class || Message::class === $message::class) {
            throw new \LogicException('Unable to test a message attachment on a RawMessage or Message instance.');
        }

        ret

edValue,
    ) {
    }

    public function toString(): string
    {
        return \sprintf('has sent "%d" attachment(s)', $this->expectedValue);
    }

    /**
     * @param RawMessage $message
     */
    protected function ma


/*
 * 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\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Mime\RawMessage;

final class EmailHasHeader extends Constraint
{
    public function __construct(
        private string $headerName,
    ) {
    }

    public f

:class === $message::class) {
            throw new \LogicException('Unable to test a message header on a RawMessage instance.');
        }

        return $message->getHeaders()->has($this->headerName);
    }

    /**
     * @param RawMessage $message

unction toString(): string
    {
        return \sprintf('has header "%s"', $this->headerName);
    }

    /**
     * @param RawMessage $message
     */
    protected function matches($message): bool
    {
        if (RawMessage:


 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\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Mime\Header\UnstructuredHeader;
use Symfony\Component\Mime\RawMessage;

final class EmailHeaderSame extends Constraint
{
    public function __construct(
        private string $hea

* @param RawMessage $message
     */
    protected function matches($message): bool
    {
        if (RawMessage::class === $message::class) {
            throw new \LogicException('Unable to test a message header on a RawMessage instance.');
        }

derName,
        private string $expectedValue,
    ) {
    }

    public function toString(): string
    {
        return \sprintf('has header "%s" with value "%s"', $this->headerName, $this->expectedValue);
    }

    /**


/*
 * 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\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\RawMessage;

final class EmailHtmlBodyContains extends Constraint
{
    public function __construct(
        private stri

$message): bool
    {
        if (RawMessage::class === $message::class || Message::class === $message::class) {
            throw new \LogicException('Unable to test a message HTML body on a RawMessage or Message instance.');
        }

        return str

ng $expectedText,
    ) {
    }

    public function toString(): string
    {
        return \sprintf('contains "%s"', $this->expectedText);
    }

    /**
     * @param RawMessage $message
     */
    protected function matches(


 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\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Mime\Email;

final class EmailSubjectContains extends Constraint
{
    public function __construct(
        private readonly string $expectedSubjectValue,
    ) {
    }

il) {
            throw new \LogicException('Can only test a message subject on an Email instance.');
        }

        return str_contains((string) $other->getSubject(), $this->expectedSubjectValue);
    }

    protected function failureDescription($othe

    public function toString(): string
    {
        return \sprintf('contains subject with value "%s"', $this->expectedSubjectValue);
    }

    protected function matches($other): bool
    {
        if (!$other instanceof Ema


/*
 * 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\Test\Constraint;

use PHPUnit\Framework\Constraint\Constraint;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\RawMessage;

final class EmailTextBodyContains extends Constraint
{
    public function __construct(
        private stri

$message): bool
    {
        if (RawMessage::class === $message::class || Message::class === $message::class) {
            throw new \LogicException('Unable to test a message text body on a RawMessage or Message instance.');
        }

        return str


 *
 * 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 AbstractMimeTypeGuesserTestCase extends TestCase
{
    public static function tearDownAfterClass(): void
    {
        $path = __DIR__.'/Fixtures/mimetypes/to_delete';
        if (file_exists($path)) {
            @chmod($path, 0o666)

{
            $this->markTestSkipped('Guesser is not supported');
        }

        $cwd = getcwd();
        chdir(__DIR__.'/Fixtures/mimetypes');
        try {
            $this->assertEquals('image/gif', $this->getGuesser()->guessMimeType('-test'));

;
            @unlink($path);
        }
    }

    abstract protected function getGuesser(): MimeTypeGuesserInterface;

    public function testGuessWithLeadingDash()
    {
        if (!$this->getGuesser()->isGuesserSupported())


 *
 * 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,

ream->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));
        $

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), $st


use Symfony\Component\Mime\Test\Constraint\EmailHeaderSame;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\MimeMessageNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;

class EmailTestToStringGadget
{

public function testUnserializeRejectsObjectInTypedCharsetProperty(int $slot)
    {
        $email = new Email();
        $email->text('text body');
        $email->html('html body');
        $data = $email->__serialize();
        $data[$slot] = new EmailT

   public static bool $fired = false;

    public function __toString(): string
    {
        self::$fired = true;

        return '';
    }
}

class EmailTest extends TestCase
{
    #[DataProvider('provideTrampolineSlots')]


ponent\Mime\Exception\LogicException;
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\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\AlternativePart;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
use Symfony\Component\Mime\Part\TextPart;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializ

omponent\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;

class MessageTest extends TestCase
{
    public function testConstruct()
    {
        $m = new Message();
        $this->assertNull($m->getBody());
        $t

er\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\MimeMessageNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\C


ge.
 *
 * (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\Attributes\RequiresPhpExtension;
use Symfony\Component\Mime\Exception\RuntimeException;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
use Symfony\Component\Mime\MimeTypes;

#[RequiresPhpExtension('fileinfo')]
class MimeTypesTest extends AbstractMimeTypeGu

r->registerGuesser(new class implements MimeTypeGuesserInterface {
            public function isGuesserSupported(): bool
            {
                return false;
            }

            public function guessMimeType(string $mimeType): ?string

esserTestCase
{
    protected function getGuesser(): MimeTypeGuesserInterface
    {
        return new MimeTypes();
    }

    public function testUnsupportedGuesser()
    {
        $guesser = $this->getGuesser();
        $guesse


 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\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Part\SMimePart;

class SMimePartTestToStringGadget
{
    public static bool $fired = false;

    public function __toString(): string
    {

   $template = (new SMimePart('body content', 'application', 'pkcs7-mime', []))->__serialize();
        $template[$key] = new SMimePartTestToStringGadget();
        $payload = \sprintf('O:%d:"%s":%d:{', \strlen(SMimePart::class), SMimePart::class, \count($

 self::$fired = true;

        return '';
    }
}

class SMimePartTest extends TestCase
{
    #[DataProvider('provideTrampolineKeys')]
    public function testUnserializeRejectsObjectInTypedStringProperty(string $key)
    {


ge.
 *
 * (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;

use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Exception\LogicException;

/**
 * Guesses the MIME type with the binary "file" (only available on *nix).
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class FileBinaryMimeTypeGuesser imple

  *
     * @param string $cmd The command to run to get the MIME type of a file
     */
    public function __construct(
        private string $cmd = 'file -b --mime -- %s 2>/dev/null',
    ) {
    }

    public function isGuesserSupported(): bool
    {

ments MimeTypeGuesserInterface
{
    /**
     * The $cmd pattern must contain a "%s" string that will be replaced
     * with the file name to guess.
     *
     * The command output must start with the MIME type of the file.


 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;

use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Exception\LogicException;
use Symfony\Component\Mime\Exception\RuntimeException;

/**
 * Guesses the MIME type using the PECL extension FileInfo.
 *
 * @author Bernhard Schus

e A magic file to use with the finfo instance
     *
     * @see https://php.net/finfo-open
     */
    public function __construct(
        private ?string $magicFile = null,
    ) {
    }

    public function isGuesserSupported(): bool
    {
        retu

sek <bschussek@gmail.com>
 */
class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
{
    /**
     * @var array<string, \finfo>
     */
    private static $finfoCache = [];

    /**
     * @param string|null $magicFil


buted with this source code.
 */

namespace Symfony\Component\Mime;

use Symfony\Component\Mime\Exception\RuntimeException;
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;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class MessageConverter
{
    /**
     * @throws RuntimeException when una

ple" Email instance
        $body = $message->getBody();
        if ($body instanceof TextPart) {
            return self::createEmailFromTextPart($message, $body);
        }

        if ($body instanceof AlternativePart) {
            return self::createE

ble to convert the message to an email
     */
    public static function toEmail(Message $message): Email
    {
        if ($message instanceof Email) {
            return $message;
        }

        // try to convert to a "sim


   'application/fdf' => ['fdf'],
        'application/fdt+xml' => ['fdt'],
        'application/fits' => ['fits', 'fit', 'fts'],
        'application/font-tdpfr' => ['pfr'],
        'application/font-woff' => ['woff'],
        'application/futuresplash' => ['swf', 'spl'],
        'application/geo+json' => ['geojson', 'geo.json'],
        'application/gml+xml' => ['gml'],
        'application/gnunet-directory' => ['gnd'],
        'application/gpx' => ['gpx'],
        'application/gpx+xml' => ['gpx'],

['stk'],
        'application/ico' => ['ico'],
        'application/ics' => ['vcs', 'ics', 'ifb', 'icalendar'],
        'application/illustrator' => ['ai'],
        'application/inkml+xml' => ['ink', 'inkml'],
        'application/ipfix' => ['ipfix'],

 'application/gxf' => ['gxf'],
        'application/gzip' => ['gz'],
        'application/har+json' => ['har'],
        'application/hjson' => ['hjson'],
        'application/hta' => ['hta'],
        'application/hyperstudio' =>


ge.
 *
 * (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;

use Symfony\Component\Mime\Exception\LogicException;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class RawMessage
{
    private bool $isGeneratorClosed;

    /**
     * @param iterable<string>|string|resource $message
     */
    public function __construct(
        private $

this->message)) {
            return $this->message;
        }

        if (\is_resource($this->message)) {
            return stream_get_contents($this->message, -1, 0);
        }

        $message = '';
        foreach ($this->message as $chunk) {

message,
    ) {
    }

    public function __destruct()
    {
        if (\is_resource($this->message)) {
            fclose($this->message);
        }
    }

    public function toString(): string
    {
        if (\is_string($


ge.
 *
 * (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\Part;

use Symfony\Component\Mime\Header\Headers;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class AbstractMultipartPart extends AbstractPart
{
    private ?string $boundary = null;
    private array $parts = [];

    public function __construct(AbstractPart ...$parts

turn $this->parts;
    }

    public function getMediaType(): string
    {
        return 'multipart';
    }

    public function getPreparedHeaders(): Headers
    {
        $headers = parent::getPreparedHeaders();
        $headers->setHeaderParameter('Con

)
    {
        parent::__construct();

        foreach ($parts as $part) {
            $this->parts[] = $part;
        }
    }

    /**
     * @return AbstractPart[]
     */
    public function getParts(): array
    {
        re


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Mime\Part;

use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Header\Headers;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DataPart extends TextPart
{
    private ?string $filename = null;
    private string $mediaType;
    private ?string $cid = null;

    /**
     * @param resource|stri

nstanceof File && !$filename) {
            $filename = $body->getFilename();
        }

        $contentType ??= $body instanceof File ? $body->getContentType() : 'application/octet-stream';
        [$this->mediaType, $subtype] = explode('/', $contentType

ng|File $body Use a File instance to defer loading the file until rendering
     */
    public function __construct($body, ?string $filename = null, ?string $contentType = null, ?string $encoding = null)
    {
        if ($body i


hp

/*
 * 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\Part;

use Symfony\Component\Mime\MimeTypes;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class File
{
    private static MimeTypes $mimeTypes;

    public function __construct(
        private string $path,
        private ?string

ON));
        self::$mimeTypes ??= new MimeTypes();

        return self::$mimeTypes->getMimeTypes($ext)[0] ?? 'application/octet-stream';
    }

    public function getSize(): int
    {
        return filesize($this->path);
    }

    public function getF

 $filename = null,
    ) {
    }

    public function getPath(): string
    {
        return $this->path;
    }

    public function getContentType(): string
    {
        $ext = strtolower(pathinfo($this->path, \PATHINFO_EXTENSI


buted with this source code.
 */

namespace Symfony\Component\Mime\Part;

use Symfony\Component\Mime\Encoder\Base64ContentEncoder;
use Symfony\Component\Mime\Encoder\ContentEncoderInterface;
use Symfony\Component\Mime\Encoder\EightBitContentEncoder;
use Symfony\Component\Mime\Encoder\QpContentEncoder;
use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Header\Headers;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TextPart extends AbstractPart
{
    pri

   private ?string $disposition = null;
    private ?string $name = null;
    private string $encoding;
    private ?bool $seekable = null;

    /**
     * @param resource|string|File $body Use a File instance to defer loading the file until rendering

vate const DEFAULT_ENCODERS = ['quoted-printable', 'base64', '8bit'];

    private static array $encoders = [];

    /** @var resource|string|File */
    private $body;
    private ?string $charset;
    private string $subtype;


buted with this source code.
 */

if ('cli' !== \PHP_SAPI) {
    throw new Exception('This script must be run from the command line.');
}

// load new map
$data = json_decode(file_get_contents('https://cdn.jsdelivr.net/gh/jshttp/mime-db/db.json'), true);
$new = [];
foreach ($data as $mimeType => $mimeTypeInformation) {
    if (!array_key_exists('extensions', $mimeTypeInformation)) {
        continue;
    }
    $new[$mimeType] = $mimeTypeInformation['extensions'];
}

$xml = simplexml_load_string(file_get_con

        if ('*' != $pattern[0] || '.' != $pattern[1]) {
            continue;
        }

        $exts[] = substr($pattern, 2);
    }

    if (!$exts) {
        continue;
    }

    $mt = strtolower((string) $node['type']);
    $new[$mt] = array_merge($new

tents('https://gitlab.freedesktop.org/xdg/shared-mime-info/-/raw/master/data/freedesktop.org.xml.in'));
foreach ($xml as $node) {
    $exts = [];
    foreach ($node->glob as $glob) {
        $pattern = (string) $glob['pattern'];


buted with this source code.
 */

namespace Symfony\Component\Mime\Tests;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Exception\RfcComplianceException;

class AddressTest extends TestCase
{
    public function testConstructor()
    {
        $a = new Address('fabien@symfonï.com');
        $this->assertEquals('fabien@symfonï.com', $a->getAddress());

s->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-

        $this->assertEquals('fabien@xn--symfon-nwa.com', $a->toString());
        $this->assertEquals('fabien@xn--symfon-nwa.com', $a->getEncodedAddress());

        $a = new Address('fabien@symfonï.com', 'Fabien');
        $thi


 *
 * 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;
use Symfony\Component\Mime\Part\DataPart;

class MessageConverterTest extends TestCase
{
    public function testToEmail()
    {
        $file = file_get_contents(__DIR__.'/Fixtures/mimety

content'));
        $this->assertConversion((clone $email)->html('HTML content '));
        $this->assertConversion((clone $email)
            ->text('text content')
            ->html('HTML content ')

pes/test.gif');
        $email = (new Email())->from('fabien@symfony.com')->to('you@example.com');
        $this->assertSame($email, MessageConverter::toEmail($email));

        $this->assertConversion((clone $email)->text('text


der\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;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;

class DataPartTest extends TestCase
{
    public function testConstructor()
    {
        $p = new DataPart('content');
        $this->assertEquals('content', $p->getBody());
        $this->assertEqual

assertEquals(base64_encode('content'), implode('', iterator_to_array($p->bodyToIterable())));
        $this->assertEquals('application', $p->getMediaType());
        $this->assertEquals('octet-stream', $p->getMediaSubType());

        $p = new DataPart('co

s(base64_encode('content'), $p->bodyToString());
        $this->assertEquals(base64_encode('content'), implode('', iterator_to_array($p->bodyToIterable())));
        // bodyToIterable() can be called several times
        $this->


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Mime;

use Symfony\Component\Mime\Exception\LogicException;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Part\AbstractPart;
use Symfony\Component\Mime\Part\TextPart;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Message extends RawMessage
{
    private Headers $headers;

    public function __construct(

this->headers;

        if (null !== $this->body) {
            $this->body = clone $this->body;
        }
    }

    /**
     * @return $this
     */
    public function setBody(?AbstractPart $body): static
    {
        $this->body = $body;

        retu

        ?Headers $headers = null,
        private ?AbstractPart $body = null,
    ) {
        $this->headers = $headers ? clone $headers : new Headers();
    }

    public function __clone()
    {
        $this->headers = clone $


he 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\HtmlToTextConverter;

use League\HTMLToMarkdown\HtmlConverter;
use League\HTMLToMarkdown\HtmlConverterInterface;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class LeagueHtmlToMarkdownConverter implements HtmlToTextConverterInterface
{
    public function __const

ter = new HtmlConverter([
            'hard_break' => true,
            'strip_tags' => true,
            'remove_nodes' => 'head style',
        ]),
    ) {
    }

    public function convert(string $html, string $charset): string
    {
        return $th

ruct(
        private HtmlConverterInterface $conver


 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\Part;

use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\RawMessage;

/**
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class MessagePart extends DataPart
{
    public function __construct(
        private RawMessage $message,
    )

    public function getMediaType(): string
    {
        return 'message';
    }

    public function getMediaSubtype(): string
    {
        return 'rfc822';
    }

    public function getBody(): string
    {
        return $this->message->toString();

{
        if ($message instanceof Message) {
            $name = $message->getHeaders()->getHeaderBody('Subject').'.eml';
        } else {
            $name = 'email.eml';
        }
        parent::__construct('', $name);
    }


 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\DraftEmail;
use Symfony\Component\Mime\Exception\LogicException;

/**
 * @author Kevin Bond <kevinbond@gmail.com>
 */
final class DraftEmailTest extends TestCase
{
    public function testCanHaveJust

is->assertStringContainsString('X-Unsent: 1', $email);
    }

    public function testBccIsRemoved()
    {
        $email = (new DraftEmail())->text('some text')->bcc('sam@example.com')->toString();

        $this->assertStringNotContainsString('sam@exampl

Body()
    {
        $email = (new DraftEmail())->text('some text')->toString();

        $this->assertStringContainsString('some text', $email);
        $this->assertStringContainsString('MIME-Version: 1.0', $email);
        $th


buted with this source code.
 */

namespace Symfony\Component\Mime\Tests\Part;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Encoder\ContentEncoderInterface;
use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Exception\RuntimeException;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\ParameterizedHeader;
use Symfony\Component\Mime\Header\UnstructuredHeader;
use Symfony\Component\M

    }
}

class TextPartTest extends TestCase
{
    #[DataProvider('provideTrampolineKeys')]
    public function testUnserializeRejectsObjectInTypedStringProperty(string $key)
    {
        $template = (new TextPart('body content'))->__serialize();

ime\Part\File;
use Symfony\Component\Mime\Part\TextPart;

class TextPartTestToStringGadget
{
    public static bool $fired = false;

    public function __toString(): string
    {
        self::$fired = true;

        return '';


/*
 * 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;

/**
 * Guesses the MIME type of a file.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface MimeTypeGuesserInterface
{
    /**
     * Returns true if this guesser is supported.
     */
    public function isGuesserSupported(): boo

 with the given path.
     *
     * @throws \LogicException           If the guesser is not supported
     * @throws \InvalidArgumentException If the file does not exist or is not readable
     */
    public function guessMimeType(string $path): ?string;
}

l;

    /**
     * Guesses the MIME type of the file


ge.
 *
 * (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 MessagePa

tent', $p->getBody());
        $this->assertStringContainsString('content', $p->bodyToString());
        $this->assertStringContainsString('content', implode('', iterator_to_array($p->bodyToIterable())));
        $this->assertEquals('message', $p->getMedia

rtTest extends TestCase
{
    public function testConstructor()
    {
        $p = new MessagePart((new Email())->from('fabien@symfony.com')->to('you@example.com')->text('content'));
        $this->assertStringContainsString('con


 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\Part\Multipart;

use Symfony\Component\Mime\Part\AbstractMultipartPart;
use Symfony\Component\Mime\Part\AbstractPart;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class RelatedPart extends AbstractMultipartPart
{
    public function _

lic function getParts(): array
    {
        return array_merge([$this->mainPart], parent::getParts());
    }

    public function getMediaSubtype(): string
    {
        return 'related';
    }

    private function generateContentId(): string
    {

_construct(
        private AbstractPart $mainPart,
        AbstractPart $part,
        AbstractPart ...$parts,
    ) {
        $this->prepareParts($part, ...$parts);

        parent::__construct($part, ...$parts);
    }

    pub


 *
 * 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\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\Exception\LogicException;
use Symfony\Component\Mime\RawMessage;

class RawMessageTest extends TestCase
{
    #[DataProvider('provideMessages')]
    public function testToString(mixed $messageParameter, bool $supportReuse)
    {

if ($supportReuse) {
            // calling methods more than once work
            $this->assertEquals('some string', $message->toString());
            $this->assertEquals('some string', implode('', iterator_to_array($message->toIterable())));
        }

        $message = new RawMessage($messageParameter);
        $this->assertEquals('some string', $message->toString());
        $this->assertEquals('some string', implode('', iterator_to_array($message->toIterable())));


 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\Part;

use Symfony\Component\Mime\Header\Headers;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class AbstractPart
{
    private Headers $headers;

    public function __construct()
    {
        $this->headers = new Headers();
    }

    public

t-Type', $this->getMediaType().'/'.$this->getMediaSubtype());

        return $headers;
    }

    public function toString(): string
    {
        return $this->getPreparedHeaders()->toString()."\r\n".$this->bodyToString();
    }

    public function toIt

 function getHeaders(): Headers
    {
        return $this->headers;
    }

    public function getPreparedHeaders(): Headers
    {
        $headers = clone $this->headers;
        $headers->setHeaderBody('Parameterized', 'Conten


ge.
 *
 * (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\Part;

use Symfony\Component\Mime\Header\Headers;

/**
 * @author Sebastiaan Stok <s.stok@rollerscapes.net>
 */
class SMimePart extends AbstractPart
{
    public function __construct(
        private iterable|string $body,
        private string $type,
        private string $subtype,

is->subtype;
    }

    public function bodyToString(): string
    {
        if (\is_string($this->body)) {
            return $this->body;
        }

        $body = '';
        foreach ($this->body as $chunk) {
            $body .= $chunk;
        }

  private array $parameters,
    ) {
        parent::__construct();
    }

    public function getMediaType(): string
    {
        return $this->type;
    }

    public function getMediaSubtype(): string
    {
        return $th


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Scheduler;

use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Clock\Clock;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\Scheduler\Event\FailureEvent;
use Symfony\Component\Scheduler\Event\PostRunEvent;
use Symfony\Component\Scheduler\Event\PreRunEvent;
use Symfony\Component\Scheduler\Generator\MessageGen

$schedules
     */
    public function __construct(
        private readonly array $handlers,
        array $schedules,
        private readonly ClockInterface $clock = new Clock(),
        private readonly ?EventDispatcherInterface $dispatcher = null,

erator;

final class Scheduler
{
    /**
     * @var array<MessageGenerator>
     */
    private array $generators = [];
    private int $index = 0;
    private bool $shouldStop = false;

    /**
     * @param iterable<Schedule>


buted with this source code.
 */

namespace Symfony\Component\Scheduler\Generator;

use Psr\Clock\ClockInterface;
use Symfony\Component\Clock\Clock;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Component\Scheduler\Trigger\StatefulTriggerInterface;

final class MessageGenerator implements MessageGeneratorInterface
{
    private ?Schedule $schedule = null;
    private TriggerHeap $triggerHeap;

,
        private readonly ClockInterface $clock = new Clock(),
        private ?CheckpointInterface $checkpoint = null,
    ) {
        $this->waitUntil = new \DateTimeImmutable('@0');
    }

    /**
     * @return \Generator<MessageContext, object>

    private bool $heapInitialized = false;
    private ?\DateTimeImmutable $waitUntil;

    public function __construct(
        private readonly ScheduleProviderInterface $scheduleProvider,
        private readonly string $name


ackage.
 *
 * (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\Scheduler\Generator;

use Symfony\Component\Scheduler\RecurringMessage;

/**
 * @internal
 *
 * @extends \SplHeap<array{\DateTimeImmutable, int, RecurringMessage}>
 */
final class TriggerHeap extends \SplHeap
{
    public function __construct(
        public \DateTimeImmutable $time,
    ) {

e, int, RecurringMessage} $value1
     * @param array{\DateTimeImmutable, int, RecurringMessage} $value2
     */
    protected function compare(mixed $value1, mixed $value2): int
    {
        return $value2[0] <=> $value1[0] ?: $value2[1] <=> $value1[1];

    }

    /**
     * @param array{\DateTimeImmutabl


ge.
 *
 * (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\Scheduler\Generator;

use Psr\Cache\CacheItemInterface;
use Symfony\Component\Lock\LockInterface;
use Symfony\Contracts\Cache\CacheInterface;

final class Checkpoint implements CheckpointInterface
{
    private const CACHE_EXPIRY = 5 * 365 * 86400; // 5 years

    private \DateTimeImmutable $from

        private readonly ?CacheInterface $cache = null,
    ) {
    }

    public function acquire(\DateTimeImmutable $now): bool
    {
        if ($this->lock && !$this->lock->acquire()) {
            // Reset local state if a Lock is acquired by another

;
    private \DateTimeImmutable $time;
    private int $index = -1;
    private bool $reset = false;

    public function __construct(
        private readonly string $name,
        private readonly ?LockInterface $lock = null,


buted with this source code.
 */

namespace Symfony\Component\Mime\Crypto;

use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Exception\RuntimeException;
use Symfony\Component\Mime\Header\UnstructuredHeader;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\Part\AbstractPart;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * RFC 6376 and 8301
 */
final class DkimSigner
{
    public const CANON_SIMPLE = 'simple';
    public const CANON_RELAXED =

 or the path to the file containing the private key, should be prefixed with file:// (in PEM format)
     * @param string $passphrase A passphrase of the private key (if any)
     */
    public function __construct(
        string $pk,
        private stri

'relaxed';

    public const ALGO_SHA256 = 'rsa-sha256';
    public const ALGO_ED25519 = 'ed25519-sha256'; // RFC 8463

    private \OpenSSLAsymmetricKey $key;

    /**
     * @param string $pk         The private key as a string


ge.
 *
 * (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\Crypto;

use Symfony\Component\Mime\Exception\RuntimeException;
use Symfony\Component\Mime\Message;

/**
 * @author Sebastiaan Stok <s.stok@rollerscapes.net>
 */
final class SMimeEncrypter extends SMime
{
    private string|array $certs;
    private int $cipher;

    /**
     * @param string

: https://php.net/openssl.ciphers
     */
    public function __construct(string|array $certificate, ?int $cipher = null)
    {
        if (!\extension_loaded('openssl')) {
            throw new \LogicException('PHP extension "openssl" is required to use S

|string[] $certificate The path (or array of paths) of the file(s) containing the X.509 certificate(s)
     * @param int|null        $cipher      A set of algorithms used to encrypt the message. Must be one of these PHP constants


 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\Header;

/**
 * A MIME Header.
 *
 * @author Chris Corbyn
 */
interface HeaderInterface
{
    /**
     * Sets the body.
     *
     * The type depends on the Header concrete class.
     */
    public function setBody(mixed $body): void;

    /**
     *

    public function setLanguage(string $lang): void;

    public function getLanguage(): ?string;

    public function getName(): string;

    public function setMaxLineLength(int $lineLength): void;

    public function getMaxLineLength(): int;

    /**

Gets the body.
     *
     * The return type depends on the Header concrete class.
     */
    public function getBody(): mixed;

    public function setCharset(string $charset): void;

    public function getCharset(): ?string;


ge.
 *
 * (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\Header;

use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;

/**
 * An ID MIME Header for something like Message-ID or Content-ID (one or more addresses).
 *
 * @author Chris Corbyn
 */
final class IdentificationHeader extends AbstractHeader
{

ram string|string[] $body a string ID or an array of IDs
     *
     * @throws RfcComplianceException
     */
    public function setBody(mixed $body): void
    {
        $this->setId($body);
    }

    public function getBody(): array
    {
        return

  private array $ids = [];
    private array $idsAsAddresses = [];

    public function __construct(string $name, string|array $ids)
    {
        parent::__construct($name);

        $this->setId($ids);
    }

    /**
     * @pa


 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\Header;

use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;

/**
 * A Mailbox MIME Header for something like Sender (one named address).
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class MailboxHeader e

ddress $body
     *
     * @throws RfcComplianceException
     */
    public function setBody(mixed $body): void
    {
        $this->setAddress($body);
    }

    /**
     * @throws RfcComplianceException
     */
    public function getBody(): Address

xtends AbstractHeader
{
    private Address $address;

    public function __construct(string $name, Address $address)
    {
        parent::__construct($name);

        $this->setAddress($address);
    }

    /**
     * @param A


 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\Header;

use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;

/**
 * A Path Header, such a Return-Path (one address).
 *
 * @author Chris Corbyn
 */
final class PathHeader extends AbstractHeader
{
    private

RfcComplianceException
     */
    public function setBody(mixed $body): void
    {
        $this->setAddress($body);
    }

    public function getBody(): Address
    {
        return $this->getAddress();
    }

    public function setAddress(Address $add

 Address $address;

    public function __construct(string $name, Address $address)
    {
        parent::__construct($name);

        $this->setAddress($address);
    }

    /**
     * @param Address $body
     *
     * @throws


 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\Header;

/**
 * A Simple MIME Header.
 *
 * @author Chris Corbyn
 */
class UnstructuredHeader extends AbstractHeader
{
    private string $value;

    public function __construct(string $name, string $value)
    {
        parent::__construct($name);

rn $this->getValue();
    }

    /**
     * Get the (unencoded) value of this header.
     */
    public function getValue(): string
    {
        return $this->value;
    }

    /**
     * Set the (unencoded) value of this header.
     */
    public funct

      $this->setValue($value);
    }

    /**
     * @param string $body
     */
    public function setBody(mixed $body): void
    {
        $this->setValue($body);
    }

    public function getBody(): string
    {
        retu


 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\Encoder;

/**
 * @author Chris Corbyn
 */
final class Base64MimeHeaderEncoder extends Base64Encoder implements MimeHeaderEncoderInterface
{
    public function getName(): string
    {
        return 'B';
    }

    /**
     * Takes an unencoded string a

eString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
    {
        if ('iso-2022-jp' === strtolower($charset)) {
            $old = mb_internal_encoding();
            mb_internal_encoding('utf-8');

nd produces a Base64 encoded string from it.
     *
     * If the charset is iso-2022-jp, it uses mb_encode_mimeheader instead of
     * default encodeString, otherwise pass to the parent method.
     */
    public function encod


 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\Encoder;

/**
 * An IDN email address encoder.
 *
 * Encodes the domain part of an address using IDN. This is compatible will all
 * SMTP servers.
 *
 * Note: It leaves the local part as is. In case there are non-ASCII characters
 * in the local part th

    public function encodeString(string $address): string
    {
        $i = strrpos($address, '@');
        if (false !== $i) {
            $local = substr($address, 0, $i);
            $domain = substr($address, $i + 1);

            if (preg_match('/[^

en it depends on the SMTP Server if this is supported.
 *
 * @author Christian Schmidt
 */
final class IdnAddressEncoder implements AddressEncoderInterface
{
    /**
     * Encodes the domain part of an address using IDN.
     */


 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\Encoder;

/**
 * @author Lars Strojny
 */
final class QpContentEncoder implements ContentEncoderInterface
{
    public function encodeByteStream($stream, int $maxLineLength = 0): iterable
    {
        if (!\is_resource($stream)) {
            throw new \TypeError(\

am), 'utf-8', 0, $maxLineLength);
    }

    public function getName(): string
    {
        return 'quoted-printable';
    }

    public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): s

sprintf('Method "%s" takes a stream as a first argument.', __METHOD__));
        }

        // we don't use PHP stream filters here as the content should be small enough
        yield $this->encodeString(stream_get_contents($stre


hp

/*
 * 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\Encoder;

/**
 * @author Chris Corbyn
 */
final class QpMimeHeaderEncoder extends QpEncoder implements MimeHeaderEncoderInterface
{
    protected function initSafeMap(): void
    {
        foreach (array_merge(
            range(0x61, 0x7A), ra

rn 'Q';
    }

    public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
    {
        return str_replace([' ', '=20', "=\r\n"], ['_', '_', "\r\n"],
            parent::encodeStri

nge(0x41, 0x5A),
            range(0x30, 0x39), [0x20, 0x21, 0x2A, 0x2B, 0x2D, 0x2F]
        ) as $byte) {
            $this->safeMap[$byte] = \chr($byte);
        }
    }

    public function getName(): string
    {
        retu


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Scheduler;

use Symfony\Component\Scheduler\Exception\InvalidArgumentException;
use Symfony\Component\Scheduler\Generator\MessageContext;
use Symfony\Component\Scheduler\Trigger\CronExpressionTrigger;
use Symfony\Component\Scheduler\Trigger\JitterTrigger;
use Symfony\Component\Scheduler\Trigger\MessageProviderInterface;
use Symfony\Component\Sche

vate string $id;

    private function __construct(
        private readonly TriggerInterface $trigger,
        private readonly MessageProviderInterface $provider,
    ) {
    }

    /**
     * Sets the trigger frequency.
     *
     * Supported frequency

duler\Trigger\PeriodicalTrigger;
use Symfony\Component\Scheduler\Trigger\StaticMessageProvider;
use Symfony\Component\Scheduler\Trigger\TriggerInterface;

final class RecurringMessage implements MessageProviderInterface
{
    pri


ge.
 *
 * (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\Scheduler\Attribute;

/**
 * A marker to call a service method from scheduler.
 *
 * @author valtzu <valtzu@gmail.com>
 */
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class AsCronTask
{
    /**
     * @param string                   $expression

in seconds; for example, if set to 60, the cron
     *                                             will randomly wait for a number of seconds between 0 and 60 before
     *                                             executing which allows to avoid load sp

The cron expression to define the task schedule (i.e. "5 * * * *")
     * @param string|null              $timezone   The timezone used with the cron expression
     * @param int|null                 $jitter     The cron jitter,


 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\Crypto;

/**
 * A helper providing autocompletion for available DkimSigner options.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class DkimOptions
{
    private array $options = [];

    public function toArray(): array
    {
        return $this->o

lic function signatureExpirationDelay(int $show): static
    {
        $this->options['signature_expiration_delay'] = $show;

        return $this;
    }

    /**
     * @return $this
     */
    public function bodyMaxLength(int $max): static
    {

ptions;
    }

    /**
     * @return $this
     */
    public function algorithm(string $algo): static
    {
        $this->options['algorithm'] = $algo;

        return $this;
    }

    /**
     * @return $this
     */
    pub


ge.
 *
 * (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\Crypto;

use Symfony\Component\Mime\Exception\RuntimeException;
use Symfony\Component\Mime\Message;

/**
 * @author Sebastiaan Stok <s.stok@rollerscapes.net>
 */
final class SMimeSigner extends SMime
{
    private string $signCertificate;
    private string|array $signPrivateKey;
    private

The path of the file containing the private key (in PEM format)
     * @param string|null $privateKeyPassphrase A passphrase of the private key (if any)
     * @param string|null $extraCerts           The path of the file containing intermediate certificat

 int $signOptions;
    private ?string $extraCerts;

    /**
     * @param string      $certificate          The path of the file containing the signing certificate (in PEM format)
     * @param string      $privateKey


author Fabien Potencier <fabien@symfony.com>
 */
final class Headers
{
    private const UNIQUE_HEADERS = [
        'date', 'from', 'sender', 'reply-to', 'to', 'cc', 'bcc',
        'message-id', 'in-reply-to', 'references', 'subject',
    ];
    private const HEADER_CLASS_MAP = [
        'date' => DateHeader::class,
        'from' => MailboxListHeader::class,
        'sender' => MailboxHeader::class,
        'reply-to' => MailboxListHeader::class,
        'to' => MailboxListHeader::class,
        'cc' => Ma

rences` are less strict than RFC 2822 (3.6.4) to allow users entering the original email's ...
        'references' => [UnstructuredHeader::class, IdentificationHeader::class], // ... `Message-ID`, even if that is no valid `msg-id`
        'return-path' =>

ilboxListHeader::class,
        'bcc' => MailboxListHeader::class,
        'message-id' => IdentificationHeader::class,
        'in-reply-to' => [UnstructuredHeader::class, IdentificationHeader::class], // `In-Reply-To` and `Refe


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Mime\Header;

use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;

/**
 * A Mailbox list MIME Header for something like From, To, Cc, and Bcc (one or more named addresses).
 *
 * @author Chris Corbyn
 */
final class MailboxListHeader extends AbstractHeader
{
    private array $addresses = [];

    /**

 $body
     *
     * @throws RfcComplianceException
     */
    public function setBody(mixed $body): void
    {
        $this->setAddresses($body);
    }

    /**
     * @return Address[]
     *
     * @throws RfcComplianceException
     */
    public fun

     * @param Address[] $addresses
     */
    public function __construct(string $name, array $addresses)
    {
        parent::__construct($name);

        $this->setAddresses($addresses);
    }

    /**
     * @param Address[]


 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\Encoder;

use Symfony\Component\Mime\Exception\RuntimeException;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class Base64ContentEncoder extends Base64Encoder implements ContentEncoderInterface
{
    public function encodeByteStream($

filter_append($stream, 'convert.base64-encode', \STREAM_FILTER_READ, [
            'line-length' => 0 >= $maxLineLength || 76 < $maxLineLength ? 76 : $maxLineLength,
            'line-break-chars' => "\r\n",
        ]);
        if (!\is_resource($filter))

stream, int $maxLineLength = 0): iterable
    {
        if (!\is_resource($stream)) {
            throw new \TypeError(\sprintf('Method "%s" takes a stream as a first argument.', __METHOD__));
        }

        $filter = stream_


* 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\Encoder;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class EightBitContentEncoder implements ContentEncoderInterface
{
    public function encodeByteStream($stream, int $maxLineLength = 0): iterable
    {
        while (!feof($strea

   }
    }

    public function getName(): string
    {
        return '8bit';
    }

    public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
    {
        return $string;
    }

m)) {
            yield fread($stream, 16372);


 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\Encoder;

use Symfony\Component\Mime\CharacterStream;

/**
 * @author Chris Corbyn
 */
final class Rfc2231Encoder implements EncoderInterface
{
    /**
     * Takes an unencoded string and produces a string encoded according to RFC 2231 from it.
     */

e = &$lines[$lineCount++];

        if (0 >= $maxLineLength) {
            $maxLineLength = 75;
        }

        $charStream = new CharacterStream($string, $charset);
        $thisLineLength = $maxLineLength - $firstLineOffset;

        while (null !== $

    public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
    {
        $lines = [];
        $lineCount = 0;
        $lines[] = '';
        $currentLin


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Scheduler;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Lock\LockInterface;
use Symfony\Component\Scheduler\Event\FailureEvent;
use Symfony\Component\Scheduler\Event\PostRunEvent;
use Symfony\Component\Scheduler\Event\PreRunEvent;
use Symfony\Component\Scheduler\Exception\LogicException;
use Symfony\Contr

nterface $state = null;
    private bool $shouldRestart = false;
    private bool $onlyLastMissed = false;

    public function __construct(
        private readonly ?EventDispatcherInterface $dispatcher = null,
    ) {
    }

    public function with(Recu

acts\Cache\CacheInterface;

final class Schedule implements ScheduleProviderInterface
{
    /** @var array<string,RecurringMessage> */
    private array $messages = [];
    private ?LockInterface $lock = null;
    private ?CacheI


ge.
 *
 * (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\Scheduler\Attribute;

/**
 * A marker to call a service method from scheduler.
 *
 * @author valtzu <valtzu@gmail.com>
 */
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class AsPeriodicTask
{
    /**
     * @param string|int               $frequen

00:00")
     * @param string|null              $until      A string representing the end time of the periodic task (i.e. "20:00:00")
     * @param int|null                 $jitter     The cron jitter, in seconds; for example, if set to 60, the cron
     *

cy  A string (i.e. "every hour") or an integer (the number of seconds) representing the frequency of the task
     * @param string|null              $from       A string representing the start time of the periodic task (i.e. "08:


 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\Header;

/**
 * A Date MIME Header.
 *
 * @author Chris Corbyn
 */
final class DateHeader extends AbstractHeader
{
    private \DateTimeImmutable $dateTime;

    public function __construct(string $name, \DateTimeInterface $date)
    {
        parent::_

on getBody(): \DateTimeImmutable
    {
        return $this->getDateTime();
    }

    public function getDateTime(): \DateTimeImmutable
    {
        return $this->dateTime;
    }

    /**
     * Set the date-time of the Date in this Header.
     *
     *

_construct($name);

        $this->setDateTime($date);
    }

    /**
     * @param \DateTimeInterface $body
     */
    public function setBody(mixed $body): void
    {
        $this->setDateTime($body);
    }

    public functi


buted with this source code.
 */

namespace Symfony\Component\Mime\Header;

use Symfony\Component\Mime\Encoder\Rfc2231Encoder;

/**
 * @author Chris Corbyn
 */
final class ParameterizedHeader extends UnstructuredHeader
{
    /**
     * RFC 2231's definition of a token.
     *
     * @var string
     */
    public const TOKEN_REGEX = '(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)';

    private ?Rfc2231Encoder $encoder = null;
    private array $parameters = [];

    public function __const

e' !== strtolower($name)) {
            $this->encoder = new Rfc2231Encoder();
        }
    }

    public function setParameter(string $parameter, ?string $value): void
    {
        $this->setParameters(array_merge($this->getParameters(), [$parameter =>

ruct(string $name, string $value, array $parameters = [])
    {
        parent::__construct($name, $value);

        foreach ($parameters as $k => $v) {
            $this->setParameter($k, $v);
        }

        if ('content-typ


 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\Encoder;

/**
 * @author Chris Corbyn
 */
class Base64Encoder implements EncoderInterface
{
    /**
     * Takes an unencoded string and produces a Base64 encoded string from it.
     *
     * Base64 encoded strings have a maximum line length of 76 char

neLength = 0): string
    {
        if (0 >= $maxLineLength || 76 < $maxLineLength) {
            $maxLineLength = 76;
        }

        $encodedString = base64_encode($string);
        $firstLine = '';
        if (0 !== $firstLineOffset) {
            $f

acters.
     * If the first line needs to be shorter, indicate the difference with
     * $firstLineOffset.
     */
    public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLi


/*
 * 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\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Registers custom mime types gu

me_types')) {
            return;
        }
        $definition = $container->findDefinition('mime_types');
        $id = null;
        foreach ($container->findTaggedServiceIds('mime.mime_type_guesser', true) as $id => $attributes) {
            $definiti

essers.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class AddMimeTypeGuesserPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container): void
    {
        if (!$container->has('mi


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Mime\Crypto;

use Symfony\Component\Mime\Exception\RuntimeException;
use Symfony\Component\Mime\Part\SMimePart;

/**
 * @author Sebastiaan Stok <s.stok@rollerscapes.net>
 *
 * @internal
 */
abstract class SMime
{
    protected function normalizeFilePath(string $path): string
    {
        if (!file_exists($path)) {
            throw new RuntimeEx

reach ($iterator as $chunk) {
            fwrite($stream, $chunk);
        }
    }

    protected function convertMessageToSMimePart($stream, string $type, string $subtype): SMimePart
    {
        rewind($stream);

        $headers = '';

        while (!

ception(\sprintf('File does not exist: "%s".', $path));
        }

        return 'file://'.str_replace('\\', '/', realpath($path));
    }

    protected function iteratorToFile(iterable $iterator, $stream): void
    {
        fo


 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\Scheduler\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\S

ringMessage::every('1 second', new Message()));
        $scheduler = new Scheduler([Message::class => $handler], [$schedule], $clock);
        $handler->scheduler = $scheduler;

        $scheduler->run(['sleep' => 100]);

        $this->assertSame(3, $hand

cheduler;

class SchedulerTest extends TestCase
{
    public function testCanRunAndStop()
    {
        $handler = new Handler();
        $handler->clock = $clock = new MockClock();
        $schedule = (new Schedule())->add(Recur


ArrayAdapter;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Scheduler\Generator\Checkpoint;
use Symfony\Component\Scheduler\Generator\MessageContext;
use Symfony\Component\Scheduler\Generator\MessageGenerator;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Component\Scheduler\Trigger\TriggerInterface;

class MessageGeneratorTest extends TestCase
{
    #[DataProvider('messagesPro

  if (\is_array($s)) {
                $schedule[$i] = $this->createMessage(...$s);
            }
        }
        $schedule = (new Schedule())->add(...$schedule);
        $schedule->stateful(new ArrayAdapter());

        $scheduler = new MessageGenerator

vider')]
    public function testGetMessagesFromSchedule(string $startTime, array $runs, array $schedule)
    {
        $clock = new MockClock(self::makeDateTime($startTime));

        foreach ($schedule as $i => $s) {


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Scheduler\Tests\EventListener;

use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
use S

Component\Scheduler\Event\PreRunEvent;
use Symfony\Component\Scheduler\EventListener\DispatchSchedulerEventListener;
use Symfony\Component\Scheduler\Generator\MessageContext;
use Symfony\Component\Scheduler\Messenger\ScheduledStamp;
use Symfony\Component\S

ymfony\Component\Messenger\Event\WorkerMessageReceivedEvent;
use Symfony\Component\Messenger\Stamp\HandledStamp;
use Symfony\Component\Scheduler\Event\FailureEvent;
use Symfony\Component\Scheduler\Event\PostRunEvent;
use Symfony\


 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\Scheduler\Tests\Trigger;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Scheduler\Generator\MessageContext;
use Symfony\Component\Scheduler\Trigger\CallbackMessageProvider;
use Symfony\Component\Scheduler\Trigger\TriggerInterface;

class CallbackMessageProviderTe

ssageProvider(static fn () => []);
        $this->assertEquals([], $messageProvider->getMessages($context));
        $this->assertEquals('', $messageProvider->getId());

        $messageProvider = new CallbackMessageProvider(static fn () => [new \stdClass(

st extends TestCase
{
    public function testToString()
    {
        $context = new MessageContext('test', 'test', $this->createStub(TriggerInterface::class), new \DateTimeImmutable());
        $messageProvider = new CallbackMe


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Scheduler\Tests\Trigger;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Scheduler\Trigger\CronExpressionTrigger;

class CronExpressionTriggerTest extends TestCase
{
    #[DataProvider('hashedExpressionProvider')]
    public function testHashedExpressionParsing(string $input, string $expected)

        $this->assertSame($expected, (string) $triggerA);
        $this->assertSame((string) $triggerB, (string) $triggerA);
        $this->assertNotSame((string) $triggerC, (string) $triggerA);
    }

    public static function hashedExpressionProvider():

    {
        $triggerA = CronExpressionTrigger::fromSpec($input, 'my task');
        $triggerB = CronExpressionTrigger::fromSpec($input, 'my task');
        $triggerC = CronExpressionTrigger::fromSpec($input, 'another task');


/*
 * 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\Scheduler\Tests\Trigger;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Scheduler\Trigger\JitterTrigger;
use Symfony\Component\Scheduler\Trigger\TriggerInterface;

class JitterTriggerTest extends TestCase
{
    public function testCanAddJitter(

values = array_map(
            static fn () => (int) $trigger->getNextRunDate($time)?->getTimestamp(),
            array_fill(0, 100, null)
        );

        foreach ($values as $value) {
            $this->assertGreaterThanOrEqual($time->getTimestamp()

)
    {
        $time = new \DateTimeImmutable();
        $inner = $this->createStub(TriggerInterface::class);
        $inner->method('getNextRunDate')->willReturn($time);

        $trigger = new JitterTrigger($inner);

        $


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Scheduler\Tests\Messenger;

use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Scheduler\Exception\InvalidArgumentException;
use Symfony\Component\Scheduler\Generator\MessageGenerator;
use Sy

ace;
use Symfony\Component\Scheduler\Tests\Fixtures\SomeScheduleProvider;
use Symfony\Component\Scheduler\Trigger\TriggerInterface;
use Symfony\Contracts\Service\ServiceLocatorTrait;

class SchedulerTransportFactoryTest extends TestCase
{
    public functi

mfony\Component\Scheduler\Messenger\SchedulerTransport;
use Symfony\Component\Scheduler\Messenger\SchedulerTransportFactory;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\ScheduleProviderInterf


 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Scheduler\Tests\Messenger\Serializer\Normalizer;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Scheduler\Messenger\Serializer\Normalizer\SchedulerTriggerNormalizer;
use Symfony\Component\Scheduler\Trigger\CallbackTrigger;
use Symfony\Component\Scheduler\Trigger\PeriodicalTrigger;
use Symfony

ew SchedulerTriggerNormalizer();
    }

    #[DataProvider('normalizeProvider')]
    public function testNormalize(mixed $data, mixed $expected)
    {
        self::assertSame($expected, $this->normalizer->normalize($data));
    }

    public static functi

\Component\Scheduler\Trigger\TriggerInterface;

class SchedulerTriggerNormalizerTest extends TestCase
{
    private SchedulerTriggerNormalizer $normalizer;

    protected function setUp(): void
    {
        $this->normalizer = n


/*
 * 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\Scheduler\Tests\DependencyInjection;

use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\Scheduler\DependencyIn

    $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionCode(1);

        $container = new ContainerBuilder();

        $container->register('provider_a', SomeScheduleProvider::class)->addTag('scheduler.schedule_provider'

jection\AddScheduleMessengerPass;
use Symfony\Component\Scheduler\Tests\Fixtures\SomeScheduleProvider;

class RegisterProviderTest extends TestCase
{
    public function testErrorOnMultipleProvidersForTheSameSchedule()
    {


hp

/*
 * 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\Scheduler\Event;

use Symfony\Component\Scheduler\Generator\MessageContext;
use Symfony\Component\Scheduler\ScheduleProviderInterface;

class PostRunEvent
{
    public function __construct(
        private readonly ScheduleProviderInterface $schedul

     return $this->messageContext;
    }

    public function getSchedule(): ScheduleProviderInterface
    {
        return $this->schedule;
    }

    public function getMessage(): object
    {
        return $this->message;
    }

    public function get

e,
        private readonly MessageContext $messageContext,
        private readonly object $message,
        private readonly mixed $result = null,
    ) {
    }

    public function getMessageContext(): MessageContext
    {


/*
 * 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\Scheduler\Trigger;

/**
 * @author Kevin Bond <kevinbond@gmail.com>
 */
abstract class AbstractDecoratedTrigger implements StatefulTriggerInterface
{
    public function __construct(private TriggerInterface $inner)
    {
    }

    public function cont

    {
        $inner = $this->inner;

        while ($inner instanceof self) {
            $inner = $inner->inner;
        }

        return $inner;
    }

    /**
     * @return \Traversable<self>
     */
    final public function decorators(): \Traversab

inue(\DateTimeImmutable $startedAt): void
    {
        if ($this->inner instanceof StatefulTriggerInterface) {
            $this->inner->continue($startedAt);
        }
    }

    final public function inner(): TriggerInterface


he 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\Scheduler\Trigger;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class CallbackTrigger implements TriggerInterface
{
    private \Closure $callback;
    private string $description;

    public function __construct(callable $callback, ?string $description = null)

     $this->description = $description ?? spl_object_hash($this->callback);
    }

    public function __toString(): string
    {
        return $this->description;
    }

    public function getNextRunDate(\DateTimeImmutable $run): ?\DateTimeImmutable

    {
        $this->callback = $callback(...);


buted with this source code.
 */

namespace Symfony\Component\Mime\Encoder;

use Symfony\Component\Mime\CharacterStream;

/**
 * @author Chris Corbyn
 */
class QpEncoder implements EncoderInterface
{
    /**
     * Pre-computed QP for HUGE optimization.
     */
    private const QP_MAP = [
        0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04',
        5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09',
        10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E',
        15 =