Browse Source

Merge pull request #14641 from cakephp/backport-14639

3.8 - Backport #14639
ADmad 5 years ago
parent
commit
cc1c227d3b

+ 59 - 17
src/Mailer/Transport/SmtpTransport.php

@@ -257,24 +257,66 @@ class SmtpTransport extends AbstractTransport
      */
     protected function _auth()
     {
-        if (isset($this->_config['username'], $this->_config['password'])) {
-            $replyCode = (string)$this->_smtpSend('AUTH LOGIN', '334|500|502|504');
-            if ($replyCode === '334') {
-                try {
-                    $this->_smtpSend(base64_encode($this->_config['username']), '334');
-                } catch (SocketException $e) {
-                    throw new SocketException('SMTP server did not accept the username.', null, $e);
-                }
-                try {
-                    $this->_smtpSend(base64_encode($this->_config['password']), '235');
-                } catch (SocketException $e) {
-                    throw new SocketException('SMTP server did not accept the password.', null, $e);
-                }
-            } elseif ($replyCode === '504') {
-                throw new SocketException('SMTP authentication method not allowed, check if SMTP server requires TLS.');
-            } else {
-                throw new SocketException('AUTH command not recognized or not implemented, SMTP server may not require authentication.');
+        if (!isset($this->_config['username'], $this->_config['password'])) {
+            return;
+        }
+
+        $username = $this->_config['username'];
+        $password = $this->_config['password'];
+
+        $replyCode = $this->_authPlain($username, $password);
+        if ($replyCode === '235') {
+            return;
+        }
+
+        $this->_authLogin($username, $password);
+    }
+
+    /**
+     * Authenticate using AUTH PLAIN mechanism.
+     *
+     * @param string $username Username.
+     * @param string $password Password.
+     * @return string|null Response code for the command.
+     */
+    protected function _authPlain($username, $password)
+    {
+        return $this->_smtpSend(
+            sprintf(
+                'AUTH PLAIN %s',
+                base64_encode(chr(0) . $username . chr(0) . $password)
+            ),
+            '235|504|534|535'
+        );
+    }
+
+    /**
+     * Authenticate using AUTH LOGIN mechanism.
+     *
+     * @param string $username Username.
+     * @param string $password Password.
+     * @return void
+     */
+    protected function _authLogin($username, $password)
+    {
+        $replyCode = $this->_smtpSend('AUTH LOGIN', '334|500|502|504');
+        if ($replyCode === '334') {
+            try {
+                $this->_smtpSend(base64_encode($username), '334');
+            } catch (SocketException $e) {
+                throw new SocketException('SMTP server did not accept the username.', null, $e);
             }
+            try {
+                $this->_smtpSend(base64_encode($password), '235');
+            } catch (SocketException $e) {
+                throw new SocketException('SMTP server did not accept the password.', null, $e);
+            }
+        } elseif ($replyCode === '504') {
+            throw new SocketException('SMTP authentication method not allowed, check if SMTP server requires TLS.');
+        } else {
+            throw new SocketException(
+                'AUTH command not recognized or not implemented, SMTP server may not require authentication.'
+            );
         }
     }
 

+ 81 - 32
tests/TestCase/Mailer/Transport/SmtpTransportTest.php

@@ -68,6 +68,19 @@ class SmtpTransportTest extends TestCase
 {
 
     /**
+     * @var array
+     */
+    protected $credentials = [
+        'username' => 'mark',
+        'password' => 'story',
+    ];
+
+    /**
+     * @var string
+     */
+    protected $credentialsEncoded;
+
+    /**
      * Setup
      *
      * @return void
@@ -82,6 +95,8 @@ class SmtpTransportTest extends TestCase
         $this->SmtpTransport = new SmtpTestTransport();
         $this->SmtpTransport->setSocket($this->socket);
         $this->SmtpTransport->setConfig(['client' => 'localhost']);
+
+        $this->credentialsEncoded = base64_encode(chr(0) . 'mark' . chr(0) . 'story');
     }
 
     /**
@@ -156,13 +171,18 @@ class SmtpTransportTest extends TestCase
     {
         $this->expectException(\Cake\Network\Exception\SocketException::class);
         $this->expectExceptionMessage('SMTP authentication method not allowed, check if SMTP server requires TLS.');
-        $this->SmtpTransport->setConfig(['tls' => false, 'username' => 'user', 'password' => 'pass']);
+        $this->SmtpTransport->setConfig(['tls' => false] + $this->credentials);
+
         $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true));
         $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
         $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
         $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 Accepted\r\n"));
-        $this->socket->expects($this->at(4))->method('write')->with("AUTH LOGIN\r\n");
-        $this->socket->expects($this->at(5))->method('read')
+
+        $this->socket->expects($this->at(4))->method('write')->with("AUTH PLAIN {$this->credentialsEncoded}\r\n");
+        $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized Authentication Type\r\n"));
+
+        $this->socket->expects($this->at(6))->method('write')->with("AUTH LOGIN\r\n");
+        $this->socket->expects($this->at(7))->method('read')
             ->will($this->returnValue("504 5.7.4 Unrecognized authentication type\r\n"));
         $this->SmtpTransport->connect();
     }
@@ -209,20 +229,31 @@ class SmtpTransportTest extends TestCase
         $this->assertContains('200 Not Accepted', $e->getPrevious()->getMessage());
     }
 
+    public function testAuthPlain()
+    {
+        $this->socket->expects($this->at(0))->method('write')->with("AUTH PLAIN {$this->credentialsEncoded}\r\n");
+        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("235 OK\r\n"));
+        $this->SmtpTransport->setConfig($this->credentials);
+        $this->SmtpTransport->auth();
+    }
+
     /**
      * testAuth method
      *
      * @return void
      */
-    public function testAuth()
+    public function testAuthLogin()
     {
-        $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n");
-        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("334 Login\r\n"));
-        $this->socket->expects($this->at(2))->method('write')->with("bWFyaw==\r\n");
-        $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("334 Pass\r\n"));
-        $this->socket->expects($this->at(4))->method('write')->with("c3Rvcnk=\r\n");
-        $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("235 OK\r\n"));
-        $this->SmtpTransport->setConfig(['username' => 'mark', 'password' => 'story']);
+        $this->socket->expects($this->at(0))->method('write')->with("AUTH PLAIN {$this->credentialsEncoded}\r\n");
+        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized Authentication Type\r\n"));
+
+        $this->socket->expects($this->at(2))->method('write')->with("AUTH LOGIN\r\n");
+        $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("334 Login\r\n"));
+        $this->socket->expects($this->at(4))->method('write')->with("bWFyaw==\r\n");
+        $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("334 Pass\r\n"));
+        $this->socket->expects($this->at(6))->method('write')->with("c3Rvcnk=\r\n");
+        $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("235 OK\r\n"));
+        $this->SmtpTransport->setConfig($this->credentials);
         $this->SmtpTransport->auth();
     }
 
@@ -235,10 +266,14 @@ class SmtpTransportTest extends TestCase
     {
         $this->expectException(\Cake\Network\Exception\SocketException::class);
         $this->expectExceptionMessage('AUTH command not recognized or not implemented, SMTP server may not require authentication.');
-        $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n");
-        $this->socket->expects($this->at(1))->method('read')
+
+        $this->socket->expects($this->at(0))->method('write')->with("AUTH PLAIN {$this->credentialsEncoded}\r\n");
+        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized Authentication Type\r\n"));
+
+        $this->socket->expects($this->at(2))->method('write')->with("AUTH LOGIN\r\n");
+        $this->socket->expects($this->at(3))->method('read')
             ->will($this->returnValue("500 5.3.3 Unrecognized command\r\n"));
-        $this->SmtpTransport->setConfig(['username' => 'mark', 'password' => 'story']);
+        $this->SmtpTransport->setConfig($this->credentials);
         $this->SmtpTransport->auth();
     }
 
@@ -251,10 +286,14 @@ class SmtpTransportTest extends TestCase
     {
         $this->expectException(\Cake\Network\Exception\SocketException::class);
         $this->expectExceptionMessage('AUTH command not recognized or not implemented, SMTP server may not require authentication.');
-        $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n");
-        $this->socket->expects($this->at(1))->method('read')
+
+        $this->socket->expects($this->at(0))->method('write')->with("AUTH PLAIN {$this->credentialsEncoded}\r\n");
+        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized Authentication Type\r\n"));
+
+        $this->socket->expects($this->at(2))->method('write')->with("AUTH LOGIN\r\n");
+        $this->socket->expects($this->at(3))->method('read')
             ->will($this->returnValue("502 5.3.3 Command not implemented\r\n"));
-        $this->SmtpTransport->setConfig(['username' => 'mark', 'password' => 'story']);
+        $this->SmtpTransport->setConfig($this->credentials);
         $this->SmtpTransport->auth();
     }
 
