Commit 2afd27b4 authored by Romain Bignon's avatar Romain Bignon

use StateBrowser, s/BrowserToBeContinued/BrowserQuestion/ and coding style fixes

parent 24883075
......@@ -23,7 +23,7 @@
import re
from collections import defaultdict
from weboob.deprecated.browser import Browser, BrowserIncorrectPassword
from weboob.deprecated.browser import StateBrowser, BrowserIncorrectPassword
from import Account
from .pages import (LoginPage, AccountsList, AccountHistory, CardHistory, UpdateInfoPage,
......@@ -37,33 +37,31 @@ class BrowserIncorrectAuthenticationCode(BrowserIncorrectPassword):
class Boursorama(Browser):
class Boursorama(StateBrowser):
PROTOCOL = 'https'
CERTHASH = ['6bdf8b6dd177bd417ddcb1cfb818ede153288e44115eb269f2ddd458c8461039',
ENCODING = None # refer to the HTML encoding
'.*/connexion/securisation.*': AuthenticationPage,
'.*connexion.phtml.*': LoginPage,
'.*/comptes/synthese.phtml': AccountsList,
'.*/comptes/banque/detail/mouvements.phtml.*': AccountHistory,
'.*/comptes/banque/cartes/mouvements.phtml.*': CardHistory,
'.*/comptes/epargne/mouvements.phtml.*': AccountHistory,
'.*/date_anniversaire.phtml.*': UpdateInfoPage,
'.*/detail.phtml.*': AccountInvestment,
'.*/opcvm.phtml.*': InvestmentDetail
PAGES = {r'.*/connexion/securisation.*': AuthenticationPage,
r'.*connexion.phtml.*': LoginPage,
r'.*/comptes/synthese.phtml': AccountsList,
r'.*/comptes/banque/detail/mouvements.phtml.*': AccountHistory,
r'.*/comptes/banque/cartes/mouvements.phtml.*': CardHistory,
r'.*/comptes/epargne/mouvements.phtml.*': AccountHistory,
r'.*/date_anniversaire.phtml.*': UpdateInfoPage,
r'.*/detail.phtml.*': AccountInvestment,
r'.*/opcvm.phtml.*': InvestmentDetail
__states__ = []
__states__ = ('auth_token',)
def __init__(self, config=None, *args, **kwargs):
self.config = config
self.auth_token = None
kwargs['get_home'] = False
kwargs['username'] = self.config['login'].get()
kwargs['password'] = self.config['password'].get()
Browser.__init__(self, *args, **kwargs)
StateBrowser.__init__(self, *args, **kwargs)
def home(self):
if not self.is_logged():
......@@ -77,10 +75,7 @@ class Boursorama(Browser):
def handle_authentication(self):
if self.is_on_page(AuthenticationPage):
if self.config['enable_twofactors'].get():
if not self.config['pin_code'].get() or not self.auth_token:
raise BrowserIncorrectAuthenticationCode(
"""Boursorama - activate the two factor authentication in boursorama config."""
......@@ -92,8 +87,8 @@ class Boursorama(Browser):
assert isinstance(self.config['enable_twofactors'].get(), bool)
assert self.password.isdigit()
if self.is_on_page(AuthenticationPage):
if self.auth_token and self.config['pin_code'].get():
if not self.is_on_page(LoginPage):
self.location('https://' + self.DOMAIN + '/connexion.phtml', no_login=True)
......@@ -112,7 +107,7 @@ class Boursorama(Browser):
#if the login was correct but authentication code failed,
#we need to verify if bourso redirect us to login page or authentication page
if self.is_on_page(LoginPage):
raise BrowserIncorrectAuthenticationCode()
raise BrowserIncorrectAuthenticationCode('Invalid PIN code')
def get_accounts_list(self):
if self.is_on_page(AuthenticationPage):
......@@ -49,8 +49,7 @@ class BoursoramaModule(Module, CapBank):
return self.create_browser(self.config)
def iter_accounts(self):
for account in self.browser.get_accounts_list():
yield account
return self.browser.get_accounts_list()
def get_account(self, _id):
with self.browser:
......@@ -61,17 +60,7 @@ class BoursoramaModule(Module, CapBank):
raise AccountNotFound()
def iter_history(self, account):
with self.browser:
for history in self.browser.get_history(account):
yield history
return self.browser.get_history(account)
def iter_investment(self, account):
with self.browser:
for investment in self.browser.get_investment(account):
yield investment
#def iter_coming(self, account):
# with self.browser:
# for coming in self.browser.get_coming_operations(account):
# yield coming
return self.browser.get_investment(account)
......@@ -20,8 +20,10 @@
import re
import urllib2
from weboob.exceptions import BrowserToBeContinued
from weboob.exceptions import BrowserQuestion
from weboob.deprecated.browser import Page, BrowserIncorrectPassword
from import Value
class BrowserAuthenticationCodeMaxLimit(BrowserIncorrectPassword):
......@@ -34,36 +36,38 @@ class AuthenticationPage(Page):
headers = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows "
"NT 5.1; en-US; rv: Gecko/20100722 Firefox/3.6.8"
" GTB7.1 (.NET CLR 3.5.30729)",
"Referer": REFERER,
"NT 5.1; en-US; rv: Gecko/20100722 Firefox/3.6.8"
" GTB7.1 (.NET CLR 3.5.30729)",
"Referer": REFERER,
headers_ajax = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows "
"NT 5.1; en-US; rv: Gecko/20100722 Firefox/3.6.8"
" GTB7.1 (.NET CLR 3.5.30729)",
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest",
"X-Request": "JSON",
"X-Brs-Xhr-Request": "true",
"X-Brs-Xhr-Schema": "DATA+OUT",
"Referer": REFERER,
"NT 5.1; en-US; rv: Gecko/20100722 Firefox/3.6.8"
" GTB7.1 (.NET CLR 3.5.30729)",
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest",
"X-Request": "JSON",
"X-Brs-Xhr-Request": "true",
"X-Brs-Xhr-Schema": "DATA+OUT",
"Referer": REFERER,
def on_loaded(self):
def authenticate(self):
url = "https://" + self.browser.DOMAIN + "/ajax/banque/otp.phtml"
data = "authentificationforteToken=%s&authentificationforteStep=otp&alertType=10100&org=%s&otp=%s&validate=" % (self.browser.auth_token, self.REFERER, self.browser.config['pin_code'].get())
req = urllib2.Request(url, data, self.headers_ajax)
response =
def authenticate(cls, browser):'Using the PIN Code %s to login', browser.auth_token)
url = "https://" + browser.DOMAIN + "/ajax/banque/otp.phtml"
data = "authentificationforteToken=%s&authentificationforteStep=otp&alertType=10100&org=%s&otp=%s&validate=" % (browser.auth_token, cls.REFERER, browser.config['pin_code'].get())
req = urllib2.Request(url, data, cls.headers_ajax)
url = "%s?" % (self.SECURE_PAGE)
data = "org=/&device=%s" % (self.browser.config['device'].get())
req = urllib2.Request(url, data, headers=self.headers)
response =
self.browser.auth_token = None
url = "%s?" % (cls.SECURE_PAGE)
data = "org=/&device=%s" % (browser.config['device'].get())
req = urllib2.Request(url, data, headers=cls.headers)
browser.auth_token = None
def send_sms(self):
"""This function simulates the registration of a device on
......@@ -71,7 +75,6 @@ class AuthenticationPage(Page):
@param device device name to register
@exception BrowserAuthenticationCodeMaxLimit when daily limit is consumed
@exception BrowserIncorrectAuthenticationCode when code is not correct
url = "https://%s/ajax/banque/otp.phtml?org=%s&alertType=10100" % (self.browser.DOMAIN, self.REFERER)
req = urllib2.Request(url, headers=self.headers_ajax)
......@@ -82,8 +85,7 @@ class AuthenticationPage(Page):
regex = re.compile(self.MAX_LIMIT)
r =
if r:"Boursorama - Vous avez atteint le nombre maximum d'utilisation de l'authentification forte")
raise BrowserAuthenticationCodeMaxLimit()
raise BrowserAuthenticationCodeMaxLimit("Vous avez atteint le nombre maximum d'utilisation de l'authentification forte")
regex = re.compile(r"name=\\\"authentificationforteToken\\\" "
......@@ -95,4 +97,4 @@ class AuthenticationPage(Page):
data = "authentificationforteToken=%s&authentificationforteStep=start&alertType=10100&org=%s&validate=" % (self.browser.auth_token, self.REFERER)
req = urllib2.Request(url, data, self.headers_ajax)
response =
raise BrowserToBeContinued('pin_code')
raise BrowserQuestion(Value('pin_code', label='Enter the PIN Code'))
......@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <>.
from import Value
class BrowserIncorrectPassword(Exception):
......@@ -39,11 +39,12 @@ class BrowserUnavailable(Exception):
class BrowserToBeContinued(BrowserUnavailable):
def __init__(self, *args):
self.fields = []
for arg in args:
class BrowserQuestion(BrowserIncorrectPassword):
When raised by a browser,
def __init__(self, *fields):
self.fields = fields
class BrowserHTTPNotFound(BrowserUnavailable):
......@@ -32,7 +32,7 @@ from weboob.capabilities.account import CapAccount, Account, AccountRegisterErro
from weboob.core.backendscfg import BackendAlreadyExists
from weboob.core.modules import ModuleLoadError
from weboob.core.repositories import ModuleInstallError, IProgress
from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword, BrowserForbidden, BrowserSSLError, BrowserToBeContinued
from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword, BrowserForbidden, BrowserSSLError, BrowserQuestion
from import Value, ValueBool, ValueFloat, ValueInt, ValueBackendPassword
from import to_unicode
from import OrderedDict
......@@ -547,7 +547,12 @@ class ConsoleApplication(Application):
This method can be overrided to support more exceptions types.
if isinstance(error, BrowserIncorrectPassword):
if isinstance(error, BrowserQuestion):
for field in error.fields:
v = self.ask(field)
if v:
elif isinstance(error, BrowserIncorrectPassword):
msg = unicode(error)
if not msg:
msg = 'invalid login/password.'
......@@ -560,11 +565,6 @@ class ConsoleApplication(Application):
print(u'FATAL(%s): ' % + self.BOLD + '/!\ SERVER CERTIFICATE IS INVALID /!\\' + self.NC, file=self.stderr)
elif isinstance(error, BrowserForbidden):
print(u'Error(%s): %s' % (, msg or 'Forbidden'), file=self.stderr)
elif isinstance(error, BrowserToBeContinued):
for field in error.fields:
v = self.ask(field)
if v:
elif isinstance(error, BrowserUnavailable):
msg = unicode(error)
if not msg:
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment