diff --git a/.github/workflows/php-tests.yml b/.github/workflows/php-tests.yml new file mode 100644 index 0000000..0a3df8a --- /dev/null +++ b/.github/workflows/php-tests.yml @@ -0,0 +1,30 @@ +name: PHP Tests + +on: + push: + pull_request: + +jobs: + test: + name: PHP ${{ matrix.php-version }} + runs-on: ubuntu-latest + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository + + strategy: + matrix: + php-version: [ '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] + max-parallel: 1 + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + + - name: Install Dependencies + run: composer install + + - name: Run Tests + run: ./vendor/bin/phpunit --configuration phpunit.xml.dist --verbose diff --git a/.gitignore b/.gitignore index 96e828d..07cc02f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ composer.lock .idea/ docs/ phpunit.xml +.phpunit.result.cache diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b174b06..0000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: php -sudo: false -php: - - "7.4" - - "7.3" - - "7.2" - - "7.1" - - "7.0" - - "5.6" -install: composer update -script: ./vendor/bin/phpunit --verbose diff --git a/composer.json b/composer.json index e3620d2..e9cc2e2 100644 --- a/composer.json +++ b/composer.json @@ -18,11 +18,11 @@ } ], "require": { - "php": ">=5.3.2", + "php": ">=5.4", "psr/log": "~1.0" }, "require-dev": { - "phpunit/phpunit": "~5.0", + "phpunit/phpunit": "~8.5", "monolog/monolog": "~1.13" }, "autoload": { @@ -31,8 +31,8 @@ } }, "autoload-dev": { - "classmap": [ - "tests/TestCase.php" - ] + "psr-4": { + "Tx\\Mailer\\Tests\\": "tests/" + } } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3732559..81b1c2f 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ - - vendor - + + + vendor + + - + diff --git a/src/Mailer/SMTP.php b/src/Mailer/SMTP.php index 9ed80ef..50bc3c1 100644 --- a/src/Mailer/SMTP.php +++ b/src/Mailer/SMTP.php @@ -108,7 +108,8 @@ public function __construct(LoggerInterface $logger=null) * set server and port * @param string $host server * @param int $port port - * @param string $secure ssl tls tlsv1.0 tlsv1.1 tlsv1.2 + * @param string $secure ssl tls + * @param bool $allowInsecure skip certificate verification? * @return $this */ public function setServer($host, $port, $secure=null, $allowInsecure=null) @@ -116,7 +117,7 @@ public function setServer($host, $port, $secure=null, $allowInsecure=null) $this->host = $host; $this->port = $port; $this->secure = $secure; - $this->allowInsecure = $allowInsecure; + $this->allowInsecure = (bool) $allowInsecure; if(!$this->ehlo) $this->ehlo = $host; $this->logger && $this->logger->debug("Set: the server"); return $this; @@ -204,7 +205,7 @@ protected function connect() $this->logger && $this->logger->debug("Connecting to {$this->host} at {$this->port}"); $host = ($this->secure == 'ssl') ? 'ssl://' . $this->host : $this->host; // Create connection - $context = null; + $context = stream_context_create([]); if ($this->allowInsecure) { $context = stream_context_create([ 'ssl' => [ @@ -214,7 +215,7 @@ protected function connect() ] ]); } - $this->smtp = stream_socket_client( + $this->smtp = @stream_socket_client( $host.':'.$this->port, $error_code, $error_message, @@ -222,10 +223,8 @@ protected function connect() STREAM_CLIENT_CONNECT, $context ); - //set block mode - // stream_set_blocking($this->smtp, 1); if (!$this->smtp){ - throw new SMTPException("Could not open SMTP Port."); + throw new SMTPException("Could not open SMTP Port to $host:{$this->port}"); } $code = $this->getCode(); if ($code !== '220'){ @@ -254,24 +253,13 @@ protected function starttls() throw new CryptoException('Crypto type expected PHP 5.6 or greater'); } - switch ($this->secure) { - case 'tlsv1.0': - $crypto_type = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT; - break; - case 'tlsv1.1': - $crypto_type = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; - break; - case 'tlsv1.2': - $crypto_type = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; - break; - default: - $crypto_type = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT | - STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | - STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; - break; + if ($this->allowInsecure) { + stream_context_set_option($this->smtp, 'ssl', 'verify_peer', false); + stream_context_set_option($this->smtp, 'ssl', 'verify_peer_name', false); + stream_context_set_option($this->smtp, 'ssl', 'allow_self_signed', true); } - if(!\stream_socket_enable_crypto($this->smtp, true, $crypto_type)) { + if(!\stream_socket_enable_crypto($this->smtp, true, STREAM_CRYPTO_METHOD_ANY_CLIENT)) { throw new CryptoException("Start TLS failed to enable crypto"); } return $this; diff --git a/tests/MailerTest.php b/tests/MailerTest.php index 55f54e1..bd5c3e3 100644 --- a/tests/MailerTest.php +++ b/tests/MailerTest.php @@ -1,18 +1,13 @@ addTo(self::TO_NAME, self::TO_EMAIL) ->addCc(self::CC_NAME, self::CC_EMAIL) ->addBcc(self::BCC_NAME, self::BCC_EMAIL) - ->setSubject('Test Mailer '. time()) + ->setSubject('Test Mailer ' . time()) ->setBody('Hi, boy') ->addAttachment('test', __FILE__) ->send(); diff --git a/tests/MessageTest.php b/tests/MessageTest.php new file mode 100644 index 0000000..55df3c3 --- /dev/null +++ b/tests/MessageTest.php @@ -0,0 +1,208 @@ +message = new Message(); + } + + public function testSetFrom() + { + $name = 'Sender Name'; + $email = 'sender@example.com'; + $expectedHeader = 'From: =?utf-8?B?U2VuZGVyIE5hbWU=?= '; + + $this->message->setFrom($name, $email); + + // Test getters + $this->assertEquals($name, $this->message->getFromName()); + $this->assertEquals($email, $this->message->getFromEmail()); + + // Test toString output + $messageString = $this->message->toString(); + $this->assertStringContainsString($expectedHeader, $messageString); + } + + public function testSetFakeFrom() + { + $name = 'Fake Name'; + $email = 'fake@example.com'; + $expectedHeader = 'From: =?utf-8?B?RmFrZSBOYW1l?= '; + + $this->message->setFakeFrom($name, $email); + + // Test getters + $this->assertEquals($name, $this->message->getFakeFromName()); + $this->assertEquals($email, $this->message->getFakeFromEmail()); + + // Test toString output + $messageString = $this->message->toString(); + $this->assertStringContainsString($expectedHeader, $messageString); + } + + public function testAddTo() + { + $name = 'Recipient'; + $email = 'recipient@example.com'; + $expectedHeader = 'To: =?utf-8?B?UmVjaXBpZW50?= '; + + $this->message->addTo($name, $email); + + // Test getter + $to = $this->message->getTo(); + $this->assertArrayHasKey($email, $to); + $this->assertEquals($name, $to[$email]); + + // Test toString output + $messageString = $this->message->toString(); + $this->assertStringContainsString($expectedHeader, $messageString); + } + + public function testAddCc() + { + $name = 'CC Recipient'; + $email = 'cc@example.com'; + $expectedHeader = 'Cc: =?utf-8?B?Q0MgUmVjaXBpZW50?= '; + + $this->message->addCc($name, $email); + + // Test getter + $cc = $this->message->getCc(); + $this->assertArrayHasKey($email, $cc); + $this->assertEquals($name, $cc[$email]); + + // Test toString output + $messageString = $this->message->toString(); + $this->assertStringContainsString($expectedHeader, $messageString); + } + + public function testAddBcc() + { + $name = 'BCC Recipient'; + $email = 'bcc@example.com'; + $expectedHeader = 'Bcc: =?utf-8?B?QkNDIFJlY2lwaWVudA==?= '; + + $this->message->addBcc($name, $email); + + // Test getter + $bcc = $this->message->getBcc(); + $this->assertArrayHasKey($email, $bcc); + $this->assertEquals($name, $bcc[$email]); + + // Test toString output + $messageString = $this->message->toString(); + $this->assertStringContainsString($expectedHeader, $messageString); + } + + public function testSetSubject() + { + $subject = 'Test Subject with Special Chars: äöü'; + + $this->message->setSubject($subject); + + // Test getter + $this->assertEquals($subject, $this->message->getSubject()); + + // Test toString output + $messageString = $this->message->toString(); + $expectedHeader = 'Subject: =?utf-8?B?VGVzdCBTdWJqZWN0IHdpdGggU3BlY2lhbCBDaGFyczogw6TDtsO8?='; + $this->assertStringContainsString($expectedHeader, $messageString); + } + + public function testSetBody() + { + $body = 'Test email body content with special chars: äöü'; + + $this->message->setBody($body); + + // Test getter + $this->assertEquals($body, $this->message->getBody()); + + // Test toString output + $messageString = $this->message->toString(); + $expectedBody = "VGVzdCBlbWFpbCBib2R5IGNvbnRlbnQgd2l0aCBzcGVjaWFsIGNoYXJzOiDDpMO2w7w=\r\n"; + $this->assertStringContainsString($expectedBody, $messageString); + } + + public function testSetReplyTo() + { + $name = 'Reply Name'; + $email = 'reply@example.com'; + $expectedHeader = 'Reply-To: =?utf-8?B?UmVwbHkgTmFtZQ==?= '; + + $this->message->setReplyTo($name, $email); + + // Test toString output + $messageString = $this->message->toString(); + $this->assertStringContainsString($expectedHeader, $messageString); + } + + public function testAddAttachment() + { + $name = 'test.txt'; + $path = tempnam(sys_get_temp_dir(), 'test_'); + + + // Create a test file + file_put_contents($path, 'Test content'); + + $this->message->addAttachment($name, $path); + + // Test getter + $attachments = $this->message->getAttachment(); + $this->assertArrayHasKey($name, $attachments); + $this->assertEquals($path, $attachments[$name]); + + // Test toString output + $messageString = $this->message->toString(); + $this->assertStringContainsString( + 'Content-Type: application/octet-stream; name="' . $name . '"', + $messageString + ); + $this->assertStringContainsString( + 'Content-Disposition: attachment; filename="' . $name . '"', + $messageString + ); + + // Cleanup + unlink($path); + } + + public function testCompleteMessage() + { + $fromName = 'Sender'; + $fromEmail = 'sender@example.com'; + $toName = 'Recipient'; + $toEmail = 'recipient@example.com'; + $subject = 'Complete Test'; + $body = 'Complete test body'; + $expectedHeaderFrom = 'From: =?utf-8?B?U2VuZGVy?= '; + $expectedHeaderTo = 'To: =?utf-8?B?UmVjaXBpZW50?= '; + $expectedHeaderSubject = 'Subject: =?utf-8?B?Q29tcGxldGUgVGVzdA==?='; + $expectedBody = "Q29tcGxldGUgdGVzdCBib2R5\r\n"; + + $this->message + ->setFrom($fromName, $fromEmail) + ->addTo($toName, $toEmail) + ->setSubject($subject) + ->setBody($body); + + $messageString = $this->message->toString(); + + // Verify all parts are present and properly encoded + + $this->assertStringContainsString($expectedHeaderFrom, $messageString); + $this->assertStringContainsString($expectedHeaderTo, $messageString); + $this->assertStringContainsString($expectedHeaderSubject, $messageString); + $this->assertStringContainsString($expectedBody, $messageString); + $this->assertStringContainsString('MIME-Version: 1.0', $messageString); + $this->assertStringContainsString('Content-Type: multipart/alternative', $messageString); + } +} diff --git a/tests/OAuthTest.php b/tests/OAuthTest.php index c9a55d5..02472ae 100644 --- a/tests/OAuthTest.php +++ b/tests/OAuthTest.php @@ -1,17 +1,16 @@ markTestSkipped('No oauth token set, test skipped'); } $mail = new Mailer(new Logger('Mailer.OAuth')); $status = $mail->setServer(self::OAUTH_SERVER, self::OAUTH_PORT, 'tls') diff --git a/tests/SMTPTest.php b/tests/SMTPTest.php index 1e885cd..0b5f6b8 100644 --- a/tests/SMTPTest.php +++ b/tests/SMTPTest.php @@ -1,14 +1,10 @@ message = new Message(); $this->message @@ -69,47 +65,9 @@ public function testTLSSend() usleep(self::DELAY); } - public function testTLSv10Send() - { - $this->smtp = new SMTP(new Logger('SMTP.tlsv1.0')); - $this->smtp - ->setServer(self::SERVER, self::PORT_TLS, 'tlsv1.0') - ->setAuth(self::USER, self::PASS); - - $status = $this->smtp->send($this->message); - $this->assertTrue($status); - usleep(self::DELAY); - } - - public function testTLSv11Send() - { - $this->smtp = new SMTP(new Logger('SMTP.tlsv1.1')); - $this->smtp - ->setServer(self::SERVER, self::PORT_TLS, 'tlsv1.1') - ->setAuth(self::USER, self::PASS); - - $status = $this->smtp->send($this->message); - $this->assertTrue($status); - usleep(self::DELAY); - } - - public function testTLSv12Send() - { - $this->smtp = new SMTP(new Logger('SMTP.tlsv1.2')); - $this->smtp - ->setServer(self::SERVER, self::PORT_TLS, 'tlsv1.2') - ->setAuth(self::USER, self::PASS); - - $status = $this->smtp->send($this->message); - $this->assertTrue($status); - usleep(self::DELAY); - } - - /** - * @expectedException \Tx\Mailer\Exceptions\SMTPException - */ public function testConnectSMTPException() { + $this->expectException(\Tx\Mailer\Exceptions\SMTPException::class); $this->smtp = new SMTP(new Logger('SMTP.FakePort')); $this->smtp ->setServer('localhost', "99999", null) diff --git a/tests/TestCase.php b/tests/TestCase.php index d3b2495..24d37dd 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,9 +1,13 @@