3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
11 namespace Civi\FlexMailer\Listener
;
13 use Civi\FlexMailer\Event\SendBatchEvent
;
15 class DefaultSender
extends BaseListener
{
16 const BULK_MAIL_INSERT_COUNT
= 10;
18 public function onSend(SendBatchEvent
$e) {
19 static $smtpConnectionErrors = 0;
21 if (!$this->isActive()) {
25 $e->stopPropagation();
28 $mailing = $e->getMailing();
29 $job_date = \CRM_Utils_Date
::isoToMysql($job->scheduled_date
);
30 $mailer = \Civi
::service('pear_mail');
32 $targetParams = $deliveredParams = array();
36 foreach ($e->getTasks() as $key => $task) {
37 /** @var \Civi\FlexMailer\FlexMailerTask $task */
38 /** @var \Mail_mime $message */
39 if (!$task->hasContent()) {
43 $message = \Civi\FlexMailer\MailParams
::convertMailParamsToMime($task->getMailParams());
45 if (empty($message)) {
46 // lets keep the message in the queue
47 // most likely a permissions related issue with smarty templates
48 // or a bad contact id? CRM-9833
52 // disable error reporting on real mailings (but leave error reporting for tests), CRM-5744
54 $errorScope = \CRM_Core_TemporaryErrorScope
::ignoreException();
57 $headers = $message->headers();
58 $result = $mailer->send($headers['To'], $message->headers(), $message->get());
64 if (is_a($result, 'PEAR_Error')) {
65 /** @var \PEAR_Error $result */
67 $message = $result->getMessage();
68 if ($this->isTemporaryError($result->getMessage())) {
69 // lets log this message and code
70 $code = $result->getCode();
71 \CRM_Core_Error
::debug_log_message("SMTP Socket Error or failed to set sender error. Message: $message, Code: $code");
73 // these are socket write errors which most likely means smtp connection errors
74 // lets skip them and reconnect.
75 $smtpConnectionErrors++
;
76 if ($smtpConnectionErrors <= 5) {
77 $mailer->disconnect();
82 // seems like we have too many of them in a row, we should
83 // write stuff to disk and abort the cron job
84 $job->writeToDB($deliveredParams, $targetParams, $mailing, $job_date);
86 \CRM_Core_Error
::debug_log_message("Too many SMTP Socket Errors. Exiting");
87 \CRM_Utils_System
::civiExit();
90 $this->recordBounce($job, $task, $result->getMessage());
94 // Register the delivery event.
95 $deliveredParams[] = $task->getEventQueueId();
96 $targetParams[] = $task->getContactId();
99 if ($count % self
::BULK_MAIL_INSERT_COUNT
== 0) {
100 $job->writeToDB($deliveredParams, $targetParams, $mailing, $job_date);
103 // hack to stop mailing job at run time, CRM-4246.
104 // to avoid making too many DB calls for this rare case
105 // lets do it when we snapshot
106 $status = \CRM_Core_DAO
::getFieldValue(
107 'CRM_Mailing_DAO_MailingJob',
114 if ($status != 'Running') {
115 $e->setCompleted(FALSE);
123 // seems like a successful delivery or bounce, lets decrement error count
124 // only if we have smtp connection errors
125 if ($smtpConnectionErrors > 0) {
126 $smtpConnectionErrors--;
129 // If we have enabled the Throttle option, this is the time to enforce it.
130 $mailThrottleTime = \CRM_Core_Config
::singleton()->mailThrottleTime
;
131 if (!empty($mailThrottleTime)) {
132 usleep((int) $mailThrottleTime);
136 $completed = $job->writeToDB(
145 $e->setCompleted($completed);
149 * Determine if an SMTP error is temporary or permanent.
151 * @param string $message
152 * PEAR error message.
154 * TRUE - Temporary/retriable error
155 * FALSE - Permanent/non-retriable error
157 protected function isTemporaryError($message) {
158 // SMTP response code is buried in the message.
159 $code = preg_match('/ \(code: (.+), response: /', $message, $matches) ?
$matches[1] : '';
161 if (strpos($message, 'Failed to write to socket') !== FALSE) {
165 // Register 5xx SMTP response code (permanent failure) as bounce.
166 if (isset($code{0}) && $code{0} === '5') {
170 if (strpos($message, 'Failed to set sender') !== FALSE) {
174 if (strpos($message, 'Failed to add recipient') !== FALSE) {
178 if (strpos($message, 'Failed to send data') !== FALSE) {
186 * @param \CRM_Mailing_BAO_MailingJob $job
187 * @param \Civi\FlexMailer\FlexMailerTask $task
188 * @param string $errorMessage
190 protected function recordBounce($job, $task, $errorMessage) {
192 'event_queue_id' => $task->getEventQueueId(),
193 'job_id' => $job->id
,
194 'hash' => $task->getHash(),
196 $params = array_merge($params,
197 \CRM_Mailing_BAO_BouncePattern
::match($errorMessage)
199 \CRM_Mailing_Event_BAO_Bounce
::create($params);