<?php
/**
 * Verify Email Adddress
 */
error_reporting(E_ALL);
ini_set("display_errors", 1);
#[\AllowDynamicProperties]
class VerifyEmail
{
  public $port = 25;
  protected $stream = false;
  protected $db;
  public $from = "localhost"; // change this $from email to yours
  protected $stream_timeout_wait;
  protected $scan_timeout_lock;
  protected $timeout_increment = 5; // Add this line to declare the timeout_increment property
  const CRLF = "\r\n";
  protected $email;
  protected $domain;
  protected $email_acc;
  protected $pub_verify;
  protected $pub_valid;
  protected $public_catch_skip;
  protected $code;
  //  protected $get_response;
  protected $skip_mx;
  protected $filter_skipMX; // Add this line to declare the filter_skipMX property
  protected $filter_skipEmail; // Add this line to declare the filter_skipEmail property
  protected $public_domains;
  protected $not_supported;
  protected $priv_domain;
  public $debug;
  public $status;
  protected $mx_records;
  protected $mx_response;
  //	protected $pub_domain;
  protected $code_error = false;
  protected $matched_outlook;
  protected $matched_yahoo;
  protected $matched_non_w;
  protected $print;
  protected $user_id;
  protected $catchAllCehked = false;
  protected $last_code;
  protected $reason; // Add this line to declare the reason property
  private $smtp_cmd = 'EHLO';
  public $pub_d_outlook = [];
  const EmailRegularExpr = "^([-!#\$%&'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#\$%&'*+/0-9=?A-Z^_`a-z{|}~]+\\.)+[a-zA-Z]{2,24}\$";
  protected $EMAIL_VALIDATION_STATUS_SYNTAX_ERROR = "Syntax error";
  protected $EMAIL_VALIDATION_STATUS_OK = 0;
  protected $EMAIL_VALIDATION_STATUS_DOMAIN_FOUND = "exists";
  protected $EMAIL_VALIDATION_STATUS_DOMAIN_NOT_FOUND = "null";
  protected $EMAIL_VALIDATION_STATUS_DOMAIN_MX_NOT_FOUND = "Domain MX record not found";
  protected $EMAIL_VALIDATION_STATUS_DOMAIN_MX_SKIPPED = "Verification Skipped";
  protected $EMAIL_VALIDATION_STATUS_GREYLIST = "Greylisted";
  protected $EMAIL_VALIDATION_STATUS_GET_HOST_FAILED = "Failed to verify host";
  protected $EMAIL_VALIDATION_STATUS_DISPOSABLE_EMAIL = "Email domain is disposable";
  protected $EMAIL_VALIDATION_STATUS_SMTP_CONNECTION_FAILED = "Smtp connection Failed";
  protected $EMAIL_VALIDATION_STATUS_CATCH_ALL_SERVER = "Catch-All mail server";
  protected $EMAIL_VALIDATION_STATUS_INVALID_USER = "Invalid User Id Provided";
  protected $validation_status_code = "0";

  // Helper for organized logging (single timestamp, always scan_debug)
  private function logStep($label, $data = null)
  {
    global $log;
    $msg = $label;
    if ($data !== null) {
      if (is_array($data) || is_object($data)) {
        $msg .= ': ' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
      } else {
        $msg .= ': ' . $data;
      }
    }
    if (isset($log) && method_exists($log, 'scan_debug')) {
      $log->scan_debug($msg);
    }
  }

