Updates cmb, new features and bug fix

- fix a bug in the accounts listing
- adds proxy support
- adds ssl checks :
   - checks the certificate chain with the Verisign authority
   - checks that the sha256sum of the server's certificate is what's expected (like in Browser).
   - adds an option to avoid ssl checks (useful for debug)

Signed-off-by: Johann Broudin <Johann.Broudin@6-8.fr>
Signed-off-by: Romain Bignon <romain@symlink.me>
This commit is contained in:
Johann Broudin 2012-10-26 00:57:11 +02:00 committed by Romain Bignon
commit 654c2bafee
3 changed files with 206 additions and 17 deletions

View file

@ -17,7 +17,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.capabilities.bank import ICapBank, AccountNotFound
from weboob.capabilities.bank import Account, Transaction
from weboob.tools.backend import BaseBackend, BackendConfig
@ -26,28 +25,41 @@ from weboob.capabilities.base import NotAvailable
from weboob.tools.browser import BrowserIncorrectPassword, BrokenPageError
from re import match, compile, sub
from httplib import HTTPSConnection
from urllib import urlencode
from decimal import Decimal
from lxml import etree
from datetime import date
from StringIO import StringIO
from ssl import DER_cert_to_PEM_cert
from hashlib import sha256
import os
# import a library that adds certificate verification and proxy support to
# HTTPSConnection
from hellhttp import HellHTTPS
__all__ = ['CmbBackend']
class CmbBackend(BaseBackend, ICapBank):
NAME = 'cmb'
MAINTAINER = u'Johann Broudin'
EMAIL = 'Johann.Broudin@6-8.fr'
VERSION = '0.d'
LICENSE = 'AGPLv3+'
AUTH_CERT = os.path.dirname(__file__)
AUTH_CERT += '/Verisign_Class_3_Public_Primary_Certification_Authority.pem'
CERTHASH = '684d79eb02f59497b5a9c5dcc4c26db1ee637db12f29d703fdf6a80aafef892d'
DESCRIPTION = u'Crédit Mutuel de Bretagne French bank website'
CONFIG = BackendConfig(
ValueBackendPassword('login', label='Account ID', masked=False),
ValueBackendPassword('password', label='Password', masked=True))
ValueBackendPassword('password', label='Password', masked=True),
ValueBackendPassword('no_check', label='SSL Check ? [y,n]', masked=False))
LABEL_PATTERNS = [
( # card
compile('^CARTE (?P<text>.*)'),
@ -86,6 +98,8 @@ class CmbBackend(BaseBackend, ICapBank):
)
]
cookie = None
headers = {
'User-Agent':
@ -93,6 +107,11 @@ class CmbBackend(BaseBackend, ICapBank):
'AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405'
}
def sslCallBack(self, cert):
pemcert = DER_cert_to_PEM_cert(cert)
certhash = sha256(pemcert).hexdigest()
return certhash == self.CERTHASH
def login(self):
params = urlencode({
'codeEspace': 'NO',
@ -101,13 +120,18 @@ class CmbBackend(BaseBackend, ICapBank):
'noPersonne': self.config['login'].get(),
'motDePasse': self.config['password'].get()
})
conn = HTTPSConnection("www.cmb.fr")
if 'no_check' in self.config and self.config['no_check'].get() == "y":
conn = HellHTTPS("www.cmb.fr")
else:
conn = HellHTTPS("www.cmb.fr", ca_file=self.AUTH_CERT, callBack=self.sslCallBack)
conn.connect()
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
conn.request("POST",
"/domiweb/servlet/Identification",
params,
headers)
response = conn.getresponse()
conn.close()
if response.status == 302:
self.cookie = response.getheader('Set-Cookie').split(';')[0]
self.cookie += ';'
@ -121,7 +145,11 @@ class CmbBackend(BaseBackend, ICapBank):
self.login()
def do_http():
conn = HTTPSConnection("www.cmb.fr")
if 'no_check' in self.config and self.config['no_check'].get() == "y":
conn = HellHTTPS("www.cmb.fr")
else:
conn = HellHTTPS("www.cmb.fr", ca_file=self.AUTH_CERT, callBack=self.sslCallBack)
conn.connect()
headers = self.headers
headers['Cookie'] = self.cookie
conn.request("GET",
@ -157,22 +185,22 @@ class CmbBackend(BaseBackend, ICapBank):
account = Account()
td = tr.xpath('td')
a = td[0].xpath('a')
a = td[1].xpath('a')
account.label = unicode(a[0].text).strip()
href = a[0].get('href')
m = match(r"javascript:releve\((.*),'(.*)','(.*)'\)",
href)
account.id = unicode(m.group(1) + m.group(2) + m.group(3))
account.cmbvaleur = m.group(1)
account.cmbvaleur2 = m.group(2)
account.cmbtype = m.group(3)
account._cmbvaleur = m.group(1)
account._cmbvaleur2 = m.group(2)
account._cmbtype = m.group(3)
balance = td[1].text
balance = td[2].text
balance = balance.replace(',', '.').replace(u"\xa0", '')
account.balance = Decimal(balance)
span = td[3].xpath('a/span')
span = td[4].xpath('a/span')
if len(span):
coming = span[0].text.replace(' ', '').replace(',', '.')
coming = coming.replace(u"\xa0", '')
@ -194,18 +222,22 @@ class CmbBackend(BaseBackend, ICapBank):
self.login()
page = "/domiweb/prive/particulier/releve/"
if account.cmbtype == 'D':
if account._cmbtype == 'D':
page += "10-releve.act"
else:
page += "2-releve.act"
page +="?noPageReleve=1&indiceCompte="
page += account.cmbvaleur
page += account._cmbvaleur
page += "&typeCompte="
page += account.cmbvaleur2
page += account._cmbvaleur2
page += "&deviseOrigineEcran=EUR"
def do_http():
conn = HTTPSConnection("www.cmb.fr")
if 'no_check' in self.config and self.config['no_check'].get() == "y":
conn = HellHTTPS("www.cmb.fr")
else:
conn = HellHTTPS("www.cmb.fr", ca_file=self.AUTH_CERT, callBack=self.sslCallBack)
conn.connect()
headers = self.headers
headers['Cookie'] = self.cookie
conn.request("GET", page, {}, headers)
@ -256,7 +288,8 @@ class CmbBackend(BaseBackend, ICapBank):
mm = pattern.match(operation.raw)
if mm:
operation.type = _type
operation.label = sub('[ ]+', ' ', _label % mm.groupdict()).strip()
operation.label = sub('[ ]+', ' ',
_label % mm.groupdict()).strip()
break
amount = td[3].text