Actually check for TLS support in update_com.woltlab.wcf_5.5_starttls.php
authorTim Düsterhus <duesterhus@woltlab.com>
Tue, 13 Jul 2021 12:41:12 +0000 (14:41 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 13 Jul 2021 12:41:12 +0000 (14:41 +0200)
wcfsetup/install/files/acp/update_com.woltlab.wcf_5.5_starttls.php

index 6a817b94ceeaae1d058cddae6f8c356deb48e3e7..35400298e22f71c717fd5ca3ba58c43c0cff020b 100644 (file)
@@ -10,6 +10,9 @@
  */
 
 use wcf\data\option\OptionAction;
+use wcf\system\email\Email;
+use wcf\system\email\transport\exception\TransientFailure;
+use wcf\system\io\RemoteFile;
 use wcf\util\StringUtil;
 
 if (MAIL_SMTP_STARTTLS != 'may') {
@@ -18,9 +21,76 @@ if (MAIL_SMTP_STARTTLS != 'may') {
 
 $value = 'encrypt';
 if (StringUtil::startsWith(MAIL_SMTP_HOST, 'ssl://')) {
+    // Anything using proper SSL can't use STARTTLS.
     $value = 'none';
 } elseif (MAIL_SMTP_PORT == 465) {
+    // Anything on port 465 must be using proper SSL.
     $value = 'none';
+} elseif (MAIL_SEND_METHOD == 'smtp') {
+    // For all the other configurations that use SMTP as the transport we
+    // need to verify whether TLS works or not.
+
+    $getCode = static function (RemoteFile $connection) {
+        $code = null;
+        do {
+            $data = $connection->gets();
+            if (\preg_match('/^(\d{3})([- ])(.*)$/', $data, $matches)) {
+                if ($code === null) {
+                    $code = \intval($matches[1]);
+                }
+
+                if ($code == $matches[1]) {
+                    if ($matches[2] === ' ') {
+                        return $code;
+                    }
+                } else {
+                    throw new TransientFailure("Unexpected reply '" . $data . "' from SMTP server. Code does not match previous codes from multiline answer.");
+                }
+            } else {
+                if ($connection->eof()) {
+                    throw new TransientFailure("Unexpected EOF / connection close from SMTP server.");
+                }
+
+                throw new TransientFailure("Unexpected reply '" . $data . "' from SMTP server.");
+            }
+        } while (true);
+    };
+
+    try {
+        $connection = new RemoteFile(MAIL_SMTP_HOST, MAIL_SMTP_PORT, 5);
+        $success = false;
+        if ($getCode($connection) == 220) {
+            $connection->write('EHLO ' . Email::getHost() . "\r\n");
+            if ($getCode($connection) == 250) {
+                $connection->write("STARTTLS\r\n");
+                if ($getCode($connection) == 220) {
+                    if ($connection->setTLS(true)) {
+                        $connection->write('EHLO ' . Email::getHost() . "\r\n");
+                        if ($getCode($connection) == 250) {
+                            $success = true;
+                            try {
+                                $connection->write("QUIT\r\n");
+                            } catch (\Exception $e) {
+                                // Ignore errors during disconnect.
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!$success) {
+            $value = 'none';
+        }
+    } catch (\Exception $e) {
+        $value = 'none';
+    } finally {
+        try {
+            $connection->close();
+        } catch (\Exception $e) {
+            // Ignore errors during disconnect.
+        }
+    }
 }
 
 $optionAction = new OptionAction([], 'import', [