  /**
   * Set Emails, Domains, Check DNS record and finaly verify email etc
   */
  public function __construct($email, $db = null, $user_id = 0, $print = false)
  {
    $this->print = $print;
    $this->db = $db;
    if ($port = self::get_site_option("scan_port")) {
      $this->port = $port;
    }
    if ($frmEmail = self::get_site_option("scan_mail")) {
      $this->from = $frmEmail;
    }
    if ($stream_timeout_wait = self::get_site_option("scan_time_out")) {
      $this->stream_timeout_wait = $stream_timeout_wait; // value in seconds
    }
    if ($scan_timeout_lock = self::get_site_option("scan_timeout_lock")) {
      $this->scan_timeout_lock = $scan_timeout_lock; // true or false
    }
    if ($smtp_cmd = self::get_site_option("smtp_cmd")) {
      $this->smtp_cmd = $smtp_cmd;
    }
    $this->filter_skipMX = self::get_site_option("filter_skip_mx") === 'true'; // Fetch skip_max_filter option
    $this->filter_skipEmail = self::get_site_option("filter_skip_email") === 'true'; // Fetch skip_email_filter option

    $this->status = [0, "status" => "Unknown", "v_response" => "Mail server error", "mx_server" => "", "mx_response" => "", "safe_to_send" => "No", "email_score" => 0, "bounce_type" => "", "type" => "",];
    $this->logStep('--- [EMAIL VALIDATION START]', [
      'email' => $email,
      'user_id' => $user_id
    ]);
    if ($user_id == 0) {
      $this->markAsInvalid();
      $this->setResponse($this->EMAIL_VALIDATION_STATUS_INVALID_USER);
      $this->debug[] = "Invalid User Found...";
      return;
    }
    // first check if email is correct in format...
    if (filter_var($email, FILTER_VALIDATE_EMAIL) && preg_match("/" . str_replace("/", "\\/", self::EmailRegularExpr) . "/", $email) && !strpos($email, '\'')) {
      // debugger;
      $this->email = $email;
      // get domain or hostname from email address
      $this->domain = $this->getDomainFromEmail($this->email);
      $this->email_acc = $this->getEmailAccFromEmail($this->email);
      $email_acc = $this->email_acc;
      // Profiling: Disposable domain DB query
      global $log;
      $start_disposable = microtime(true);
      //   $log->scan_debug("[PROFILE] Querying disposable domain for: $this->email_acc / {$this->domain}");
      // check if email is disposible...
      $disposable = $this
        ->db
        ->select("SELECT * FROM evp_domain_filter_list WHERE e_type = 'Disposable Account' AND (name = '$this->email_acc' OR name = '{$this->domain}') ");
      $end_disposable = microtime(true);
      //   $log->scan_debug("[PROFILE] Disposable domain query took: " . round($end_disposable - $start_disposable, 4) . " seconds");
      // check if domain is in disposable db
      if (mysqli_num_rows($disposable) > 0) {
        $this->debug[] = "Email Domain is Disposable...";
        $this->markAsInvalid();
        $this->setResponse($this->EMAIL_VALIDATION_STATUS_DISPOSABLE_EMAIL);
        $this->status["type"] = "Disposable Account";
      }
      // Profiling: Unsupported domain DB query
      $start_unsupported = microtime(true);
      // $log->scan_debug("[PROFILE] Querying unsupported domain for: $this->email_acc / {$this->domain}");
      $unsupported = $this
        ->db
        ->select("SELECT * FROM evp_domain_filter_list WHERE e_type = 'Unsupported Domain' AND (name = '$this->email_acc' OR name = '{$this->domain}') ");
      $end_unsupported = microtime(true);
      //   $log->scan_debug("[PROFILE] Unsupported domain query took: " . round($end_unsupported - $start_unsupported, 4) . " seconds");
      // check if domain is in unsupported db
      if (mysqli_num_rows($unsupported) > 0) {
        $this->debug[] = "Domain is not supported";
        $this->markAsInvalid();
        $this->setResponse($this->EMAIL_VALIDATION_STATUS_DOMAIN_MX_SKIPPED);
        $this->status["safe_to_send"] = "Unknown";
        $this->status["status"] = "Skipped";
        $this->status["type"] = "Unsupported Domain";
      } else {
        // if domain has DNS A record
        if ($this->checkDNS()) {
          $this->status["domain_dns"] = $this->EMAIL_VALIDATION_STATUS_DOMAIN_FOUND;
          $this->logStep('[INFO] Valid Domain DNS Record Found', $this->domain);
          $this->debug[] = "Valid Domain DNS Record Found";
        } else {
          // $this->markAsInvalid();
          $this->status["domain_dns"] = $this->EMAIL_VALIDATION_STATUS_DOMAIN_NOT_FOUND;
          // $this->setResponse($this->EMAIL_VALIDATION_STATUS_DOMAIN_NOT_FOUND);
          $this->debug[] = "Invalid Domain DNS Record";
        }
        // if domain has DNS MX record
        if ($this->getMXrecords()) {
          $this->logStep('[INFO] Valid MX Records Found', $this->domain);
          $this->logStep('MX record selected', $this->mx_records);
          $this->debug[] = "Valid MX Records Found";
          // var_dump($this->skip_mx);
          if ($this->skip_mx != null) {
            foreach ($this->mx_records as $host) {
              $maxr = $host;
            }
            $this->markAsInvalid();
            $this->setResponse($this->EMAIL_VALIDATION_STATUS_DOMAIN_MX_SKIPPED);
            $this->status["safe_to_send"] = "Unknown";
            $this->status["status"] = "Skipped";
            $this->status["mx_server"] = $maxr;
            $this->status["mx_response"] = $this->reason; // Use the class property
            $this->logStep('skip_account_mx', [
              'mx_server_checked' => $maxr,
              'exists' => $this->skip_mx ? 'Yes' : 'No'
            ]);
            $this->debug[] = "Scanner Skipped This MX Server Check";
          } else {
            if ($this->public_domains == false) {
              if ($this->connect()) {
                $this->verify();
              }
            } else {
              $this->public_check();
              $this->verify();
            }
          }
        } else {
          $this->markAsInvalid();
          $this->setResponse($this->EMAIL_VALIDATION_STATUS_DOMAIN_MX_NOT_FOUND);
          $this->debug[] = "No MX Records Found...";
        }
      }
    } else {
      $this->markAsInvalid();
      $this->setResponse($this->EMAIL_VALIDATION_STATUS_SYNTAX_ERROR);
      $this->debug[] = "Incorrect Email Adddress Found...";
    }
    $this->logStep('[EMAIL VALIDATION RESULT]', $this->status);
    $this->logStep('--- [EMAIL VALIDATION END] ---', $email);
    if ($this->print) {
      echo "<pre>";
      print_r(array_map("htmlentities", $this->debug));
      echo "</pre>";
    }
  }
  /**
   * Get Domain part from full email address
   * @return string return only domain
   */
  private function getDomainFromEmail($email)
  {
    $email_address = explode("@", $email);
    $domain = array_pop($email_address);
    return $domain;
  }
  /**
   * Get Email username part from full email address
   * @return string return only domain
   */
  private function getEmailAccFromEmail($email)
  {
    $email_address = explode("@", $email);
    $domain = array_shift($email_address);
    return $domain;
  }
  // set current email as Valid
  private function markAsValid()
  {
    $this->status[0] = true;
  }
  // set current email as Invalid
  private function markAsInvalid()
  {
    $this->status[0] = false;
    $this->status["status"] = "Invalid";
    $this->status["bounce_type"] = "hard";
  }
  // set current email validation response
  private function setResponse($response)
  {
    $this->status["v_response"] = $response;
  }
  /**
   * Check Domain A DNS Records
   * @return boolean return A records if found
   */
  private function checkDNS()
  {
    return checkdnsrr($this->domain, "A");
  }
  /**
   * Get Domain MX Records
   * @return bool return True on success, False on failure
   */
  private function getMXrecords()
  {
    $this->logStep('Retrieving MX records for domain', $this->domain);
    $mx_records = [];
    $mx_weights = [];
    // Get MX records
    if (getmxrr($this->domain, $mx_records, $mx_weights)) {
      // Group MX hosts by weight
      $mx_grouped = [];
      foreach ($mx_records as $i => $host) {
        $weight = $mx_weights[$i];
        $mx_grouped[$weight][] = strtolower($host);
      }
      ksort($mx_grouped, SORT_NUMERIC); // Sort by weight ascending
      // For each weight, sort hosts alphabetically
      $final_mx = [];
      foreach ($mx_grouped as $weight => $hosts) {
        sort($hosts, SORT_STRING);
        foreach ($hosts as $host) {
          $final_mx[] = $host;
        }
      }
      // Only keep the first MX record (lowest weight, alphabetically first)
      $this->mx_records = count($final_mx) > 0 ? [$final_mx[0]] : [];
      $this->logStep('MX record selected', $this->mx_records);
      if ($this->filter_skipMX) { // Check if skip_max_filter is enabled
        $this->skip_account_mx();
      }
      return true;
    }
    $this->logStep('No MX records found for domain', $this->domain);
    return false;
  }
  /*
   * Public Mail Adress Check
   */


  private function skip_account_mx()
  {
    $filter_mx_records = $this->mx_records;
    $filter_mx_records = array_map("strtolower", $filter_mx_records);
    global $db;
    $mx_server_list = array();
    $reason = '';
    $get_mx_server_list = $db->select("SELECT mx_server, reason FROM evp_mx_skiplist");
    $get_mx_server_count = mysqli_num_rows($get_mx_server_list);
    if ($get_mx_server_count > 0) {
      while ($mx_server = mysqli_fetch_array($get_mx_server_list)) {
        // $mx_server_list[] = $mx_server;
        array_push($mx_server_list, $mx_server[0]);
        $reason = $mx_server['reason'];
      }
    }
    $this->reason = $reason; // Set the class property
    $filter_mx_records_str = implode(',', $filter_mx_records);
    // Harden regex: match only exact .google.com, not subdomains like aspmx.l.google.com
    $this->public_catch_skip = preg_grep("/(.rediffmail.com|.internet.ru|.bk.ru|.inbox.ru|.list.ru|.mail.ru|.my.com|.sfr.fr|.free.fr|.neuf.fr|.mail.ua|.outlook.com|.ymail.com|.yahoo.com|.msn.com|.aol.com|.live.com|.hotmail.com|.gmail.com|.gmail.com.br|.comcast.net|.icloud.com|.me.com|.mac.com|.yandex.com|.yandex.ru)/i", $filter_mx_records);
    $this->debug[] = 'public_catch_skip: ' . json_encode($this->public_catch_skip);
    $this->microsoft_list = ["hotmail.com", "live.com", "outlook.com", "msn.com",];
    $this->public_domains = preg_grep("/(outlook.com|yahoo.com|yahoodns.net|msn.com|live.com|hotmail.com|mail.ru)$/i", $filter_mx_records);
    $this->not_supported = preg_grep("/(mimecast|yahoo.co)$/i", $filter_mx_records);
  }

