/** * Process the mailbox defined by the named set of settings from civicrm_mail_settings * * @param string $name name of the set of settings from civicrm_mail_settings (null for default set) * @return void */ static function process($name = null) { require_once 'CRM/Core/DAO/MailSettings.php'; $dao = new CRM_Core_DAO_MailSettings(); $dao->domain_id = CRM_Core_Config::domainID(); $name ? $dao->name = $name : ($dao->is_default = 1); if (!$dao->find(true)) { throw new Exception("Could not find entry named {$name} in civicrm_mail_settings"); } $config =& CRM_Core_Config::singleton(); $verpSeperator = preg_quote($config->verpSeparator); $twoDigitStringMin = $verpSeperator . '(\\d+)' . $verpSeperator . '(\\d+)'; $twoDigitString = $twoDigitStringMin . $verpSeperator; $threeDigitString = $twoDigitString . '(\\d+)' . $verpSeperator; // FIXME: legacy regexen to handle CiviCRM 2.1 address patterns, with domain id and possible VERP part $commonRegex = '/^' . preg_quote($dao->localpart) . '(b|bounce|c|confirm|o|optOut|r|reply|re|e|resubscribe|u|unsubscribe)' . $threeDigitString . '([0-9a-f]{16})(-.*)?@' . preg_quote($dao->domain) . '$/'; $subscrRegex = '/^' . preg_quote($dao->localpart) . '(s|subscribe)' . $twoDigitStringMin . '@' . preg_quote($dao->domain) . '$/'; // a common-for-all-actions regex to handle CiviCRM 2.2 address patterns $regex = '/^' . preg_quote($dao->localpart) . '(b|c|e|o|r|u)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '$/'; // a tighter regex for finding bounce info in soft bounces’ mail bodies $rpRegex = '/Return-Path: ' . preg_quote($dao->localpart) . '(b)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '/'; // retrieve the emails require_once 'CRM/Mailing/MailStore.php'; $store = CRM_Mailing_MailStore::getStore($name); require_once 'api/Mailer.php'; // process fifty at a time, CRM-4002 while ($mails = $store->fetchNext(50)) { foreach ($mails as $key => $mail) { // for every addressee: match address elements if it's to CiviMail $matches = array(); foreach ($mail->to as $address) { if (preg_match($regex, $address->email, $matches)) { list($match, $action, $job, $queue, $hash) = $matches; break; // FIXME: the below elseifs should be dropped when we drop legacy support } elseif (preg_match($commonRegex, $address->email, $matches)) { list($match, $action, $_, $job, $queue, $hash) = $matches; break; } elseif (preg_match($subscrRegex, $address->email, $matches)) { list($match, $action, $_, $job) = $matches; break; } } // CRM-5471: if $matches is empty, it still might be a soft bounce sent // to another address, so scan the body for ‘Return-Path: …bounce-pattern…’ if (!$matches and preg_match($rpRegex, $mail->generateBody(), $matches)) { list($match, $action, $job, $queue, $hash) = $matches; } // if $matches is empty, this email is not CiviMail-bound if (!$matches) { $store->markIgnored($key); continue; } // get $replyTo from either the Reply-To header or from From // FIXME: make sure it works with Reply-Tos containing non-email stuff $replyTo = $mail->getHeader('Reply-To') ? $mail->getHeader('Reply-To') : $mail->from->email; // handle the action by passing it to the proper API call // FIXME: leave only one-letter cases when dropping legacy support switch ($action) { case 'b': case 'bounce': $text = ''; if ($mail->body instanceof ezcMailText) { $text = $mail->body->text; } elseif ($mail->body instanceof ezcMailMultipart) { if ($mail->body instanceof ezcMailMultipartRelated) { foreach ($mail->body->getRelatedParts() as $part) { if (isset($part->subType) and $part->subType == 'plain') { $text = $part->text; break; } } } else { foreach ($mail->body->getParts() as $part) { if (isset($part->subType) and $part->subType == 'plain') { $text = $part->text; break; } } } } crm_mailer_event_bounce($job, $queue, $hash, $text); break; case 'c': case 'confirm': crm_mailer_event_confirm($job, $queue, $hash); break; case 'o': case 'optOut': crm_mailer_event_domain_unsubscribe($job, $queue, $hash); break; case 'r': case 'reply': // instead of text and HTML parts (4th and 6th params) send the whole email as the last param crm_mailer_event_reply($job, $queue, $hash, null, $replyTo, null, $mail->generate()); break; case 'e': case 're': case 'resubscribe': crm_mailer_event_resubscribe($job, $queue, $hash); break; case 's': case 'subscribe': crm_mailer_event_subscribe($mail->from->email, $job); break; case 'u': case 'unsubscribe': crm_mailer_event_unsubscribe($job, $queue, $hash); break; } $store->markProcessed($key); } } }
public function mailer_event_subscribe($key, $email, $domain, $group) { $this->verify($key); return crm_mailer_event_subscribe($email, $domain, $group); }
/** * Process the mailbox for all the settings from civicrm_mail_settings * * @param string $civiMail if true, processing is done in CiviMail context, or Activities otherwise. * @return void */ static function process($civiMail = true) { require_once 'CRM/Core/OptionGroup.php'; $emailActivityTypeId = defined('EMAIL_ACTIVITY_TYPE_ID') && EMAIL_ACTIVITY_TYPE_ID ? EMAIL_ACTIVITY_TYPE_ID : CRM_Core_OptionGroup::getValue('activity_type', 'Inbound Email', 'name'); if (!$emailActivityTypeId) { CRM_Core_Error::fatal(ts('Could not find a valid Activity Type ID for Inbound Email')); } require_once 'CRM/Core/DAO/MailSettings.php'; $dao = new CRM_Core_DAO_MailSettings(); $dao->domain_id = CRM_Core_Config::domainID(); $dao->find(); while ($dao->fetch()) { // FIXME: legacy regexen to handle CiviCRM 2.1 address patterns, with domain id and possible VERP part $commonRegex = '/^' . preg_quote($dao->localpart) . '(b|bounce|c|confirm|o|optOut|r|reply|re|e|resubscribe|u|unsubscribe)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.([0-9a-f]{16})(-.*)?@' . preg_quote($dao->domain) . '$/'; $subscrRegex = '/^' . preg_quote($dao->localpart) . '(s|subscribe)\\.(\\d+)\\.(\\d+)@' . preg_quote($dao->domain) . '$/'; // a common-for-all-actions regex to handle CiviCRM 2.2 address patterns $regex = '/^' . preg_quote($dao->localpart) . '(b|c|e|o|r|u)\\.(\\d+)\\.(\\d+)\\.([0-9a-f]{16})@' . preg_quote($dao->domain) . '$/'; // retrieve the emails require_once 'CRM/Mailing/MailStore.php'; $store = CRM_Mailing_MailStore::getStore($dao->name); require_once 'api/Mailer.php'; // process fifty at a time, CRM-4002 while ($mails = $store->fetchNext(MAIL_BATCH_SIZE)) { foreach ($mails as $key => $mail) { // for every addressee: match address elements if it's to CiviMail $matches = array(); if ($civiMail) { foreach ($mail->to as $address) { if (preg_match($regex, $address->email, $matches)) { list($match, $action, $job, $queue, $hash) = $matches; break; // FIXME: the below elseifs should be dropped when we drop legacy support } elseif (preg_match($commonRegex, $address->email, $matches)) { list($match, $action, $_, $job, $queue, $hash) = $matches; break; } elseif (preg_match($subscrRegex, $address->email, $matches)) { list($match, $action, $_, $job) = $matches; break; } } } else { // if its the activities that needs to be processed .. require_once 'CRM/Utils/Mail/Incoming.php'; $mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail); require_once 'api/v2/Activity.php'; $params = _civicrm_activity_buildmailparams($mailParams, $emailActivityTypeId); $result = civicrm_activity_create($params); if ($result['is_error']) { $matches = false; echo "Failed Processing: {$mail->subject}. Reason: {$result['error_message']}\n"; } else { $matches = true; echo "Processed: {$mail->subject}\n"; } } // if $matches is empty, this email is not CiviMail-bound if (!$matches) { $store->markIgnored($key); continue; } // get $replyTo from either the Reply-To header or from From // FIXME: make sure it works with Reply-Tos containing non-email stuff $replyTo = $mail->getHeader('Reply-To') ? $mail->getHeader('Reply-To') : $mail->from->email; // handle the action by passing it to the proper API call // FIXME: leave only one-letter cases when dropping legacy support if (!empty($action)) { switch ($action) { case 'b': case 'bounce': $text = ''; if ($mail->body instanceof ezcMailText) { $text = $mail->body->text; } elseif ($mail->body instanceof ezcMailMultipart) { foreach ($mail->body->getParts() as $part) { if (isset($part->subType) and $part->subType == 'plain') { $text = $part->text; break; } } } crm_mailer_event_bounce($job, $queue, $hash, $text); break; case 'c': case 'confirm': crm_mailer_event_confirm($job, $queue, $hash); break; case 'o': case 'optOut': crm_mailer_event_domain_unsubscribe($job, $queue, $hash); break; case 'r': case 'reply': // instead of text and HTML parts (4th and 6th params) send the whole email as the last param crm_mailer_event_reply($job, $queue, $hash, null, $replyTo, null, $mail->generate()); break; case 'e': case 're': case 'resubscribe': crm_mailer_event_resubscribe($job, $queue, $hash); break; case 's': case 'subscribe': crm_mailer_event_subscribe($mail->from->email, $job); break; case 'u': case 'unsubscribe': crm_mailer_event_unsubscribe($job, $queue, $hash); break; } } $store->markProcessed($key); } } } }