<commit_before><?php
/**
 * @package     Mautic
 * @copyright   2015 Mautic Contributors. All rights reserved.
 * @author      Mautic
 * @link        http://mautic.org
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 *
 * Bounce parsing modified from:
 * .---------------------------------------------------------------------------.
 * |  Software: PHPMailer-BMH (Bounce Mail Handler)                            |
 * |   Version: 5.0.0rc1                                                       |
 * |   Contact: <EMAIL>                             |
 * |      Info: http://phpmailer.codeworxtech.com                              |
 * | ------------------------------------------------------------------------- |
 * |    Author: <NAME> <EMAIL> (admin)                 |
 * | Copyright (c) 2002-2009, <NAME>. All Rights Reserved.               |
 * | ------------------------------------------------------------------------- |
 * |   License: Distributed under the General Public License (GPL)             |
 * |            (http://www.gnu.org/licenses/gpl.html)                         |
 * | This program is distributed in the hope that it will be useful - WITHOUT  |
 * | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
 * | FITNESS FOR A PARTICULAR PURPOSE.                                         |
 * .---------------------------------------------------------------------------.
 */

namespace Mautic\EmailBundle\Helper;

use Mautic\CoreBundle\Factory\MauticFactory;
use Mautic\CoreBundle\Helper\DateTimeHelper;
use Mautic\EmailBundle\MonitoredEmail\Message;

/**
 * Class MessageHelper
 */
class MessageHelper
{
    /**
     * @var MauticFactory
     */
    protected $factory;

    /**
     * @var \Doctrine\DBAL\Connection
     */
    protected $db;

    /**
     * @var \Monolog\Logger
     */
    protected $logger;

    /**
     * @param MauticFactory $factory
     */
    public function __construct(MauticFactory $factory)
    {
        $this->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}");

        // 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 (!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') {
                $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);
            }

            if (!$isBounce && !empty($messageDetails['email'])) {
                // Bounce was found in message content
                $isBounce      = true;
                $isUnsubscribe = false;
            }
        }

        $this->logger->debug(print_r($messageDetails, true));

        if (!$isBounce && !$isUnsubscribe) {
            $this->logger->debug('No reason found to process.');
            return false;
        }

        // Search for the lead
        $stat = $leadId = $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['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 <EMAIL>
            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')<commit_msg>fixes an sql error when trying to save bounced emails to db<commit_after><?php
/**
 * @package     Mautic
 * @copyright   2015 Mautic Contributors. All rights reserved.
 * @author      Mautic
 * @link        http://mautic.org
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 *
 * Bounce parsing modified from:
 * .---------------------------------------------------------------------------.
 * |  Software: PHPMailer-BMH (Bounce Mail Handler)                            |
 * |   Version: 5.0.0rc1                                                       |
 * |   Contact: <EMAIL>                             |
 * |      Info: http://phpmailer.codeworxtech.com                              |
 * | ------------------------------------------------------------------------- |
 * |    Author: <NAME> <EMAIL> (admin)                 |
 * | Copyright (c) 2002-2009, <NAME>. All Rights Reserved.               |
 * | ------------------------------------------------------------------------- |
 * |   License: Distributed under the General Public License (GPL)             |
 * |            (http://www.gnu.org/licenses/gpl.html)                         |
 * | This program is distributed in the hope that it will be useful - WITHOUT  |
 * | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
 * | FITNESS FOR A PARTICULAR PURPOSE.                                         |
 * .---------------------------------------------------------------------------.
 */

namespace Mautic\EmailBundle\Helper;

use Mautic\CoreBundle\Factory\MauticFactory;
use Mautic\CoreBundle\Helper\DateTimeHelper;
use Mautic\EmailBundle\MonitoredEmail\Message;

/**
 * Class MessageHelper
 */
class MessageHelper
{
    /**
     * @var MauticFactory
     */
    protected $factory;

    /**
     * @var \Doctrine\DBAL\Connection
     */
    protected $db;

    /**
     * @var \Monolog\Logger
     */
    protected $logger;

    /**
     * @param MauticFactory $factory
     */
    public function __construct(MauticFactory $factory)
    {
        $this->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}");

        // 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 (!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') {
                $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);
            }

            if (!$isBounce && !empty($messageDetails['email'])) {
                // Bounce was found in message content
                $isBounce      = true;
                $isUnsubscribe = false;
            }
        }

        $this->logger->debug(print_r($messageDetails, true));

        if (!$isBounce && !$isUnsubscribe) {
            $this->logger->debug('No reason found to process.');
            return false;
        }

        // Search for the lead
        $stat = $leadId = $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 <EMAIL>
            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')
{'fraction_non_alphanumeric': 0.17625314209670265, 'fraction_numerical': 0.0032529942333284047, 'mean_word_length': 1.3582635983263598, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5898027', 'n_tokens_mistral': 3654, 'n_tokens_neox': 3472, 'n_words': 814}