  private function skip_account_email()
  {
    global $db;
    $email_to_check = strtolower($this->email);
    $reason = '';
    $get_email_filter_list = $db->select("SELECT email_address, reason FROM evp_email_filter_list WHERE email_address = '$email_to_check' AND uid = '{$this->user_id}'");
    if ($get_email_filter_list && mysqli_num_rows($get_email_filter_list) > 0) {
      $email_filter_row = $get_email_filter_list->fetch_assoc();
      $reason = $email_filter_row['reason'];
      $this->reason = $reason; // Set the class property
      $this->markAsInvalid();
      $this->setResponse($this->EMAIL_VALIDATION_STATUS_DOMAIN_MX_SKIPPED);
      $this->status["safe_to_send"] = "Unknown";
      $this->status["status"] = "Skipped";
      $this->status["type"] = "Filtered Email";
      $this->debug[] = "Email skipped due to filter: $reason";
      return true;
    }
    return false;
  }

  private function public_check()
  {
    $filter_mx_records = $this->mx_records;

    $aol_domains = ['aol.com'];
    $aol_check = (preg_grep("/(?=.*mx-aol.mail)(?=.*yahoodns.net).*/i", $filter_mx_records) && in_array(strtolower($this->domain), $aol_domains));

    //$yahoo_check = preg_grep("/(?=.*mta)(?=.*yahoodns.net).*/i", $filter_mx_records);

    $pub_valid = false;
    $non_w = ["yahoo.co.jp", "mimecast.com"];
    $matched_non_w = str_replace($non_w, "", $this->not_supported) != $this->not_supported;
    $this->matched_non_w = $matched_non_w;
    $pub_d_mailRu = ["mail.ru"];
    $matched_mailRu = str_replace($pub_d_mailRu, "", $this->public_domains) != $this->public_domains;
    $pub_d_yahoo = ["yahoodns.net", "yahoo.co", "yahoo.net"];
    $matched_yahoo = str_replace($pub_d_yahoo, "", $this->public_domains) != $this->public_domains;
    $pub_d_outlook = ["msn.com", "outlook.com", "live.com", "hotmail.com"];
    $this->pub_d_outlook = $pub_d_outlook;
    $matched_outlook = str_replace($pub_d_outlook, "", $this->public_domains) != $this->public_domains;

    if ($aol_check == true) {
      //	var_dump('checking aol');
      include_once __DIR__ . "/providers/aol.php";
      $pub_verify = new pub_aol_verify($this->email, $this->from);
    } elseif ($matched_yahoo == true) {
      //  var_dump('public check yahoo');
      include_once __DIR__ . "/providers/yahoo.php";
      $pub_verify = new pub_yahoo_verify($this->email, $this->from);
    } elseif ($matched_mailRu == true) {
      include_once __DIR__ . "/providers/mail.ru.php";
      $pub_verify = new mailRu_verify($this->email, $this->from);
    } elseif ($matched_outlook == true) {
      //var_dump(preg_grep("/".strtolower($this->domain)."/i", $filter_mx_records));
      if (preg_grep("/" . strtolower($this->domain) . "/i", $filter_mx_records) && !in_array(strtolower($this->domain), $pub_d_outlook)) {
        //  var_dump('private check outlook365');
        include_once __DIR__ . "/providers/outlook365.php";
        $pub_verify = new outlook365_verify($this->email, $this->from);
      } else {
        //  var_dump('public check outlook');
        include_once __DIR__ . "/providers/pub_outlook.php";
        $pub_verify = new pub_outlook_verify($this->email, $this->from);
      }
    }
    if ($pub_verify->verify()) {
      //		echo '<pre>';print_r($pub_verify->get_debug());
      $pub_valid = true;
      $this->pub_valid = $pub_valid;
    } else {
      //		echo '<pre>';print_r($pub_verify->get_debug());
      $pub_valid = false;
      $this->pub_valid = $pub_valid;
    }
  }
  /**
   * Connect To Socket Server
   * @return boolean True if connection success
   */
  private function connect()
  {
    global $log;
    foreach ($this->mx_records as $host) {
      // $log->scan_debug("Attempting to connect to MX host: $host"); // Log statement
      $this->stream = @fsockopen($host, $this->port, $errno, $errstr, $this->stream_timeout_wait);
      $smtp_start = microtime(true);
      $smtp_end = microtime(true);
      $this->logStep('[TIMING] SMTP connect', [
        'host' => $host,
        'seconds' => round($smtp_end - $smtp_start, 3)
      ]);
      if ($this->stream !== false) {
        // $log->scan_debug("Connected to MX host: $host"); // Log statement
        stream_set_timeout($this->stream, $this->stream_timeout_wait);
        stream_set_blocking($this->stream, 1);
        $get_response = @fread($this->stream, 4096);
        // $log->scan_debug("Initial response from server: $get_response"); // Log statement
        preg_match('/^\b([0-9]{3})/ims', $get_response, $matches);
        $code = isset($matches[1]) ? $matches[1] : '';
        $code = rtrim($code);
        if ($code == '220') {
          break;
        } elseif ($code == '554') {
          $this->debug[] = $get_response;
          $this->code_error = true;
          @fclose($this->stream);
          $this->stream = false;
          break;
        } else {
          // $log->scan_debug("Received unexpected code from server: $code"); // Log statement
          $this->code_error = true;
          @fclose($this->stream);
          $this->stream = false;
        }
      } else {
        // $log->scan_debug("Failed to connect to MX host: $host with error: $errstr ($errno)"); // Log statement
        if ($errno == 0) {
          foreach ($this->mx_records as $host) {
            $maxr = $host;
          }
          $this->debug[] = "Problem initializing the socket";
          $this->markAsInvalid();
          $this->setResponse($this->EMAIL_VALIDATION_STATUS_SMTP_CONNECTION_FAILED);
          $this->status['mx_server'] = $maxr;
          $this->status["mx_response"] = $this->mx_response;
        }
      }
    }

    if ($this->stream === false) {
      foreach ($this->mx_records as $host) {
        $maxr = $host;
      }
      if ($this->code_error === true) {
        global $db;
        $mx_skip_q = "SELECT * FROM evp_mx_skiplist WHERE mx_server = '$maxr'";
        $mx_skip_r = $db->select($mx_skip_q);
        if ($mx_skip_r) {
          $mx_skip_count = mysqli_num_rows($mx_skip_r);
          if ($mx_skip_count == null) {
            $mx_skip_q = $db->select("INSERT INTO evp_mx_skiplist (mx_server, reason) VALUES ('$maxr', 'Smtp connection Failed')");
          }
        }
      }
      $this->debug[] = "All connection fails";
      $this->markAsInvalid();
      $this->setResponse($this->EMAIL_VALIDATION_STATUS_SMTP_CONNECTION_FAILED);
      $this->status['mx_server'] = $maxr;
      $this->status["mx_response"] = $this->mx_response;
      return false;
    }
    return true;
  }
  /**
   * disconnect from Socket Server
   * @return null
   */
  private function disconnect()
  {
    @fclose($this->stream);
  }
  /**
   * Validate email
   * @param string $email Email address
   * @return boolean True if the valid email exist
   */
  private function validate($email)
  {
    global $db, $log;
    $valid = true;
    if ($this->catchAllCehked == false) {
      // $log->scan_debug("Sending EHLO command"); // Log statement
      $start_time = microtime(true);
      $get_response = $this->_streamQuery($this->smtp_cmd . " " . $this->getDomainFromEmail($this->from));
      $end_time = microtime(true);
      $time_taken = round($end_time - $start_time, 2) . ' secs';
      $this->logStep('[SMTP] Sent', [
        'command' => $this->smtp_cmd . ' ' . $this->getDomainFromEmail($this->from),
        'response' => $get_response,
        'time' => $time_taken
      ]);
      preg_match('/^([0-9]{3})/ims', $get_response, $matches);
      $code = isset($matches[1]) ? $matches[1] : '';
      if ($code == '250') {
        // $log->scan_debug("Sending MAIL FROM command"); // Log statement
        $start_time = microtime(true);
        $get_response = $this->_streamQuery("MAIL FROM: <{$this->from}>");
        $end_time = microtime(true);
        $time_taken = round($end_time - $start_time, 2) . ' secs';
        $this->logStep('[SMTP] Sent', [
          'command' => 'MAIL FROM: <' . $this->from . '>',
          'response' => $get_response,
          'time' => $time_taken
        ]);
        preg_match('/^([0-9]{3})/ims', $get_response, $matches);
        $code = isset($matches[1]) ? $matches[1] : '';
        if ($code == '250') {
          $valid = true;
        } else {
          $valid = false;
        }
      } else {
        $valid = false;
      }
    }
    if ($valid) {
      global $get_response;
      $start_time = microtime(true);
      $get_response = $this->_streamQuery("RCPT TO: <{$email}>");
      $end_time = microtime(true);
      $time_taken = round($end_time - $start_time, 2) . ' secs';
      $this->logStep('[SMTP] Sent', [
        'command' => 'RCPT TO: <' . $email . '>',
        'response' => $get_response,
        'time' => $time_taken
      ]);
      $this->mx_response = $get_response; // Ensure mx_response is always set
      preg_match('/^([0-9]{3})/ims', $get_response, $matches);
      $code = isset($matches[1]) ? $matches[1] : '';
      if ($code == '250') {
        $valid = true;
      } elseif ($code == '451' || $code == '452') {
        $valid = false;
      } elseif ($code == '450') {
        $this->code = $code;
        $valid = false;
      } elseif ($code == '550') {
        $this->code = $code;
        $valid = false;
      } else {
        $valid = false;
      }
      if ($this->catchAllCehked !== false) {
        $this->_streamQuery("RSET");
        $this->_streamQuery("QUIT");
      }
    }
    return $valid;
  }

