Skip to content

Instantly share code, notes, and snippets.

@alimranahmed
Last active October 28, 2019 17:55
Show Gist options
  • Save alimranahmed/6d3351922e67fa7f0bef8a8bb0760995 to your computer and use it in GitHub Desktop.
Save alimranahmed/6d3351922e67fa7f0bef8a8bb0760995 to your computer and use it in GitHub Desktop.
Some wrap classes to make laravel life easy.
<?php
namespace App\Services\EmailVerify;
use \DOMDocument;
use \DOMXpath;
use Log;
/**
* Verifies email address by attempting to connect and check with the mail server of that account
*/
class EmailVerifier
{
public $email;
private $verifierEmail;
public $port;
private $mx;
private $connect;
private $debug;
private $debugRaw;
private $_yahooSignupPageUrl = 'https://login.yahoo.com/account/create?specId=yidReg&lang=en-US&src=&done=https%3A%2F%2Fwww.yahoo.com&display=login';
private $_yahooSignupAjaxUrl = 'https://login.yahoo.com/account/module/create?validateField=yid';
private $yahooSignupPageContent;
private $yahooSignupPageHeaders;
public function __construct($verifierEmail = '[email protected]', $port = 25)
{
$this->debug = array();
$this->debugRaw = array();
if (!is_null($verifierEmail)) {
Log::info('[' . __METHOD__ . '] Initialized with Verifier Email: ' . $verifierEmail . ', Port: ' . $port);
$this->setVerifierEmail($verifierEmail);
} else {
Log::error('[' . __METHOD__ . '] Initialized with verifier email values');
}
$this->setPort($port);
}
private function setVerifierEmail($email)
{
$this->verifierEmail = $email;
Log::info('[' . __METHOD__ . '] Verifier Email was set to ' . $email);
}
public function getVerifierEmail()
{
return $this->verifierEmail;
}
private function setEmail($email)
{
$this->email = $email;
Log::info('[' . __METHOD__ . '] Email was set to ' . $email);
}
public function getEmail()
{
return $this->email;
}
public function setPort($port)
{
$this->port = $port;
Log::info('[' . __METHOD__ . '] Port was set to ' . $port);
}
public function getPort()
{
return $this->port;
}
public function getDebug($raw = false)
{
if ($raw) {
return $this->debugRaw;
} else {
return $this->debug;
}
}
public function verify($email)
{
$this->setEmail($email);
Log::info('[' . __METHOD__ . '] Verifying email...');
$is_valid = false;
//check if this is a yahoo email
$domain = $this->getDomain($this->email);
if (strtolower(trim($domain)) == 'yahoo.com') {
$is_valid = $this->validateYahoo();
} else {//otherwise check the normal way
//find mx
Log::info('[' . __METHOD__ . '] Finding MX record...');
$this->findMx();
if (!$this->mx) {
Log::error('[' . __METHOD__ . '] 100: No suitable MX records found');
return $is_valid;
} else {
Log::info('[' . __METHOD__ . '] Found MX: ' . $this->mx);
}
Log::info('[' . __METHOD__ . '] Connecting to the server...');
$this->connectMx();
if (!$this->connect) {
Log::error('[' . __METHOD__ . '] 110: Connection to server failed.');
return $is_valid;
} else {
Log::info('[' . __METHOD__ . '] Connection to server was successful.');
}
Log::info('[' . __METHOD__ . '] Starting verification...');
if (preg_match("/^220/i", $out = fgets($this->connect))) {
Log::info('[' . __METHOD__ . '] Got a 220 response. Sending HELO...');
fputs($this->connect, "HELO " . $this->getDomain($this->verifierEmail) . "\r\n");
$out = fgets($this->connect);
$this->debugRaw['helo'] = $out;
Log::info('[' . __METHOD__ . '] Response: ' . $out);
Log::info('[' . __METHOD__ . '] Sending MAIL FROM...');
fputs($this->connect, "MAIL FROM: <" . $this->verifierEmail . ">\r\n");
$from = fgets($this->connect);
$this->debugRaw['mail_from'] = $from;
Log::info('[' . __METHOD__ . '] Response: ' . $from);
Log::info('[' . __METHOD__ . '] Sending RCPT TO...');
fputs($this->connect, "RCPT TO: <" . $this->email . ">\r\n");
$to = fgets($this->connect);
$this->debugRaw['rcpt_to'] = $to;
Log::info('[' . __METHOD__ . '] Response: ' . $to);
Log::info('[' . __METHOD__ . '] Sending QUIT...');
$quit = fputs($this->connect, "QUIT");
$this->debugRaw['quit'] = $quit;
Log::info('[' . __METHOD__ . '] Response: ' . $quit);
fclose($this->connect);
Log::info('[' . __METHOD__ . '] Looking for 250 response...');
if (!preg_match("/^250/i", $from) || !preg_match("/^250/i", $to)) {
Log::error('[' . __METHOD__ . '] Not found! Email is invalid.');
$is_valid = false;
} else {
Log::info('[' . __METHOD__ . '] Found! Email is valid.');
$is_valid = true;
}
} else {
Log::error('[' . __METHOD__ . '] Encountered an unknown response code.');
}
}
return $is_valid;
}
private function getDomain($email)
{
$email_arr = explode("@", $email);
$domain = array_slice($email_arr, -1);
return $domain[0];
}
private function findMx()
{
$domain = $this->getDomain($this->email);
$mx_ip = false;
// Trim [ and ] from beginning and end of domain string, respectively
$domain = ltrim($domain, "[");
$domain = rtrim($domain, "]");
if ("IPv6:" == substr($domain, 0, strlen("IPv6:"))) {
$domain = substr($domain, strlen("IPv6") + 1);
}
$mxhosts = array();
if (filter_var($domain, FILTER_VALIDATE_IP)) {
$mx_ip = $domain;
} else {
getmxrr($domain, $mxhosts, $mxweight);
}
if (!empty($mxhosts)) {
$mx_ip = $mxhosts[array_search(min($mxweight), $mxweight)];
} else {
if (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$record_a = dns_get_record($domain, DNS_A);
} elseif (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$record_a = dns_get_record($domain, DNS_AAAA);
}
if (!empty($record_a)) {
$mx_ip = $record_a[0]['ip'];
}
}
$this->mx = $mx_ip;
}
private function connectMx()
{
//connect
$this->connect = @fsockopen($this->mx, $this->port);
}
private function validateYahoo()
{
$this->debug[] = 'Validating a yahoo email address...';
$this->debug[] = 'Getting the sign up page content...';
$this->fetchYahooSignupPage();
$cookies = $this->getYahooCookies();
$fields = $this->getYahooFields();
$this->debug[] = 'Adding the email to fields...';
$fields['yid'] = str_replace('@yahoo.com', '', strtolower($this->email));
$this->debug[] = 'Ready to submit the POST request to validate the email.';
$response = $this->requestYahooAjax($cookies, $fields);
$this->debug[] = 'Parsing the response...';
$response_errors = json_decode($response, true)['errors'] ?? [];
$this->debug[] = 'Searching errors for existing username error...';
foreach ($response_errors as $err) {
if ($err['name'] == 'yid' && $err['error'] == 'IDENTIFIER_EXISTS') {
$this->debug[] = 'Found an error about existing email.';
return true;
}
}
return false;
}
private function fetchYahooSignupPage()
{
$this->yahooSignupPageContent = file_get_contents($this->_yahooSignupPageUrl);
if ($this->yahooSignupPageContent === false) {
Log::error('200: Cannot load the sign up page');
} else {
$this->debug[] = 'Sign up page content stored.';
$this->debug[] = 'Getting headers...';
$this->yahooSignupPageHeaders = $http_response_header;
$this->debug[] = 'Sign up page headers stored.';
}
}
private function getYahooCookies()
{
$this->debug[] = 'Attempting to get the cookies from the sign up page...';
if ($this->yahooSignupPageContent !== false) {
$this->debug[] = 'Extracting cookies from headers...';
$cookies = array();
foreach ($this->yahooSignupPageHeaders as $hdr) {
if (preg_match('/^Set-Cookie:\s*(.*?;).*?$/', $hdr, $matches)) {
$cookies[] = $matches[1];
}
}
if (count($cookies) > 0) {
$this->debug[] = 'Cookies found: ' . implode(' ', $cookies);
return $cookies;
} else {
$this->debug[] = 'Could not find any cookies.';
}
}
return [];
//return false;
}
private function getYahooFields()
{
$dom = new DOMDocument();
$fields = array();
if (@$dom->loadHTML($this->yahooSignupPageContent)) {
$this->debug[] = 'Parsing the page for input fields...';
$xp = new DOMXpath($dom);
$nodes = $xp->query('//input');
foreach ($nodes as $node) {
$fields[$node->getAttribute('name')] = $node->getAttribute('value');
}
$this->debug[] = 'Extracted fields.';
} else {
Log::error('[' . __METHOD__ . '] 210: Could not load the dom HTML.');
}
return $fields;
}
private function requestYahooAjax($cookies, $fields)
{
$headers = array();
$headers[] = 'Origin: https://login.yahoo.com';
$headers[] = 'X-Requested-With: XMLHttpRequest';
$headers[] = 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36';
$headers[] = 'content-type: application/x-www-form-urlencoded; charset=UTF-8';
$headers[] = 'Accept: */*';
$headers[] = 'Referer: https://login.yahoo.com/account/create?specId=yidReg&lang=en-US&src=&done=https%3A%2F%2Fwww.yahoo.com&display=login';
$headers[] = 'Accept-Encoding: gzip, deflate, br';
$headers[] = 'Accept-Language: en-US,en;q=0.8,ar;q=0.6';
$cookies_str = implode(' ', $cookies);
$headers[] = 'Cookie: ' . $cookies_str;
$postdata = http_build_query($fields);
$opts = array(
'http' =>
array(
'method' => 'POST',
'header' => $headers,
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents($this->_yahooSignupAjaxUrl, false, $context);
return $result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment