1 #include "dat.h"
2 #include <draw.h>
3 #include <mouse.h>
4 #include <keyboard.h>
5 #include <control.h>
6
7 int ctldeletequits = 1;
8
9 typedef struct RequestType RequestType;
10 typedef struct Request Request;
11 typedef struct Memory Memory;
12
13 struct RequestType
14 {
15 char *file; /* file to read requests from */
16 void (*f)(Request*); /* request handler */
17 void (*r)(Controlset*); /* resize handler */
18 int fd; /* fd = open(file, ORDWR) */
19 Channel *rc; /* channel requests are multiplexed to */
20 Controlset *cs;
21 };
22
23 struct Request
24 {
25 RequestType *rt;
26 Attr *a;
27 Attr *tag;
28 };
29
30 struct Memory
31 {
32 Memory *next;
33 Attr *a;
34 Attr *val;
35 };
36 Memory *mem;
37
38 static void readreq(void*);
39 static void hide(void);
40 static void unhide(void);
41 static void openkmr(void);
42 static void closekmr(void);
43 static Memory* searchmem(Attr*);
44 static void addmem(Attr*, Attr*);
45
46 static void confirm(Request*);
47 static void resizeconfirm(Controlset*);
48 static void needkey(Request*);
49 static void resizeneedkey(Controlset*);
50
51 Control *b_remember;
52 Control *b_accept;
53 Control *b_refuse;
54
55 RequestType rt[] =
56 {
57 { "/mnt/factotum/confirm", confirm, resizeconfirm, },
58 { "/mnt/factotum/needkey", needkey, resizeneedkey, },
59 { 0 },
60 };
61
62 enum
63 {
64 ButtonDim= 15,
65 };
66
67 void
threadmain(int argc,char * argv[])68 threadmain(int argc, char *argv[])
69 {
70 Request r;
71 Channel *rc;
72 RequestType *p;
73 Font *invis;
74
75 ARGBEGIN{
76 }ARGEND;
77
78 if(newwindow("-hide") < 0)
79 sysfatal("newwindow: %r");
80
81 fmtinstall('A', _attrfmt);
82
83 /* create the proc's that read */
84 rc = chancreate(sizeof(Request), 0);
85 for(p = rt; p->file != 0; p++){
86 p->fd = -1;
87 p->rc = rc;
88 proccreate(readreq, p, 16*1024);
89 }
90
91 /* gui initialization */
92 if(initdraw(0, 0, "auth/fgui") < 0)
93 sysfatal("initdraw failed: %r");
94 initcontrols();
95 hide();
96
97 /* get an invisible font for passwords */
98 invis = openfont(display, "/lib/font/bit/lucm/passwd.9.font");
99 if (invis == nil)
100 sysfatal("fgui: %s: %r", "/lib/font/bit/lucm/passwd.9.font");
101 namectlfont(invis, "invisible");
102
103 /* serialize all requests */
104 for(;;){
105 if(recv(rc, &r) < 0)
106 break;
107 (*r.rt->f)(&r);
108 _freeattr(r.a);
109 _freeattr(r.tag);
110 }
111
112 threadexitsall(nil);
113 }
114
115 /*
116 * read requests and pass them to the main loop
117 */
118 enum
119 {
120 Requestlen=4096,
121 };
122 static void
readreq(void * a)123 readreq(void *a)
124 {
125 RequestType *rt = a;
126 char *buf, *p;
127 int n;
128 Request r;
129 Attr **l;
130
131 rt->fd = open(rt->file, ORDWR);
132 if(rt->fd < 0)
133 sysfatal("opening %s: %r", rt->file);
134 rt->cs = nil;
135
136 buf = malloc(Requestlen);
137 if(buf == nil)
138 sysfatal("allocating read buffer: %r");
139 r.rt = rt;
140
141 for(;;){
142 n = read(rt->fd, buf, Requestlen-1);
143 if(n < 0)
144 break;
145 buf[n] = 0;
146
147 /* skip verb, parse attributes, and remove tag */
148 p = strchr(buf, ' ');
149 if(p != nil)
150 p++;
151 else
152 p = buf;
153 r.a = _parseattr(p);
154
155 /* separate out the tag */
156 r.tag = nil;
157 for(l = &r.a; *l != nil; l = &(*l)->next)
158 if(strcmp((*l)->name, "tag") == 0){
159 r.tag = *l;
160 *l = r.tag->next;
161 r.tag->next = nil;
162 break;
163 }
164
165 /* if no tag, forget it */
166 if(r.tag == nil){
167 _freeattr(r.a);
168 continue;
169 }
170
171 send(rt->rc, &r);
172 }
173 }
174 #ifdef asdf
175 static void
readreq(void * a)176 readreq(void *a)
177 {
178 RequestType *rt = a;
179 char *buf, *p;
180 int n;
181 Request r;
182
183 rt->fd = -1;
184 rt->cs = nil;
185
186 buf = malloc(Requestlen);
187 if(buf == nil)
188 sysfatal("allocating read buffer: %r");
189 r.rt = rt;
190
191 for(;;){
192 strcpy(buf, "adfasdf=afdasdf asdfasdf=asdfasdf");
193 r.a = _parseattr(buf);
194 send(rt->rc, &r);
195 sleep(5000);
196 }
197 }
198 #endif asdf
199
200 /*
201 * open/close the keyboard, mouse and resize channels
202 */
203 static Channel *kbdc;
204 static Channel *mousec;
205 static Channel *resizec;
206 static Keyboardctl *kctl;
207 static Mousectl *mctl;
208
209 static void
openkmr(void)210 openkmr(void)
211 {
212 /* get channels for subsequent newcontrolset calls */
213 kctl = initkeyboard(nil);
214 if(kctl == nil)
215 sysfatal("can't initialize keyboard: %r");
216 kbdc = kctl->c;
217 mctl = initmouse(nil, screen);
218 if(mctl == nil)
219 sysfatal("can't initialize mouse: %r");
220 mousec = mctl->c;
221 resizec = mctl->resizec;
222 }
223 static void
closekmr(void)224 closekmr(void)
225 {
226 Mouse m;
227
228 while(nbrecv(kbdc, &m) > 0)
229 ;
230 closekeyboard(kctl);
231 while(nbrecv(mousec, &m) > 0)
232 ;
233 closemouse(mctl);
234 }
235
236
237 /*
238 * called when the window is resized
239 */
240 void
resizecontrolset(Controlset * cs)241 resizecontrolset(Controlset *cs)
242 {
243 RequestType *p;
244
245 for(p = rt; p->file != 0; p++){
246 if(p->cs == cs){
247 (*p->r)(cs);
248 break;
249 }
250 }
251 }
252
253 /*
254 * hide window when not in use
255 */
256 static void
unhide(void)257 unhide(void)
258 {
259 int wctl;
260
261 wctl = open("/dev/wctl", OWRITE);
262 if(wctl < 0)
263 return;
264 fprint(wctl, "unhide");
265 close(wctl);
266 }
267 static void
hide(void)268 hide(void)
269 {
270 int wctl;
271 int tries;
272
273 wctl = open("/dev/wctl", OWRITE);
274 if(wctl < 0)
275 return;
276 for(tries = 0; tries < 10; tries++){
277 if(fprint(wctl, "hide") >= 0)
278 break;
279 sleep(100);
280 }
281 close(wctl);
282 }
283
284 /*
285 * set up the controls for the confirmation window
286 */
287 static Channel*
setupconfirm(Request * r)288 setupconfirm(Request *r)
289 {
290 Controlset *cs;
291 Channel *c;
292 Attr *a;
293
294 /* create a new control set for the confirmation */
295 openkmr();
296 cs = newcontrolset(screen, kbdc, mousec, resizec);
297
298 createtext(cs, "msg");
299 chanprint(cs->ctl, "msg image paleyellow");
300 chanprint(cs->ctl, "msg border 1");
301 chanprint(cs->ctl, "msg add 'The following key is being used:'");
302 for(a = r->a; a != nil; a = a->next)
303 chanprint(cs->ctl, "msg add ' %s = %s'", a->name,
304 a->val);
305
306 namectlimage(display->white, "i_white");
307 namectlimage(display->black, "i_black");
308
309 b_remember = createbutton(cs, "b_remember");
310 chanprint(cs->ctl, "b_remember border 1");
311 chanprint(cs->ctl, "b_remember mask i_white");
312 chanprint(cs->ctl, "b_remember image i_white");
313 chanprint(cs->ctl, "b_remember light i_black");
314
315 createtext(cs, "t_remember");
316 chanprint(cs->ctl, "t_remember image white");
317 chanprint(cs->ctl, "t_remember bordercolor white");
318 chanprint(cs->ctl, "t_remember add 'Remember this answer for future confirmations'");
319
320 b_accept = createtextbutton(cs, "b_accept");
321 chanprint(cs->ctl, "b_accept border 1");
322 chanprint(cs->ctl, "b_accept align center");
323 chanprint(cs->ctl, "b_accept text Accept");
324 chanprint(cs->ctl, "b_accept image i_white");
325 chanprint(cs->ctl, "b_accept light i_black");
326
327 b_refuse = createtextbutton(cs, "b_refuse");
328 chanprint(cs->ctl, "b_refuse border 1");
329 chanprint(cs->ctl, "b_refuse align center");
330 chanprint(cs->ctl, "b_refuse text Refuse");
331 chanprint(cs->ctl, "b_refuse image i_white");
332 chanprint(cs->ctl, "b_refuse light i_black");
333
334 c = chancreate(sizeof(char*), 0);
335 controlwire(b_remember, "event", c);
336 controlwire(b_accept, "event", c);
337 controlwire(b_refuse, "event", c);
338
339 /* make the controls interactive */
340 activate(b_remember);
341 activate(b_accept);
342 activate(b_refuse);
343 r->rt->cs = cs;
344 unhide();
345 resizecontrolset(cs);
346
347 return c;
348 }
349
350 /*
351 * resize the controls for the confirmation window
352 */
353 static void
resizeconfirm(Controlset * cs)354 resizeconfirm(Controlset *cs)
355 {
356 Rectangle r, mr, nr, ntr, ar, rr;
357 int fontwidth;
358
359 fontwidth = font->height;
360
361 /* get usable window rectangle */
362 if(getwindow(display, Refnone) < 0)
363 ctlerror("resize failed: %r");
364 r = insetrect(screen->r, 10);
365
366 /* message box fills everything not needed for buttons */
367 mr = r;
368 mr.max.y = mr.min.y + font->height*((Dy(mr)-3*ButtonDim-font->height-4)/font->height);
369
370 /* remember button */
371 nr.min = Pt(mr.min.x, mr.max.y+ButtonDim);
372 nr.max = Pt(mr.max.x, r.max.y);
373 if(Dx(nr) > ButtonDim)
374 nr.max.x = nr.min.x+ButtonDim;
375 if(Dy(nr) > ButtonDim)
376 nr.max.y = nr.min.y+ButtonDim;
377 ntr.min = Pt(nr.max.x+ButtonDim, nr.min.y);
378 ntr.max = Pt(r.max.x, nr.min.y+font->height);
379
380 /* accept/refuse buttons */
381 ar.min = Pt(r.min.x+Dx(r)/2-ButtonDim-6*fontwidth, nr.max.y+ButtonDim);
382 ar.max = Pt(ar.min.x+6*fontwidth, ar.min.y+font->height+4);
383 rr.min = Pt(r.min.x+Dx(r)/2+ButtonDim, nr.max.y+ButtonDim);
384 rr.max = Pt(rr.min.x+6*fontwidth, rr.min.y+font->height+4);
385
386 /* make the controls visible */
387 chanprint(cs->ctl, "msg rect %R\nmsg show", mr);
388 chanprint(cs->ctl, "b_remember rect %R\nb_remember show", nr);
389 chanprint(cs->ctl, "t_remember rect %R\nt_remember show", ntr);
390 chanprint(cs->ctl, "b_accept rect %R\nb_accept show", ar);
391 chanprint(cs->ctl, "b_refuse rect %R\nb_refuse show", rr);
392 }
393
394 /*
395 * free the controls for the confirmation window
396 */
397 static void
teardownconfirm(Request * r)398 teardownconfirm(Request *r)
399 {
400 Controlset *cs;
401
402 cs = r->rt->cs;
403 r->rt->cs = nil;
404 hide();
405 closecontrolset(cs);
406 closekmr();
407 }
408
409 /*
410 * get user confirmation of a key
411 */
412 static void
confirm(Request * r)413 confirm(Request *r)
414 {
415 Channel *c;
416 char *s;
417 int n;
418 char *args[3];
419 int remember;
420 Attr *val;
421 Memory *m;
422
423 /* if it's something that the user wanted us not to ask again about */
424 m = searchmem(r->a);
425 if(m != nil){
426 fprint(r->rt->fd, "%A %A", r->tag, m->val);
427 return;
428 }
429
430 /* set up the controls */
431 c = setupconfirm(r);
432
433 /* wait for user to reply */
434 remember = 0;
435 for(;;){
436 s = recvp(c);
437 n = tokenize(s, args, nelem(args));
438 if(n==3 && strcmp(args[1], "value")==0){
439 if(strcmp(args[0], "b_remember:") == 0){
440 remember = atoi(args[2]);
441 }
442 if(strcmp(args[0], "b_accept:") == 0){
443 val = _mkattr(AttrNameval, "answer", "yes", nil);
444 free(s);
445 break;
446 }
447 if(strcmp(args[0], "b_refuse:") == 0){
448 val = _mkattr(AttrNameval, "answer", "no", nil);
449 free(s);
450 break;
451 }
452 }
453 free(s);
454 }
455 teardownconfirm(r);
456 fprint(r->rt->fd, "%A %A", r->tag, val);
457 if(remember)
458 addmem(_copyattr(r->a), val);
459 else
460 _freeattr(val);
461 }
462
463 /*
464 * confirmations that are remembered
465 */
466 static int
match(Attr * a,Attr * b)467 match(Attr *a, Attr *b)
468 {
469 Attr *x;
470
471 for(; a != nil; a = a->next){
472 x = _findattr(b, a->name);
473 if(x == nil || strcmp(a->val, x->val) != 0)
474 return 0;
475 }
476 return 1;
477 }
478 static Memory*
searchmem(Attr * a)479 searchmem(Attr *a)
480 {
481 Memory *m;
482
483 for(m = mem; m != nil; m = m->next){
484 if(match(a, m->a))
485 break;
486 }
487 return m;
488 }
489 static void
addmem(Attr * a,Attr * val)490 addmem(Attr *a, Attr *val)
491 {
492 Memory *m;
493
494 m = malloc(sizeof *m);
495 if(m == nil)
496 return;
497 m->a = a;
498 m->val = val;
499 m->next = mem;
500 mem = m;
501 }
502
503 /* controls for needkey */
504 Control *msg;
505 Control *b_done;
506 enum {
507 Pprivate= 1<<0,
508 Pneed= 1<<1,
509 };
510 typedef struct Entry Entry;
511 struct Entry {
512 Control *name;
513 Control *val;
514 Control *eq;
515 Attr *a;
516 };
517 static Entry *entry;
518 static int entries;
519
520 /*
521 * set up the controls for the confirmation window
522 */
523 static Channel*
setupneedkey(Request * r)524 setupneedkey(Request *r)
525 {
526 Controlset *cs;
527 Channel *c;
528 Attr *a;
529 Attr **l;
530 char cn[10];
531 int i;
532
533 /* create a new control set for the confirmation */
534 openkmr();
535 cs = newcontrolset(screen, kbdc, mousec, resizec);
536
537 /* count attributes and allocate entry controls */
538 entries = 0;
539 for(l = &r->a; *l; l = &(*l)->next)
540 entries++;
541 if(entries == 0){
542 closecontrolset(cs);
543 closekmr();
544 return nil;
545 }
546 *l = a = mallocz(sizeof *a, 1);
547 a->type = AttrQuery;
548 entries++;
549 l = &(*l)->next;
550 *l = a = mallocz(sizeof *a, 1);
551 a->type = AttrQuery;
552 entries++;
553 entry = malloc(entries*sizeof(Entry));
554 if(entry == nil){
555 closecontrolset(cs);
556 closekmr();
557 return nil;
558 }
559
560 namectlimage(display->white, "i_white");
561 namectlimage(display->black, "i_black");
562
563 /* create controls */
564 msg = createtext(cs, "msg");
565 chanprint(cs->ctl, "msg image white");
566 chanprint(cs->ctl, "msg bordercolor white");
567 chanprint(cs->ctl, "msg add 'You need the following key. Fill in the blanks'");
568 chanprint(cs->ctl, "msg add 'and click on the DONE button.'");
569
570 for(i = 0, a = r->a; a != nil; i++, a = a->next){
571 entry[i].a = a;
572 snprint(cn, sizeof cn, "name_%d", i);
573 if(entry[i].a->name == nil){
574 entry[i].name = createentry(cs, cn);
575 chanprint(cs->ctl, "%s image yellow", cn);
576 chanprint(cs->ctl, "%s border 1", cn);
577 } else {
578 entry[i].name = createtext(cs, cn);
579 chanprint(cs->ctl, "%s image white", cn);
580 chanprint(cs->ctl, "%s bordercolor white", cn);
581 chanprint(cs->ctl, "%s add '%s'", cn, a->name);
582 }
583
584 snprint(cn, sizeof cn, "val_%d", i);
585 if(a->type == AttrQuery){
586 entry[i].val = createentry(cs, cn);
587 chanprint(cs->ctl, "%s image yellow", cn);
588 chanprint(cs->ctl, "%s border 1", cn);
589 if(a->name != nil){
590 if(strcmp(a->name, "user") == 0)
591 chanprint(cs->ctl, "%s value %q", cn, getuser());
592 if(*a->name == '!')
593 chanprint(cs->ctl, "%s font invisible", cn);
594 }
595 } else {
596 entry[i].val = createtext(cs, cn);
597 chanprint(cs->ctl, "%s image white", cn);
598 chanprint(cs->ctl, "%s add %q", cn, a->val);
599 }
600
601 snprint(cn, sizeof cn, "eq_%d", i);
602 entry[i].eq = createtext(cs, cn);
603 chanprint(cs->ctl, "%s image white", cn);
604 chanprint(cs->ctl, "%s add ' = '", cn);
605 }
606
607 b_done = createtextbutton(cs, "b_done");
608 chanprint(cs->ctl, "b_done border 1");
609 chanprint(cs->ctl, "b_done align center");
610 chanprint(cs->ctl, "b_done text DONE");
611 chanprint(cs->ctl, "b_done image green");
612 chanprint(cs->ctl, "b_done light green");
613
614 /* wire controls for input */
615 c = chancreate(sizeof(char*), 0);
616 controlwire(b_done, "event", c);
617 for(i = 0; i < entries; i++)
618 if(entry[i].a->type == AttrQuery)
619 controlwire(entry[i].val, "event", c);
620
621 /* make the controls interactive */
622 activate(msg);
623 activate(b_done);
624 for(i = 0; i < entries; i++){
625 if(entry[i].a->type != AttrQuery)
626 continue;
627 if(entry[i].a->name == nil)
628 activate(entry[i].name);
629 activate(entry[i].val);
630 }
631
632 /* change the display */
633 r->rt->cs = cs;
634 unhide();
635 resizecontrolset(cs);
636
637 return c;
638 }
639
640 /*
641 * resize the controls for the confirmation window
642 */
643 static void
resizeneedkey(Controlset * cs)644 resizeneedkey(Controlset *cs)
645 {
646 Rectangle r, mr;
647 int mid, i, n, lasty;
648
649 /* get usable window rectangle */
650 if(getwindow(display, Refnone) < 0)
651 ctlerror("resize failed: %r");
652 r = insetrect(screen->r, 10);
653
654 /* find largest name */
655 mid = 0;
656 for(i = 0; i < entries; i++){
657 if(entry[i].a->name == nil)
658 continue;
659 n = strlen(entry[i].a->name);
660 if(n > mid)
661 mid = n;
662 }
663 mid = (mid+2) * font->height;
664
665 /* top line is the message */
666 mr = r;
667 mr.max.y = mr.min.y + 2*font->height + 2;
668 chanprint(cs->ctl, "msg rect %R\nmsg show", mr);
669
670 /* one line per attribute */
671 mr.min.x += 2*font->height;
672 lasty = mr.max.y;
673 for(i = 0; i < entries; i++){
674 r.min.x = mr.min.x;
675 r.min.y = lasty+2;
676 r.max.x = r.min.x + mid - 3*stringwidth(font, "=");
677 r.max.y = r.min.y + font->height;
678 chanprint(cs->ctl, "name_%d rect %R\nname_%d show", i, r, i);
679
680 r.min.x = r.max.x;
681 r.max.x = r.min.x + 3*stringwidth(font, "=");
682 chanprint(cs->ctl, "eq_%d rect %R\neq_%d show", i, r, i);
683
684 r.min.x = r.max.x;
685 r.max.x = mr.max.x;
686 if(Dx(r) > 32*font->height)
687 r.max.x = r.min.x + 32*font->height;
688 chanprint(cs->ctl, "val_%d rect %R\nval_%d show", i, r, i);
689 lasty = r.max.y;
690 }
691
692 /* done button */
693 mr.min.x -= 2*font->height;
694 r.min.x = mr.min.x + mid - 3*font->height;
695 r.min.y = lasty+10;
696 r.max.x = r.min.x + 6*font->height;
697 r.max.y = r.min.y + font->height + 2;
698 chanprint(cs->ctl, "b_done rect %R\nb_done show", r);
699 }
700
701 /*
702 * free the controls for the confirmation window
703 */
704 static void
teardownneedkey(Request * r)705 teardownneedkey(Request *r)
706 {
707 Controlset *cs;
708
709 cs = r->rt->cs;
710 r->rt->cs = nil;
711 hide();
712 closecontrolset(cs);
713 closekmr();
714
715 if(entry != nil)
716 free(entry);
717 entry = nil;
718 }
719
720 static void
needkey(Request * r)721 needkey(Request *r)
722 {
723 Channel *c;
724 char *nam, *val;
725 int i, n;
726 int fd;
727 char *args[3];
728
729 /* set up the controls */
730 c = setupneedkey(r);
731 if(c == nil)
732 goto out;
733
734 /* wait for user to reply */
735 for(;;){
736 val = recvp(c);
737 n = tokenize(val, args, nelem(args));
738 if(n==3 && strcmp(args[1], "value")==0){ /* user hit 'enter' */
739 free(val);
740 break;
741 }
742 free(val);
743 }
744
745 /* get entry values */
746 for(i = 0; i < entries; i++){
747 if(entry[i].a->type != AttrQuery)
748 continue;
749
750 chanprint(r->rt->cs->ctl, "val_%d data", i);
751 val = recvp(entry[i].val->data);
752 if(entry[i].a->name == nil){
753 chanprint(r->rt->cs->ctl, "name_%d data", i);
754 nam = recvp(entry[i].name->data);
755 if(nam == nil || *nam == 0){
756 free(val);
757 continue;
758 }
759 entry[i].a->val = estrdup(val);
760 free(val);
761 entry[i].a->name = estrdup(nam);
762 free(nam);
763 } else {
764 if(val != nil){
765 entry[i].a->val = estrdup(val);
766 free(val);
767 }
768 }
769 entry[i].a->type = AttrNameval;
770 }
771
772 /* enter the new key !!!!need to do something in case of error!!!! */
773 fd = open("/mnt/factotum/ctl", OWRITE);
774 fprint(fd, "key %A", r->a);
775 close(fd);
776
777 teardownneedkey(r);
778 out:
779 fprint(r->rt->fd, "%A", r->tag);
780 }
781