[citibank] Use V8 for login. Closes #1743
This commit is contained in:
parent
9a1d638374
commit
45d55856ac
1 changed files with 32 additions and 46 deletions
|
|
@ -21,18 +21,17 @@
|
||||||
from weboob.browser import LoginBrowser, URL, need_login
|
from weboob.browser import LoginBrowser, URL, need_login
|
||||||
from weboob.browser.pages import HTMLPage, JsonPage, RawPage
|
from weboob.browser.pages import HTMLPage, JsonPage, RawPage
|
||||||
from weboob.capabilities.bank import Account, AccountNotFound, Transaction
|
from weboob.capabilities.bank import Account, AccountNotFound, Transaction
|
||||||
from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable
|
from weboob.exceptions import BrowserIncorrectPassword
|
||||||
from weboob.tools.capabilities.bank.transactions import \
|
from weboob.tools.capabilities.bank.transactions import \
|
||||||
AmericanTransaction as AmTr
|
AmericanTransaction as AmTr
|
||||||
|
|
||||||
from .parser import StatementParser, clean_label
|
from .parser import StatementParser, clean_label
|
||||||
|
|
||||||
import gc
|
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from time import sleep
|
from tempfile import mkstemp
|
||||||
from urllib import unquote
|
from subprocess import check_output, STDOUT
|
||||||
from shutil import rmtree
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['Citibank']
|
__all__ = ['Citibank']
|
||||||
|
|
@ -44,6 +43,25 @@ class SomePage(HTMLPage):
|
||||||
return bool(self.doc.xpath(u'//a[text()="Sign Off"]'))
|
return bool(self.doc.xpath(u'//a[text()="Sign Off"]'))
|
||||||
|
|
||||||
|
|
||||||
|
class IndexPage(SomePage):
|
||||||
|
def extra(self):
|
||||||
|
APPEND = r'jQuery\( "form" \).each\(function\(\) {' \
|
||||||
|
r'if\(isValidUrl\(jQuery\(this\).attr\("action"\)\)\){' \
|
||||||
|
r'jQuery\(this\).append\(([^)]+)\);}}\);'
|
||||||
|
script = self.doc.xpath(
|
||||||
|
'//script[contains(text(),"XXX_Extra")]/text()')[0]
|
||||||
|
script = re.sub(APPEND, lambda m: 'return %s;' % m.group(1), script)
|
||||||
|
script = re.sub(r'jQuery\(document\)[^\n]+\n', '', script)
|
||||||
|
for x in re.findall('function ([^(]+)\(', script):
|
||||||
|
script += '\nvar x = %s(); if (x) print(x);' % x
|
||||||
|
scriptFd, scriptName = mkstemp('.js')
|
||||||
|
os.write(scriptFd, script)
|
||||||
|
os.close(scriptFd)
|
||||||
|
html = check_output(["d8", scriptName], stderr=STDOUT)
|
||||||
|
os.remove(scriptName)
|
||||||
|
return re.findall(r'name=([^ ]+) value=([^>]+)>', html)
|
||||||
|
|
||||||
|
|
||||||
class AccountsPage(JsonPage):
|
class AccountsPage(JsonPage):
|
||||||
logged = True
|
logged = True
|
||||||
|
|
||||||
|
|
@ -129,13 +147,11 @@ class StatementPage(RawPage):
|
||||||
class Citibank(LoginBrowser):
|
class Citibank(LoginBrowser):
|
||||||
"""
|
"""
|
||||||
Citibank website uses some kind of Javascript magic during login
|
Citibank website uses some kind of Javascript magic during login
|
||||||
negotiation, hence a real browser is being used to log in.
|
negotiation, hence a real JS interpreter is being used to log in.
|
||||||
|
|
||||||
External dependencies:
|
External dependencies:
|
||||||
Firefox (https://www.mozilla.org/firefox).
|
V8 JavaScript Engine (http://code.google.com/p/v8/).
|
||||||
MuPDF (http://www.mupdf.com).
|
MuPDF (http://www.mupdf.com).
|
||||||
Python bindings for Selenium (https://pypi.python.org/pypi/selenium).
|
|
||||||
Xvfb (http://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml).
|
|
||||||
|
|
||||||
Tested on Arch Linux snapshot of 2014-11-11 (official and user packages).
|
Tested on Arch Linux snapshot of 2014-11-11 (official and user packages).
|
||||||
|
|
||||||
|
|
@ -143,10 +159,11 @@ class Citibank(LoginBrowser):
|
||||||
Contributions are welcome!
|
Contributions are welcome!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MAX_RETRIES = 10
|
|
||||||
MAX_DELAY = 10
|
|
||||||
BASEURL = 'https://online.citibank.com'
|
BASEURL = 'https://online.citibank.com'
|
||||||
home = URL(r'/US/JPS/portal/Home.do', SomePage)
|
MAX_RETRIES = 10
|
||||||
|
TIMEOUT = 30.0
|
||||||
|
index = URL(r'/US/JPS/portal/Index.do', IndexPage)
|
||||||
|
signon = URL(r'/US/JSO/signon/ProcessUsernameSignon.do', SomePage)
|
||||||
accounts = URL(r'/US/REST/accountsPanel'
|
accounts = URL(r'/US/REST/accountsPanel'
|
||||||
r'/getCustomerAccounts.jws\?ttc=(?P<ttc>.*)$',
|
r'/getCustomerAccounts.jws\?ttc=(?P<ttc>.*)$',
|
||||||
AccountsPage)
|
AccountsPage)
|
||||||
|
|
@ -195,39 +212,8 @@ class Citibank(LoginBrowser):
|
||||||
return self.page if url.is_here(**data) else url.go(data=data, **data)
|
return self.page if url.is_here(**data) else url.go(data=data, **data)
|
||||||
|
|
||||||
def do_login(self):
|
def do_login(self):
|
||||||
# To avoid ImportError during e.g. building modules list.
|
|
||||||
from selenium import webdriver
|
|
||||||
|
|
||||||
browser = webdriver.Firefox()
|
|
||||||
browser.get(self.BASEURL)
|
|
||||||
browser.execute_script('''
|
|
||||||
$("input[name='username']").val("%s");
|
|
||||||
$("input[name='password']").val("%s");
|
|
||||||
''' % (self.username, self.password))
|
|
||||||
self.wait(browser, 'form[name="SignonForm"]')[0].submit()
|
|
||||||
self.session.cookies.clear()
|
self.session.cookies.clear()
|
||||||
for c in browser.get_cookies():
|
data = dict([('username', self.username), ('password', self.password)]+
|
||||||
self.session.cookies.set(name=c['name'], value=unquote(c['value']))
|
self.index.go().extra())
|
||||||
browser.close()
|
if not self.signon.go(data=data).logged:
|
||||||
prof_dir = browser.firefox_profile.profile_dir
|
|
||||||
browser = None
|
|
||||||
gc.collect() # Make sure Firefox process is dead.
|
|
||||||
for i in xrange(self.MAX_RETRIES):
|
|
||||||
try:
|
|
||||||
rmtree(prof_dir)
|
|
||||||
break
|
|
||||||
except OSError:
|
|
||||||
sleep(min(1 << i, self.MAX_DELAY))
|
|
||||||
if not self.home.go().logged:
|
|
||||||
raise BrowserIncorrectPassword()
|
raise BrowserIncorrectPassword()
|
||||||
|
|
||||||
def wait(self, browser, selector):
|
|
||||||
self.logger.debug('Finding selector """%s""" on page %s' % (
|
|
||||||
selector, browser.current_url))
|
|
||||||
for i in xrange(self.MAX_RETRIES):
|
|
||||||
els = browser.find_elements_by_css_selector(selector)
|
|
||||||
if els:
|
|
||||||
return els
|
|
||||||
sleep(min(1 << i, self.MAX_DELAY))
|
|
||||||
raise BrowserUnavailable('Unexpected site behavior. '
|
|
||||||
'Perhaps this module needs some fixing...')
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue