diff --git a/modules/hsbc/backend.py b/modules/hsbc/backend.py index 4900dd84..c3dee1ca 100644 --- a/modules/hsbc/backend.py +++ b/modules/hsbc/backend.py @@ -20,6 +20,7 @@ from weboob.capabilities.bank import ICapBank, AccountNotFound +from weboob.capabilities.base import find_object from weboob.tools.backend import BaseBackend, BackendConfig from weboob.tools.value import ValueBackendPassword, Value @@ -51,12 +52,7 @@ class HSBCBackend(BaseBackend, ICapBank): yield account def get_account(self, _id): - with self.browser: - account = self.browser.get_account(_id) - if account: - return account - else: - raise AccountNotFound() + return find_object(self.browser.get_accounts_list(), id=_id, error=AccountNotFound) def iter_history(self, account): for tr in self.browser.get_history(account): diff --git a/modules/hsbc/browser.py b/modules/hsbc/browser.py index 256ceeed..5a10b184 100644 --- a/modules/hsbc/browser.py +++ b/modules/hsbc/browser.py @@ -18,6 +18,7 @@ # along with weboob. If not, see . +import ssl from datetime import timedelta from weboob.tools.date import LinearDateGuesser @@ -30,22 +31,30 @@ __all__ = ['HSBC'] class HSBC(LoginBrowser): - VERIFY = False BASEURL = 'https://client.hsbc.fr' - CERTHASH = '48d84a782728eeeb622e9ff721688365e24f555ae1aec49b3be33831c7fe24e6' - connection = URL('https://www.hsbc.fr/1/2/hsbc-france/particuliers/connexion', LoginPage) - login = URL('https://www.hsbc.fr/1/*', LoginPage) - cptPage = URL('/cgi-bin/emcgi.*\&CPT_IdPrestation.*', - '/cgi-bin/emcgi.*\&Ass_IdPrestation.*', - CPTOperationPage) - cbPage = URL('/cgi-bin/emcgi.*\&CB_IdPrestation.*', - CBOperationPage) - accounts = URL('/cgi-bin/emcgi', AccountsPage) + + connection = URL(r'https://www.hsbc.fr/1/2/hsbc-france/particuliers/connexion', LoginPage) + login = URL(r'https://www.hsbc.fr/1/*', LoginPage) + cptPage = URL(r'/cgi-bin/emcgi.*\&CPT_IdPrestation.*', + r'/cgi-bin/emcgi.*\&Ass_IdPrestation.*', + CPTOperationPage) + cbPage = URL(r'/cgi-bin/emcgi.*\&CB_IdPrestation.*', + CBOperationPage) + accounts = URL(r'/cgi-bin/emcgi', AccountsPage) def __init__(self, username, password, secret, *args, **kwargs): self.secret = secret LoginBrowser.__init__(self, username, password, *args, **kwargs) + def prepare_request(self, req): + preq = super(HSBC, self).prepare_request(req) + + conn = self.session.adapters['https://'].get_connection(preq.url) + conn.ssl_version = ssl.PROTOCOL_TLSv1 + + return preq + + def home(self): return self.login.go() @@ -58,7 +67,7 @@ class HSBC(LoginBrowser): raise BrowserIncorrectPassword() self.location(no_secure_key_link) - self.page.login_w_secure(self.login, self.password, self.secret) + self.page.login_w_secure(self.password, self.secret) self.page.useless_form() home_url = self.page.get_frame() @@ -90,7 +99,7 @@ class HSBC(LoginBrowser): if self.cbPage.is_here(): guesser = LinearDateGuesser(date_max_bump=timedelta(45)) - return self.pagination(lambda: self.page.get_history(date_guesser=guesser)) + return self.page.get_history(date_guesser=guesser) else: return self._get_history() diff --git a/modules/hsbc/pages.py b/modules/hsbc/pages.py index 459b9e36..ac06123b 100644 --- a/modules/hsbc/pages.py +++ b/modules/hsbc/pages.py @@ -20,25 +20,26 @@ from urlparse import urlparse, parse_qs import re -from weboob.tools.browser2.page import HTMLPage, method, ListElement, ItemElement, SkipItem, LoggedPage -from weboob.tools.browser2.filters import Filter, Env, CleanText, CleanDecimal, Link, Field, DateGuesser, TableCell from weboob.capabilities import NotAvailable from weboob.capabilities.bank import Account from weboob.tools.capabilities.bank.transactions import FrenchTransaction -class Transaction(FrenchTransaction): - PATTERNS = [(re.compile('^VIR(EMENT)? (?P.*)'), FrenchTransaction.TYPE_TRANSFER), - (re.compile('^PRLV (?P.*)'), FrenchTransaction.TYPE_ORDER), - (re.compile('^(?P.*) CARTE \d+ PAIEMENT CB\s+(?P
\d{2})(?P\d{2}) ?(.*)$'), - FrenchTransaction.TYPE_CARD), - (re.compile('^RETRAIT DAB (?P
\d{2})(?P\d{2}) (?P.*) CARTE [\*\d]+'), - FrenchTransaction.TYPE_WITHDRAWAL), - (re.compile('^CHEQUE( (?P.*))?$'), FrenchTransaction.TYPE_CHECK), - (re.compile('^(F )?COTIS\.? (?P.*)'),FrenchTransaction.TYPE_BANK), - (re.compile('^(REMISE|REM CHQ) (?P.*)'),FrenchTransaction.TYPE_DEPOSIT), - ] +from weboob.tools.browser import BrowserIncorrectPassword +from weboob.tools.browser2.page import HTMLPage, method, ListElement, ItemElement, SkipItem, LoggedPage, pagination +from weboob.tools.browser2.filters import Filter, Env, CleanText, CleanDecimal, Link, Field, DateGuesser, TableCell - _is_coming = False + +class Transaction(FrenchTransaction): + PATTERNS = [(re.compile(r'^VIR(EMENT)? (?P.*)'), FrenchTransaction.TYPE_TRANSFER), + (re.compile(r'^PRLV (?P.*)'), FrenchTransaction.TYPE_ORDER), + (re.compile(r'^CB (?P.*)\s+(?P
\d+)/(?P\d+)\s*(?P.*)'), + FrenchTransaction.TYPE_CARD), + (re.compile(r'^DAB (?P
\d{2})/(?P\d{2}) ((?P\d{2})H(?P\d{2}) )?(?P.*?)( CB N°.*)?$'), + FrenchTransaction.TYPE_WITHDRAWAL), + (re.compile(r'^CHEQUE$'), FrenchTransaction.TYPE_CHECK), + (re.compile(r'^COTIS\.? (?P.*)'), FrenchTransaction.TYPE_BANK), + (re.compile(r'^REMISE (?P.*)'), FrenchTransaction.TYPE_DEPOSIT), + ] class AccountsPage(LoggedPage, HTMLPage): def get_frame(self): @@ -114,6 +115,7 @@ class Pagination(object): class CBOperationPage(LoggedPage, HTMLPage): + @pagination @method class get_history(Pagination, Transaction.TransactionsElement): head_xpath = '//table//tr/th' @@ -127,18 +129,22 @@ class CBOperationPage(LoggedPage, HTMLPage): class CPTOperationPage(LoggedPage, HTMLPage): def get_history(self): - for script in self.doc.xpath('//script'): - if script.text is None or script.text.find('\nCL(0') < 0: - continue + for script in self.doc.xpath('//script'): + if script.text is None or script.text.find('\nCL(0') < 0: + continue - for m in re.finditer(r"CL\((\d+),'(.+)','(.+)','(.+)','([\d -\.,]+)',('([\d -\.,]+)',)?'\d+','\d+','[\w\s]+'\);", script.text, flags=re.MULTILINE): - op = Transaction(m.group(1)) - op.parse(date=m.group(3), raw=re.sub(u'[ ]+', u' ', m.group(4).replace(u'\n', u' '))) - op.set_amount(m.group(5)) - op._coming = (re.match('\d+/\d+/\d+', m.group(2)) is None) - yield op + for m in re.finditer(r"CL\((\d+),'(.+)','(.+)','(.+)','([\d -\.,]+)',('([\d -\.,]+)',)?'\d+','\d+','[\w\s]+'\);", script.text, flags=re.MULTILINE): + op = Transaction(m.group(1)) + op.parse(date=m.group(3), raw=re.sub(u'[ ]+', u' ', m.group(4).replace(u'\n', u' '))) + op.set_amount(m.group(5)) + op._coming = (re.match(r'\d+/\d+/\d+', m.group(2)) is None) + yield op class LoginPage(HTMLPage): + def on_load(self): + for message in self.doc.getroot().cssselect('div.csPanelErrors, div.csPanelAlert'): + raise BrowserIncorrectPassword(CleanText('.')(message)) + def login(self, login): form = self.get_form(nr=2) form['userid'] = login @@ -152,12 +158,15 @@ class LoginPage(HTMLPage): else: return a.attrib['href'] - def login_w_secure(self, login, password, secret): + def login_w_secure(self, password, secret): form = self.get_form(nr=0) form['memorableAnswer'] = secret inputs = self.doc.xpath(u'//input[starts-with(@id, "keyrcc_password_first")]') split_pass = u'' - for i,inpu in enumerate(inputs): + if len(password) != len(inputs): + raise BrowserIncorrectPassword('Your password must be %d chars long' % len(inputs)) + + for i, inpu in enumerate(inputs): #The good field are 1,2,3 and the bad one are 11,12,21,23,24,31 and so one if int(inpu.attrib['id'].split('first')[1]) < 10: split_pass += password[i]