Skip to content

Instantly share code, notes, and snippets.

@Sapistudio
Forked from krazybean/validate_yahoo.py
Created February 19, 2024 09:42
Show Gist options
  • Save Sapistudio/670dbf715e47860316bccdc9431e439c to your computer and use it in GitHub Desktop.
Save Sapistudio/670dbf715e47860316bccdc9431e439c to your computer and use it in GitHub Desktop.
Yahoo email validation
import sys
import time
import urllib
import requests
class Yahoo(object):
"""
Hokey way of Yahoo validation since their SMTP check returns
250 for every request.
"""
def __init__(self):
"""
Putting together some constants needed or maybe for PEP8 fashion.
"""
ua = []
ua.append('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1)')
ua.append('AppleWebKit/537.36 (KHTML, like Gecko)')
ua.append('Chrome/63.0.3239.108 Safari/537.36')
content_type = 'application/x-www-form-urlencoded; charset=UTF-8'
url = 'https://login.yahoo.com/account/module/create'
params = 'validateField=yid'
self.url = '{0}?{1}'.format(url, params)
self.headers = {'origin': 'https://login.yahoo.com',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9',
'user-agent': ' '.join(ua),
'content-type': content_type,
'accept': '*/*',
'referer': self.url,
'authority': 'login.yahoo.com',
'x-requested-with': 'XMLHttpRequest'}
def _valid_response_(self, response):
""" Internal function for checking the response code.
Args:
response (:obj:): Requests response object.
Returns:
bool: True or False based on if 2xx status code.
"""
if response and response.status_code:
if response.status_code in range(200, 299):
return True
return False
def _extract_crumb(self, cookies):
""" Crumb needed for the post body element.
Args:
cookies (:obj:): Requests.cookies response object.
Returns:
acrumb (str): Parsed out string from AS cookie for body.
"""
acrumb = None
for cookie in cookies:
if cookie.name == 'AS':
dataset = cookie.value.split('&')
for data in dataset:
if data.startswith('s='):
acrumb = data[len('s='):]
return acrumb
def _browser_data(self, acrumb, mailbox):
""" Construct of the post return data for second request.
Args:
acrumb (str): Hash value returned to post body.
mailbox (str): {mailbox}@yahoo.com also needed for post body.
Returns:
browser, dict_args (tuple): 2 sets of prepared data for response.
"""
ftime_start = int(time.time())
ftime_end = ftime_start + 10
hash1 = '24700f9f1986800ab4fcc880530dd0ed'
hash2 = 'd52a1516cfb5f1c2d8a427c14bc3645f'
browser_fp_data = {"language": "en-US",
"colorDepth": 24,
"deviceMemory": 8,
"pixelRatio": 2,
"hardwareConcurrency": 8,
"resolution": {"w": 1440,
"h": 900},
"availableResolution": {"w": 1440,
"h": 846},
"timezoneOffset": 300,
"timezone": "America/Chicago",
"sessionStorage": 1,
"localStorage": 1,
"indexedDb": 1,
"openDatabase": 1,
"cpuClass": "unknown",
"platform": "MacIntel",
"doNotTrack": 1,
"canvas": "canvas winding:yes~canvas",
"webgl": 1,
"webglVendorAndRenderer": "Google Inc. (ATI Technologies Inc.)~ANGLE (ATI Technologies Inc., AMD Radeon Pro 555 OpenGL Engine, OpenGL 4.1)",
"adblock": 0,
"hasLiedLanguages": 0,
"hasLiedResolution": 0,
"hasLiedOs": 0,
"hasLiedBrowser": 0,
"touch_support": {"points": 0,
"event": 0,
"start": 0},
"plugins": {"count": 0,
"hash": hash1},
"fonts": {"count": 27,
"hash": hash2},
"audio": "124.04347657808103",
"ts": {"serve": ftime_start,
"render": ftime_end}}
dict_args = {'specId': 'yidregsimplified',
'cacheStored': 'true',
'crumb': 'QFM5cNikkUV',
'acrumb': acrumb,
'sessionIndex': 'QQ--',
'done': 'https%3A%2F%2Fwww.yahoo.com',
'attrSetIndex': 0,
'googleIdToken': '',
'authCode': '',
'tos0': 'oath_freereg|us|en-US',
'firstName': '',
'lastName': '',
'attrSetIndex': 0,
'userid-domain': 'yahoo',
'userId': mailbox,
'yidDomainDefault': 'yahoo.com',
'yidDomain': 'yahoo.com',
'password': '',
'shortCountryCode': 'US',
'phone': '',
'mm': '',
'dd': '',
'yyyy': '',
'multiDomain': '',
'signup': '',
'freeformGender': ''}
return browser_fp_data, dict_args
def _reshape_cookie(self, cookies):
""" Returns string representation of cookie body
Args:
cookies (:obj:`list`): List of cookies
Returns:
string: Concatenated string of cookie name+body
"""
string = []
for cookie in cookies:
string.append('{0}={1}'.format(cookie.name, cookie.value))
return ';'.join(string)
def _digest_response(self, response):
""" Tests response output if mailbox exists.
Args:
response (:obj:): Requests response object.json()
Returns:
string: "exists" or "does not exist"
"""
output = None
if 'errors' in response:
for error in response['errors']:
if error['name'] == 'userId':
output = error['error']
if output == 'IDENTIFIER_EXISTS':
return 'exists'
return 'does not exist'
def _clean_mailbox(self, mailbox):
""" Incase someone passes in a full email in instead of a mailbox
Args:
mailbox (str): "[email protected]"
Returns:
mailbox (str): "test" without the "@yahoo.com"
"""
if '@' in mailbox:
return mailbox[:mailbox.find("@") - 1]
return mailbox
def validate(self, mailbox):
""" Validation method that runs the check against the validation.
Args:
mailbox (str): mailbox to check validation against.
Returns:
string: "{mailbox} exists" or "{mailbox} does not exist"
"""
mailbox = self._clean_mailbox(mailbox)
r = requests.get(self.url)
if not self._valid_response_(r):
return {'Error': 'Initial request error'}
acrumb = self._extract_crumb(r.cookies)
self.headers['cookie'] = self._reshape_cookie(r.cookies)
browser_fp_data, dict_args = self._browser_data(acrumb, mailbox)
payload = {'browser-fp-data': browser_fp_data}
payload.update(dict_args)
params = {'validateField': 'userId'}
req = requests.post(self.url, headers=self.headers, cookies=r.cookies,
data=payload, params=params)
if not self._valid_response_(r):
return {'Error': 'Validation call error'}
return '{0} {1}'.format(mailbox, self._digest_response(req.json()))
if __name__ == '__main__':
yahoo = Yahoo()
mailbox = 'krazybean1'
if len(sys.argv) == 2:
mailbox = sys.argv[1]
print(yahoo.validate(mailbox))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment