fsb: Handle some bankid failures better.
[fulbank.git] / netbank
CommitLineData
7f511828
FT
1#!/usr/bin/python3
2
6532632d 3import sys, os, getopt, pwd, operator
7f511828
FT
4from fulbank import auth
5
6sesstype = None
7sess = None
8
6532632d
FT
9def pfxmatch(pfx, item):
10 return str(item)[:len(pfx)] == pfx
11
12class ambiguous(LookupError):
13 def __init__(self, a, b):
14 super().__init__("ambigous match: %s and %s" % (a, b))
15 self.a = a
16 self.b = b
17
18def find(seq, *, item=None, test=None, match=None, key=None, default=LookupError):
7f511828
FT
19 if key is None:
20 key = lambda o: o
21 if match is None and item is not None:
6532632d
FT
22 match = lambda o: test(item, o)
23 if test is None:
24 test = operator.eq
25 found = None
7f511828
FT
26 for thing in seq:
27 if match(key(thing)):
6532632d
FT
28 if found is None:
29 found = thing
30 else:
31 if default is LookupError:
32 raise ambiguous(key(found), key(thing))
33 else:
34 return default
35 if found is not None:
36 return found
7f511828
FT
37 if default is LookupError:
38 raise LookupError()
39 else:
40 return default
41
42def usage(out):
df34dfd3 43 out.write("usage: netbank [-h] BANK-ID COMMAND [ARGS...]\n")
7f511828
FT
44
45def requiresess(fn):
46 def wrap(cmd, args):
47 if sess is None:
48 sys.stderr.write("netbank: %s: no current session\n" % (cmd))
49 sys.exit(1)
50 return fn(cmd, args)
51 return wrap
52
53commands = {}
54
55def cmd_login(cmd, args):
56 global sess
57 if len(args) < 1:
58 sys.stderr.write("usage: login TYPE\n")
59 sys.exit(1)
60 sess = sesstype.create()
61 if args[0] == "bankid":
62 if len(args) < 2:
63 sys.stderr.write("usage: login bankid USER-ID\n")
64 sys.exit(1)
65 with auth.ttyconv() as conv:
66 sess.auth_bankid(args[1], conv)
67 else:
68 sys.stderr.write("netbank: %s: unknown authentication type\n" % (args[0]))
69 sys.exit(1)
70commands["login"] = cmd_login
71
72@requiresess
73def cmd_logout(cmd, args):
d6d73d98 74 global sess
7f511828
FT
75 if sess is not None:
76 sess.close()
77 sess = None
78commands["logout"] = cmd_logout
79
80@requiresess
81def cmd_ping(cmd, args):
82 sess.keepalive()
83commands["ping"] = cmd_ping
84
85@requiresess
86def cmd_lsacct(cmd, args):
87 for acct in sess.accounts:
88 sys.stdout.write("%s (%s): %s\n" % (acct.number, acct.name, acct.balance))
89commands["lsacct"] = cmd_lsacct
90
91@requiresess
92def cmd_lstxn(cmd, args):
93 opts, args = getopt.getopt(args, "n:")
94 num = 10
95 for o, a in opts:
96 if o == "-n":
97 num = int(a)
98 if len(args) < 1:
99 sys.stderr.write("usage: lstxn [-n NUM] ACCOUNT\n")
100 sys.exit(1)
101 try:
6532632d
FT
102 acct = find(sess.accounts, item=args[0], key=lambda acct: acct.number, test=pfxmatch)
103 except ambiguous as exc:
104 sys.stderr.write("netbank: %s: ambiguous match between %s and %s\n" % (args[0], exc.a, exc.b))
105 sys.exit(1)
7f511828
FT
106 except LookupError:
107 sys.stderr.write("netbank: %s: no such account\n" % (args[0]))
108 sys.exit(1)
109 for i, txn in zip(range(num), acct.transactions()):
110 sys.stdout.write("%s %s: %s\n" % (txn.date.isoformat(), txn.value, txn.message))
111commands["lstxn"] = cmd_lstxn
112
113def main():
114 global sess, sesstype
115
116 opts, args = getopt.getopt(sys.argv[1:], "h")
117 for o, a in opts:
118 if o == "-h":
119 usage(sys.stdout)
120 sys.exit(0)
121 if len(args) < 2:
122 usage(sys.stderr)
123 sys.exit(1)
124
125 if args[0] == "fsb":
126 import fulbank.fsb
127 sesstype = fulbank.fsb.session
128 else:
129 sys.stderr.write("netbank: %s: unknown bank id\n" % (args[0]))
130 sys.exit(1)
131 sesspath = os.path.join(pwd.getpwuid(os.getuid()).pw_dir, ".cache/fulbank", args[0])
132 cmd = args[1]
133 args = args[2:]
134
135 if os.path.exists(sesspath):
136 sess = sesstype.load(sesspath)
137 else:
138 sess = None
139 if cmd in commands:
140 commands[cmd](cmd, args)
141 else:
142 sys.stderr.write("netbank: %s: unknown command\n" % (cmd))
143 sys.exit(1)
144 if sess is not None:
145 sessdir = os.path.dirname(sesspath)
146 if not os.path.isdir(sessdir):
147 os.makedirs(sessdir)
148 sess.save(sesspath)
149 else:
150 if os.path.exists(sesspath):
151 os.unlink(sesspath)
152
153try:
154 if __name__ == "__main__":
155 main()
156except KeyboardInterrupt:
157 sys.exit(1)