diff --git a/CRM/Mailing/BAO/MailingJob.php b/CRM/Mailing/BAO/MailingJob.php index 398afeeaa202..12379bcc8e68 100644 --- a/CRM/Mailing/BAO/MailingJob.php +++ b/CRM/Mailing/BAO/MailingJob.php @@ -676,10 +676,7 @@ public function deliverGroup(&$fields, &$mailing, &$mailer, &$job_date, &$attach if (is_a($result, 'PEAR_Error') && !$mailing->sms_provider_id) { // CRM-9191 $message = $result->getMessage(); - if ( - strpos($message, 'Failed to write to socket') !== FALSE || - strpos($message, 'Failed to set sender') !== FALSE - ) { + if ($this->isTemporaryError($message)) { // lets log this message and code $code = $result->getCode(); CRM_Core_Error::debug_log_message("SMTP Socket Error or failed to set sender error. Message: $message, Code: $code"); @@ -786,6 +783,43 @@ public function deliverGroup(&$fields, &$mailing, &$mailer, &$job_date, &$attach return $result; } + /** + * Determine if an SMTP error is temporary or permanent. + * + * @param string $message + * PEAR error message. + * @return bool + * TRUE - Temporary/retriable error + * FALSE - Permanent/non-retriable error + */ + protected function isTemporaryError($message) { + // SMTP response code is buried in the message. + $code = preg_match('/ \(code: (.+), response: /', $message, $matches) ? $matches[1] : ''; + + if (strpos($message, 'Failed to write to socket') !== FALSE) { + return TRUE; + } + + // Register 5xx SMTP response code (permanent failure) as bounce. + if (isset($code{0}) && $code{0} === '5') { + return FALSE; + } + + if (strpos($message, 'Failed to set sender') !== FALSE) { + return TRUE; + } + + if (strpos($message, 'Failed to add recipient') !== FALSE) { + return TRUE; + } + + if (strpos($message, 'Failed to send data') !== FALSE) { + return TRUE; + } + + return FALSE; + } + /** * Cancel a mailing. * diff --git a/tests/phpunit/CRM/Mailing/BAO/MailingJobTest.php b/tests/phpunit/CRM/Mailing/BAO/MailingJobTest.php new file mode 100644 index 000000000000..1ca02517c20c --- /dev/null +++ b/tests/phpunit/CRM/Mailing/BAO/MailingJobTest.php @@ -0,0 +1,67 @@ +getMethod($name); + $method->setAccessible(TRUE); + return $method->invokeArgs($obj, $args); + } + + /** + * Tests CRM_Mailing_BAO_MailingJob::isTemporaryError() method. + */ + public function testIsTemporaryError() { + $testcases[] = ['return' => TRUE, 'message' => 'Failed to set sender: test@example.org [SMTP: Invalid response code received from SMTP server while sending email. This is often caused by a misconfiguration in Outbound Email settings. Please verify the settings at Administer CiviCRM >> Global Settings >> Outbound Email (SMTP). (code: 421, response: Timeout waiting for data from client.)]']; + $testcases[] = ['return' => TRUE, 'message' => 'Failed to send data [SMTP: Invalid response code received from SMTP server while sending email. This is often caused by a misconfiguration in Outbound Email settings. Please verify the settings at Administer CiviCRM >> Global Settings >> Outbound Email (SMTP). (code: 454, response: Throttling failure: Maximum sending rate exceeded.)]']; + $testcases[] = ['return' => TRUE, 'message' => 'Failed to set sender: test@example.org [SMTP: Failed to write to socket: not connected (code: -1, response: )]']; + // @fixme: These errors also seem to be temporary, but are not yet handled as temporary. + $testcases[] = ['return' => FALSE, 'message' => 'Failed to connect to email.example.com:587 [SMTP: Failed to connect socket: Connection timed out (code: -1, response: )]']; + $testcases[] = ['return' => FALSE, 'message' => 'Failed to send data [SMTP: Invalid response code received from SMTP server while sending email. This is often caused by a misconfiguration in Outbound Email settings. Please verify the settings at Administer CiviCRM >> Global Settings >> Outbound Email (SMTP). (code: 554, response: Message rejected: Sending suspended for this account. For more information, please check the inbox of the email address associated with your AWS account.)]']; + $testcases[] = ['return' => FALSE, 'message' => 'authentication failure [SMTP: Invalid response code received from SMTP server while sending email. This is often caused by a misconfiguration in Outbound Email settings. Please verify the settings at Administer CiviCRM >> Global Settings >> Outbound Email (SMTP). (code: 454, response: Temporary authentication failure)]']; + $object = new CRM_Mailing_BAO_MailingJob(); + foreach ($testcases as $testcase) { + $isTemporaryError = self::callMethod($object, 'isTemporaryError', [$testcase['message']]); + if ($testcase['return']) { + $this->assertTrue($isTemporaryError); + } + else { + $this->assertFalse($isTemporaryError); + } + } + } + +}