code sign all binaries when we can
[heimdal.git] / appl / xnlock / xnlock.c
blobb3692df2632842c78cab8a97134c44f912e391c8
1 /*
2 * xnlock -- Dan Heller, 1990
3 * "nlock" is a "new lockscreen" type program... something that prevents
4 * screen burnout by making most of it "black" while providing something
5 * of interest to be displayed in case anyone is watching.
6 * "xnlock" is the X11 version of the program.
7 * Original sunview version written by Dan Heller 1985 (not included here).
8 */
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 RCSID("$Id$");
12 #endif
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <signal.h>
18 #include <X11/StringDefs.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/keysym.h>
21 #include <X11/Shell.h>
22 #include <ctype.h>
23 #ifdef HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif
26 #ifdef HAVE_PWD_H
27 #include <pwd.h>
28 #endif
29 #ifdef HAVE_CRYPT_H
30 #undef des_encrypt
31 #define des_encrypt wingless_pigs_mostly_fail_to_fly
32 #include <crypt.h>
33 #undef des_encrypt
34 #endif
36 #ifdef KRB5
37 #include <krb5.h>
38 #include <kafs.h>
39 #endif
41 #include <roken.h>
42 #include <err.h>
44 static char login[16];
45 static char userprompt[128];
46 #ifdef KRB5
47 static krb5_context context;
48 static krb5_principal client;
49 #endif
51 #define font_height(font) (font->ascent + font->descent)
53 static char *SPACE_STRING = " ";
54 static char STRING[] = "****************";
56 #define STRING_LENGTH (sizeof(STRING))
57 #define MAX_PASSWD_LENGTH 256
58 /* (sizeof(STRING)) */
60 #define PROMPT "Password: "
61 #define FAIL_MSG "Sorry, try again"
62 #define LEFT 001
63 #define RIGHT 002
64 #define DOWN 004
65 #define UP 010
66 #define FRONT 020
67 #define X_INCR 3
68 #define Y_INCR 2
69 #define XNLOCK_CTRL 1
70 #define XNLOCK_NOCTRL 0
72 static XtAppContext app;
73 static Display *dpy;
74 static unsigned short Width, Height;
75 static Widget widget;
76 static GC gc;
77 static XtIntervalId timeout_id;
78 static char *words;
79 static int x, y;
80 static Pixel Black, White;
81 static XFontStruct *font;
82 static char root_cpass[128];
83 static char user_cpass[128];
84 static int time_left, prompt_x, prompt_y, time_x, time_y;
85 static unsigned long interval;
86 static Pixmap left0, left1, right0, right1, left_front,
87 right_front, front, down;
89 #define MAXLINES 40
91 #define IS_MOVING 1
92 #define GET_PASSWD 2
93 static int state; /* indicates states: walking or getting passwd */
95 static int ALLOW_LOGOUT = (60*10); /* Allow logout after nn seconds */
96 #define LOGOUT_PASSWD "enuHDmTo5Lq4g" /* when given password "LOGOUT" */
97 static time_t locked_at;
99 struct appres_t {
100 Pixel bg;
101 Pixel fg;
102 XFontStruct *font;
103 Boolean ignore_passwd;
104 Boolean do_reverse;
105 Boolean accept_root;
106 char *text, *text_prog, *file, *logoutPasswd;
107 Boolean no_screensaver;
108 Boolean destroytickets;
109 } appres;
111 static XtResource resources[] = {
112 { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
113 XtOffsetOf(struct appres_t, bg), XtRString, "black" },
115 { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
116 XtOffsetOf(struct appres_t, fg), XtRString, "white" },
118 { XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
119 XtOffsetOf(struct appres_t, font),
120 XtRString, "-*-new century schoolbook-*-*-*-18-*" },
122 { "ignorePasswd", "IgnorePasswd", XtRBoolean, sizeof(Boolean),
123 XtOffsetOf(struct appres_t,ignore_passwd),XtRImmediate,(XtPointer)False },
125 { "acceptRootPasswd", "AcceptRootPasswd", XtRBoolean, sizeof(Boolean),
126 XtOffsetOf(struct appres_t, accept_root), XtRImmediate, (XtPointer)True },
128 { "text", "Text", XtRString, sizeof(String),
129 XtOffsetOf(struct appres_t, text), XtRString, "I'm out running around." },
131 { "program", "Program", XtRString, sizeof(String),
132 XtOffsetOf(struct appres_t, text_prog), XtRImmediate, NULL },
134 { "file", "File", XtRString, sizeof(String),
135 XtOffsetOf(struct appres_t,file), XtRImmediate, NULL },
137 { "logoutPasswd", "logoutPasswd", XtRString, sizeof(String),
138 XtOffsetOf(struct appres_t, logoutPasswd), XtRString, LOGOUT_PASSWD },
140 { "noScreenSaver", "NoScreenSaver", XtRBoolean, sizeof(Boolean),
141 XtOffsetOf(struct appres_t,no_screensaver), XtRImmediate, (XtPointer)True },
143 { "destroyTickets", "DestroyTickets", XtRBoolean, sizeof(Boolean),
144 XtOffsetOf(struct appres_t,destroytickets), XtRImmediate, (XtPointer)True },
147 static XrmOptionDescRec options[] = {
148 { "-fg", ".foreground", XrmoptionSepArg, NULL },
149 { "-foreground", ".foreground", XrmoptionSepArg, NULL },
150 { "-fn", ".font", XrmoptionSepArg, NULL },
151 { "-font", ".font", XrmoptionSepArg, NULL },
152 { "-ip", ".ignorePasswd", XrmoptionNoArg, "True" },
153 { "-noip", ".ignorePasswd", XrmoptionNoArg, "False" },
154 { "-ar", ".acceptRootPasswd", XrmoptionNoArg, "True" },
155 { "-noar", ".acceptRootPasswd", XrmoptionNoArg, "False" },
156 { "-nonoscreensaver", ".noScreenSaver", XrmoptionNoArg, "False" },
157 { "-nodestroytickets", ".destroyTickets", XrmoptionNoArg, "False" },
160 static char*
161 get_words(void)
163 FILE *pp = NULL;
164 static char buf[512];
165 long n;
167 if (appres.text_prog) {
168 pp = popen(appres.text_prog, "r");
169 if (!pp) {
170 warn("popen %s", appres.text_prog);
171 return appres.text;
173 n = fread(buf, 1, sizeof(buf) - 1, pp);
174 buf[n] = 0;
175 pclose(pp);
176 return buf;
178 if (appres.file) {
179 pp = fopen(appres.file, "r");
180 if (!pp) {
181 warn("fopen %s", appres.file);
182 return appres.text;
184 n = fread(buf, 1, sizeof(buf) - 1, pp);
185 buf[n] = 0;
186 fclose(pp);
187 return buf;
190 return appres.text;
193 static void
194 usage(int exit_code)
196 fprintf(stderr, "usage: %s [options] [message]\n", getprogname());
197 fprintf(stderr, "-fg color foreground color\n");
198 fprintf(stderr, "-bg color background color\n");
199 fprintf(stderr, "-rv reverse foreground/background colors\n");
200 fprintf(stderr, "-nrv no reverse video\n");
201 fprintf(stderr, "-ip ignore passwd\n");
202 fprintf(stderr, "-nip don't ignore passwd\n");
203 fprintf(stderr, "-ar accept root's passwd to unlock\n");
204 fprintf(stderr, "-nar don't accept root's passwd\n");
205 fprintf(stderr, "-f [file] message is read from file or ~/.msgfile\n");
206 fprintf(stderr, "-prog program text is gotten from executing `program'\n");
207 fprintf(stderr, "-nodestroytickets keep kerberos tickets\n");
208 fprintf(stderr, "--version\n");
209 fprintf(stderr, "--help\n");
210 exit(exit_code);
213 static void
214 init_words (int argc, char **argv)
216 int i = 0;
218 while(argv[i]) {
219 if(strcmp(argv[i], "-p") == 0
220 || strcmp(argv[i], "-prog") == 0) {
221 i++;
222 if(argv[i]) {
223 appres.text_prog = argv[i];
224 i++;
225 } else {
226 warnx ("-p requires an argument");
227 usage(1);
229 } else if(strcmp(argv[i], "-f") == 0) {
230 i++;
231 if(argv[i]) {
232 appres.file = argv[i];
233 i++;
234 } else {
235 int ret;
236 ret = asprintf (&appres.file,
237 "%s/.msgfile", getenv("HOME"));
238 if (ret == -1)
239 errx (1, "cannot allocate memory for message");
241 } else if(strcmp(argv[i], "--version") == 0) {
242 print_version(NULL);
243 exit(0);
244 } else if(strcmp(argv[i], "--help") == 0) {
245 usage(0);
246 } else {
247 int j;
248 int len = 1;
249 for(j = i; argv[j]; j++)
250 len += strlen(argv[j]) + 1;
251 appres.text = malloc(len);
252 if (appres.text == NULL)
253 errx (1, "cannot allocate memory for message");
254 appres.text[0] = 0;
255 for(; i < j; i++){
256 strlcat(appres.text, argv[i], len);
257 strlcat(appres.text, " ", len);
263 static void
264 ScreenSaver(int save)
266 static int timeout, ival, prefer_blank, allow_exp;
267 if(!appres.no_screensaver){
268 if (save) {
269 XGetScreenSaver(dpy, &timeout, &ival,
270 &prefer_blank, &allow_exp);
271 XSetScreenSaver(dpy, 0, ival, prefer_blank, allow_exp);
272 } else
273 /* restore state */
274 XSetScreenSaver(dpy, timeout, ival, prefer_blank, allow_exp);
278 /* Forward decls necessary */
279 static void talk(int force_erase);
280 static unsigned long look(void);
282 static int
283 zrefresh(void)
285 switch (fork()) {
286 case -1:
287 warn ("zrefresh: fork");
288 return -1;
289 case 0:
290 /* Child */
291 execlp("zrefresh", "zrefresh", NULL);
292 execl(BINDIR "/zrefresh", "zrefresh", NULL);
293 return -1;
294 default:
295 /* Parent */
296 break;
298 return 0;
301 static void
302 leave(void)
304 XUngrabPointer(dpy, CurrentTime);
305 XUngrabKeyboard(dpy, CurrentTime);
306 ScreenSaver(0);
307 XCloseDisplay(dpy);
308 zrefresh();
309 exit(0);
312 static void
313 walk(int dir)
315 int incr = 0;
316 static int lastdir;
317 static int up = 1;
318 static Pixmap frame;
320 XSetForeground(dpy, gc, White);
321 XSetBackground(dpy, gc, Black);
322 if (dir & (LEFT|RIGHT)) { /* left/right movement (mabye up/down too) */
323 up = -up; /* bouncing effect (even if hit a wall) */
324 if (dir & LEFT) {
325 incr = X_INCR;
326 frame = (up < 0) ? left0 : left1;
327 } else {
328 incr = -X_INCR;
329 frame = (up < 0) ? right0 : right1;
331 if ((lastdir == FRONT || lastdir == DOWN) && dir & UP) {
332 /* workaround silly bug that leaves screen dust when
333 * guy is facing forward or down and moves up-left/right.
335 XCopyPlane(dpy, frame, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L);
336 XFlush(dpy);
338 /* note that maybe neither UP nor DOWN is set! */
339 if (dir & UP && y > Y_INCR)
340 y -= Y_INCR;
341 else if (dir & DOWN && y < (int)Height - 64)
342 y += Y_INCR;
344 /* Explicit up/down movement only (no left/right) */
345 else if (dir == UP)
346 XCopyPlane(dpy, front, XtWindow(widget), gc,
347 0,0, 64,64, x, y -= Y_INCR, 1L);
348 else if (dir == DOWN)
349 XCopyPlane(dpy, down, XtWindow(widget), gc,
350 0,0, 64,64, x, y += Y_INCR, 1L);
351 else if (dir == FRONT && frame != front) {
352 if (up > 0)
353 up = -up;
354 if (lastdir & LEFT)
355 frame = left_front;
356 else if (lastdir & RIGHT)
357 frame = right_front;
358 else
359 frame = front;
360 XCopyPlane(dpy, frame, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L);
362 if (dir & LEFT)
363 while(--incr >= 0) {
364 XCopyPlane(dpy, frame, XtWindow(widget), gc,
365 0,0, 64,64, --x, y+up, 1L);
366 XFlush(dpy);
368 else if (dir & RIGHT)
369 while(++incr <= 0) {
370 XCopyPlane(dpy, frame, XtWindow(widget), gc,
371 0,0, 64,64, ++x, y+up, 1L);
372 XFlush(dpy);
374 lastdir = dir;
377 static long
378 my_random (void)
380 #ifdef HAVE_RANDOM
381 return random();
382 #else
383 return rand();
384 #endif
387 static int
388 think(void)
390 if (my_random() & 1)
391 walk(FRONT);
392 if (my_random() & 1) {
393 words = get_words();
394 return 1;
396 return 0;
399 static void
400 move(XtPointer _p, XtIntervalId *_id)
402 static int dir;
403 static unsigned int length;
405 if (!length) {
406 int tries = 0;
407 dir = 0;
408 if ((my_random() & 1) && think()) {
409 talk(0); /* sets timeout to itself */
410 return;
412 if (!(my_random() % 3) && (interval = look())) {
413 timeout_id = XtAppAddTimeOut(app, interval, move, NULL);
414 return;
416 interval = 20 + my_random() % 100;
417 do {
418 if (!tries)
419 length = Width/100 + my_random() % 90, tries = 8;
420 else
421 tries--;
422 switch (my_random() % 8) {
423 case 0:
424 if (x - X_INCR*length >= 5)
425 dir = LEFT;
426 case 1:
427 if (x + X_INCR*length <= (int)Width - 70)
428 dir = RIGHT;
429 case 2:
430 if (y - (Y_INCR*length) >= 5)
431 dir = UP, interval = 40;
432 case 3:
433 if (y + Y_INCR*length <= (int)Height - 70)
434 dir = DOWN, interval = 20;
435 case 4:
436 if (x - X_INCR*length >= 5 && y - (Y_INCR*length) >= 5)
437 dir = (LEFT|UP);
438 case 5:
439 if (x + X_INCR * length <= (int)Width - 70 &&
440 y-Y_INCR * length >= 5)
441 dir = (RIGHT|UP);
442 case 6:
443 if (x - X_INCR * length >= 5 &&
444 y + Y_INCR * length <= (int)Height - 70)
445 dir = (LEFT|DOWN);
446 case 7:
447 if (x + X_INCR*length <= (int)Width - 70 &&
448 y + Y_INCR*length <= (int)Height - 70)
449 dir = (RIGHT|DOWN);
451 } while (!dir);
453 walk(dir);
454 --length;
455 timeout_id = XtAppAddTimeOut(app, interval, move, NULL);
458 static void
459 post_prompt_box(Window window)
461 int width = (Width / 3);
462 int height = font_height(font) * 6;
463 int box_x, box_y;
465 /* make sure the entire nose icon fits in the box */
466 if (height < 100)
467 height = 100;
469 if(width < 105 + font->max_bounds.width*STRING_LENGTH)
470 width = 105 + font->max_bounds.width*STRING_LENGTH;
471 box_x = (Width - width) / 2;
472 time_x = prompt_x = box_x + 105;
474 time_y = prompt_y = Height / 2;
475 box_y = prompt_y - 3 * font_height(font);
477 /* erase current guy -- text message may still exist */
478 XSetForeground(dpy, gc, Black);
479 XFillRectangle(dpy, window, gc, x, y, 64, 64);
480 talk(1); /* forcefully erase message if one is being displayed */
481 /* Clear area in middle of screen for prompt box */
482 XSetForeground(dpy, gc, White);
483 XFillRectangle(dpy, window, gc, box_x, box_y, width, height);
485 /* make a box that's 5 pixels thick. Then add a thin box inside it */
486 XSetForeground(dpy, gc, Black);
487 XSetLineAttributes(dpy, gc, 5, 0, 0, 0);
488 XDrawRectangle(dpy, window, gc, box_x+5, box_y+5, width-10, height-10);
489 XSetLineAttributes(dpy, gc, 0, 0, 0, 0);
490 XDrawRectangle(dpy, window, gc, box_x+12, box_y+12, width-23, height-23);
492 XDrawString(dpy, window, gc,
493 prompt_x, prompt_y-font_height(font),
494 userprompt, strlen(userprompt));
495 XDrawString(dpy, window, gc, prompt_x, prompt_y, PROMPT, strlen(PROMPT));
496 /* set background for copyplane and DrawImageString; need reverse video */
497 XSetBackground(dpy, gc, White);
498 XCopyPlane(dpy, right0, window, gc, 0,0, 64,64,
499 box_x + 20, box_y + (height - 64)/2, 1L);
500 prompt_x += XTextWidth(font, PROMPT, strlen(PROMPT));
501 time_y += 2*font_height(font);
504 static void
505 RaiseWindow(Widget w, XEvent *ev, String *s, Cardinal *n)
507 Widget new;
508 if(!XtIsRealized(w))
509 return;
510 new = XtParent(w);
511 XRaiseWindow(dpy, XtWindow(new));
515 static void
516 ClearWindow(Widget w, XEvent *_event, String *_s, Cardinal *_n)
518 XExposeEvent *event = (XExposeEvent *)_event;
519 if (!XtIsRealized(w))
520 return;
521 XClearArea(dpy, XtWindow(w), event->x, event->y,
522 event->width, event->height, False);
523 if (state == GET_PASSWD)
524 post_prompt_box(XtWindow(w));
525 if (timeout_id == 0 && event->count == 0) {
526 timeout_id = XtAppAddTimeOut(app, 1000L, move, NULL);
527 /* first grab the input focus */
528 XSetInputFocus(dpy, XtWindow(w), RevertToPointerRoot, CurrentTime);
529 /* now grab the pointer and keyboard and contrain to this window */
530 XGrabPointer(dpy, XtWindow(w), TRUE, 0, GrabModeAsync,
531 GrabModeAsync, XtWindow(w), None, CurrentTime);
535 static void
536 countdown(XtPointer _t, XtIntervalId *_d)
538 int *timeout = (int *)_t;
539 char buf[128];
540 time_t seconds;
542 if (--(*timeout) < 0) {
543 XExposeEvent event;
544 XtRemoveTimeOut(timeout_id);
545 state = IS_MOVING;
546 event.x = event.y = 0;
547 event.width = Width, event.height = Height;
548 ClearWindow(widget, (XEvent *)&event, 0, 0);
549 timeout_id = XtAppAddTimeOut(app, 200L, move, NULL);
550 return;
552 seconds = time(0) - locked_at;
553 if (seconds >= 3600)
554 snprintf(buf, sizeof(buf),
555 "Locked for %d:%02d:%02d ",
556 (int)seconds/3600, (int)seconds/60%60, (int)seconds%60);
557 else
558 snprintf(buf, sizeof(buf),
559 "Locked for %2d:%02d ",
560 (int)seconds/60, (int)seconds%60);
562 XDrawImageString(dpy, XtWindow(widget), gc,
563 time_x, time_y, buf, strlen(buf));
564 XtAppAddTimeOut(app, 1000L, countdown, timeout);
565 return;
568 #ifdef KRB5
569 static int
570 verify_krb5(const char *password)
572 krb5_error_code ret;
573 krb5_ccache id;
575 krb5_cc_default(context, &id);
576 ret = krb5_verify_user(context,
577 client,
579 password,
581 NULL);
582 if (ret == 0){
583 if (k_hasafs())
584 krb5_afslog(context, id, NULL, NULL);
585 return 0;
587 if (ret != KRB5KRB_AP_ERR_MODIFIED)
588 krb5_warn(context, ret, "verify_krb5");
590 return -1;
592 #endif
594 static int
595 verify(char *password)
598 * First try with root password, if allowed.
600 if ( appres.accept_root
601 && strcmp(crypt(password, root_cpass), root_cpass) == 0)
602 return 0;
605 * Password that log out user
607 if (getuid() != 0 &&
608 geteuid() != 0 &&
609 (time(0) - locked_at) > ALLOW_LOGOUT &&
610 strcmp(crypt(password, appres.logoutPasswd), appres.logoutPasswd) == 0)
612 signal(SIGHUP, SIG_IGN);
613 kill(-1, SIGHUP);
614 sleep(5);
615 /* If the X-server shut down then so will we, else
616 * continue */
617 signal(SIGHUP, SIG_DFL);
621 * Try copy of users password.
623 if (strcmp(crypt(password, user_cpass), user_cpass) == 0)
624 return 0;
627 * Try to verify as user in case password change.
629 if (unix_verify_user(login, password) == 0)
630 return 0;
632 #ifdef KRB5
634 * Try to verify as user with kerberos 5.
636 if(verify_krb5(password) == 0)
637 return 0;
638 #endif
640 return -1;
644 static void
645 GetPasswd(Widget w, XEvent *_event, String *_s, Cardinal *_n)
647 XKeyEvent *event = (XKeyEvent *)_event;
648 static char passwd[MAX_PASSWD_LENGTH];
649 static unsigned int cnt;
650 static int is_ctrl = XNLOCK_NOCTRL;
651 char c;
652 KeySym keysym;
653 int echolen;
654 int old_state = state;
656 if (event->type == ButtonPress) {
657 x = event->x, y = event->y;
658 return;
660 if (state == IS_MOVING) {
661 /* guy is running around--change to post prompt box. */
662 XtRemoveTimeOut(timeout_id);
663 state = GET_PASSWD;
664 if (appres.ignore_passwd || !strlen(user_cpass))
665 leave();
666 post_prompt_box(XtWindow(w));
667 cnt = 0;
668 time_left = 30;
669 countdown((XtPointer)&time_left, 0);
671 if (event->type == KeyRelease) {
672 keysym = XLookupKeysym(event, 0);
673 if (keysym == XK_Control_L || keysym == XK_Control_R) {
674 is_ctrl = XNLOCK_NOCTRL;
677 if (event->type != KeyPress)
678 return;
680 time_left = 30;
682 keysym = XLookupKeysym(event, 0);
683 if (keysym == XK_Control_L || keysym == XK_Control_R) {
684 is_ctrl = XNLOCK_CTRL;
685 return;
687 if (!XLookupString(event, &c, 1, &keysym, 0))
688 return;
689 if (keysym == XK_Return || keysym == XK_Linefeed) {
690 passwd[cnt] = 0;
691 if(old_state == IS_MOVING)
692 return;
693 XtRemoveTimeOut(timeout_id);
695 if(verify(passwd) == 0)
696 leave();
698 cnt = 0;
700 XDrawImageString(dpy, XtWindow(widget), gc,
701 time_x, time_y, FAIL_MSG, strlen(FAIL_MSG));
702 time_left = 0;
703 timeout_id = XtAppAddTimeOut(app, 2000L, countdown, &time_left);
704 return;
706 if (keysym == XK_BackSpace || keysym == XK_Delete || keysym == XK_Left) {
707 if (cnt)
708 passwd[cnt--] = ' ';
709 } else if (keysym == XK_u && is_ctrl == XNLOCK_CTRL) {
710 while (cnt) {
711 passwd[cnt--] = ' ';
712 echolen = min(cnt, STRING_LENGTH);
713 XDrawImageString(dpy, XtWindow(w), gc,
714 prompt_x, prompt_y, STRING, echolen);
715 XDrawImageString(dpy, XtWindow(w), gc,
716 prompt_x + XTextWidth(font, STRING, echolen),
717 prompt_y, SPACE_STRING, STRING_LENGTH - echolen + 1);
719 } else if (isprint((unsigned char)c)) {
720 if ((cnt + 1) >= MAX_PASSWD_LENGTH)
721 XBell(dpy, 50);
722 else
723 passwd[cnt++] = c;
724 } else
725 return;
726 echolen = min(cnt, STRING_LENGTH);
727 XDrawImageString(dpy, XtWindow(w), gc,
728 prompt_x, prompt_y, STRING, echolen);
729 XDrawImageString(dpy, XtWindow(w), gc,
730 prompt_x + XTextWidth(font, STRING, echolen),
731 prompt_y, SPACE_STRING, STRING_LENGTH - echolen +1);
734 #include "nose.0.left"
735 #include "nose.1.left"
736 #include "nose.0.right"
737 #include "nose.1.right"
738 #include "nose.left.front"
739 #include "nose.right.front"
740 #include "nose.front"
741 #include "nose.down"
743 static void
744 init_images(void)
746 static Pixmap *images[] = {
747 &left0, &left1, &right0, &right1,
748 &left_front, &right_front, &front, &down
750 static unsigned char *bits[] = {
751 nose_0_left_bits, nose_1_left_bits, nose_0_right_bits,
752 nose_1_right_bits, nose_left_front_bits, nose_right_front_bits,
753 nose_front_bits, nose_down_bits
755 int i;
757 for (i = 0; i < XtNumber(images); i++)
758 if (!(*images[i] =
759 XCreatePixmapFromBitmapData(dpy, DefaultRootWindow(dpy),
760 (char*)(bits[i]), 64, 64, 1, 0, 1)))
761 XtError("Can't load nose images");
764 static void
765 talk(int force_erase)
767 unsigned int width = 0, height, Z, total = 0;
768 static unsigned int X, Y;
769 static int talking;
770 static struct { int x, y, width, height; } s_rect;
771 char *p, *p2;
772 char buf[BUFSIZ], args[MAXLINES][256];
774 /* clear what we've written */
775 if (talking || force_erase) {
776 if (!talking)
777 return;
778 if (talking == 2) {
779 XSetForeground(dpy, gc, Black);
780 XDrawString(dpy, XtWindow(widget), gc, X, Y, words, strlen(words));
781 } else if (talking == 1) {
782 XSetForeground(dpy, gc, Black);
783 XFillRectangle(dpy, XtWindow(widget), gc, s_rect.x-5, s_rect.y-5,
784 s_rect.width+10, s_rect.height+10);
786 talking = 0;
787 if (!force_erase)
788 timeout_id = XtAppAddTimeOut(app, 40L,
789 (XtTimerCallbackProc)move,
790 NULL);
791 return;
793 XSetForeground(dpy, gc, White);
794 talking = 1;
795 walk(FRONT);
796 strlcpy (buf, words, sizeof(buf));
797 p = buf;
799 /* possibly avoid a lot of work here
800 * if no CR or only one, then just print the line
802 if (!(p2 = strchr(p, '\n')) || !p2[1]) {
803 int w;
805 if (p2)
806 *p2 = 0;
807 w = XTextWidth(font, words, strlen(words));
808 X = x + 32 - w/2;
809 Y = y - 5 - font_height(font);
810 /* give us a nice 5 pixel margin */
811 if (X < 5)
812 X = 5;
813 else if (X + w + 15 > (int)Width + 5)
814 X = Width - w - 5;
815 if (Y < 5)
816 Y = y + 64 + 5 + font_height(font);
817 XDrawString(dpy, XtWindow(widget), gc, X, Y, words, strlen(words));
818 timeout_id = XtAppAddTimeOut(app, 5000L, (XtTimerCallbackProc)talk,
819 NULL);
820 talking++;
821 return;
824 /* p2 now points to the first '\n' */
825 for (height = 0; p[0]; height++) {
826 int w;
827 *p2 = 0;
828 if ((w = XTextWidth(font, p, p2 - p)) > width)
829 width = w;
830 total += p2 - p; /* total chars; count to determine reading time */
831 strlcpy(args[height], p, sizeof(args[height]));
832 if (height == MAXLINES - 1) {
833 puts("Message too long!");
834 break;
836 p = p2+1;
837 if (!(p2 = strchr(p, '\n')))
838 break;
840 height++;
842 /* Figure out the height and width in pixels (height, width) extend
843 * the new box by 15 pixels on the sides (30 total) top and bottom.
845 s_rect.width = width + 30;
846 s_rect.height = height * font_height(font) + 30;
847 if (x - s_rect.width - 10 < 5)
848 s_rect.x = 5;
849 else
850 if ((s_rect.x = x+32-(s_rect.width+15)/2)
851 + s_rect.width+15 > (int)Width-5)
852 s_rect.x = Width - 15 - s_rect.width;
853 if (y - s_rect.height - 10 < 5)
854 s_rect.y = y + 64 + 5;
855 else
856 s_rect.y = y - 5 - s_rect.height;
858 XSetForeground(dpy, gc, White);
859 XFillRectangle(dpy, XtWindow(widget), gc,
860 s_rect.x-5, s_rect.y-5, s_rect.width+10, s_rect.height+10);
862 /* make a box that's 5 pixels thick. Then add a thin box inside it */
863 XSetForeground(dpy, gc, Black);
864 XSetLineAttributes(dpy, gc, 5, 0, 0, 0);
865 XDrawRectangle(dpy, XtWindow(widget), gc,
866 s_rect.x, s_rect.y, s_rect.width-1, s_rect.height-1);
867 XSetLineAttributes(dpy, gc, 0, 0, 0, 0);
868 XDrawRectangle(dpy, XtWindow(widget), gc,
869 s_rect.x + 7, s_rect.y + 7, s_rect.width - 15,
870 s_rect.height - 15);
872 X = 15;
873 Y = 15 + font_height(font);
875 /* now print each string in reverse order (start at bottom of box) */
876 for (Z = 0; Z < height; Z++) {
877 XDrawString(dpy, XtWindow(widget), gc, s_rect.x+X, s_rect.y+Y,
878 args[Z], strlen(args[Z]));
879 Y += font_height(font);
881 timeout_id = XtAppAddTimeOut(app, (total/15) * 1000,
882 (XtTimerCallbackProc)talk, NULL);
885 static unsigned long
886 look(void)
888 XSetForeground(dpy, gc, White);
889 XSetBackground(dpy, gc, Black);
890 if (my_random() % 3) {
891 XCopyPlane(dpy, (my_random() & 1)? down : front, XtWindow(widget), gc,
892 0, 0, 64,64, x, y, 1L);
893 return 1000L;
895 if (!(my_random() % 5))
896 return 0;
897 if (my_random() % 3) {
898 XCopyPlane(dpy, (my_random() & 1)? left_front : right_front,
899 XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L);
900 return 1000L;
902 if (!(my_random() % 5))
903 return 0;
904 XCopyPlane(dpy, (my_random() & 1)? left0 : right0, XtWindow(widget), gc,
905 0, 0, 64,64, x, y, 1L);
906 return 1000L;
910 main (int argc, char **argv)
912 int i;
913 Widget override;
914 XGCValues gcvalues;
916 setprogname (argv[0]);
919 * Must be setuid root to read /etc/shadow, copy encrypted
920 * passwords here and then switch to sane uid.
923 struct passwd *pw;
924 uid_t uid = getuid();
925 if (!(pw = k_getpwuid(0)))
926 errx (1, "can't get root's passwd!");
927 strlcpy(root_cpass, pw->pw_passwd, sizeof(root_cpass));
929 if (!(pw = k_getpwuid(uid)))
930 errx (1, "Can't get your password entry!");
931 strlcpy(user_cpass, pw->pw_passwd, sizeof(user_cpass));
932 setuid(uid);
933 if (uid != 0 && setuid(0) != -1) {
934 fprintf(stderr, "Failed to drop privileges!\n");
935 exit(1);
937 /* Now we're no longer running setuid root. */
938 strlcpy(login, pw->pw_name, sizeof(login));
941 #if defined(HAVE_SRANDOMDEV)
942 srandomdev();
943 #elif defined(HAVE_RANDOM)
944 srandom(time(NULL));
945 #else
946 srand (time(NULL));
947 #endif
948 for (i = 0; i < STRING_LENGTH; i++)
949 STRING[i] = ((unsigned long)my_random() % ('~' - ' ')) + ' ';
951 locked_at = time(0);
953 snprintf(userprompt, sizeof(userprompt), "User: %s", login);
954 #ifdef KRB5
956 krb5_error_code ret;
957 char *str;
959 ret = krb5_init_context(&context);
960 if (ret)
961 errx (1, "krb5_init_context failed: %d", ret);
963 ret = krb5_get_default_principal(context, &client);
964 if (ret)
965 krb5_err(context, 1, ret, "getting default principal failed");
967 ret = krb5_unparse_name(context, client, &str);
968 if (ret)
969 krb5_err(context, 1, ret, "krb5_unparse_name");
970 snprintf(userprompt, sizeof(userprompt), "User: %s", str);
971 free(str);
973 #endif
975 override = XtVaAppInitialize(&app, "XNlock", options, XtNumber(options),
976 &argc, argv, NULL,
977 XtNoverrideRedirect, True,
978 NULL);
980 XtVaGetApplicationResources(override,(XtPointer)&appres,
981 resources,XtNumber(resources),
982 NULL);
983 /* the background is black and the little guy is white */
984 Black = appres.bg;
985 White = appres.fg;
987 if (appres.destroytickets) {
988 #ifdef KRB5
989 /*XXX add krb4 code here */
990 #endif
993 dpy = XtDisplay(override);
995 if (dpy == 0)
996 errx (1, "Error: Can't open display");
998 Width = DisplayWidth(dpy, DefaultScreen(dpy)) + 2;
999 Height = DisplayHeight(dpy, DefaultScreen(dpy)) + 2;
1001 for(i = 0; i < ScreenCount(dpy); i++){
1002 Widget shell, core;
1004 struct xxx{
1005 Pixel bg;
1006 }res;
1008 XtResource Res[] = {
1009 { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
1010 XtOffsetOf(struct xxx, bg), XtRString, "black" }
1013 if(i == DefaultScreen(dpy))
1014 continue;
1016 shell = XtVaAppCreateShell(NULL,NULL, applicationShellWidgetClass, dpy,
1017 XtNscreen, ScreenOfDisplay(dpy, i),
1018 XtNoverrideRedirect, True,
1019 XtNx, -1,
1020 XtNy, -1,
1021 NULL);
1023 XtVaGetApplicationResources(shell, (XtPointer)&res,
1024 Res, XtNumber(Res),
1025 NULL);
1027 core = XtVaCreateManagedWidget("_foo", widgetClass, shell,
1028 XtNwidth, DisplayWidth(dpy, i),
1029 XtNheight, DisplayHeight(dpy, i),
1030 XtNbackground, res.bg,
1031 NULL);
1032 XtRealizeWidget(shell);
1035 widget = XtVaCreateManagedWidget("_foo", widgetClass, override,
1036 XtNwidth, Width,
1037 XtNheight, Height,
1038 XtNbackground, Black,
1039 NULL);
1041 init_words(--argc, ++argv);
1042 init_images();
1044 gcvalues.foreground = Black;
1045 gcvalues.background = White;
1048 font = appres.font;
1049 gcvalues.font = font->fid;
1050 gcvalues.graphics_exposures = False;
1051 gc = XCreateGC(dpy, DefaultRootWindow(dpy),
1052 GCForeground | GCBackground | GCGraphicsExposures | GCFont,
1053 &gcvalues);
1055 x = Width / 2;
1056 y = Height / 2;
1057 srand (time(0));
1058 state = IS_MOVING;
1061 static XtActionsRec actions[] = {
1062 { "ClearWindow", ClearWindow },
1063 { "GetPasswd", GetPasswd },
1064 { "RaiseWindow", RaiseWindow },
1066 XtAppAddActions(app, actions, XtNumber(actions));
1067 XtOverrideTranslations(widget,
1068 XtParseTranslationTable(
1069 "<Expose>: ClearWindow() \n"
1070 "<BtnDown>: GetPasswd() \n"
1071 "<Visible>: RaiseWindow() \n"
1072 "<KeyRelease>: GetPasswd() \n"
1073 "<KeyPress>: GetPasswd()"));
1076 XtRealizeWidget(override);
1077 if((i = XGrabPointer(dpy, XtWindow(widget), True, 0, GrabModeAsync,
1078 GrabModeAsync, XtWindow(widget),
1079 None, CurrentTime)) != 0)
1080 errx(1, "Failed to grab pointer (%d)", i);
1082 if((i = XGrabKeyboard(dpy, XtWindow(widget), True, GrabModeAsync,
1083 GrabModeAsync, CurrentTime)) != 0)
1084 errx(1, "Failed to grab keyboard (%d)", i);
1085 ScreenSaver(1);
1086 XtAppMainLoop(app);
1087 exit(0);