@@ -267,10 +306,14 @@ class SmtpTransportTest extends TestCase
     {
         $this->expectException(\Cake\Network\Exception\SocketException::class);
         $this->expectExceptionMessage('SMTP Error: 503 5.5.1 Already authenticated');
-        $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n");
-        $this->socket->expects($this->at(1))
+
+        $this->socket->expects($this->at(0))->method('write')->with("AUTH PLAIN {$this->credentialsEncoded}\r\n");
+        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized Authentication Type\r\n"));
+
+        $this->socket->expects($this->at(2))->method('write')->with("AUTH LOGIN\r\n");
+        $this->socket->expects($this->at(3))
             ->method('read')->will($this->returnValue("503 5.5.1 Already authenticated\r\n"));
-        $this->SmtpTransport->setConfig(['username' => 'mark', 'password' => 'story']);
+        $this->SmtpTransport->setConfig($this->credentials);
         $this->SmtpTransport->auth();
     }
 
@@ -281,12 +324,15 @@ class SmtpTransportTest extends TestCase
      */
     public function testAuthBadUsername()
     {
-        $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n");
-        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("334 Login\r\n"));
-        $this->socket->expects($this->at(2))->method('write')->with("bWFyaw==\r\n");
-        $this->socket->expects($this->at(3))->method('read')
+        $this->socket->expects($this->at(0))->method('write')->with("AUTH PLAIN {$this->credentialsEncoded}\r\n");
+        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized Authentication Type\r\n"));
+
+        $this->socket->expects($this->at(2))->method('write')->with("AUTH LOGIN\r\n");
+        $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("334 Login\r\n"));
+        $this->socket->expects($this->at(4))->method('write')->with("bWFyaw==\r\n");
+        $this->socket->expects($this->at(5))->method('read')
             ->will($this->returnValue("535 5.7.8 Authentication failed\r\n"));
-        $this->SmtpTransport->setConfig(['username' => 'mark', 'password' => 'story']);
+        $this->SmtpTransport->setConfig($this->credentials);
 
         $e = null;
         try {
@@ -307,13 +353,16 @@ class SmtpTransportTest extends TestCase
      */
     public function testAuthBadPassword()
     {
-        $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n");
-        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("334 Login\r\n"));
-        $this->socket->expects($this->at(2))->method('write')->with("bWFyaw==\r\n");
-        $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("334 Pass\r\n"));
-        $this->socket->expects($this->at(4))->method('write')->with("c3Rvcnk=\r\n");
-        $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("535 5.7.8 Authentication failed\r\n"));
-        $this->SmtpTransport->setConfig(['username' => 'mark', 'password' => 'story']);
+        $this->socket->expects($this->at(0))->method('write')->with("AUTH PLAIN {$this->credentialsEncoded}\r\n");
+        $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized Authentication Type\r\n"));
+
+        $this->socket->expects($this->at(2))->method('write')->with("AUTH LOGIN\r\n");
+        $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("334 Login\r\n"));
+        $this->socket->expects($this->at(4))->method('write')->with("bWFyaw==\r\n");
+        $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("334 Pass\r\n"));
+        $this->socket->expects($this->at(6))->method('write')->with("c3Rvcnk=\r\n");
+        $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("535 5.7.8 Authentication failed\r\n"));
+        $this->SmtpTransport->setConfig($this->credentials);
 
         $e = null;
         try {