HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/ai.naniguide.com/app/Model/Reply.php
<?php

/**
 * FeedbackLoopHandler class.
 *
 * Model class for feedback loop handler
 *
 * LICENSE: This product includes software developed at
 * the Acelle Co., Ltd. (http://acellemail.com/).
 *
 * @category   MVC Model
 *
 * @author     N. Pham <[email protected]>
 * @author     L. Pham <[email protected]>
 * @copyright  Acelle Co., Ltd
 * @license    Acelle Co., Ltd
 *
 * @version    1.0
 *
 * @link       http://acellemail.com
 */

namespace App\Model;

use Illuminate\Database\Eloquent\Model;
use App\Library\StringHelper;
use Illuminate\Support\Facades\Log;

class Reply extends Model
{
    /**
     * Associations.
     *
     * @var object | collect
     */
    public function trackingLog()
    {
        return $this->belongsTo('App\Model\TrackingLog', 'message_id', 'message_id');
    }

    public static function run()
    {
        try {
            Log::warning('Checking for reply...');

            // Connect to IMAP server
            $imapPath = "{{$this->host}:{$this->port}/{$this->protocol}/{$this->encryption}}INBOX";

            // try to connect
            $inbox = @imap_open($imapPath, $this->username, $this->password);

            // try again with ssl/novalidate-cert
            if ($inbox == false) {
                if (strcasecmp($this->encryption, 'ssl') == 0) {
                    Log::warning('Try using ssl/novalidate-cert instead');
                    $imapPath = "{{$this->host}:{$this->port}/{$this->protocol}/ssl/novalidate-cert}INBOX";
                    $inbox = @imap_open($imapPath, $this->username, $this->password);
                }
            }

            // if failed
            if ($inbox == false) {
                throw new \Exception("Cannot connect to the server: $imapPath");
            }

            // search and get unseen emails, function will return email ids
            $emails = imap_search($inbox, 'UNSEEN');

            if (!empty($emails)) {
                foreach ($emails as $message) {
                    self::processMessage($inbox, $message);
                }
            }

            // colse the connection
            imap_expunge($inbox);
            imap_close($inbox);
        } catch (\Exception $e) {
            // suppress the IMAP error
            // see http://stackoverflow.com/questions/5422405/cant-silence-imap-open-error-notices-in-php
            imap_errors();
            imap_alerts();
            Log::error('Cannot connect to reply handler '.$e->getMessage());
        }
    }

    /**
     * Process feedback message, extract the feedback information (comply to RFC).
     *
     * @return mixed
     */
    public function processMessage($mbox, $msgNo)
    {
        try {
            $header = imap_fetchheader($mbox, $msgNo);
            $body = imap_body($mbox, $msgNo, FT_PEEK);
            $msgId = $this->getMessageId($body);

            if (empty($msgId)) {
                imap_setflag_full($mbox, $msgNo, '\\Seen \\Flagged');
                throw new \Exception('Cannot find Message-ID, skipped');
            }

            Log::info('Processing reply detection for message '.$msgId);

            $trackingLog = TrackingLog::where('message_id', $msgId)->first();

            if (empty($trackingLog)) {
                imap_setflag_full($mbox, $msgNo, '\\Seen \\Flagged');
                throw new \Exception('message-id not found in tracking_logs, skipped');
            }

            // record a bounce log, one message may have more than one
            $reply = new self();
            $reply->message_id = $msgId;
            $reply->content = $header.PHP_EOL.$body;
            $reply->save();

            // just delete
            // imap_delete($mbox, $msgNo);
            // flag as seen
            imap_setflag_full($mbox, $msgNo, '\\Seen \\Flagged');

            Log::info('Feedback recorded for message '.$msgId);
        } catch (\Exception $ex) {
            Log::warning($ex->getMessage());
        }
    }

    /**
     * Extract message ID from email .
     *
     * @return string
     */
    public function getMessageId($message)
    {
        preg_match('/(?<=X-Acelle-Message-Id:)\s{0,1}<{0,1}(?<id>[a-zA-Z0-9\.]+[a-zA-Z0-9]+@[a-zA-Z0-9\.\-]+[a-zA-Z0-9]+)/', $message, $matched);
        if (array_key_exists('id', $matched)) {
            return StringHelper::cleanupMessageId($matched['id']);
        }

        // more tolerant matching (case-insensitive, no need for Acelle prefix, etc.)
        preg_match('/(?<=Message-Id:)\s{0,1}<{0,1}(?<id>[a-zA-Z0-9\.]+[a-zA-Z0-9]+@[a-zA-Z0-9\.\-]+[a-zA-Z0-9]+)/i', $message, $matched);
        if (array_key_exists('id', $matched)) {
            return StringHelper::cleanupMessageId($matched['id']);
        }

        return;
    }
}