#include "dat.h" #include #include #include #include int ctldeletequits = 1; typedef struct RequestType RequestType; typedef struct Request Request; typedef struct Memory Memory; struct RequestType { char *file; /* file to read requests from */ void (*f)(Request*); /* request handler */ void (*r)(Controlset*); /* resize handler */ int fd; /* fd = open(file, ORDWR) */ Channel *rc; /* channel requests are multiplexed to */ Controlset *cs; }; struct Request { RequestType *rt; Attr *a; Attr *tag; }; struct Memory { Memory *next; Attr *a; Attr *val; }; Memory *mem; static void readreq(void*); static void hide(void); static void unhide(void); static void openkmr(void); static void closekmr(void); static Memory* searchmem(Attr*); static void addmem(Attr*, Attr*); static void confirm(Request*); static void resizeconfirm(Controlset*); static void needkey(Request*); static void resizeneedkey(Controlset*); Control *b_remember; Control *b_accept; Control *b_refuse; RequestType rt[] = { { "/mnt/factotum/confirm", confirm, resizeconfirm, }, { "/mnt/factotum/needkey", needkey, resizeneedkey, }, { 0 }, }; enum { ButtonDim= 15, }; void threadmain(int argc, char *argv[]) { Request r; Channel *rc; RequestType *p; Font *invis; ARGBEGIN{ }ARGEND; if(newwindow("-hide") < 0) sysfatal("newwindow: %r"); fmtinstall('A', _attrfmt); /* create the proc's that read */ rc = chancreate(sizeof(Request), 0); for(p = rt; p->file != 0; p++){ p->fd = -1; p->rc = rc; proccreate(readreq, p, 16*1024); } /* gui initialization */ if(initdraw(0, 0, "auth/fgui") < 0) sysfatal("initdraw failed: %r"); initcontrols(); hide(); /* get an invisible font for passwords */ invis = openfont(display, "/lib/font/bit/lucm/passwd.9.font"); if (invis == nil) sysfatal("fgui: %s: %r", "/lib/font/bit/lucm/passwd.9.font"); namectlfont(invis, "invisible"); /* serialize all requests */ for(;;){ if(recv(rc, &r) < 0) break; (*r.rt->f)(&r); _freeattr(r.a); _freeattr(r.tag); } threadexitsall(nil); } /* * read requests and pass them to the main loop */ enum { Requestlen=4096, }; static void readreq(void *a) { RequestType *rt = a; char *buf, *p; int n; Request r; Attr **l; rt->fd = open(rt->file, ORDWR); if(rt->fd < 0) sysfatal("opening %s: %r", rt->file); rt->cs = nil; buf = malloc(Requestlen); if(buf == nil) sysfatal("allocating read buffer: %r"); r.rt = rt; for(;;){ n = read(rt->fd, buf, Requestlen-1); if(n < 0) break; buf[n] = 0; /* skip verb, parse attributes, and remove tag */ p = strchr(buf, ' '); if(p != nil) p++; else p = buf; r.a = _parseattr(p); /* separate out the tag */ r.tag = nil; for(l = &r.a; *l != nil; l = &(*l)->next) if(strcmp((*l)->name, "tag") == 0){ r.tag = *l; *l = r.tag->next; r.tag->next = nil; break; } /* if no tag, forget it */ if(r.tag == nil){ _freeattr(r.a); continue; } send(rt->rc, &r); } } #ifdef asdf static void readreq(void *a) { RequestType *rt = a; char *buf, *p; int n; Request r; rt->fd = -1; rt->cs = nil; buf = malloc(Requestlen); if(buf == nil) sysfatal("allocating read buffer: %r"); r.rt = rt; for(;;){ strcpy(buf, "adfasdf=afdasdf asdfasdf=asdfasdf"); r.a = _parseattr(buf); send(rt->rc, &r); sleep(5000); } } #endif asdf /* * open/close the keyboard, mouse and resize channels */ static Channel *kbdc; static Channel *mousec; static Channel *resizec; static Keyboardctl *kctl; static Mousectl *mctl; static void openkmr(void) { /* get channels for subsequent newcontrolset calls */ kctl = initkeyboard(nil); if(kctl == nil) sysfatal("can't initialize keyboard: %r"); kbdc = kctl->c; mctl = initmouse(nil, screen); if(mctl == nil) sysfatal("can't initialize mouse: %r"); mousec = mctl->c; resizec = mctl->resizec; } static void closekmr(void) { Mouse m; while(nbrecv(kbdc, &m) > 0) ; closekeyboard(kctl); while(nbrecv(mousec, &m) > 0) ; closemouse(mctl); } /* * called when the window is resized */ void resizecontrolset(Controlset *cs) { RequestType *p; for(p = rt; p->file != 0; p++){ if(p->cs == cs){ (*p->r)(cs); break; } } } /* * hide window when not in use */ static void unhide(void) { int wctl; wctl = open("/dev/wctl", OWRITE); if(wctl < 0) return; fprint(wctl, "unhide"); close(wctl); } static void hide(void) { int wctl; int tries; wctl = open("/dev/wctl", OWRITE); if(wctl < 0) return; for(tries = 0; tries < 10; tries++){ if(fprint(wctl, "hide") >= 0) break; sleep(100); } close(wctl); } /* * set up the controls for the confirmation window */ static Channel* setupconfirm(Request *r) { Controlset *cs; Channel *c; Attr *a; /* create a new control set for the confirmation */ openkmr(); cs = newcontrolset(screen, kbdc, mousec, resizec); createtext(cs, "msg"); chanprint(cs->ctl, "msg image paleyellow"); chanprint(cs->ctl, "msg border 1"); chanprint(cs->ctl, "msg add 'The following key is being used:'"); for(a = r->a; a != nil; a = a->next) chanprint(cs->ctl, "msg add ' %s = %s'", a->name, a->val); namectlimage(display->white, "i_white"); namectlimage(display->black, "i_black"); b_remember = createbutton(cs, "b_remember"); chanprint(cs->ctl, "b_remember border 1"); chanprint(cs->ctl, "b_remember mask i_white"); chanprint(cs->ctl, "b_remember image i_white"); chanprint(cs->ctl, "b_remember light i_black"); createtext(cs, "t_remember"); chanprint(cs->ctl, "t_remember image white"); chanprint(cs->ctl, "t_remember bordercolor white"); chanprint(cs->ctl, "t_remember add 'Remember this answer for future confirmations'"); b_accept = createtextbutton(cs, "b_accept"); chanprint(cs->ctl, "b_accept border 1"); chanprint(cs->ctl, "b_accept align center"); chanprint(cs->ctl, "b_accept text Accept"); chanprint(cs->ctl, "b_accept image i_white"); chanprint(cs->ctl, "b_accept light i_black"); b_refuse = createtextbutton(cs, "b_refuse"); chanprint(cs->ctl, "b_refuse border 1"); chanprint(cs->ctl, "b_refuse align center"); chanprint(cs->ctl, "b_refuse text Refuse"); chanprint(cs->ctl, "b_refuse image i_white"); chanprint(cs->ctl, "b_refuse light i_black"); c = chancreate(sizeof(char*), 0); controlwire(b_remember, "event", c); controlwire(b_accept, "event", c); controlwire(b_refuse, "event", c); /* make the controls interactive */ activate(b_remember); activate(b_accept); activate(b_refuse); r->rt->cs = cs; unhide(); resizecontrolset(cs); return c; } /* * resize the controls for the confirmation window */ static void resizeconfirm(Controlset *cs) { Rectangle r, mr, nr, ntr, ar, rr; int fontwidth; fontwidth = font->height; /* get usable window rectangle */ if(getwindow(display, Refnone) < 0) ctlerror("resize failed: %r"); r = insetrect(screen->r, 10); /* message box fills everything not needed for buttons */ mr = r; mr.max.y = mr.min.y + font->height*((Dy(mr)-3*ButtonDim-font->height-4)/font->height); /* remember button */ nr.min = Pt(mr.min.x, mr.max.y+ButtonDim); nr.max = Pt(mr.max.x, r.max.y); if(Dx(nr) > ButtonDim) nr.max.x = nr.min.x+ButtonDim; if(Dy(nr) > ButtonDim) nr.max.y = nr.min.y+ButtonDim; ntr.min = Pt(nr.max.x+ButtonDim, nr.min.y); ntr.max = Pt(r.max.x, nr.min.y+font->height); /* accept/refuse buttons */ ar.min = Pt(r.min.x+Dx(r)/2-ButtonDim-6*fontwidth, nr.max.y+ButtonDim); ar.max = Pt(ar.min.x+6*fontwidth, ar.min.y+font->height+4); rr.min = Pt(r.min.x+Dx(r)/2+ButtonDim, nr.max.y+ButtonDim); rr.max = Pt(rr.min.x+6*fontwidth, rr.min.y+font->height+4); /* make the controls visible */ chanprint(cs->ctl, "msg rect %R\nmsg show", mr); chanprint(cs->ctl, "b_remember rect %R\nb_remember show", nr); chanprint(cs->ctl, "t_remember rect %R\nt_remember show", ntr); chanprint(cs->ctl, "b_accept rect %R\nb_accept show", ar); chanprint(cs->ctl, "b_refuse rect %R\nb_refuse show", rr); } /* * free the controls for the confirmation window */ static void teardownconfirm(Request *r) { Controlset *cs; cs = r->rt->cs; r->rt->cs = nil; hide(); closecontrolset(cs); closekmr(); } /* * get user confirmation of a key */ static void confirm(Request *r) { Channel *c; char *s; int n; char *args[3]; int remember; Attr *val; Memory *m; /* if it's something that the user wanted us not to ask again about */ m = searchmem(r->a); if(m != nil){ fprint(r->rt->fd, "%A %A", r->tag, m->val); return; } /* set up the controls */ c = setupconfirm(r); /* wait for user to reply */ remember = 0; for(;;){ s = recvp(c); n = tokenize(s, args, nelem(args)); if(n==3 && strcmp(args[1], "value")==0){ if(strcmp(args[0], "b_remember:") == 0){ remember = atoi(args[2]); } if(strcmp(args[0], "b_accept:") == 0){ val = _mkattr(AttrNameval, "answer", "yes", nil); free(s); break; } if(strcmp(args[0], "b_refuse:") == 0){ val = _mkattr(AttrNameval, "answer", "no", nil); free(s); break; } } free(s); } teardownconfirm(r); fprint(r->rt->fd, "%A %A", r->tag, val); if(remember) addmem(_copyattr(r->a), val); else _freeattr(val); } /* * confirmations that are remembered */ static int match(Attr *a, Attr *b) { Attr *x; for(; a != nil; a = a->next){ x = _findattr(b, a->name); if(x == nil || strcmp(a->val, x->val) != 0) return 0; } return 1; } static Memory* searchmem(Attr *a) { Memory *m; for(m = mem; m != nil; m = m->next){ if(match(a, m->a)) break; } return m; } static void addmem(Attr *a, Attr *val) { Memory *m; m = malloc(sizeof *m); if(m == nil) return; m->a = a; m->val = val; m->next = mem; mem = m; } /* controls for needkey */ Control *msg; Control *b_done; enum { Pprivate= 1<<0, Pneed= 1<<1, }; typedef struct Entry Entry; struct Entry { Control *name; Control *val; Control *eq; Attr *a; }; static Entry *entry; static int entries; /* * set up the controls for the confirmation window */ static Channel* setupneedkey(Request *r) { Controlset *cs; Channel *c; Attr *a; Attr **l; char cn[10]; int i; /* create a new control set for the confirmation */ openkmr(); cs = newcontrolset(screen, kbdc, mousec, resizec); /* count attributes and allocate entry controls */ entries = 0; for(l = &r->a; *l; l = &(*l)->next) entries++; if(entries == 0){ closecontrolset(cs); closekmr(); return nil; } *l = a = mallocz(sizeof *a, 1); a->type = AttrQuery; entries++; l = &(*l)->next; *l = a = mallocz(sizeof *a, 1); a->type = AttrQuery; entries++; entry = malloc(entries*sizeof(Entry)); if(entry == nil){ closecontrolset(cs); closekmr(); return nil; } namectlimage(display->white, "i_white"); namectlimage(display->black, "i_black"); /* create controls */ msg = createtext(cs, "msg"); chanprint(cs->ctl, "msg image white"); chanprint(cs->ctl, "msg bordercolor white"); chanprint(cs->ctl, "msg add 'You need the following key. Fill in the blanks'"); chanprint(cs->ctl, "msg add 'and click on the DONE button.'"); for(i = 0, a = r->a; a != nil; i++, a = a->next){ entry[i].a = a; snprint(cn, sizeof cn, "name_%d", i); if(entry[i].a->name == nil){ entry[i].name = createentry(cs, cn); chanprint(cs->ctl, "%s image yellow", cn); chanprint(cs->ctl, "%s border 1", cn); } else { entry[i].name = createtext(cs, cn); chanprint(cs->ctl, "%s image white", cn); chanprint(cs->ctl, "%s bordercolor white", cn); chanprint(cs->ctl, "%s add '%s'", cn, a->name); } snprint(cn, sizeof cn, "val_%d", i); if(a->type == AttrQuery){ entry[i].val = createentry(cs, cn); chanprint(cs->ctl, "%s image yellow", cn); chanprint(cs->ctl, "%s border 1", cn); if(a->name != nil){ if(strcmp(a->name, "user") == 0) chanprint(cs->ctl, "%s value %q", cn, getuser()); if(*a->name == '!') chanprint(cs->ctl, "%s font invisible", cn); } } else { entry[i].val = createtext(cs, cn); chanprint(cs->ctl, "%s image white", cn); chanprint(cs->ctl, "%s add %q", cn, a->val); } snprint(cn, sizeof cn, "eq_%d", i); entry[i].eq = createtext(cs, cn); chanprint(cs->ctl, "%s image white", cn); chanprint(cs->ctl, "%s add ' = '", cn); } b_done = createtextbutton(cs, "b_done"); chanprint(cs->ctl, "b_done border 1"); chanprint(cs->ctl, "b_done align center"); chanprint(cs->ctl, "b_done text DONE"); chanprint(cs->ctl, "b_done image green"); chanprint(cs->ctl, "b_done light green"); /* wire controls for input */ c = chancreate(sizeof(char*), 0); controlwire(b_done, "event", c); for(i = 0; i < entries; i++) if(entry[i].a->type == AttrQuery) controlwire(entry[i].val, "event", c); /* make the controls interactive */ activate(msg); activate(b_done); for(i = 0; i < entries; i++){ if(entry[i].a->type != AttrQuery) continue; if(entry[i].a->name == nil) activate(entry[i].name); activate(entry[i].val); } /* change the display */ r->rt->cs = cs; unhide(); resizecontrolset(cs); return c; } /* * resize the controls for the confirmation window */ static void resizeneedkey(Controlset *cs) { Rectangle r, mr; int mid, i, n, lasty; /* get usable window rectangle */ if(getwindow(display, Refnone) < 0) ctlerror("resize failed: %r"); r = insetrect(screen->r, 10); /* find largest name */ mid = 0; for(i = 0; i < entries; i++){ if(entry[i].a->name == nil) continue; n = strlen(entry[i].a->name); if(n > mid) mid = n; } mid = (mid+2) * font->height; /* top line is the message */ mr = r; mr.max.y = mr.min.y + 2*font->height + 2; chanprint(cs->ctl, "msg rect %R\nmsg show", mr); /* one line per attribute */ mr.min.x += 2*font->height; lasty = mr.max.y; for(i = 0; i < entries; i++){ r.min.x = mr.min.x; r.min.y = lasty+2; r.max.x = r.min.x + mid - 3*stringwidth(font, "="); r.max.y = r.min.y + font->height; chanprint(cs->ctl, "name_%d rect %R\nname_%d show", i, r, i); r.min.x = r.max.x; r.max.x = r.min.x + 3*stringwidth(font, "="); chanprint(cs->ctl, "eq_%d rect %R\neq_%d show", i, r, i); r.min.x = r.max.x; r.max.x = mr.max.x; if(Dx(r) > 32*font->height) r.max.x = r.min.x + 32*font->height; chanprint(cs->ctl, "val_%d rect %R\nval_%d show", i, r, i); lasty = r.max.y; } /* done button */ mr.min.x -= 2*font->height; r.min.x = mr.min.x + mid - 3*font->height; r.min.y = lasty+10; r.max.x = r.min.x + 6*font->height; r.max.y = r.min.y + font->height + 2; chanprint(cs->ctl, "b_done rect %R\nb_done show", r); } /* * free the controls for the confirmation window */ static void teardownneedkey(Request *r) { Controlset *cs; cs = r->rt->cs; r->rt->cs = nil; hide(); closecontrolset(cs); closekmr(); if(entry != nil) free(entry); entry = nil; } static void needkey(Request *r) { Channel *c; char *nam, *val; int i, n; int fd; char *args[3]; /* set up the controls */ c = setupneedkey(r); if(c == nil) goto out; /* wait for user to reply */ for(;;){ val = recvp(c); n = tokenize(val, args, nelem(args)); if(n==3 && strcmp(args[1], "value")==0){ /* user hit 'enter' */ free(val); break; } free(val); } /* get entry values */ for(i = 0; i < entries; i++){ if(entry[i].a->type != AttrQuery) continue; chanprint(r->rt->cs->ctl, "val_%d data", i); val = recvp(entry[i].val->data); if(entry[i].a->name == nil){ chanprint(r->rt->cs->ctl, "name_%d data", i); nam = recvp(entry[i].name->data); if(nam == nil || *nam == 0){ free(val); continue; } entry[i].a->val = estrdup(val); free(val); entry[i].a->name = estrdup(nam); free(nam); } else { if(val != nil){ entry[i].a->val = estrdup(val); free(val); } } entry[i].a->type = AttrNameval; } /* enter the new key !!!!need to do something in case of error!!!! */ fd = open("/mnt/factotum/ctl", OWRITE); fprint(fd, "key %A", r->a); close(fd); teardownneedkey(r); out: fprint(r->rt->fd, "%A", r->tag); }