Fix handling of failed connects / logins in SmtpEmailTransport
authorTim Düsterhus <duesterhus@woltlab.com>
Sun, 21 Jun 2015 18:53:46 +0000 (20:53 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 23 Jun 2015 22:28:57 +0000 (00:28 +0200)
wcfsetup/install/files/lib/system/email/transport/SmtpEmailTransport.class.php

index 9668b86525da9a6dc90ad9249907d097322d128f..a92427e6dcef6af03d1abb96f3835e738532514d 100644 (file)
@@ -67,6 +67,13 @@ class SmtpEmailTransport implements EmailTransport {
         */
        protected $features = [ ];
        
+       /**
+        * if this property is an instance of \Exception email delivery will be locked
+        * and the \Exception will be thrown when attempting to deliver() an email
+        * @var \Exception
+        */
+       protected $locked = null;
+       
        /**
         * Creates a new SmtpEmailTransport using the given host.
         * 
@@ -234,34 +241,41 @@ class SmtpEmailTransport implements EmailTransport {
                        if ($parameters[0] == 'auth') {
                                // try mechanisms in order of preference
                                foreach ([ 'login', 'plain' ] as $method) {
-                                       try {
-                                               if (in_array($method, $parameters)) {
-                                                       switch ($method) {
-                                                               case 'login':
+                                       if (in_array($method, $parameters)) {
+                                               switch ($method) {
+                                                       case 'login':
+                                                               try {
                                                                        $this->write('AUTH LOGIN');
                                                                        $this->read([ 334 ]);
-                                                                       $this->write(base64_encode($this->username));
-                                                                       $this->lastWrite = '*redacted*';
-                                                                       $this->read([ 334 ]);
-                                                                       $this->write(base64_encode($this->password));
-                                                                       $this->lastWrite = '*redacted*';
-                                                                       $this->read([ 235 ]);
-                                                                       return;
-                                                               break;
-                                                               case 'plain':
-                                                                       // RFC 4616
+                                                               }
+                                                               catch (SystemException $e) {
+                                                                       // try next authentication method
+                                                                       continue 2;
+                                                               }
+                                                               $this->write(base64_encode($this->username));
+                                                               $this->lastWrite = '*redacted*';
+                                                               $this->read([ 334 ]);
+                                                               $this->write(base64_encode($this->password));
+                                                               $this->lastWrite = '*redacted*';
+                                                               $this->read([ 235 ]);
+                                                               return;
+                                                       break;
+                                                       case 'plain':
+                                                               // RFC 4616
+                                                               try {
                                                                        $this->write('AUTH PLAIN');
                                                                        $this->read([ 334 ]);
-                                                                       $this->write(base64_encode("\0".$this->username."\0".$this->password));
-                                                                       $this->lastWrite = '*redacted*';
-                                                                       $this->read([ 235 ]);
-                                                                       return;
-                                                       }
+                                                               }
+                                                               catch (SystemException $e) {
+                                                                       // try next authentication method
+                                                                       continue 2;
+                                                               }
+                                                               $this->write(base64_encode("\0".$this->username."\0".$this->password));
+                                                               $this->lastWrite = '*redacted*';
+                                                               $this->read([ 235 ]);
+                                                               return;
                                                }
                                        }
-                                       catch (SystemException $e) {
-                                               // try next authentication method
-                                       }
                                }
                                
                                return;
@@ -296,10 +310,28 @@ class SmtpEmailTransport implements EmailTransport {
         * @param       \wcf\system\email\Mailbox       $envelopeTo
         */
        public function deliver(Email $email, Mailbox $envelopeTo) {
+               // delivery is locked
+               if ($this->locked instanceof \Exception) {
+                       throw $this->locked;
+               }
+               
                if (!$this->connection || $this->connection->eof()) {
-                       $this->connect();
-                       $this->auth();
+                       try {
+                               $this->connect();
+                               $this->auth();
+                       }
+                       catch (PermanentFailure $e) {
+                               // lock delivery on permanent failure to avoid spamming the SMTP server
+                               $this->locked = $e;
+                               $this->disconnect();
+                               throw $e;
+                       }
+                       catch (SystemException $e) {
+                               $this->disconnect();
+                               throw $e;
+                       }
                }
+               
                $this->write('RSET');
                $this->read([ 250 ]);
                $this->write('MAIL FROM:<'.$email->getSender()->getAddress().'>');