<?php
error_reporting(0);
class pub_aol_verify
{
  public $email;
  public $verifier_email;
  public $port;
  public $email_acc;
  private $errors;
  private $debug;
  private $debug_raw;

  private $_aol_signup_page_url = 'https://login.aol.com/account/create?specId=yidReg&lang=en-US&src=&done=https%3A%2F%2Fwww.aol.com&display=login';
  private $_aol_signup_ajax_url = 'https://login.aol.com/account/module/create?validateField=yId';

  private $page_content;
  private $page_headers;

  public function __construct($email = null, $verifier_email = null, $port = 25)
  {
    $this->debug = array();
    $this->debug_raw = array();
    if (!is_null($email) && !is_null($verifier_email)) {
      $this->debug[] = 'Initialized with Email: ' . $email . ', Verifier Email: ' . $verifier_email . ', Port: ' . $port;
      $this->set_email($email);
      $this->set_verifier_email($verifier_email);
    } else {
      $this->debug[] = 'Initialized with no email or verifier email values';
    }
    $this->set_port($port);
  }

  public function set_verifier_email($email)
  {
    $this->verifier_email = $email;
    $this->debug[] = 'Verifier Email was set to ' . $email;
  }

  public function get_verifier_email()
  {
    return $this->verifier_email;
  }

  public function set_email($email)
  {
    $this->email = $email;
    $this->debug[] = 'Email was set to ' . $email;
  }

  public function get_email()
  {
    return $this->email;
  }

  public function set_port($port)
  {
    $this->port = $port;
    $this->debug[] = 'Port was set to ' . $port;
  }

  public function get_port()
  {
    return $this->port;
  }

  public function get_errors()
  {
    return array('errors' => $this->errors);
  }

  public function get_debug($raw = false)
  {
    if ($raw) {
      return $this->debug_raw;
    } else {
      return $this->debug;
    }
  }

  public function verify()
  {
    $this->debug[] = 'Verify function was called.';
    $is_valid = false;
    //check if this is a aol email
    $is_valid = $this->validate_aol();
    return $is_valid;
  }

  private function getEmailAccFromEmail($email)
  {
    $email_address = explode('@', $email);

    $domain = array_shift($email_address);

    return $domain;
  }

  private function add_error($code, $msg)
  {
    $this->errors[] = array('code' => $code, 'message' => $msg);
  }

  private function clear_errors()
  {
    $this->errors = array();
  }

  private function validate_aol()
  {

    $this->debug[] = 'Validating a aol email address...';
    $this->debug[] = 'Getting the sign up page content...';
    $this->fetch_page('aol');

    $cookies = $this->get_cookies();
    $fields = $this->get_fields();

    $this->debug[] = 'Adding the email to fields...';

    $this->email_acc = $this->getEmailAccFromEmail($this->email);

    $fields['yid'] = $this->email_acc;

    $this->debug[] = 'Ready to submit the POST request to validate the email.';

    $response = $this->request_validation($cookies, $fields);

    $this->debug[] = 'Parsing the response...';
    $decoded = json_decode($response, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
      $this->debug[] = 'JSON decode error: ' . json_last_error_msg();
      $this->add_error('220', 'Invalid JSON response from AOL validation API.');
      return false;
    }

    $errors = [];
    if (isset($decoded['errors']) && is_array($decoded['errors'])) {
      $errors = $decoded['errors'];
    } elseif (isset($decoded['fields']['yid']['error']['id'])) {
      $errors = [['name' => 'yid', 'error' => $decoded['fields']['yid']['error']['id']]];
    }

    $this->debug[] = 'Searching errors for exisiting username error...';

    // No errors => available (email does NOT exist)
    if (is_array($errors) && count($errors) === 0) {
      $this->debug[] = 'Identifier available (no errors) -> Email does not exist';
      return false;
    }

    // Any yid/yId error starting with ERROR_ or IDENTIFIER_ => taken (exists)
    foreach ($errors as $err) {
      $name = isset($err['name']) ? $err['name'] : '';
      $code = isset($err['error']) ? $err['error'] : '';
      if (($name === 'yid' || $name === 'yId') && (strpos($code, 'ERROR_') === 0 || strpos($code, 'IDENTIFIER_') === 0)) {
        $this->debug[] = 'Identifier not available (' . $code . ') -> Email exists';
        return true;
      }
    }

    $this->debug[] = 'Unrecognized error set, treating as not existing';
    return false;
  }

  private function fetch_page($cookies = '')
  {
    if ($cookies) {
      $opts = array(
        'http' => array(
          'method' => "GET",
          'header' => "Accept-language: en\r\n" .
            "Cookie: " . $cookies . "\r\n"
        )
      );
      $context = stream_context_create($opts);
    }

    if ($cookies) {
      $this->page_content = file_get_contents($this->_aol_signup_page_url, false, $context);
    } else {
      $this->page_content = file_get_contents($this->_aol_signup_page_url);
    }


    if ($this->page_content === false) {
      $this->debug[] = 'Could not read the sign up page.';
      $this->add_error('200', 'Cannot not load the sign up page.');
    } else {
      $this->debug[] = 'Sign up page content stored.';
      $this->debug[] = 'Getting headers...';
      $this->page_headers = $http_response_header;
      $this->debug[] = 'Sign up page headers stored.';
    }
  }