  /**
   * writes the contents of string to the file stream pointed to by handle
   * If an error occurs, returns FALSE.
   * @access protected
   * @param string $string The string that is to be written
   * @return string Returns a result code, as an integer.
   */
  protected function _streamQuery($query)
  {
    global $db, $log;
    $start_time = microtime(true);
    @fwrite($this->stream, $query . self::CRLF);
    if ($this->scan_timeout_lock == 'false') {
      stream_set_timeout($this->stream, $this->stream_timeout_wait * $this->timeout_increment); // Use the timeout_increment variable
    } else {
      stream_set_timeout($this->stream, $this->stream_timeout_wait); // Default timeout
    }
    stream_set_blocking($this->stream, 1);
    $get_response = '';
    while ($str = @fgets($this->stream, 4096)) {
      $get_response .= $str;
      if (preg_match('/^\d{3} /', $str)) {
        break;
      }
    }
    $end_time = microtime(true);
    $time_taken = round($end_time - $start_time, 2) . ' secs';

    if (($time_taken > $this->stream_timeout_wait) && ($this->scan_timeout_lock == 'true')) {
      // $log->scan_debug("Query: $query (Time taken: $time_taken) exceeded timeout, returning greylist response."); // Log statement
      $get_response = '450 - Greylisted';
    }

    // $log->scan_debug("Query: $query (Time taken: $time_taken)"); // Log statement
    // $log->scan_debug("Response: $get_response"); // Log statement
    return $get_response;
  }

