factory = $factory; $this->db = $factory->getDatabase(); $this->logger = $this->factory->getLogger(); } /** * @param Message $message * @param bool|false $allowBounce * @param bool|false $allowUnsubscribe * * @return bool */ public function analyzeMessage(Message $message, $allowBounce = false, $allowUnsubscribe = false) { $dtHelper = new DateTimeHelper(); // Assume is an unsubscribe $isUnsubscribe = $allowUnsubscribe; $isBounce = false; $toEmail = reset($message->to); // Check for bounce emails via + notation if applicable foreach ($message->to as $to => $name) { if (strpos($to, '+bounce') !== false) { $isBounce = true; $isUnsubscribe = false; $toEmail = $to; break; } elseif (strpos($to, '+unsubscribe')) { $isBounce = false; $isUnsubscribe = true; $toEmail = $to; break; } } $this->logger->debug("Analyzing message to {$message->toString}"); // If message from Amazon SNS collect bounces and complaints if ($message->fromAddress=='no-reply@sns.amazonaws.com') { $message = json_decode(strtok($message->textPlain, "\n"), true); if ($message['notificationType']=='Bounce') { $isBounce = true; $isUnsubscribe = false; $toEmail = $message['mail']['source']; $amazonEmail = $message['bounce']['bouncedRecipients'][0]['emailAddress']; } elseif ($message['notificationType']=='Complaint') { $isBounce = false; $isUnsubscribe = true; $toEmail = $message['mail']['source']; $amazonEmail = $message['complaint']['complainedRecipients'][0]['emailAddress']; } } // Parse the to email if applicable if (preg_match('#^(.*?)\+(.*?)@(.*?)$#', $toEmail, $parts)) { if (strstr($parts[2], '_')) { // Has an ID hash so use it to find the lead list($ignore, $hashId) = explode('_', $parts[2]); } } $messageDetails = array(); if ($allowBounce) { // If message from Amazon SNS fill details and don't process further if (isset($amazonEmail)){ $messageDetails['email']=$amazonEmail; $messageDetails['rule_cat'] = 'unknown'; $messageDetails['rule_no'] = '0013'; $messageDetails['bounce_type'] = 'hard'; $messageDetails['remove'] = 1; } else { if (!empty($message->dsnReport)) { // Parse the bounce $dsnMessage = ($message->dsnMessage) ? $message->dsnMessage : $message->textPlain; $dsnReport = $message->dsnReport; $this->logger->addDebug('Delivery report found in message.'); // Try parsing the report $messageDetails = $this->parseDsn($dsnMessage, $dsnReport); } if (empty($messageDetails['email']) || $messageDetails['rule_cat'] == 'unrecognized') { // Check for the X-Failed-Recipients header $bouncedEmail = (isset($message->xHeaders['x-failed-recipients'])) ? $message->xHeaders['x-failed-recipients'] : null; if ($bouncedEmail) { // Definitely a bounced email but need to find the reason $this->logger->debug('Email found through x-failed-recipients header but need to search for a reason.'); } else { $this->logger->debug('Bounce email or reason not found so attempting to parse the body.'); } // Let's try parsing through the body parser $messageDetails = $this->parseBody($message->textPlain, $bouncedEmail); } if (!$isBounce && !empty($messageDetails['email'])) { // Bounce was found in message content $isBounce = true; $isUnsubscribe = false; } } } if (!$isBounce && !$isUnsubscribe) { $this->logger->debug('No reason found to process.'); return false; } // Search for the lead $stat = $leadId = $leadEmail = $emailId = null; if (!empty($hashId)) { $q = $this->db->createQueryBuilder(); // Search by hashId $q->select('*') ->from(MAUTIC_TABLE_PREFIX.'email_stats', 's') ->where( $q->expr()->eq('s.tracking_hash', ':hash') ) ->setParameter('hash', $hashId); $results = $q->execute()->fetchAll(); if (count($results)) { $stat = $results[0]; $leadId = $stat['lead_id']; $leadEmail = $stat['email_address']; $emailId = $stat['email_id']; $this->logger->debug('Stat found with ID# '.$stat['id']); } unset($results); } if (!$leadId) { if ($isBounce) { if (!empty($messageDetails['email'])) { $leadEmail = $messageDetails['email']; } else { // Email not found for the bounce so abort $this->logger->error('BOUNCE ERROR: A lead could be found from the bounce email. From: ' . $message->fromAddress . '; To: ' . $message->toString . '; Subject: ' . $message->subject); return false; } } else { $leadEmail = $message->fromAddress; $this->logger->debug('From address used: ' . $leadEmail); } // Search by first part and domain of email to find cases like me+mautic@domain.com list($email, $domain) = explode('@', strtolower($leadEmail)); $email = $email . '%'; $domain = '%@' . $domain; $q = $this->db->createQueryBuilder(); $q->select('l.id, l.email') ->from(MAUTIC_TABLE_PREFIX.'leads', 'l') ->where( $q->expr()->orX( $q->expr()->eq('LOWER(l.email)', ':leademail'), $q->expr()->andX( $q->expr()->like('LOWER(l.email)', ':email'), $q->expr()->like('LOWER(l.email)', ':domain') ) ) ) ->setParameter('leademail', strtolower($leadEmail)) ->setParameter('email', strtolower($email)) ->setParameter('domain', strtolower($domain)); $foundLeads = $q->execute()->fetchAll(); ; foreach ($foundLeads as $lead) { if (strtolower($lead['email']) == strtolower($leadEmail)) { // Exact match $leadId = $lead['id']; break; } elseif (strpos($lead['email'], '+') === false) { // Not a plus style email so not a match break; } if (preg_match('#^(.*?)\+(.*?)@(.*?)$#', $lead['email'], $parts)) { $email = $parts[1] . '@' . $parts[3]; if (strtolower($email) == strtolower($leadEmail)) { $this->logger->debug('Lead found through + alias: '. $lead['email']); $leadId = $lead['id']; $leadEmail = $lead['email']; } } } $this->logger->debug('Lead ID: ' . ($leadId ? $leadId : 'not found')); } if (!$leadId) { // A lead still could not be found return false; } // Set message details for unsubscribe requests if ($isUnsubscribe) { $messageDetails = array( 'remove' => true, 'email' => $leadEmail, 'rule_cat' => 'unsubscribed', 'rule_no' => '0000' ); } if ($isBounce && $stat) { // Update the stat with some details $openDetails = unserialize($stat['open_details']); if (!is_array($openDetails)) { $openDetails = array(); } $openDetails['bounces'][] = array( 'datetime' => $dtHelper->toUtcString(), 'reason' => $messageDetails['rule_cat'], 'code' => $messageDetails['rule_no'], 'type' => ($messageDetails['bounce_type'] === false) ? 'unknown' : $messageDetails['bounce_type'] ); $this->db->update( MAUTIC_TABLE_PREFIX.'email_stats', array( 'open_details' => serialize($openDetails), 'retry_count' => $stat['retry_count']++, 'is_failed' => ($messageDetails['remove'] || $stat['retry_count'] == 5) ? 1 : 0 ), array('id' => $stat['id']) ); $this->logger->debug('Stat updated'); } // Is this a hard bounce or AN unsubscribe? if ($messageDetails['remove'] || ($stat && $stat['retry_count'] >= 5)) { $this->logger->debug('Adding DNC entry for ' . $leadEmail); // Check for an existing DNC entry $q = $this->db->createQueryBuilder(); $q->select('dnc.id') ->from(MAUTIC_TABLE_PREFIX . 'lead_donotcontact', 'dnc') ->where('dnc.channel = "email"') ->where( $q->expr()->eq('dnc.lead_id', ':leadId') ) ->setParameter('leadId', $leadId); try { $exists = $q->execute()->fetchColumn(); } catch (\Exception $e) { $this->logger->error($e->getMessage()); } if (!empty($exists)) { $this->logger->debug('A DNC entry already exists for ' . $leadEmail); } else { $this->logger->debug('Existing not found so creating a new one.'); // Create a DNC entry try { $this->db->insert( MAUTIC_TABLE_PREFIX . 'lead_donotcontact', array( 'lead_id' => $leadId, 'channel' => 'email', 'channel_id' => $emailId, 'date_added' => $dtHelper->toUtcString(), 'reason' => ($isUnsubscribe) ? DoNotContact::UNSUBSCRIBED : DoNotContact::BOUNCED, 'comments' => $this->factory->getTranslator()->trans('mautic.email.bounce.reason.'.$messageDetails['rule_cat']) ) ); } catch (\Exception $e) { $this->logger->error($e->getMessage()); } } } $this->logger->debug(print_r($messageDetails, true)); } /** * next rule number (BODY): 0238
* default category: unrecognized:
* default rule no.: 0000
*/ static $rule_categories = array( 'antispam' => array('remove' => 0, 'bounce_type' => 'blocked'), 'autoreply' => array('remove' => 0, 'bounce_type' => 'autoreply'), 'concurrent' => array('remove' => 0, 'bounce_type' => 'soft'), 'content_reject' => array('remove' => 0, 'bounce_type' => 'soft'), 'command_reject' => array('remove' => 1, 'bounce_type' => 'hard'), 'internal_error' => array('remove' => 0, 'bounce_type' => 'temporary'), 'defer' => array('remove' => 0, 'bounce_type' => 'soft'), 'delayed' => array('remove' => 0, 'bounce_type' => 'temporary'), 'dns_loop' => array('remove' => 1, 'bounce_type' => 'hard'), 'dns_unknown' => array('remove' => 1, 'bounce_type' => 'hard'), 'full' => array('remove' => 0, 'bounce_type' => 'soft'), 'inactive' => array('remove' => 1, 'bounce_type' => 'hard'), 'latin_only' => array('remove' => 0, 'bounce_type' => 'soft'), 'other' => array('remove' => 1, 'bounce_type' => 'generic'), 'oversize' => array('remove' => 0, 'bounce_type' => 'soft'), 'outofoffice' => array('remove' => 0, 'bounce_type' => 'soft'), 'unknown' => array('remove' => 1, 'bounce_type' => 'hard'), 'unrecognized' => array('remove' => 0, 'bounce_type' => false,), 'user_reject' => array('remove' => 1, 'bounce_type' => 'hard'), 'warning' => array('remove' => 0, 'bounce_type' => 'soft') ); /* * var for new line ending */ static $bmh_newline = "
\n"; /** * Defined bounce parsing rules for non-standard DSN * * @param string $body body of the email * @param string|null $knownEmail Bounced email if known through a x-failed-recipient header or the like and need to parse the body for a reason * @param boolean $debug_mode show debug info. or not * * @return array $result an array include the following fields: 'email', 'bounce_type','remove','rule_no','rule_cat' * if we could NOT detect the type of bounce, return rule_no = '0000' */ static public function parseBody($body, $knownEmail = '', $debug_mode = false) { // initialize the result array $result = array( 'email' => $knownEmail, 'bounce_type' => false, 'remove' => 0, 'rule_cat' => 'unrecognized', 'rule_no' => '0000' ); // ======== rule ========= /** * Email is already known likely for a x-failed-recipients header; most likely Gmail bounce */ if ('' !== $knownEmail) { /* * rule: mailbox unknown; * sample: * The error that the other server returned was: * 550-5.1.1 The email account that you tried to reach does not exist. */ if (preg_match("/email.*?does not exist/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0237'; } /* * rule: mailbox unknown; * sample: * The error that the other server returned was: * 553-5.1.2 We weren't able to find the recipient domain. */ elseif (preg_match("/find the recipient domain/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0237'; } /* * rule: mailbox inactive; * sample: * The error that the other server returned was: * 550-5.2.1 The email account that you tried to reach is disabled. */ elseif (preg_match("/email.*?disabled/i", $body, $match)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0171'; } /* * rule: mailbox warning; * sample: * The error that the other server returned was: * 550-5.2.1 The user you are trying to contact is receiving mail at a rate that prevents additional messages from being delivered. */ elseif (preg_match("/user.*?rate that prevents/i", $body, $match)) { $result['rule_cat'] = 'warning'; $result['rule_no'] = '0000'; } /* * rule: mailbox full; * sample: * The error that the other server returned was: * 550-5.7.1 Email quota exceeded. */ elseif (preg_match("/email quota exceeded/i", $body, $match)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0219'; } /* * rule: mailbox full; * sample: * The error that the other server returned was: * 552-5.2.2 The email account that you tried to reach is over quota. */ if (preg_match("/email.*?over quota/i", $body, $match)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0219'; } /* * rule: mailbox antispam; * sample: * The error that the other server returned was: * 550-5.7.1 Our system has detected an unusual rate of unsolicited mail originating from your IP address. To protect our users from spam, * mail sent from your IP address has been blocked. */ elseif (preg_match("/unsolicited mail/i", $body, $match)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0230'; } /* * rule: mailbox antispam; * sample: * The error that the other server returned was: * 550-5.7.1 The user or domain that you are sending to (or from) has a policy that prohibited the mail that you sent. */ elseif (preg_match("/policy that prohibited/i", $body, $match)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0230'; } /* * rule: mailbox oversize; * sample: * The error that the other server returned was: * 552-5.2.3 Your message exceeded Google's message size limits. */ elseif (preg_match("/message size limits/i", $body, $match)) { $result['rule_cat'] = 'oversize'; $result['rule_no'] = '0146'; } } /* * rule: mailbox unknown; * sample: * xxxxx@yourdomain.com * no such address here */ if (preg_match("/(\S+@\S+\w).*\n?.*no such address here/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0237'; $result['email'] = $match[1]; } /* * : * 111.111.111.111 does not like recipient. * Remote host said: 550 User unknown */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*\n?.*user unknown/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0236'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * : * Sorry, no mailbox here by that name. vpopmail (#5.1.1) */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*no mailbox/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0157'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * xxxxx@yourdomain.com
* local: Sorry, can't find user's mailbox. (#5.1.1)
*/ elseif (preg_match("/(\S+@\S+\w)
.*\n?.*\n?.*can't find.*mailbox/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0164'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * ########################################################## * # This is an automated response from a mail delivery # * # program. Your message could not be delivered to # * # the following address: # * # # * # "|/usr/local/bin/mailfilt -u #dkms" # * # (reason: Can't create output) # * # (expanded from: ) # * # # */ elseif (preg_match("/Can't create output.*\n?.*<(\S+@\S+\w)>/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0169'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * ????????????????: * xxxxx@yourdomain.com : ????, ?????. */ elseif (preg_match("/(\S+@\S+\w).*=D5=CA=BA=C5=B2=BB=B4=E6=D4=DA/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0174'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * xxxxx@yourdomain.com * Unrouteable address */ elseif (preg_match("/(\S+@\S+\w).*\n?.*Unrouteable address/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0179'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * Delivery to the following recipients failed. * xxxxx@yourdomain.com */ elseif (preg_match("/delivery[^\n\r]+failed[ \S]*\s+(\S+@\S+\w)\s/is", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0013'; $result['email'] = $match[1]; } /* * rule: mailbox error (Amazon SES); * sample: * An error occurred while trying to deliver the mail to the following recipients: * xxxxx@yourdomain.com */ elseif (preg_match("/an\s+error\s+occurred\s+while\s+trying\s+to\s+deliver\s+the\s+mail\s+to\s+the\s+following\s+recipients:\r\n\s*(\S+@\S+\w)/is", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0013'; $result['bounce_type'] = 'hard'; $result['remove'] = 1; $result['email'] = $match[1]; $result['email'] = preg_replace("/Reporting\-MTA/","",$result['email']); } /* * rule: mailbox unknown; * sample: * A message that you sent could not be delivered to one or more of its^M * recipients. This is a permanent error. The following address(es) failed:^M * ^M * xxxxx@yourdomain.com^M * unknown local-part "xxxxx" in domain "yourdomain.com"^M */ elseif (preg_match("/(\S+@\S+\w).*\n?.*unknown local-part/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0232'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * :^M * 111.111.111.11 does not like recipient.^M * Remote host said: 550 Invalid recipient: ^M */ elseif (preg_match("/Invalid.*(?:alias|account|recipient|address|email|mailbox|user).*<(\S+@\S+\w)>/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0233'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * Sent >>> RCPT TO: ^M * Received <<< 550 xxxxx@yourdomain.com... No such user^M * ^M * Could not deliver mail to this user.^M * xxxxx@yourdomain.com^M * ***************** End of message ***************^M */ elseif (preg_match("/\s(\S+@\S+\w).*No such.*(?:alias|account|recipient|address|email|mailbox|user)>/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0234'; $result['email'] = $match[1]; } /* * rule: mailbox unknown; * sample: * :^M * This address no longer accepts mail. */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*(?:alias|account|recipient|address|email|mailbox|user).*no.*accept.*mail>/i", $body, $match)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0235'; $result['email'] = $match[1]; } /* * rule: full * sample 1: * : * This account is over quota and unable to receive mail. * sample 2: * : * Warning: undefined mail delivery mode: normal (ignored). * The users mailfolder is over the allowed quota (size). (#5.2.2) */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*\n?.*over.*quota/i", $body, $match)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0182'; $result['email'] = $match[1]; } /* * rule: mailbox full; * sample: * ----- Transcript of session follows ----- * mail.local: /var/mail/2b/10/kellen.lee: Disc quota exceeded * 554 ... Service unavailable */ elseif (preg_match("/quota exceeded.*\n?.*<(\S+@\S+\w)>/i", $body, $match)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0126'; $result['email'] = $match[1]; } /* * rule: mailbox full; * sample: * Hi. This is the qmail-send program at 263.domain.com. * : * - User disk quota exceeded. (#4.3.0) */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*quota exceeded/i", $body, $match)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0158'; $result['email'] = $match[1]; } /* * rule: mailbox full; * sample: * xxxxx@yourdomain.com * mailbox is full (MTA-imposed quota exceeded while writing to file /mbx201/mbx011/A100/09/35/A1000935772/mail/.inbox): */ elseif (preg_match("/\s(\S+@\S+\w)\s.*\n?.*mailbox.*full/i", $body, $match)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0166'; $result['email'] = $match[1]; } /* * rule: mailbox full; * sample: * The message to xxxxx@yourdomain.com is bounced because : Quota exceed the hard limit */ elseif (preg_match("/The message to (\S+@\S+\w)\s.*bounce.*Quota exceed/i", $body, $match)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0168'; $result['email'] = $match[1]; } /* * rule: inactive * sample: * xxxxx@yourdomain.com
* 553 user is inactive (eyou mta) */ elseif (preg_match("/(\S+@\S+\w)
.*\n?.*\n?.*user is inactive/i", $body, $match)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0171'; $result['email'] = $match[1]; } /* * rule: inactive * sample: * xxxxx@yourdomain.com [Inactive account] */ elseif (preg_match("/(\S+@\S+\w).*inactive account/i", $body, $match)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0181'; $result['email'] = $match[1]; } /* * rule: internal_error * sample: * : * Unable to switch to /var/vpopmail/domains/domain.com: input/output error. (#4.3.0) */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*input\/output error/i", $body, $match)) { $result['rule_cat'] = 'internal_error'; $result['rule_no'] = '0172'; $result['bounce_type'] = 'hard'; $result['remove'] = 1; $result['email'] = $match[1]; } /* * rule: internal_error * sample: * : * can not open new email file errno=13 file=/home/vpopmail/domains/fromc.com/0/domain/Maildir/tmp/1155254417.28358.mx05,S=212350 */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*can not open new email file/i", $body, $match)) { $result['rule_cat'] = 'internal_error'; $result['rule_no'] = '0173'; $result['bounce_type'] = 'hard'; $result['remove'] = 1; $result['email'] = $match[1]; } /* * rule: defer * sample: * : * 111.111.111.111 failed after I sent the message. * Remote host said: 451 mta283.mail.scd.yahoo.com Resources temporarily unavailable. Please try again later [#4.16.5]. */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*\n?.*Resources temporarily unavailable/i", $body, $match)) { $result['rule_cat'] = 'defer'; $result['rule_no'] = '0163'; $result['email'] = $match[1]; } /* * rule: autoreply * sample: * AutoReply message from xxxxx@yourdomain.com */ elseif (preg_match("/^AutoReply message from (\S+@\S+\w)/i", $body, $match)) { $result['rule_cat'] = 'autoreply'; $result['rule_no'] = '0167'; $result['email'] = $match[1]; } /* * rule: western chars only * sample: * : * The user does not accept email in non-Western (non-Latin) character sets. */ elseif (preg_match("/<(\S+@\S+\w)>.*\n?.*does not accept[^\r\n]*non-Western/i", $body, $match)) { $result['rule_cat'] = 'latin_only'; $result['rule_no'] = '0043'; $result['email'] = $match[1]; } if ($result['rule_no'] == '0000') { if ($debug_mode) { echo 'Body:'.self::$bmh_newline.$body.self::$bmh_newline; echo self::$bmh_newline; } } else { if ($result['bounce_type'] === false) { $result['bounce_type'] = self::$rule_categories[$result['rule_cat']]['bounce_type']; $result['remove'] = self::$rule_categories[$result['rule_cat']]['remove']; } } return $result; } /** * Defined bounce parsing rules for standard DSN (Delivery Status Notification) * * @param string $dsn_msg human-readable explanation * @param string $dsn_report delivery-status report * @param boolean $debug_mode show debug info. or not * * @return array $result an array include the following fields: 'email', 'bounce_type','remove','rule_no','rule_cat' * if we could NOT detect the type of bounce, return rule_no = '0000' */ static public function parseDsn($dsn_msg, $dsn_report, $debug_mode = false) { // initialize the result array $result = array( 'email' => '', 'bounce_type' => false, 'remove' => 0, 'rule_cat' => 'unrecognized', 'rule_no' => '0000' ); $action = false; $status_code = false; $diag_code = false; // ======= parse $dsn_report ====== // get the recipient email if (preg_match("/Original-Recipient: rfc822;(.*)/i", $dsn_report, $match)) { $email_arr = imap_rfc822_parse_adrlist($match[1], 'default.domain.name'); if (isset($email_arr[0]->host) && $email_arr[0]->host != '.SYNTAX-ERROR.' && $email_arr[0]->host != 'default.domain.name') { $result['email'] = $email_arr[0]->mailbox.'@'.$email_arr[0]->host; } } else if (preg_match("/Final-Recipient: rfc822;(.*)/i", $dsn_report, $match)) { $email_arr = imap_rfc822_parse_adrlist($match[1], 'default.domain.name'); if (isset($email_arr[0]->host) && $email_arr[0]->host != '.SYNTAX-ERROR.' && $email_arr[0]->host != 'default.domain.name') { $result['email'] = $email_arr[0]->mailbox.'@'.$email_arr[0]->host; } } if (preg_match("/Action: (.+)/i", $dsn_report, $match)) { $action = strtolower(trim($match[1])); } if (preg_match("/Status: ([0-9\.]+)/i", $dsn_report, $match)) { $status_code = $match[1]; } // Could be multi-line , if the new line is beginning with SPACE or HTAB if (preg_match("/Diagnostic-Code:((?:[^\n]|\n[\t ])+)(?:\n[^\t ]|$)/is", $dsn_report, $match)) { $diag_code = $match[1]; } // ======= rules ====== if (empty($result['email'])) { /* email address is empty * rule: full * sample: DSN Message only * User quota exceeded: SMTP */ if (preg_match("/quota exceed.*<(\S+@\S+\w)>/is", $dsn_msg, $match)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0161'; $result['email'] = $match[1]; } } else { /* action could be one of them as RFC:1894 * "failed" / "delayed" / "delivered" / "relayed" / "expanded" */ switch ($action) { case 'failed': /* rule: full * sample: * Diagnostic-Code: X-Postfix; me.domain.com platform: said: 552 5.2.2 Over * quota (in reply to RCPT TO command) */ if (preg_match("/over.*quota/is", $diag_code)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0105'; } /* rule: full * sample: * Diagnostic-Code: SMTP; 552 Requested mailbox exceeds quota. */ elseif (preg_match("/exceed.*quota/is", $diag_code)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0129'; } /* rule: full * sample 1: * Diagnostic-Code: smtp;552 5.2.2 This message is larger than the current system limit or the recipient's mailbox is full. Create a shorter message body or remove attachments and try sending it again. * sample 2: * Diagnostic-Code: X-Postfix; host mta5.us4.domain.com.int[111.111.111.111] said: * 552 recipient storage full, try again later (in reply to RCPT TO command) * sample 3: * Diagnostic-Code: X-HERMES; host 127.0.0.1[127.0.0.1] said: 551 bounce as is full> queue as * 100.1.ZmxEL.720k.1140313037.xxxxx@yourdomain.com (in reply to end of * DATA command) */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*full/is", $diag_code)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0145'; } /* rule: full * sample: * Diagnostic-Code: SMTP; 452 Insufficient system storage */ elseif (preg_match("/Insufficient system storage/is", $diag_code)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0134'; } /* rule: full * sample 1: * Diagnostic-Code: X-Postfix; cannot append message to destination file^M * /var/mail/dale.me89g: error writing message: File too large^M * sample 2: * Diagnostic-Code: X-Postfix; cannot access mailbox /var/spool/mail/b8843022 for^M * user xxxxx. error writing message: File too large */ elseif (preg_match("/File too large/is", $diag_code)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0192'; } /* rule: oversize * sample: * Diagnostic-Code: smtp;552 5.2.2 This message is larger than the current system limit or the recipient's mailbox is full. Create a shorter message body or remove attachments and try sending it again. */ elseif (preg_match("/larger than.*limit/is", $diag_code)) { $result['rule_cat'] = 'oversize'; $result['rule_no'] = '0146'; } /* rule: unknown * sample: * Diagnostic-Code: X-Notes; User xxxxx (xxxxx@yourdomain.com) not listed in public Name & Address Book */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user)(.*)not(.*)list/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0103'; } /* rule: unknown * sample: * Diagnostic-Code: smtp; 450 user path no exist */ elseif (preg_match("/user path no exist/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0106'; } /* rule: unknown * sample 1: * Diagnostic-Code: SMTP; 550 Relaying denied. * sample 2: * Diagnostic-Code: SMTP; 554 : Relay access denied * sample 3: * Diagnostic-Code: SMTP; 550 relaying to prohibited by administrator */ elseif (preg_match("/Relay.*(?:denied|prohibited)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0108'; } /*rule: unknown * sample: * Diagnostic-Code: SMTP; 554 qq Sorry, no valid recipients (#5.1.3) */ elseif (preg_match("/no.*valid.*(?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0185'; } /* rule: unknown * sample 1: * Diagnostic-Code: SMTP; 550 «Dªk¦a§} - invalid address (#5.5.0) * sample 2: * Diagnostic-Code: SMTP; 550 Invalid recipient: * sample 3: * Diagnostic-Code: SMTP; 550 : Invalid User */ elseif (preg_match("/Invalid.*(?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0111'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 554 delivery error: dd Sorry your message to xxxxx@yourdomain.com cannot be delivered. This account has been disabled or discontinued [#102]. - mta173.mail.tpe.domain.com */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*(?:disabled|discontinued)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0114'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 554 delivery error: dd This user doesn't have a domain.com account (www.xxxxx@yourdomain.com) [0] - mta134.mail.tpe.domain.com */ elseif (preg_match("/user doesn't have.*account/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0127'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 5.1.1 unknown or illegal alias: xxxxx@yourdomain.com */ elseif (preg_match("/(?:unknown|illegal).*(?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0128'; } /* rule: unknown * sample 1: * Diagnostic-Code: SMTP; 450 mailbox unavailable. * sample 2: * Diagnostic-Code: SMTP; 550 5.7.1 Requested action not taken: mailbox not available */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*(?:un|not\s+)available/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0122'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 553 sorry, no mailbox here by that name (#5.7.1) */ elseif (preg_match("/no (?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0123'; } /* rule: unknown * sample 1: * Diagnostic-Code: SMTP; 550 User (xxxxx@yourdomain.com) unknown. * sample 2: * Diagnostic-Code: SMTP; 553 5.3.0 ... Addressee unknown, relay=[111.111.111.000] */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*unknown/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0125'; } /* rule: unknown * sample 1: * Diagnostic-Code: SMTP; 550 user disabled * sample 2: * Diagnostic-Code: SMTP; 452 4.2.1 mailbox temporarily disabled: xxxxx@yourdomain.com */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*disabled/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0133'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 : Recipient address rejected: No such user (xxxxx@yourdomain.com) */ elseif (preg_match("/No such (?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0143'; } /* rule: unknown * sample 1: * Diagnostic-Code: SMTP; 550 MAILBOX NOT FOUND * sample 2: * Diagnostic-Code: SMTP; 550 Mailbox ( xxxxx@yourdomain.com ) not found or inactivated */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*NOT FOUND/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0136'; } /* rule: unknown * sample: * Diagnostic-Code: X-Postfix; host m2w-in1.domain.com[111.111.111.000] said: 551 * is a deactivated mailbox (in reply to RCPT TO * command) */ elseif (preg_match("/deactivated (?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0138'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 recipient rejected * ... * <<< 550 recipient rejected * 550 5.1.1 xxxxx@yourdomain.com... User unknown */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*reject/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0148'; } /* rule: unknown * sample: * Diagnostic-Code: smtp; 5.x.0 - Message bounced by administrator (delivery attempts: 0) */ elseif (preg_match("/bounce.*administrator/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0151'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 is now disabled with MTA service. */ elseif (preg_match("/<.*>.*disabled/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0152'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 551 not our customer */ elseif (preg_match("/not our customer/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0154'; } /* rule: unknown * sample: * Diagnostic-Code: smtp; 5.1.0 - Unknown address error 540-'Error: Wrong recipients' (delivery attempts: 0) */ elseif (preg_match("/Wrong (?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0159'; } /* rule: unknown * sample: * Diagnostic-Code: smtp; 5.1.0 - Unknown address error 540-'Error: Wrong recipients' (delivery attempts: 0) * sample 2: * Diagnostic-Code: SMTP; 501 #5.1.1 bad address xxxxx@yourdomain.com */ elseif (preg_match("/(?:unknown|bad).*(?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0160'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 Command RCPT User not OK */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*not OK/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0186'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 5.7.1 Access-Denied-XM.SSR-001 */ elseif (preg_match("/Access.*Denied/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0189'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 5.1.1 ... email address lookup in domain map failed^M */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*lookup.*fail/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0195'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 User not a member of domain: ^M */ elseif (preg_match("/(?:recipient|address|email|mailbox|user).*not.*member of domain/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0198'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550-"The recipient cannot be verified. Please check all recipients of this^M */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*cannot be verified/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0202'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 Unable to relay for xxxxx@yourdomain.com */ elseif (preg_match("/Unable to relay/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0203'; } /* rule: unknown * sample 1: * Diagnostic-Code: SMTP; 550 xxxxx@yourdomain.com:user not exist * sample 2: * Diagnostic-Code: SMTP; 550 sorry, that recipient doesn't exist (#5.7.1) */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*(?:n't|not) exist/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0205'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550-I'm sorry but xxxxx@yourdomain.com does not have an account here. I will not */ elseif (preg_match("/not have an account/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0207'; } /* rule: unknown * sample: * Diagnostic-Code: SMTP; 550 This account is not allowed...xxxxx@yourdomain.com */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*is not allowed/is", $diag_code)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0220'; } /* rule: inactive * sample: * Diagnostic-Code: SMTP; 550 : inactive user */ elseif (preg_match("/inactive.*(?:alias|account|recipient|address|email|mailbox|user)/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0135'; } /* rule: inactive * sample: * Diagnostic-Code: SMTP; 550 xxxxx@yourdomain.com Account Inactive */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*Inactive/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0155'; } /* rule: inactive * sample: * Diagnostic-Code: SMTP; 550 : Recipient address rejected: Account closed due to inactivity. No forwarding information is available. */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user) closed due to inactivity/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0170'; } /* rule: inactive * sample: * Diagnostic-Code: SMTP; 550 ... User account not activated */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user) not activated/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0177'; } /* rule: inactive * sample 1: * Diagnostic-Code: SMTP; 550 User suspended * sample 2: * Diagnostic-Code: SMTP; 550 account expired */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*(?:suspend|expire)/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0183'; } /* rule: inactive * sample: * Diagnostic-Code: SMTP; 553 5.3.0 ... Recipient address no longer exists */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*no longer exist/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0184'; } /* rule: inactive * sample: * Diagnostic-Code: SMTP; 553 VS10-RT Possible forgery or deactivated due to abuse (#5.1.1) 111.111.111.211^M */ elseif (preg_match("/(?:forgery|abuse)/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0196'; } /* rule: inactive * sample: * Diagnostic-Code: SMTP; 553 mailbox xxxxx@yourdomain.com is restricted */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*restrict/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0209'; } /* rule: inactive * sample: * Diagnostic-Code: SMTP; 550 : User status is locked. */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*locked/is", $diag_code)) { $result['rule_cat'] = 'inactive'; $result['rule_no'] = '0228'; } /* rule: user_reject * sample: * Diagnostic-Code: SMTP; 553 User refused to receive this mail. */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user) refused/is", $diag_code)) { $result['rule_cat'] = 'user_reject'; $result['rule_no'] = '0156'; } /* rule: user_reject * sample: * Diagnostic-Code: SMTP; 501 xxxxx@yourdomain.com Sender email is not in my domain */ elseif (preg_match("/sender.*not/is", $diag_code)) { $result['rule_cat'] = 'user_reject'; $result['rule_no'] = '0206'; } /* rule: command_reject * sample: * Diagnostic-Code: SMTP; 554 Message refused */ elseif (preg_match("/Message refused/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0175'; } /* rule: command_reject * sample: * Diagnostic-Code: SMTP; 550 5.0.0 ... No permit */ elseif (preg_match("/No permit/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0190'; } /* rule: command_reject * sample: * Diagnostic-Code: SMTP; 553 sorry, that domain isn't in my list of allowed rcpthosts (#5.5.3 - chkuser) */ elseif (preg_match("/domain isn't in.*allowed rcpthost/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0191'; } /* rule: command_reject * sample: * Diagnostic-Code: SMTP; 553 AUTH FAILED - xxxxx@yourdomain.com^M */ elseif (preg_match("/AUTH FAILED/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0197'; } /* rule: command_reject * sample 1: * Diagnostic-Code: SMTP; 550 relay not permitted^M * sample 2: * Diagnostic-Code: SMTP; 530 5.7.1 Relaying not allowed: xxxxx@yourdomain.com */ elseif (preg_match("/relay.*not.*(?:permit|allow)/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0201'; } /* rule: command_reject * sample: * * Diagnostic-Code: SMTP; 550 not local host domain.com, not a gateway */ elseif (preg_match("/not local host/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0204'; } /* rule: command_reject * sample: * Diagnostic-Code: SMTP; 500 Unauthorized relay msg rejected */ elseif (preg_match("/Unauthorized relay/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0215'; } /* rule: command_reject * sample: * Diagnostic-Code: SMTP; 554 Transaction failed */ elseif (preg_match("/Transaction.*fail/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0221'; } /* rule: command_reject * sample: * Diagnostic-Code: smtp;554 5.5.2 Invalid data in message */ elseif (preg_match("/Invalid data/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0223'; } /* rule: command_reject * sample: * Diagnostic-Code: SMTP; 550 Local user only or Authentication mechanism */ elseif (preg_match("/Local user only/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0224'; } /* rule: command_reject * sample: * Diagnostic-Code: SMTP; 550-ds176.domain.com [111.111.111.211] is currently not permitted to * relay through this server. Perhaps you have not logged into the pop/imap * server in the last 30 minutes or do not have SMTP Authentication turned on * in your email client. */ elseif (preg_match("/not.*permit.*to/is", $diag_code)) { $result['rule_cat'] = 'command_reject'; $result['rule_no'] = '0225'; } /* rule: content_reject * sample: * Diagnostic-Code: SMTP; 550 Content reject. FAAAANsG60M9BmDT.1 */ elseif (preg_match("/Content reject/is", $diag_code)) { $result['rule_cat'] = 'content_reject'; $result['rule_no'] = '0165'; } /* rule: content_reject * sample: * Diagnostic-Code: SMTP; 552 MessageWall: MIME/REJECT: Invalid structure */ elseif (preg_match("/MIME\/REJECT/is", $diag_code)) { $result['rule_cat'] = 'content_reject'; $result['rule_no'] = '0212'; } /* rule: content_reject * sample: * Diagnostic-Code: smtp; 554 5.6.0 Message with invalid header rejected, id=13462-01 - MIME error: error: UnexpectedBound: part didn't end with expected boundary [in multipart message]; EOSToken: EOF; EOSType: EOF */ elseif (preg_match("/MIME error/is", $diag_code)) { $result['rule_cat'] = 'content_reject'; $result['rule_no'] = '0217'; } /* rule: content_reject * sample: * Diagnostic-Code: SMTP; 553 Mail data refused by AISP, rule [169648]. */ elseif (preg_match("/Mail data refused.*AISP/is", $diag_code)) { $result['rule_cat'] = 'content_reject'; $result['rule_no'] = '0218'; } /* rule: dns_unknown * sample: * Diagnostic-Code: SMTP; 550 Host unknown */ elseif (preg_match("/Host unknown/is", $diag_code)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0130'; } /* rule: dns_unknown * sample: * Diagnostic-Code: SMTP; 553 Specified domain is not allowed. */ elseif (preg_match("/Specified domain.*not.*allow/is", $diag_code)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0180'; } /* rule: dns_unknown * sample: * Diagnostic-Code: X-Postfix; delivery temporarily suspended: connect to * 111.111.11.112[111.111.11.112]: No route to host */ elseif (preg_match("/No route to host/is", $diag_code)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0188'; } /* rule: dns_unknown * sample: * Diagnostic-Code: SMTP; 550 unrouteable address */ elseif (preg_match("/unrouteable address/is", $diag_code)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0208'; } /* rule: defer * sample: * Diagnostic-Code: SMTP; 451 System(u) busy, try again later. */ elseif (preg_match("/System.*busy/is", $diag_code)) { $result['rule_cat'] = 'defer'; $result['rule_no'] = '0112'; } /* rule: defer * sample: * Diagnostic-Code: SMTP; 451 mta172.mail.tpe.domain.com Resources temporarily unavailable. Please try again later. [#4.16.4:70]. */ elseif (preg_match("/Resources temporarily unavailable/is", $diag_code)) { $result['rule_cat'] = 'defer'; $result['rule_no'] = '0116'; } /* rule: antispam, deny ip * sample: * Diagnostic-Code: SMTP; 554 sender is rejected: 0,mx20,wKjR5bDrnoM2yNtEZVAkBg==.32467S2 */ elseif (preg_match("/sender is rejected/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0101'; } /* rule: antispam, deny ip * sample: * Diagnostic-Code: SMTP; 554 : Client host rejected: Access denied */ elseif (preg_match("/Client host rejected/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0102'; } /* rule: antispam, mismatch ip * sample: * Diagnostic-Code: SMTP; 554 Connection refused(mx). MAIL FROM [xxxxx@yourdomain.com] mismatches client IP [111.111.111.000]. */ elseif (preg_match("/MAIL FROM(.*)mismatches client IP/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0104'; } /* rule: antispam, deny ip * sample: * Diagnostic-Code: SMTP; 554 Please visit http:// antispam.domain.com/denyip.php?IP=111.111.111.000 (#5.7.1) */ elseif (preg_match("/denyip/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0144'; } /* rule: antispam, deny ip * sample: * Diagnostic-Code: SMTP; 554 Service unavailable; Client host [111.111.111.211] blocked using dynablock.domain.com; Your message could not be delivered due to complaints we received regarding the IP address you're using or your ISP. See http:// blackholes.domain.com/ Error: WS-02^M */ elseif (preg_match("/client host.*blocked/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0201'; } /* rule: antispam, reject * sample: * Diagnostic-Code: SMTP; 550 Requested action not taken: mail IsCNAPF76kMDARUY.56621S2 is rejected,mx3,BM */ elseif (preg_match("/mail.*reject/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0147'; } /* rule: antispam * sample: * Diagnostic-Code: SMTP; 552 sorry, the spam message is detected (#5.6.0) */ elseif (preg_match("/spam.*detect/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0162'; } /* rule: antispam * sample: * Diagnostic-Code: SMTP; 554 5.7.1 Rejected as Spam see: http:// rejected.domain.com/help/spam/rejected.html */ elseif (preg_match("/reject.*spam/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0216'; } /* rule: antispam * sample: * Diagnostic-Code: SMTP; 553 5.7.1 ... SpamTrap=reject mode, dsn=5.7.1, Message blocked by BOX Solutions (www.domain.com) SpamTrap Technology, please contact the domain.com site manager for help: (ctlusr8012).^M */ elseif (preg_match("/SpamTrap/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0200'; } /* rule: antispam, mailfrom mismatch * sample: * Diagnostic-Code: SMTP; 550 Verify mailfrom failed,blocked */ elseif (preg_match("/Verify mailfrom failed/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0210'; } /* rule: antispam, mailfrom mismatch * sample: * Diagnostic-Code: SMTP; 550 Error: MAIL FROM is mismatched with message header from address! */ elseif (preg_match("/MAIL.*FROM.*mismatch/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0226'; } /* rule: antispam * sample: * Diagnostic-Code: SMTP; 554 5.7.1 Message scored too high on spam scale. For help, please quote incident ID 22492290. */ elseif (preg_match("/spam scale/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0211'; } /* rule: antispam * sample: * Diagnostic-Code: SMTP; 554 5.7.1 reject: Client host bypassing service provider's mail relay: ds176.domain.com 8? elseif (preg_match ("/Client host bypass/is",$diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0229'; } /* rule: antispam * sample: * Diagnostic-Code: SMTP; 550 sorry, it seems as a junk mail */ elseif (preg_match("/junk mail/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0230'; } /* rule: antispam * sample: * Diagnostic-Code: SMTP; 553-Message filtered. Please see the FAQs section on spam */ elseif (preg_match("/message filtered/is", $diag_code)) { $result['rule_cat'] = 'antispam'; $result['rule_no'] = '0227'; } /* rule: antispam, subject filter * sample: * Diagnostic-Code: SMTP; 554 5.7.1 The message from () with the subject of ( *(ca2639) 7|-{%2E* : {2"(%EJ;y} (SBI$#$@ * ----- Transcript of session follows ----- * ... while talking to mta1.domain.com.: * >>> DATA * <<< 503 All recipients are invalid * 554 5.0.0 Service unavailable */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user)(.*)invalid/i", $dsn_msg)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0107'; } /* rule: unknown * sample: * ----- Transcript of session follows ----- * xxxxx@yourdomain.com... Deferred: No such file or directory */ elseif (preg_match("/Deferred.*No such.*(?:file|directory)/i", $dsn_msg)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0141'; } /* rule: unknown * sample: * Failed to deliver to ''^M * LOCAL module(account xxxx) reports:^M * mail receiving disabled^M */ elseif (preg_match("/mail receiving disabled/i", $dsn_msg)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0194'; } /* rule: unknown * sample: * - These recipients of your message have been processed by the mail server:^M * xxxxx@yourdomain.com; Failed; 5.1.1 (bad destination mailbox address) */ elseif (preg_match("/bad.*(?:alias|account|recipient|address|email|mailbox|user)/i", $dsn_msg)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '227'; } /* rule: full * sample 1: * This Message was undeliverable due to the following reason: * The user(s) account is temporarily over quota. * * sample 2: * Recipient address: xxxxx@yourdomain.com * Reason: Over quota */ elseif (preg_match("/over.*quota/i", $dsn_msg)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0131'; } /* rule: full * sample: * Sorry the recipient quota limit is exceeded. * This message is returned as an error. */ elseif (preg_match("/quota.*exceeded/i", $dsn_msg)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0150'; } /* rule: full * sample: * The user to whom this message was addressed has exceeded the allowed mailbox * quota. Please resend the message at a later time. */ elseif (preg_match("/exceed.*\n?.*quota/i", $dsn_msg)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0187'; } /* rule: full * sample 1: * Failed to deliver to '' * LOCAL module(account xxxxxx) reports: * account is full (quota exceeded) * sample 2: * Error in fabiomod_sql_glob_init: no data source specified - database access disabled * [Fri Feb 17 23:29:38 PST 2006] full error for caltsmy: * that member's mailbox is full * 550 5.0.0 ... Can't create output */ elseif (preg_match("/(?:alias|account|recipient|address|email|mailbox|user).*full/i", $dsn_msg)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0132'; } /* rule: full * sample: * gaosong "(0), ErrMsg=Mailbox space not enough (space limit is 10240KB) */ elseif (preg_match("/space.*not.*enough/i", $dsn_msg)) { $result['rule_cat'] = 'full'; $result['rule_no'] = '0219'; } /* rule: defer * sample 1: * ----- Transcript of session follows ----- * xxxxx@yourdomain.com... Deferred: Connection refused by nomail.tpe.domain.com. * Message could not be delivered for 5 days * Message will be deleted from queue * sample 2: * 451 4.4.1 reply: read error from www.domain.com. * xxxxx@yourdomain.com... Deferred: Connection reset by www.domain.com. */ elseif (preg_match("/Deferred.*Connection (?:refused|reset)/i", $dsn_msg)) { $result['rule_cat'] = 'defer'; $result['rule_no'] = '0115'; } /* rule: dns_unknown * sample: * ----- The following addresses had permanent fatal errors ----- * Tan XXXX SSSS * ----- Transcript of session follows ----- * 553 5.1.2 XXXX SSSS ... Invalid host name */ elseif (preg_match("/Invalid host name/i", $dsn_msg)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0109'; } /* rule: dns_unknown * sample: * ----- Transcript of session follows ----- * xxxxx@yourdomain.com... Deferred: mail.domain.com.: No route to host */ elseif (preg_match("/Deferred.*No route to host/i", $dsn_msg)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0109'; } /* rule: dns_unknown * sample: * ----- Transcript of session follows ----- * 550 5.1.2 xxxxx@yourdomain.com... Host unknown (Name server: .: no data known) */ elseif (preg_match("/Host unknown/i", $dsn_msg)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0140'; } /* rule: dns_unknown * sample: * ----- Transcript of session follows ----- * 451 HOTMAIL.com.tw: Name server timeout * Message could not be delivered for 5 days * Message will be deleted from queue */ elseif (preg_match("/Name server timeout/i", $dsn_msg)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0118'; } /* rule: dns_unknown * sample: * ----- Transcript of session follows ----- * xxxxx@yourdomain.com... Deferred: Connection timed out with hkfight.com. * Message could not be delivered for 5 days * Message will be deleted from queue */ elseif (preg_match("/Deferred.*Connection.*tim(?:e|ed).*out/i", $dsn_msg)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0119'; } /* rule: dns_unknown * sample: * ----- Transcript of session follows ----- * xxxxx@yourdomain.com... Deferred: Name server: domain.com.: host name lookup failure */ elseif (preg_match("/Deferred.*host name lookup failure/i", $dsn_msg)) { $result['rule_cat'] = 'dns_unknown'; $result['rule_no'] = '0121'; } /* rule: dns_loop * sample: * ----- Transcript of session follows -----^M * 554 5.0.0 MX list for znet.ws. points back to mail01.domain.com^M * 554 5.3.5 Local configuration error^M */ elseif (preg_match("/MX list.*point.*back/i", $dsn_msg)) { $result['rule_cat'] = 'dns_loop'; $result['rule_no'] = '0199'; } /* rule: internal_error * sample: * ----- Transcript of session follows ----- * 451 4.0.0 I/O error */ elseif (preg_match("/I\/O error/i", $dsn_msg)) { $result['rule_cat'] = 'internal_error'; $result['rule_no'] = '0120'; } /* rule: internal_error * sample: * Failed to deliver to 'xxxxx@yourdomain.com'^M * SMTP module(domain domain.com) reports:^M * connection with mx1.mail.domain.com is broken^M */ elseif (preg_match("/connection.*broken/i", $dsn_msg)) { $result['rule_cat'] = 'internal_error'; $result['rule_no'] = '0231'; } /* rule: other * sample: * Delivery to the following recipients failed. * xxxxx@yourdomain.com */ elseif (preg_match("/Delivery to the following recipients failed.*\n.*\n.*".$result['email']."/i", $dsn_msg)) { $result['rule_cat'] = 'other'; $result['rule_no'] = '0176'; } // Followings are wind-up rule: must be the last one // many other rules msg end up with "550 5.1.1 ... User unknown" // many other rules msg end up with "554 5.0.0 Service unavailable" /* rule: unknown * sample 1: * ----- The following addresses had permanent fatal errors -----^M * ^M * (reason: User unknown)^M * sample 2: * 550 5.1.1 xxxxx@yourdomain.com... User unknown^M */ elseif (preg_match("/User unknown/i", $dsn_msg)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0193'; } /* rule: unknown * sample: * 554 5.0.0 Service unavailable */ elseif (preg_match("/Service unavailable/i", $dsn_msg)) { $result['rule_cat'] = 'unknown'; $result['rule_no'] = '0214'; } break; case 'delayed': $result['rule_cat'] = 'delayed'; $result['rule_no'] = '0110'; break; case 'delivered': case 'relayed': case 'expanded': // unhandled cases break; default : break; } } if ($result['rule_no'] == '0000') { if ($debug_mode) { $result['debug'] = array( 'email ' => $result['email'].self::$bmh_newline, 'Action ' => $action.self::$bmh_newline, 'Status ' => $status_code.self::$bmh_newline, 'Diagnostic-Code' => $diag_code.self::$bmh_newline, 'DSN Message' => $dsn_msg.self::$bmh_newline ); } } else { if ($result['bounce_type'] === false) { $result['bounce_type'] = self::$rule_categories[$result['rule_cat']]['bounce_type']; $result['remove'] = self::$rule_categories[$result['rule_cat']]['remove']; } } return $result; } }