ref: 8e3d8120a945bad722e218963699f0a17a853d86
dir: /totp.c/
#include <u.h> #include <libc.h> #include <libsec.h> #include <auth.h> #include <bio.h> #include <draw.h> #include <event.h> void usage(void) { fprint(2, "usage: %s [-D] [-t]\n", argv0); exits(nil); } int chatty; typedef struct Account Account; struct Account { char *user; char code[12]; int remaining; int expanded; int hitstart; int hitend; Account *next; }; Account *accounts; int numaccounts; // return seconds, write buf with OTP int readtotp(char *buf, char *user) { AuthRpc *rpc; char *s; int n, fd, rem; char *toks[2]; fd = open("/mnt/factotum/rpc", ORDWR); if (fd < 0) sysfatal("error accessing factotum: %r"); rpc = auth_allocrpc(fd); if (!rpc) sysfatal("error: %r"); s = smprint("proto=totp user=%q role=client", user); n = strlen(s); if (chatty) print("rpc: %s\n", s); if (auth_rpc(rpc, "start", s, n) != ARok) goto Err; free(s); auth_rpc(rpc, "read", nil, 0); if (tokenize(rpc->arg, toks, 2) != 2) goto Err1; snprint(buf, 10, "%s", toks[0]); rem = atoi(toks[1]); if (chatty) print("code: %s, remaining: %d\n", buf, rem); auth_freerpc(rpc); close(fd); return rem; Err: close(fd); free(s); Err1: auth_freerpc(rpc); *buf = 0; return -1; } void readaccount(Account *acc) { int rem = readtotp(acc->code, acc->user); if (rem < 0) { print("error reading data for account '%s'", acc->user); return; } if (chatty) print("readaccount: %s\n", acc->user); acc->remaining = rem; } void foraccount(void (*fn)(Account*)) { Account *a = accounts; if (a == nil) return; while (a != nil) { fn(a); a = a->next; } } void printaccount(Account *a) { if (chatty) print("print account '%s'\n", a->user); print("%s: %s (%d)\n", a->user, a->code, a->remaining); } char* parseuser(char **toks, int ntoks) { int i = 2, n; char *s; while (i < ntoks && strncmp("user=", toks[i], 5)) i++; s = toks[i] + 5; // get rid of "user=" // unquote, if needed if (*s == '\'') { s += 1; n = strlen(s); s[n-1] = 0; } return strdup(s); } int addaccount(char *s) { Account *a; char *toks[16]; int ntoks; ntoks = tokenize(s, toks, 16); if (ntoks < 3) { sysfatal("bad response from factotum!"); } if (strcmp("key", toks[0]) != 0 || strcmp("proto=totp", toks[1]) != 0) return 0; int valid = 0; for (int i = 0; i < ntoks; i++) { if (strcmp("role=client", toks[i]) == 0) { valid = 1; break; } } if (!valid) return 0; if (!accounts) { accounts = malloc(sizeof(Account)); if (!accounts) sysfatal("err: %r"); a = accounts; } else { a = accounts; while (a->next != nil) a = a->next; a->next = malloc(sizeof(Account)); if (!a->next) sysfatal("err: %r"); a = a->next; } a->hitstart = a->hitend = -1; *(a->code) = 0; a->next = nil; a->expanded = 0; a->user = parseuser(toks, ntoks); return 1; } void freeaccount(Account* acc) { Account* c = acc->next; if (acc->user) free(acc->user); free(acc); if (c) freeaccount(c); } int readaccounts(void) { Biobuf *bfd; char *s; int num = 0; if (accounts) freeaccount(accounts); bfd = Bopen("/mnt/factotum/ctl", OREAD); if (!bfd) sysfatal("error reading factotum: %r"); while (s = Brdstr(bfd, '\n', 1)) { num += addaccount(s); free(s); } Bterm(bfd); if (chatty) print("%d accounts added\n", num); return num; } /* images */ Image *back; Image *linecol; int lineheight; /* draw params */ Image *dp_screen; int dp_num; /* number of accounts */ int dp_id; /* ID of this account */ int dp_numexpanded; /* number of expanded elements to consider */ void drawaccount(Account *account) { Point start, end, l1, l2; int numnormal; int margin_l = 10; char rem[16]; numnormal = dp_id - dp_numexpanded; l1.x = margin_l; l1.y = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20) + 5; l1 = addpt(l1, dp_screen->r.min); string(dp_screen, l1, linecol, ZP, font, account->user); account->hitstart = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20); if (account->expanded) { l2.x = margin_l; l2.y = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20) + 5 + lineheight + 5; l2 = addpt(l2, dp_screen->r.min); string(dp_screen, l2, linecol, ZP, font, account->code); snprint(rem, 16, "%d", account->remaining); l2.x = screen->r.max.x - margin_l - stringwidth(font, rem); string(dp_screen, l2, linecol, ZP, font, rem); dp_numexpanded++; } else { numnormal++; } account->hitend = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20); start.x = 0; start.y = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20); end.x = dp_screen->r.max.x - dp_screen->r.min.x; end.y = start.y; start = addpt(start, dp_screen->r.min); end = addpt(end, dp_screen->r.min); line(dp_screen, start, end, 0, 0, 0, linecol, ZP); dp_id++; } /* check hit account params */ Point ch_point; Account *ch_clicked; void checkhitaccount(Account *account) { int ref; if (account->hitend < 0 || account->hitstart < 0) return; ref = ch_point.y; if (ref < account->hitend && ref > account->hitstart) { ch_clicked = account; if (chatty) print("clicked element %s\n", account->user); } } Account* accounthit(Point p) { ch_point = subpt(p, screen->r.min); ch_clicked = nil; foraccount(checkhitaccount); return ch_clicked; } void snarf(Account *account) { int fd = open("/dev/snarf", OWRITE); if (fd < 0) return; fprint(fd, "%s", account->code); close(fd); } void redraw(Image *screen) { draw(screen, screen->r, back, nil, ZP); dp_screen = screen; dp_num = numaccounts; dp_id = 0; dp_numexpanded = 0; foraccount(drawaccount); } void eresized(int new) { if (new && getwindow(display, Refnone) < 0) fprint(2, "can't reattach to window"); redraw(screen); } void main(int argc, char **argv) { int textbased = 0; chatty = 0; accounts = nil; Event e; Mouse m; Menu menu; char *mstr[] = {" update ", "exit", 0}; int key, timer, menuhit; Account *selected; ARGBEGIN { case 't': textbased++; break; case 'D': chatty++; break; } ARGEND; quotefmtinstall(); numaccounts = readaccounts(); if (textbased) { foraccount(readaccount); foraccount(printaccount); exits(nil); } if (initdraw(0, 0, "totp") < 0) sysfatal("initdraw failed: %r"); back = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DWhite); linecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DBlack); lineheight = stringsize(font, "M").y; redraw(screen); einit(Emouse); timer = etimer(0, 1000); menu.item = mstr; menu.lasthit = 0; for (;;) { key = event(&e); if (key == Emouse) { m = e.mouse; if (m.buttons & 4) { menuhit = emenuhit(3, &m, &menu); if (menuhit == 0) { numaccounts = readaccounts(); foraccount(readaccount); } if (menuhit == 1) exits(nil); } if (m.buttons & 1) { selected = accounthit(m.xy); if (selected) { selected->expanded = !selected->expanded; redraw(screen); } } if (m.buttons & 2) { selected = accounthit(m.xy); if (selected) { snarf(selected); } } } if (key == timer) { foraccount(readaccount); redraw(screen); } } }