  /**
   * Validate email
   * @param string $email Email address
   * @return boolean True if the valid email exist
   */
  private function verify()
  {
    foreach ($this->mx_records as $host) {
      $maxr = $host;
    }

    //	$maxr[0][0] = $this->mx_records;
    //	$maxr= $maxr[0][0];
    //	var_dump($this->matched_non_w);
    if ($this->pub_valid === true) {
      $this->markAsValid();
      $this->setResponse("Deliverable");
      $this->status["status"] = "Valid";
      $this->status["type"] = "Free Account";
      $this->status["mx_server"] = $maxr;
      $this->status["mx_response"] = $this->mx_response;
      $this->status["safe_to_send"] = "Yes";
      $this->status["email_score"] = "1";
      if (preg_grep("/" . strtolower($this->domain) . "/i", $this->mx_records) && !in_array(strtolower($this->domain), $this->pub_d_outlook)) {
        $this->status["type"] = "";
      }
    } elseif ($this->matched_non_w === true) {
      $this->markAsInvalid();
      $this->setResponse("Domain Not Supported");
      $this->status["status"] = "Skipped";
      $this->status["mx_server"] = $maxr;
      $this->status["mx_response"] = $this->mx_response;
    } elseif ($this->pub_valid === false) {
      $this->markAsInvalid();
      $this->setResponse("Undeliverable");
      $this->status["mx_server"] = $maxr;
      $this->status["mx_response"] = $this->mx_response;
    } else {
      if ($this->validate($this->email)) {
        $this->markAsValid();
        $this->setResponse("Deliverable");
        $this->status["status"] = "Valid";
        $this->status["safe_to_send"] = "Yes";
        $this->status["email_score"] = "1";
        $this->status["mx_server"] = $maxr;
        $this->status["mx_response"] = $this->mx_response;

        if ($this->is_domain_catch_all_enabled($this->getDomainFromEmail($this->email))) {
          $this->markAsInvalid();
          $this->setResponse($this->EMAIL_VALIDATION_STATUS_CATCH_ALL_SERVER);
          $this->status["status"] = "Catch All";
          $this->status["safe_to_send"] = "Risky";
          $this->status["email_score"] = "0.5";
          $this->status["mx_server"] = $maxr;
          $this->status["mx_response"] = $this->mx_response;
        }


      } else {
        $this->markAsInvalid();
        $this->setResponse("Undeliverable");
        $this->status["mx_server"] = $maxr;
        $this->status["mx_response"] = $this->mx_response; // Use the reason variable here
        if ($this->code == ('550' || '450')) {

          global $db, $get_response;
          $code_arr = array(
            //	'5.7.1',
            'blocked by prs',
            'unsolicited mail',
            'Greylisted'
            //	'5.1.1',
          );
          if (preg_match('(' . implode('|', $code_arr) . ')', $get_response)) {
            $mx_skip_q = "SELECT * FROM evp_mx_skiplist WHERE mx_server = '$maxr'";
            $mx_skip_r = $db->select($mx_skip_q);
            if ($mx_skip_r) {
              $mx_skip_count = mysqli_num_rows($mx_skip_r);
              $get_response = mysqli_escape_string($db->link, $get_response);
              if ($mx_skip_count == null) {
                $mx_skip_q = $db->select("INSERT INTO evp_mx_skiplist (mx_server, reason) VALUES ('$maxr', '$get_response')");
              }
            }
          }
        }
      }
      $this->disconnect();
    }
    return isset($this->status[0]) ? $this->status[0] : false;
  }