  private function get_cookies()
  {
    $this->debug[] = 'Attempting to get the cookies from the sign up page...';
    if ($this->page_content !== false) {
      $this->debug[] = 'Extracting cookies from headers...';
      $cookies = array();
      foreach ($this->page_headers as $hdr) {
        if (preg_match('/^Set-Cookie:\s*(.*?;).*?$/i', $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 false;
  }

  private function get_fields()
  {
    $dom = new DOMDocument('1.0', 'UTF-8');
    $fields = array();
    if ($this->page_content === false || $this->page_content === '') {
      $this->debug[] = 'Sign up page content empty, skipping DOM parsing.';
      $this->add_error('210', 'No HTML supplied for DOM parsing.');
      return $fields;
    }

    $libxmlPreviousState = libxml_use_internal_errors(true);
    $html = $this->page_content;
    if (strpos($html, '<meta charset=') === false) {
      $html = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . $html;
    }

    $loaded = $dom->loadHTML($html, LIBXML_NOWARNING | LIBXML_NOERROR);
    libxml_clear_errors();
    libxml_use_internal_errors($libxmlPreviousState);

    if ($loaded) {
      $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 {
      $this->debug[] = 'Something is worng with the page HTML.';
      $this->add_error('210', 'Could not load the dom HTML.');
    }

    return $fields;
  }

  private function request_validation($cookies, $fields)
  {

    $headers = array();
    $headers[] = 'Origin: https://login.aol.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: application/json, text/plain, */*';
    $headers[] = 'Referer: https://login.aol.com/account/create?specId=yidReg&lang=en-US&src=&done=https%3A%2F%2Fwww.aol.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;

    // Build AOL payload similar to Yahoo, but with yid and aol.com domain
    $firstName = isset($fields['firstName']) && $fields['firstName'] !== '' ? $fields['firstName'] : 'Test';
    $lastName = isset($fields['lastName']) && $fields['lastName'] !== '' ? $fields['lastName'] : 'User';
    $displayName = isset($fields['displayName']) && $fields['displayName'] !== '' ? $fields['displayName'] : ($firstName . ' ' . $lastName);
    $yidDomain = 'aol.com';
    $password = isset($fields['password']) && $fields['password'] !== '' ? $fields['password'] : 'Abcd1234!';
    $mm = isset($fields['mm']) && $fields['mm'] !== '' ? $fields['mm'] : '12';
    $dd = isset($fields['dd']) && $fields['dd'] !== '' ? $fields['dd'] : '12';
    $yyyy = isset($fields['yyyy']) && $fields['yyyy'] !== '' ? $fields['yyyy'] : '1991';

    $specId = isset($fields['specId']) && $fields['specId'] !== '' ? $fields['specId'] : 'yidReg';
    $crumb = isset($fields['crumb']) && $fields['crumb'] !== '' ? $fields['crumb'] : (isset($fields['acrumb']) ? $fields['acrumb'] : '');

    $postFields = array(
      'lang' => isset($fields['lang']) && $fields['lang'] !== '' ? $fields['lang'] : 'en-US',
      'src' => isset($fields['src']) && $fields['src'] !== '' ? $fields['src'] : '',
      'done' => isset($fields['done']) && $fields['done'] !== '' ? $fields['done'] : 'https://www.aol.com',
      'specId' => $specId,
      'sessionIndex' => isset($fields['sessionIndex']) ? $fields['sessionIndex'] : '',
      'acrumb' => isset($fields['acrumb']) ? $fields['acrumb'] : '',
      'crumb' => $crumb,
      'displayName' => $displayName,
      'context' => isset($fields['context']) && $fields['context'] !== '' ? $fields['context'] : 'REGISTRATION',
      'googleIdToken' => isset($fields['googleIdToken']) ? $fields['googleIdToken'] : '',
      'authCode' => isset($fields['authCode']) ? $fields['authCode'] : '',
      'attrSetIndex' => isset($fields['attrSetIndex']) ? $fields['attrSetIndex'] : '0',
      'tos0' => isset($fields['tos0']) ? $fields['tos0'] : '',
      'firstName' => $firstName,
      'lastName' => $lastName,
      'yidDomain' => $yidDomain,
      'yId' => $this->email_acc,
      'password' => $password,
      'mm' => $mm,
      'dd' => $dd,
      'yyyy' => $yyyy
    );

    $postdata = http_build_query($postFields);
    $this->debug_raw[] = array('stage' => 'aol_validation_payload', 'payload' => $postFields);

    $url = $this->_aol_signup_ajax_url;
    $this->debug_raw[] = array('stage' => 'aol_validation_request', 'url' => $url);
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_ENCODING, "");
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

    $result = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);


    $this->debug_raw[] = array('stage' => 'aol_validation_response', 'http_code' => $http_code, 'content_type' => $content_type, 'response' => is_string($result) ? (strlen($result) > 2048 ? substr($result, 0, 2048) . '...[truncated]' : $result) : $result);

    return $result;
  }
}