1 /* xscreensaver, Copyright (c) 1993-1995 Jamie Zawinski <jwz@netscape.com>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* #### If anyone ever finishes the Athena locking code, remove this.
13 But until then, locking requires Motif.
15 #if defined(NO_MOTIF) && !defined(NO_LOCKING)
35 #include <X11/Intrinsic.h>
37 #include "xscreensaver.h"
39 extern char *screensaver_version;
40 extern char *progname;
41 extern XtAppContext app;
42 extern Bool verbose_p;
45 /* SCO has some kind of goofy, nonstandard security crap. This stuff was
46 donated by one of their victims, I mean users, Didier Poirot <dp@chorus.fr>.
48 # include <sys/security.h>
49 # include <sys/audit.h>
59 # include <X11/StringDefs.h>
60 # include <X11/Xaw/Text.h>
61 # include <X11/Xaw/Label.h>
67 # include <Xm/TextF.h>
73 extern Widget passwd_dialog;
74 extern Widget passwd_form;
75 extern Widget roger_label;
76 extern Widget passwd_label1;
77 extern Widget passwd_label3;
78 extern Widget passwd_text;
79 extern Widget passwd_done;
80 extern Widget passwd_cancel;
82 extern create_passwd_dialog P((Widget));
83 extern void ungrab_keyboard_and_mouse P((void));
85 static enum { pw_read, pw_ok, pw_fail, pw_cancel, pw_time } passwd_state;
86 static char typed_passwd [1024];
88 static char root_passwd [255];
89 static char user_passwd [255];
92 # define PWTYPE struct spwd *
93 # define PWSLOT sp_pwdp
94 # define GETPW getspnam
96 # define PWTYPE struct passwd *
97 # define PWSLOT pw_passwd
98 # define GETPW getpwnam
102 # define PRPWTYPE struct pr_passwd *
103 # define GETPRPW getprpwnam
111 PWTYPE p = GETPW ("root");
114 PRPWTYPE prpwd = GETPRPW ("root");
115 if (prpwd && *prpwd->ufld.fd_encrypt)
116 strcpy (root_passwd, prpwd->ufld.fd_encrypt);
118 if (p && p->PWSLOT && p->PWSLOT[0] != '*')
119 strcpy (root_passwd, p->PWSLOT);
123 fprintf (stderr, "%s: couldn't get root's password\n", progname);
124 strcpy (root_passwd, "*");
127 /* It has been reported that getlogin() returns the wrong user id on some
128 very old SGI systems... */
129 u = (char *) getlogin ();
139 /* getlogin() fails if not attached to a terminal;
140 in that case, use getpwuid(). */
141 struct passwd *p2 = getpwuid (getuid ());
151 if (prpwd && *prpwd->ufld.fd_encrypt)
152 strcpy (user_passwd, prpwd->ufld.fd_encrypt);
154 if (p && p->PWSLOT &&
155 /* p->PWSLOT[0] != '*' */ /* sensible */
156 (strlen (p->PWSLOT) > 4) /* solaris */
158 strcpy (user_passwd, p->PWSLOT);
162 fprintf (stderr, "%s: couldn't get password of \"%s\"\n", progname, u);
163 strcpy (user_passwd, "*");
171 #if defined(NO_MOTIF) || (XmVersion >= 1002)
172 /* The `destroy' bug apears to be fixed as of Motif 1.2.1, but
173 the `verify-callback' bug is still present. */
174 # define DESTROY_WORKS
178 passwd_cancel_cb (button, client_data, call_data)
180 XtPointer client_data, call_data;
182 passwd_state = pw_cancel;
186 passwd_done_cb (button, client_data, call_data)
188 XtPointer client_data, call_data;
190 if (passwd_state != pw_read) return; /* already done */
191 if (!strcmp ((char *) crypt (typed_passwd, user_passwd), user_passwd))
192 passwd_state = pw_ok;
193 /* do not allow root to have empty passwd */
194 else if (typed_passwd [0] &&
195 !strcmp ((char *) crypt (typed_passwd, root_passwd), root_passwd))
196 passwd_state = pw_ok;
198 passwd_state = pw_fail;
201 #if !defined(NO_MOTIF) && defined(VERIFY_CALLBACK_WORKS)
203 /* #### It looks to me like adding any modifyVerify callback causes
204 #### Motif 1.1.4 to free the the TextF_Value() twice. I can't see
205 #### the bug in the Motif source, but Purify complains, even if
206 #### check_passwd_cb() is a no-op.
208 #### Update: Motif 1.2.1 also loses, but in a different way: it
209 #### writes beyond the end of a malloc'ed block in ModifyVerify().
210 #### Probably this block is the text field's text.
214 check_passwd_cb (button, client_data, call_data)
216 XtPointer client_data, call_data;
218 XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
220 if (passwd_state != pw_read)
222 else if (vcb->reason == XmCR_ACTIVATE)
224 passwd_done_cb (0, 0, 0);
226 else if (vcb->text->length > 1) /* don't allow "paste" operations */
230 else if (vcb->text->ptr != 0)
233 strncat (typed_passwd, vcb->text->ptr, vcb->text->length);
234 typed_passwd [vcb->endPos + vcb->text->length] = 0;
235 for (i = 0; i < vcb->text->length; i++)
236 vcb->text->ptr [i] = '*';
240 #else /* !VERIFY_CALLBACK_WORKS */
242 static void keypress();
243 static void backspace();
244 static void kill_line();
247 static XtActionsRec actions[] = {{"keypress", keypress},
248 {"backspace", backspace},
249 {"kill_line", kill_line},
253 #if 0 /* oh fuck, why doesn't this work? */
254 static char translations[] = "\
255 <Key>BackSpace: backspace()\n\
256 <Key>Delete: backspace()\n\
257 Ctrl<Key>H: backspace()\n\
258 Ctrl<Key>U: kill_line()\n\
259 Ctrl<Key>X: kill_line()\n\
260 Ctrl<Key>J: done()\n\
261 Ctrl<Key>M: done()\n\
265 static char translations[] = "<Key>:keypress()";
269 text_field_set_string (widget, text, position)
277 block.length = strlen (text);
280 XawTextReplace (widget, 0, -1, &block);
281 XawTextSetInsertionPoint (widget, position);
282 #else /* !NO_MOTIF */
283 XmTextFieldSetString (widget, text);
284 XmTextFieldSetInsertionPosition (widget, position);
285 #endif /* !NO_MOTIF */
290 keypress (w, event, argv, argc)
297 char s [sizeof (typed_passwd)];
298 int size = XLookupString ((XKeyEvent *) event, s, sizeof (s), 0, 0);
299 if (size != 1) return;
301 /* hack because I can't get translations to dance to my tune... */
302 if (*s == '\010') { backspace (w, event, argv, argc); return; }
303 if (*s == '\177') { backspace (w, event, argv, argc); return; }
304 if (*s == '\025') { kill_line (w, event, argv, argc); return; }
305 if (*s == '\030') { kill_line (w, event, argv, argc); return; }
306 if (*s == '\012') { done (w, event, argv, argc); return; }
307 if (*s == '\015') { done (w, event, argv, argc); return; }
309 i = j = strlen (typed_passwd);
310 typed_passwd [i] = *s;
315 text_field_set_string (passwd_text, s, j + 1);
319 backspace (w, event, argv, argc)
325 char s [sizeof (typed_passwd)];
326 int i = strlen (typed_passwd);
330 typed_passwd [--i] = 0;
335 text_field_set_string (passwd_text, s, j + 1);
339 kill_line (w, event, argv, argc)
345 memset (typed_passwd, 0, sizeof (typed_passwd));
346 text_field_set_string (passwd_text, "", 0);
350 done (w, event, argv, argc)
356 passwd_done_cb (w, 0, 0);
359 #endif /* !VERIFY_CALLBACK_WORKS || NO_MOTIF */
362 format_into_label (widget, string)
372 XtSetArg (av [ac], XtNlabel, &label); ac++;
373 XtGetValues (widget, av, ac);
375 XmString xm_label = 0;
376 XmString new_xm_label;
377 XtSetArg (av [ac], XmNlabelString, &xm_label); ac++;
378 XtGetValues (widget, av, ac);
379 XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
382 if (!label || !strcmp (label, XtName (widget)))
383 strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
385 sprintf (buf, label, string);
390 XtSetArg (av [ac], XtNlabel, buf); ac++;
392 new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
393 XtSetArg (av [ac], XmNlabelString, new_xm_label); ac++;
396 XtSetValues (widget, av, ac);
398 XmStringFree (new_xm_label);
404 extern void skull (Display *, Window, GC, GC, int, int, int, int);
408 roger (button, client_data, call_data)
410 XtPointer client_data, call_data;
412 Display *dpy = XtDisplay (button);
413 Screen *screen = XtScreen (button);
414 Window window = XtWindow (button);
419 GC draw_gc, erase_gc;
422 XWindowAttributes xgwa;
423 XGetWindowAttributes (dpy, window, &xgwa);
424 cmap = xgwa.colormap;
425 if (xgwa.width > xgwa.height) size = xgwa.height;
426 else size = xgwa.width;
427 if (size > 40) size -= 30;
428 x = (xgwa.width - size) / 2;
429 y = (xgwa.height - size) / 2;
430 XtSetArg (av [ac], XtNforeground, &fg); ac++;
431 XtSetArg (av [ac], XtNbackground, &bg); ac++;
432 XtGetValues (button, av, ac);
433 /* if it's black on white, swap it cause it looks better (hack hack) */
434 if (fg == BlackPixelOfScreen (screen) && bg == WhitePixelOfScreen (screen))
435 fg = WhitePixelOfScreen (screen), bg = BlackPixelOfScreen (screen);
437 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
439 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
440 XFillRectangle (dpy, window, erase_gc, 0, 0, xgwa.width, xgwa.height);
441 skull (dpy, window, draw_gc, erase_gc, x, y, size, size);
442 XFreeGC (dpy, draw_gc);
443 XFreeGC (dpy, erase_gc);
449 make_passwd_dialog (parent)
458 make_passwd_dialog (parent)
462 create_passwd_dialog (parent);
464 XtAddCallback (passwd_done, XmNactivateCallback, passwd_done_cb, 0);
465 XtAddCallback (passwd_cancel, XmNactivateCallback, passwd_cancel_cb, 0);
466 XtAddCallback (roger_label, XmNexposeCallback, roger, 0);
468 #ifdef VERIFY_CALLBACK_WORKS
469 XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
470 XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
472 XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
473 XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
476 #if !defined(NO_MOTIF) && (XmVersion >= 1002)
477 /* The focus stuff changed around; this didn't exist in 1.1.5. */
478 XtVaSetValues (passwd_form, XmNinitialFocus, passwd_text, 0);
481 /* Another random thing necessary in 1.2.1 but not 1.1.5... */
482 XtVaSetValues (roger_label, XmNborderWidth, 2, 0);
484 pw = getpwuid (getuid ());
485 format_into_label (passwd_label3, (pw->pw_name ? pw->pw_name : "???"));
486 format_into_label (passwd_label1, screensaver_version);
491 extern void idle_timer ();
493 static int passwd_idle_timer_tick;
494 static XtIntervalId id;
497 passwd_idle_timer (junk1, junk2)
501 Display *dpy = XtDisplay (passwd_form);
502 Window window = XtWindow (passwd_form);
503 static Dimension x, y, d, s, ss;
505 int max = passwd_timeout / 1000;
507 idle_timer (junk1, junk2);
509 if (passwd_idle_timer_tick == max) /* first time */
514 unsigned long fg, bg;
515 XtSetArg (av [ac], XtNheight, &d); ac++;
516 XtGetValues (passwd_done, av, ac);
518 XtSetArg (av [ac], XtNwidth, &x); ac++;
519 XtSetArg (av [ac], XtNheight, &y); ac++;
520 XtSetArg (av [ac], XtNforeground, &fg); ac++;
521 XtSetArg (av [ac], XtNbackground, &bg); ac++;
522 XtGetValues (passwd_form, av, ac);
527 if (gc) XFreeGC (dpy, gc);
528 gc = XCreateGC (dpy, window, GCForeground, &gcv);
529 s = 360*64 / (passwd_idle_timer_tick - 1);
531 XFillArc (dpy, window, gc, x, y, d, d, 0, 360*64);
532 XSetForeground (dpy, gc, bg);
538 if (--passwd_idle_timer_tick)
540 id = XtAppAddTimeOut (app, 1000, passwd_idle_timer, 0);
541 XFillArc (dpy, window, gc, x, y, d, d, ss, s);
546 extern void pop_up_dialog_box ();
547 extern int BadWindow_ehandler ();
550 pop_passwd_dialog (parent)
553 Display *dpy = XtDisplay (passwd_dialog);
556 typed_passwd [0] = 0;
557 passwd_state = pw_read;
558 text_field_set_string (passwd_text, "", 0);
560 XGetInputFocus (dpy, &focus, &revert_to);
561 #ifndef DESTROY_WORKS
562 /* This fucker blows up if we destroy the widget. I can't figure
563 out why. The second destroy phase dereferences freed memory...
564 So we just keep it around; but unrealizing or unmanaging it
565 doesn't work right either, so we hack the window directly. FMH.
567 if (XtWindow (passwd_form))
568 XMapRaised (dpy, XtWindow (passwd_dialog));
571 pop_up_dialog_box (passwd_dialog, passwd_form, 2);
572 XtManageChild (passwd_form);
574 #if !defined(NO_MOTIF) && (XmVersion < 1002)
575 /* The focus stuff changed around; this causes problems in 1.2.1
576 but is necessary in 1.1.5. */
577 XmProcessTraversal (passwd_text, XmTRAVERSE_CURRENT);
580 passwd_idle_timer_tick = passwd_timeout / 1000;
581 id = XtAppAddTimeOut (app, 1000, passwd_idle_timer, 0);
584 XGrabServer (dpy); /* ############ DANGER! */
586 /* this call to ungrab used to be in main_loop() - see comment in
587 xscreensaver.c around line 696. */
588 ungrab_keyboard_and_mouse ();
590 while (passwd_state == pw_read)
593 XtAppNextEvent (app, &event);
594 /* wait for timer event */
595 if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
596 passwd_state = pw_time;
597 XtDispatchEvent (&event);
600 XSync (dpy, False); /* ###### (danger over) */
602 if (passwd_state != pw_time)
603 XtRemoveTimeOut (id);
605 if (passwd_state != pw_ok)
608 switch (passwd_state)
610 case pw_time: lose = "Timed out!"; break;
611 case pw_fail: lose = "Sorry!"; break;
612 case pw_cancel: lose = 0; break;
616 XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */
620 text_field_set_string (passwd_text, lose, strlen (lose) + 1);
621 passwd_idle_timer_tick = 1;
622 id = XtAppAddTimeOut (app, 3000, passwd_idle_timer, 0);
626 XtAppNextEvent (app, &event);
627 if (event.xany.type == 0 && /* wait for timer event */
628 passwd_idle_timer_tick == 0)
630 XtDispatchEvent (&event);
634 memset (typed_passwd, 0, sizeof (typed_passwd));
635 text_field_set_string (passwd_text, "", 0);
636 XtSetKeyboardFocus (parent, None);
639 XtDestroyWidget (passwd_dialog);
642 XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
645 int (*old_handler) ();
646 old_handler = XSetErrorHandler (BadWindow_ehandler);
647 /* I don't understand why this doesn't refocus on the old selected
648 window when MWM is running in click-to-type mode. The value of
649 `focus' seems to be correct. */
650 XSetInputFocus (dpy, focus, revert_to, CurrentTime);
652 XSetErrorHandler (old_handler);
655 return (passwd_state == pw_ok ? True : False);
662 static Bool initted = False;
665 #ifndef VERIFY_CALLBACK_WORKS
666 XtAppAddActions (app, actions, XtNumber (actions));
672 make_passwd_dialog (parent);
673 return pop_passwd_dialog (parent);
676 #endif /* !NO_LOCKING */