Last active
October 28, 2019 17:55
-
-
Save alimranahmed/6d3351922e67fa7f0bef8a8bb0760995 to your computer and use it in GitHub Desktop.
Some wrap classes to make laravel life easy.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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