From 6594ec22884339bb1e3171e55e23d9b537b70ce5 Mon Sep 17 00:00:00 2001 From: Laurent Bachelier Date: Tue, 23 Jul 2013 15:35:05 +0200 Subject: [PATCH] bnporc: introduce Enterprise option Not working for now --- modules/bnporc/backend.py | 46 +++++++++++++++------ modules/bnporc/enterprise/__init__.py | 0 modules/bnporc/enterprise/browser.py | 57 +++++++++++++++++++++++++++ modules/bnporc/enterprise/pages.py | 39 ++++++++++++++++++ 4 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 modules/bnporc/enterprise/__init__.py create mode 100644 modules/bnporc/enterprise/browser.py create mode 100644 modules/bnporc/enterprise/pages.py diff --git a/modules/bnporc/backend.py b/modules/bnporc/backend.py index 6ecb398a..de2048c5 100644 --- a/modules/bnporc/backend.py +++ b/modules/bnporc/backend.py @@ -27,9 +27,10 @@ from datetime import datetime, timedelta from weboob.capabilities.bank import ICapBank, AccountNotFound, Account, Recipient from weboob.capabilities.messages import ICapMessages, Thread from weboob.tools.backend import BaseBackend, BackendConfig -from weboob.tools.value import ValueBackendPassword +from weboob.tools.value import ValueBackendPassword, Value from .browser import BNPorc +from .enterprise.browser import BNPEnterprise __all__ = ['BNPorcBackend'] @@ -42,12 +43,14 @@ class BNPorcBackend(BaseBackend, ICapBank, ICapMessages): VERSION = '0.g' LICENSE = 'AGPLv3+' DESCRIPTION = 'BNP Paribas French bank website' - CONFIG = BackendConfig(ValueBackendPassword('login', label='Account ID', masked=False), - ValueBackendPassword('password', label='Password', regexp='^(\d{6}|)$'), - ValueBackendPassword('rotating_password', default='', - label='Password to set when the allowed uses are exhausted (6 digits)', - regexp='^(\d{6}|)$')) - BROWSER = BNPorc + CONFIG = BackendConfig( + ValueBackendPassword('login', label='Account ID', masked=False), + ValueBackendPassword('password', label='Password', regexp='^(\d{6}|)$'), + ValueBackendPassword('rotating_password', default='', + label='Password to set when the allowed uses are exhausted (6 digits)', + regexp='^(\d{6}|)$'), + Value('website', label='Website to use', default='pp', + choices={'pp': 'Particuliers/Profesionnels', 'ent': 'Entreprises'})) STORAGE = {'seen': []} # Store the messages *list* for this duration @@ -59,14 +62,20 @@ class BNPorcBackend(BaseBackend, ICapBank, ICapMessages): self._threads_age = datetime.utcnow() def create_default_browser(self): + b = {'pp': BNPorc, 'ent': BNPEnterprise} + self.BROWSER = b[self.config['website'].get()] if self.config['rotating_password'].get().isdigit() and len(self.config['rotating_password'].get()) == 6: rotating_password = self.config['rotating_password'].get() else: rotating_password = None - return self.create_browser(self.config['login'].get(), - self.config['password'].get(), - password_changed_cb=self._password_changed_cb, - rotating_password=rotating_password) + if self.config['website'].get() != 'pp': + return self.create_browser(self.config['login'].get(), + self.config['password'].get()) + else: + return self.create_browser(self.config['login'].get(), + self.config['password'].get(), + password_changed_cb=self._password_changed_cb, + rotating_password=rotating_password) def _password_changed_cb(self, old, new): self.config['password'].set(new) @@ -92,10 +101,16 @@ class BNPorcBackend(BaseBackend, ICapBank, ICapMessages): return self.browser.iter_history(account) def iter_coming(self, account): + if self.config['website'].get() != 'pp': + raise NotImplementedError() + with self.browser: return self.browser.iter_coming_operations(account) def iter_transfer_recipients(self, ignored): + if self.config['website'].get() != 'pp': + raise NotImplementedError() + for account in self.browser.get_transfer_accounts().itervalues(): recipient = Recipient() recipient.id = account.id @@ -103,6 +118,9 @@ class BNPorcBackend(BaseBackend, ICapBank, ICapMessages): yield recipient def transfer(self, account, to, amount, reason=None): + if self.config['website'].get() != 'pp': + raise NotImplementedError() + if isinstance(account, Account): account = account.id @@ -141,6 +159,9 @@ class BNPorcBackend(BaseBackend, ICapBank, ICapMessages): return self.get_thread(thread) def get_thread(self, _id): + if self.config['website'].get() != 'pp': + raise NotImplementedError() + if isinstance(_id, Thread): thread = _id _id = thread.id @@ -151,6 +172,9 @@ class BNPorcBackend(BaseBackend, ICapBank, ICapMessages): return thread def iter_unread_messages(self): + if self.config['website'].get() != 'pp': + raise NotImplementedError() + threads = list(self.iter_threads(cache=True)) for thread in threads: if thread.root.flags & thread.root.IS_UNREAD: diff --git a/modules/bnporc/enterprise/__init__.py b/modules/bnporc/enterprise/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modules/bnporc/enterprise/browser.py b/modules/bnporc/enterprise/browser.py new file mode 100644 index 00000000..b18f8e36 --- /dev/null +++ b/modules/bnporc/enterprise/browser.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Laurent Bachelier +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword + +from .pages import LoginPage, AccountsPage + +__all__ = ['BNPEnterprise'] + + +class BNPEnterprise(BaseBrowser): + DOMAIN = 'entreprisesplus.bnpparibas.net' + PROTOCOL = 'https' + CERTHASH = '423f68a8162d1328bacb48269675d8b8577ebcc9d222860de8421792c4d222c1' + + PAGES = {'%s://%s/NSAccess.*' % (PROTOCOL, DOMAIN): LoginPage, + '%s://%s/UNE\?Action=DSP_VGLOBALE' % (PROTOCOL, DOMAIN): AccountsPage} + + def home(self): + self.location('%s://%s/NSAccess' % (self.PROTOCOL, self.DOMAIN)) + + def is_logged(self): + if self.page: + if self.page.get_error() is not None: + return False + return not self.is_on_page(LoginPage) + + def login(self): + assert isinstance(self.username, basestring) + assert isinstance(self.password, basestring) + assert self.password.isdigit() + + if not self.is_on_page(LoginPage): + self.home() + + self.page.login(self.username, self.password) + self.location('/UNE?Action=DSP_VGLOBALE', no_login=True) + + if not self.is_logged(): + raise BrowserIncorrectPassword() diff --git a/modules/bnporc/enterprise/pages.py b/modules/bnporc/enterprise/pages.py new file mode 100644 index 00000000..b9b3ae9f --- /dev/null +++ b/modules/bnporc/enterprise/pages.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Laurent Bachelier +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + +from weboob.tools.browser import BasePage + +__all__ = ['LoginPage', 'AccountsPage'] + + +class BEPage(BasePage): + def get_error(self): + for title in self.document.xpath('/html/head/title'): + if 'erreur' in title.text or 'error' in title.text: + return self.parser.select(self.document.getroot(), + 'input[@name="titre_page"]', 1).value + + +class LoginPage(BEPage): + def login(self, username, password): + raise NotImplementedError() + + +class AccountsPage(BEPage): + pass