  private function getRandomString($length = 10)
  {
    $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $charactersLength = strlen($characters);
    $randomString = "";
    for ($i = 0; $i < $length; $i++) {
      $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return strtolower($randomString);
  }
  public static function get_site_option($option = "")
  {
    global $db;
    if ($db) {
      $evp_options_sql = "SELECT * FROM evp_options";
      $evp_options_read = $db->select($evp_options_sql);
      if ($evp_options_read) {
        $evp_options_check = mysqli_num_rows($evp_options_read);
        if ($evp_options_check > 0) {
          // $evp_options_row   = $evp_options_read->fetch_assoc();
          while ($evp_options_row = $evp_options_read->fetch_assoc()) {
            $option_name[$evp_options_row['option_name']] = $evp_options_row['option_value'];
          }
          // var_dump($option_name['scan_time_out']);
          $evp_options_read->free();

          if ($option !== "" && isset($option_name[$option])) {
            return $option_name[$option];
          }
          return $option_name;
        }
      }
      return false;
    }
    return false;
  }

  /**
   * check if email domain is Cache-All
   * @param string $domain Email domain
   * @return boolean True if the domain is Cache-All
   */
  public function is_domain_catch_all_enabled($domain)
  {
    $catch_all_check_status = "";
    $random_c = $this->getRandomString();
    $catchAll_email = $random_c . "@" . $domain;
    if ($this->db !== null) {
      $email_acc = $this->getEmailAccFromEmail($this->email);
      $email_dom = $this->getDomainFromEmail($this->email);
      $email_type_read = $this->db->select("SELECT * FROM evp_domain_filter_list WHERE name = '$email_acc' OR name = '$email_dom' ");
      if ($email_type_read && mysqli_num_rows($email_type_read) > 0) {
        $type_row = $email_type_read->fetch_assoc();
        $email_type = $type_row["e_type"];
        $catch_all_check_status = $type_row["catch_all_check"];
        $this->status["type"] = $email_type;
      }
    }

    if ($this->filter_skipMX) { // Check if skip_max_filter is enabled
      $this->skip_account_mx();
    }
    if ($this->filter_skipEmail) { // Check if skip_email_filter is enabled
      if ($this->skip_account_email()) {
        return true; // Exit if email is skipped
      }
    }
    if (!$this->public_catch_skip) {
      $this->debug[] = "Public Domain Skiped";
      if ($catch_all_check_status != "0") {
        $this->debug[] = "Checking Catch-All Server...";
        $catch_response = $this->validate($catchAll_email);
        $this->catchAllCehked = true;
        if ($catch_response) {
          $this->debug[] = "Email Domain is Catch-All Enabled";
          return true;
        }
      }
    }
    $this->debug[] = "Email Domain is not Catch-All Enabled";
    return false;
  }
}