#!/usr/bin/env python
from getpass import getpass
from mechanize import Browser, FormNotFoundError, HTTPError, LinkNotFoundError
from urllib import urlencode

config = {
    'u': str(input('User Code: ')),
    'p': getpass('PIN: '),
    'n': str(input('Account Number: '))
}

def servlet_url(name):
    return 'https://online.bankaustria.at/servlet/%s' % name

def is_account_link(link):
    onclick = dict(link.attrs).get('onclick')
    return onclick and onclick.startswith('javascript:submit_detail(')

class BachError(Exception): pass
class AccountNotFoundError(BachError): pass

class BachBrowser(object):
    def __init__(self):
        self._b = Browser()

    def login(self, user, pin):
        # submit login form
        login_data = urlencode({'yzbks': user, 'jklwd': pin})
        self._b.open(servlet_url('SSOLogin'), login_data)

        # open the content frame
        self._b.follow_link(name='content')

        # ack the "please don't forget to logout" form (if present)
        try:
            self._b.select_form('Main')
            self._b.submit()
        except FormNotFoundError:
            pass

        # store current session id
        self._b.select_form('Main')
        self._session_id = self._b['sessionid']

        # store the url of the "overview" page
        self._root_url = self._b.geturl()

    def logout(self):
        try:
            self._b.open(servlet_url('Logout'))
        except HTTPError, e:
            if e.code != 503:
                raise e

    def _open_root(self):
        self._b.open(self._root_url)

    def _find_account(self, account_re):
        # assumes that the browser is on the proper page (root)
        try:
            link = self._b.find_link(text_regex=account_re,
                                     predicate=is_account_link)
        except LinkNotFoundError, e:
            raise AccountNotFoundError(account_re)
        args = dict(link.attrs)['onclick'].split("'")
        return args[1], args[3].replace(' ', '%20')

    def _open_account(self, account_re):
        # assumes that the browser is on the proper page (root)
        servlet, params = self._find_account(account_re)
        self._b.open('%s?sessionid=%s&language=DE&mode=no%s' % (
                servlet_url(servlet), self._session_id, params))

    def _open_download(self, mode='csv'):
        # assumes that the browser is on the proper page (account detail)
        self._b.select_form('PrintDownload')
        self._b.form.action = self._b.geturl()
        self._b.form.set_all_readonly(False)
        self._b['downloadfilename'] = 'records.%s' % mode
        self._b['downloadmode'] = '%syes' % mode
        self._b['printmode'] = 'no'
        self._b.submit()

    def read_account(self, account, mode='csv'):
        self._open_root()
        self._open_account(account)
        self._open_download(mode)
        r = self._b.response()
        t = r.info().get('content-type')
        return r.read() if t and not t.startswith('text/html') else None

    def list_accounts(self):
        self._open_root()
        return [link.text for link in self._b.links(predicate=is_account_link)]

if __name__ == '__main__':
    b = BachBrowser()
    b.login(config['u'], config['p'])
    try:
        print b.read_account(config['n']),
    finally:
        b.logout()

@@ -19,36 +19,8 @@
 hon%0A
-from getpass import getpass%0A
 from
@@ -128,125 +128,8 @@
 de%0A%0A
-config = %7B%0A    'u': str(input('User Code: ')),%0A    'p': getpass('PIN: '),%0A    'n': str(input('Account Number: '))%0A%7D%0A%0A
 def 
@@ -2993,16 +2993,17 @@
 link)%5D%0A%0A
+%0A
 if __nam
@@ -3029,73 +3029,334 @@
     
-b = BachBrowser()%0A    b.login(config%5B'u'%5D, config%5B'p'%5D)%0A    try:%0A
+import getpass, json, os.path%0A    p = os.path.expanduser('~/.bachrc')%0A    c = json.load(open(p)) if os.path.exists(p) else %7B%7D%0A    b = BachBrowser()%0A    b.login(c.get('user') or str(input('User: ')),%0A            c.get('pin') or getpass.getpass('PIN: '))%0A    try:%0A        for acct in c.get('accounts') or b.list_accounts():%0A    
     
@@ -3384,19 +3384,12 @@
 unt(
-config%5B'n'%5D
+acct
 ),%0A 

Load config from file, prompt for what's missing.