ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-1.27.tar.Z
[xscreensaver] / driver / lock.c
1 /*    xscreensaver, Copyright (c) 1993-1995 Jamie Zawinski <jwz@netscape.com>
2  *
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 
9  * implied warranty.
10  */
11
12 /* #### If anyone ever finishes the Athena locking code, remove this.
13    But until then, locking requires Motif.
14  */
15 #if defined(NO_MOTIF) && !defined(NO_LOCKING)
16 # define NO_LOCKING
17 #endif
18
19
20 #ifndef NO_LOCKING
21
22 #if __STDC__
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #endif
27
28 #ifdef  HAVE_SHADOW
29 #include <shadow.h>
30 #endif
31
32 #include <pwd.h>
33 #include <stdio.h>
34
35 #include <X11/Intrinsic.h>
36
37 #include "xscreensaver.h"
38
39 extern char *screensaver_version;
40 extern char *progname;
41 extern XtAppContext app;
42 extern Bool verbose_p;
43
44 #ifdef SCO
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>.
47  */
48 # include <sys/security.h>
49 # include <sys/audit.h>
50 # include <prot.h>
51 #endif
52
53 #if !__STDC__
54 # define _NO_PROTO
55 #endif
56
57 #ifdef NO_MOTIF
58
59 # include <X11/StringDefs.h>
60 # include <X11/Xaw/Text.h>
61 # include <X11/Xaw/Label.h>
62
63 #else /* Motif */
64
65 # include <Xm/Xm.h>
66 # include <Xm/List.h>
67 # include <Xm/TextF.h>
68
69 #endif /* Motif */
70
71 Time passwd_timeout;
72
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;
81
82 extern create_passwd_dialog P((Widget));
83 extern void ungrab_keyboard_and_mouse P((void));
84
85 static enum { pw_read, pw_ok, pw_fail, pw_cancel, pw_time } passwd_state;
86 static char typed_passwd [1024];
87
88 static char root_passwd [255];
89 static char user_passwd [255];
90
91 #ifdef HAVE_SHADOW
92 # define PWTYPE struct spwd *
93 # define PWSLOT sp_pwdp
94 # define GETPW  getspnam
95 #else
96 # define PWTYPE struct passwd *
97 # define PWSLOT pw_passwd
98 # define GETPW  getpwnam
99 #endif
100
101 #ifdef SCO
102 # define PRPWTYPE struct pr_passwd *
103 # define GETPRPW getprpwnam
104 #endif
105
106 Bool
107 lock_init ()
108 {
109   Bool ok = True;
110   char *u;
111   PWTYPE p = GETPW ("root");
112
113 #ifdef SCO
114   PRPWTYPE prpwd = GETPRPW ("root");
115   if (prpwd && *prpwd->ufld.fd_encrypt)
116     strcpy (root_passwd, prpwd->ufld.fd_encrypt);
117 #else /* !SCO */
118   if (p && p->PWSLOT && p->PWSLOT[0] != '*')
119     strcpy (root_passwd, p->PWSLOT);
120 #endif /* !SCO */
121   else
122     {
123       fprintf (stderr, "%s: couldn't get root's password\n", progname);
124       strcpy (root_passwd, "*");
125     }
126
127   /* It has been reported that getlogin() returns the wrong user id on some
128      very old SGI systems... */
129   u = (char *) getlogin ();
130   if (u)
131     {
132 #ifdef SCO
133       prpwd = GETPRPW (u);
134 #endif /* SCO */
135       p = GETPW (u);
136     }
137   else
138     {
139       /* getlogin() fails if not attached to a terminal;
140          in that case, use getpwuid(). */
141       struct passwd *p2 = getpwuid (getuid ());
142       u = p2->pw_name;
143 #ifdef HAVE_SHADOW
144       p = GETPW (u);
145 #else
146       p = p2;
147 #endif
148     }
149
150 #ifdef SCO
151   if (prpwd && *prpwd->ufld.fd_encrypt)
152     strcpy (user_passwd, prpwd->ufld.fd_encrypt);
153 #else /* !SCO */
154   if (p && p->PWSLOT &&
155       /* p->PWSLOT[0] != '*' */         /* sensible */
156       (strlen (p->PWSLOT) > 4)          /* solaris */
157       )
158     strcpy (user_passwd, p->PWSLOT);
159 #endif /* !SCO */
160   else
161     {
162       fprintf (stderr, "%s: couldn't get password of \"%s\"\n", progname, u);
163       strcpy (user_passwd, "*");
164       ok = False;
165     }
166   return ok;
167 }
168
169
170 \f
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
175 #endif
176
177 static void
178 passwd_cancel_cb (button, client_data, call_data)
179      Widget button;
180      XtPointer client_data, call_data;
181 {
182   passwd_state = pw_cancel;
183 }
184
185 static void
186 passwd_done_cb (button, client_data, call_data)
187      Widget button;
188      XtPointer client_data, call_data;
189 {
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;
197   else
198     passwd_state = pw_fail;
199 }
200
201 #if !defined(NO_MOTIF) && defined(VERIFY_CALLBACK_WORKS)
202
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.
207
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.
211    */
212
213 static void 
214 check_passwd_cb (button, client_data, call_data)
215      Widget button;
216      XtPointer client_data, call_data;
217 {
218   XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
219
220   if (passwd_state != pw_read)
221     return;
222   else if (vcb->reason == XmCR_ACTIVATE)
223     {
224       passwd_done_cb (0, 0, 0);
225     }
226   else if (vcb->text->length > 1)       /* don't allow "paste" operations */
227     {
228       vcb->doit = False;
229     }
230   else if (vcb->text->ptr != 0)
231     {
232       int i;
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] = '*';
237     }
238 }
239
240 #else /* !VERIFY_CALLBACK_WORKS */
241
242 static void keypress();
243 static void backspace();
244 static void kill_line();
245 static void done();
246
247 static XtActionsRec actions[] = {{"keypress",  keypress},
248                                  {"backspace", backspace},
249                                  {"kill_line", kill_line},
250                                  {"done",      done}
251                                 };
252
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\
262 <Key>:                  keypress()\n\
263 ";
264 #else
265 static char translations[] = "<Key>:keypress()";
266 #endif
267
268 static void
269 text_field_set_string (widget, text, position)
270      Widget widget;
271      char *text;
272      int position;
273 {
274 #ifdef NO_MOTIF
275   XawTextBlock block;
276   block.firstPos = 0;
277   block.length = strlen (text);
278   block.ptr = text;
279   block.format = 0;
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 */
286 }
287
288
289 static void
290 keypress (w, event, argv, argc)
291      Widget w;
292      XEvent *event;
293      String *argv;
294      Cardinal *argc;
295 {
296   int i, j;
297   char s [sizeof (typed_passwd)];
298   int size = XLookupString ((XKeyEvent *) event, s, sizeof (s), 0, 0);
299   if (size != 1) return;
300
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; }
308
309   i = j = strlen (typed_passwd);
310   typed_passwd [i] = *s;
311   s [++i] = 0;
312   while (i--)
313     s [i] = '*';
314
315   text_field_set_string (passwd_text, s, j + 1);
316 }
317
318 static void
319 backspace (w, event, argv, argc)
320      Widget w;
321      XEvent *event;
322      String *argv;
323      Cardinal *argc;
324 {
325   char s [sizeof (typed_passwd)];
326   int i = strlen (typed_passwd);
327   int j = i;
328   if (i == 0)
329     return;
330   typed_passwd [--i] = 0;
331   s [i] = 0;
332   while (i--)
333     s [i] = '*';
334
335   text_field_set_string (passwd_text, s, j + 1);
336 }
337
338 static void
339 kill_line (w, event, argv, argc)
340      Widget w;
341      XEvent *event;
342      String *argv;
343      Cardinal *argc;
344 {
345   memset (typed_passwd, 0, sizeof (typed_passwd));
346   text_field_set_string (passwd_text, "", 0);
347 }
348
349 static void
350 done (w, event, argv, argc)
351      Widget w;
352      XEvent *event;
353      String *argv;
354      Cardinal *argc;
355 {
356   passwd_done_cb (w, 0, 0);
357 }
358
359 #endif /* !VERIFY_CALLBACK_WORKS || NO_MOTIF */
360
361 static void
362 format_into_label (widget, string)
363      Widget widget;
364      char *string;
365 {
366   char *label;
367   char buf [255];
368   Arg av[10];
369   int ac = 0;
370
371 #ifdef NO_MOTIF
372   XtSetArg (av [ac], XtNlabel, &label); ac++;
373   XtGetValues (widget, av, ac);
374 #else  /* Motif */
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);
380 #endif /* Motif */
381
382   if (!label || !strcmp (label, XtName (widget)))
383     strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
384   else
385     sprintf (buf, label, string);
386
387   ac = 0;
388
389 #ifdef NO_MOTIF
390   XtSetArg (av [ac], XtNlabel, buf); ac++;
391 #else  /* Motif */
392   new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
393   XtSetArg (av [ac], XmNlabelString, new_xm_label); ac++;
394 #endif /* Motif */
395
396   XtSetValues (widget, av, ac);
397 #ifndef NO_MOTIF
398   XmStringFree (new_xm_label);
399 #endif
400   XtFree (label);
401 }
402
403 #if __STDC__
404 extern void skull (Display *, Window, GC, GC, int, int, int, int);
405 #endif
406
407 static void
408 roger (button, client_data, call_data)
409      Widget button;
410      XtPointer client_data, call_data;
411 {
412   Display *dpy = XtDisplay (button);
413   Screen *screen = XtScreen (button);
414   Window window = XtWindow (button);
415   Arg av [10];
416   int ac = 0;
417   XGCValues gcv;
418   Colormap cmap;
419   GC draw_gc, erase_gc;
420   unsigned int fg, bg;
421   int x, y, size;
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);
436   gcv.foreground = bg;
437   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
438   gcv.foreground = fg;
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);
444 }
445
446 #ifdef NO_MOTIF
447
448 static void
449 make_passwd_dialog (parent)
450      Widget parent;
451 {
452   abort (); /* #### */
453 }
454
455 #else  /* Motif */
456
457 static void
458 make_passwd_dialog (parent)
459      Widget parent;
460 {
461   struct passwd *pw;
462   create_passwd_dialog (parent);
463
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);
467
468 #ifdef VERIFY_CALLBACK_WORKS
469   XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
470   XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
471 #else
472   XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
473   XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
474 #endif
475
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);
479 #endif
480
481   /* Another random thing necessary in 1.2.1 but not 1.1.5... */
482   XtVaSetValues (roger_label, XmNborderWidth, 2, 0);
483
484   pw = getpwuid (getuid ());
485   format_into_label (passwd_label3, (pw->pw_name ? pw->pw_name : "???"));
486   format_into_label (passwd_label1, screensaver_version);
487 }
488
489 #endif /* Motif */
490
491 extern void idle_timer ();
492
493 static int passwd_idle_timer_tick;
494 static XtIntervalId id;
495
496 static void
497 passwd_idle_timer (junk1, junk2)
498      void *junk1;
499      XtPointer junk2;
500 {
501   Display *dpy = XtDisplay (passwd_form);
502   Window window = XtWindow (XtParent(passwd_done));
503   static Dimension x, y, d, s, ss;
504   static GC gc = 0;
505   int max = passwd_timeout / 1000;
506
507   idle_timer (junk1, junk2);
508
509   if (passwd_idle_timer_tick == max)  /* first time */
510     {
511       Arg av [10];
512       int ac = 0;
513       XGCValues gcv;
514       unsigned long fg, bg, ts, bs;
515       Dimension w = 0, h = 0;
516       XtVaGetValues(XtParent(passwd_done),
517                     XmNwidth, &w,
518                     0);
519       XtVaGetValues(passwd_done,
520                     XmNheight, &h,
521                     XmNy, &y,
522                     XtNforeground, &fg,
523                     XtNbackground, &bg,
524                     XmNtopShadowColor, &ts,
525                     XmNbottomShadowColor, &bs,
526                     0);
527
528       if (ts != bg && ts != fg)
529         fg = ts;
530       if (bs != bg && bs != fg)
531         fg = bs;
532
533       d = h / 2;
534       if (d & 1) d++;
535
536       x = (w / 2);
537
538       x -= d/2;
539       y += d/2;
540
541       gcv.foreground = fg;
542       if (gc) XFreeGC (dpy, gc);
543       gc = XCreateGC (dpy, window, GCForeground, &gcv);
544       s = 360*64 / (passwd_idle_timer_tick - 1);
545       ss = 90*64;
546       XFillArc (dpy, window, gc, x, y, d, d, 0, 360*64);
547       XSetForeground (dpy, gc, bg);
548       x += 1;
549       y += 1;
550       d -= 2;
551     }
552
553   if (--passwd_idle_timer_tick)
554     {
555       id = XtAppAddTimeOut (app, 1000,
556                             (XtTimerCallbackProc) passwd_idle_timer, 0);
557       XFillArc (dpy, window, gc, x, y, d, d, ss, s);
558       ss += s;
559     }
560 }
561
562 extern void pop_up_dialog_box ();
563 extern int BadWindow_ehandler ();
564
565 static Bool
566 pop_passwd_dialog (parent)
567      Widget parent;
568 {
569   Display *dpy = XtDisplay (passwd_dialog);
570   Window focus;
571   int revert_to;
572   typed_passwd [0] = 0;
573   passwd_state = pw_read;
574   text_field_set_string (passwd_text, "", 0);
575
576   XGetInputFocus (dpy, &focus, &revert_to);
577 #ifndef DESTROY_WORKS
578   /* This fucker blows up if we destroy the widget.  I can't figure
579      out why.  The second destroy phase dereferences freed memory...
580      So we just keep it around; but unrealizing or unmanaging it
581      doesn't work right either, so we hack the window directly. FMH.
582    */
583   if (XtWindow (passwd_form))
584     XMapRaised (dpy, XtWindow (passwd_dialog));
585 #endif
586
587   pop_up_dialog_box (passwd_dialog, passwd_form, 2);
588   XtManageChild (passwd_form);
589
590 #if !defined(NO_MOTIF) && (XmVersion < 1002)
591   /* The focus stuff changed around; this causes problems in 1.2.1
592      but is necessary in 1.1.5. */
593   XmProcessTraversal (passwd_text, XmTRAVERSE_CURRENT);
594 #endif
595
596   passwd_idle_timer_tick = passwd_timeout / 1000;
597   id = XtAppAddTimeOut (app, 1000, (XtTimerCallbackProc) passwd_idle_timer, 0);
598
599
600   XGrabServer (dpy);                            /* ############ DANGER! */
601
602   /* this call to ungrab used to be in main_loop() - see comment in
603       xscreensaver.c around line 696. */
604   ungrab_keyboard_and_mouse ();
605
606   while (passwd_state == pw_read)
607     {
608       XEvent event;
609       XtAppNextEvent (app, &event);
610       /* wait for timer event */
611       if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
612         passwd_state = pw_time;
613       XtDispatchEvent (&event);
614     }
615   XUngrabServer (dpy);
616   XSync (dpy, False);                           /* ###### (danger over) */
617
618   if (passwd_state != pw_time)
619     XtRemoveTimeOut (id);
620
621   if (passwd_state != pw_ok)
622     {
623       char *lose;
624       switch (passwd_state)
625         {
626         case pw_time: lose = "Timed out!"; break;
627         case pw_fail: lose = "Sorry!"; break;
628         case pw_cancel: lose = 0; break;
629         default: abort ();
630         }
631 #ifndef NO_MOTIF
632       XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */
633 #endif
634       if (lose)
635         {
636           text_field_set_string (passwd_text, lose, strlen (lose) + 1);
637           passwd_idle_timer_tick = 1;
638           id = XtAppAddTimeOut (app, 3000,
639                                 (XtTimerCallbackProc) passwd_idle_timer, 0);
640           while (1)
641             {
642               XEvent event;
643               XtAppNextEvent (app, &event);
644               if (event.xany.type == 0 &&       /* wait for timer event */
645                   passwd_idle_timer_tick == 0)
646                 break;
647               XtDispatchEvent (&event);
648             }
649         }
650     }
651   memset (typed_passwd, 0, sizeof (typed_passwd));
652   text_field_set_string (passwd_text, "", 0);
653   XtSetKeyboardFocus (parent, None);
654
655 #ifdef DESTROY_WORKS
656   XtDestroyWidget (passwd_dialog);
657   passwd_dialog = 0;
658 #else
659   XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
660 #endif
661   {
662     int (*old_handler) ();
663     old_handler = XSetErrorHandler (BadWindow_ehandler);
664     /* I don't understand why this doesn't refocus on the old selected
665        window when MWM is running in click-to-type mode.  The value of
666        `focus' seems to be correct. */
667     XSetInputFocus (dpy, focus, revert_to, CurrentTime);
668     XSync (dpy, False);
669     XSetErrorHandler (old_handler);
670   }
671
672   return (passwd_state == pw_ok ? True : False);
673 }
674
675 Bool
676 unlock_p (parent)
677      Widget parent;
678 {
679   static Bool initted = False;
680   if (! initted)
681     {
682 #ifndef VERIFY_CALLBACK_WORKS
683       XtAppAddActions (app, actions, XtNumber (actions));
684 #endif
685       passwd_dialog = 0;
686       initted = True;
687     }
688   if (! passwd_dialog)
689     make_passwd_dialog (parent);
690   return pop_passwd_dialog (parent);
691 }
692
693 #endif /* !NO_LOCKING */