ftp://ftp.zenez.com/pub/SCO/Skunk96/UnixWare/FreeBird/x11/utils/xscreensaver-1.18...
[xscreensaver] / driver / lock.c
1 /*    xscreensaver, Copyright (c) 1993 Jamie Zawinski <jwz@lucid.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 __STDC__
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #endif
17
18 #ifdef  HAVE_SHADOW
19 #include <shadow.h>
20 #endif
21
22 #include <pwd.h>
23 #include <stdio.h>
24
25 #include <X11/Intrinsic.h>
26
27 #if !__STDC__
28 # define _NO_PROTO
29 #endif
30
31 #include <Xm/Xm.h>
32 #include <Xm/List.h>
33 #include <Xm/TextF.h>
34
35 #include "xscreensaver.h"
36
37 Time passwd_timeout;
38 #ifndef NO_LOCKING
39
40
41 extern char *screensaver_version;
42 extern char *progname;
43 extern XtAppContext app;
44 extern Bool verbose_p;
45
46 extern Widget passwd_dialog;
47 extern Widget passwd_form;
48 extern Widget roger_label;
49 extern Widget passwd_label1;
50 extern Widget passwd_label3;
51 extern Widget passwd_text;
52 extern Widget passwd_done;
53 extern Widget passwd_cancel;
54
55 extern create_passwd_dialog ();
56
57 static enum
58 {
59   pw_read, pw_ok, pw_fail, pw_cancel, pw_time
60 } passwd_state;
61 static char typed_passwd[1024];
62
63 static char root_passwd[255];
64 static char user_passwd[255];
65
66 #ifdef HAVE_SHADOW
67 # define PWTYPE struct spwd *
68 # define PWSLOT sp_pwdp
69 # define GETPW  getspnam
70 #else
71 # define PWTYPE struct passwd *
72 # define PWSLOT pw_passwd
73 # define GETPW  getpwnam
74 #endif
75
76 Bool
77 lock_init ()
78 {
79   Bool ok = True;
80   char *u;
81   PWTYPE p = GETPW ("root");
82
83   if (p && p->PWSLOT && p->PWSLOT[0] != '*')
84     strcpy (root_passwd, p->PWSLOT);
85   else
86   {
87     fprintf (stderr, "%s: couldn't get root's password\n", progname);
88     strcpy (root_passwd, "*");
89   }
90
91   /* It has been reported that getlogin() returns the wrong user id on some
92      very old SGI systems... */
93   u = (char *) getlogin ();
94   if (u)
95     p = GETPW (u);
96   else
97   {
98     /* getlogin() fails if not attached to a terminal;
99          in that case, use getpwuid(). */
100     p = getpwuid (getuid ());
101     u = p->pw_name;
102   }
103
104   if (p && p->PWSLOT && p->PWSLOT[0] != '*')
105     strcpy (user_passwd, p->PWSLOT);
106   else
107   {
108     fprintf (stderr, "%s: couldn't get password of \"%s\"\n", progname, u);
109     strcpy (user_passwd, "*");
110     ok = False;
111   }
112   return ok;
113 }
114 \f
115 #ifdef XmVersion
116 # if (XmVersion >= 1002)        /* The `destroy' bug apears to be fixed as   */
117 #  define DESTROY_WORKS         /* of Motif 1.2.1, but the `verify-callback' */
118 # endif                         /* bug is still present. */
119 #endif
120
121 static void
122 passwd_cancel_cb (button, client_data, call_data)
123      Widget button;
124      XtPointer client_data, call_data;
125 {
126   passwd_state = pw_cancel;
127 }
128
129 static void
130 passwd_done_cb (button, client_data, call_data)
131      Widget button;
132      XtPointer client_data, call_data;
133 {
134   if (passwd_state != pw_read)
135     return;                     /* already done */
136
137   if (!strcmp ((char *) crypt (typed_passwd, user_passwd), user_passwd))
138     passwd_state = pw_ok;
139   /* do not allow root to have empty passwd */
140   else if (typed_passwd[0] &&
141            !strcmp ((char *) crypt (typed_passwd, root_passwd), root_passwd))
142     passwd_state = pw_ok;
143   else
144     passwd_state = pw_fail;
145 }
146
147 #ifdef VERIFY_CALLBACK_WORKS
148
149 /* ####  It looks to me like adding any modifyVerify callback causes
150      ####  Motif 1.1.4 to free the the TextF_Value() twice.  I can't see
151      ####  the bug in the Motif source, but Purify complains, even if
152      ####  check_passwd_cb() is a no-op.
153
154      ####  Update: Motif 1.2.1 also loses, but in a different way: it
155      ####  writes beyond the end of a malloc'ed block in ModifyVerify().
156      ####  Probably this block is the text field's text.
157    */
158
159 static void
160 check_passwd_cb (button, client_data, call_data)
161      Widget button;
162      XtPointer client_data, call_data;
163 {
164   XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
165
166   if (passwd_state != pw_read)
167     return;
168   else if (vcb->reason == XmCR_ACTIVATE)
169   {
170     passwd_done_cb (0, 0, 0);
171   }
172   else if (vcb->text->length > 1)       /* don't allow "paste" operations */
173   {
174     vcb->doit = False;
175   }
176   else if (vcb->text->ptr != 0)
177   {
178     int i;
179
180     strncat (typed_passwd, vcb->text->ptr, vcb->text->length);
181     typed_passwd[vcb->endPos + vcb->text->length] = 0;
182     for (i = 0; i < vcb->text->length; i++)
183       vcb->text->ptr[i] = '*';
184   }
185 }
186
187 #else /* !VERIFY_CALLBACK_WORKS */
188
189 static void keypress ();
190 static void backspace ();
191 static void kill_line ();
192 static void done ();
193
194 static XtActionsRec actions[] =
195 {
196   {"keypress", keypress},
197   {"backspace", backspace},
198   {"kill_line", kill_line},
199   {"done", done}
200 };
201
202 #if 0                           /* oh fuck, why doesn't this work? */
203 static char translations[] = "\
204 <Key>BackSpace:         backspace()\n\
205 <Key>Delete:            backspace()\n\
206 Ctrl<Key>H:             backspace()\n\
207 Ctrl<Key>U:             kill_line()\n\
208 Ctrl<Key>X:             kill_line()\n\
209 Ctrl<Key>J:             done()\n\
210 Ctrl<Key>M:             done()\n\
211 <Key>:                  keypress()\n\
212 ";
213
214 #else
215 static char translations[] = "<Key>:keypress()";
216
217 #endif
218
219 static void
220 keypress (w, event, argv, argc)
221      Widget w;
222      XEvent *event;
223      String *argv;
224      Cardinal *argc;
225 {
226   int i, j;
227   char s[sizeof (typed_passwd)];
228   int size = XLookupString ((XKeyEvent *) event, s, sizeof (s), 0, 0);
229
230   if (size != 1)
231     return;
232
233   /* hack because I can't get translations to dance to my tune... */
234   if (*s == '\010')
235   {
236     backspace (w, event, argv, argc);
237     return;
238   }
239   if (*s == '\177')
240   {
241     backspace (w, event, argv, argc);
242     return;
243   }
244   if (*s == '\025')
245   {
246     kill_line (w, event, argv, argc);
247     return;
248   }
249   if (*s == '\030')
250   {
251     kill_line (w, event, argv, argc);
252     return;
253   }
254   if (*s == '\012')
255   {
256     done (w, event, argv, argc);
257     return;
258   }
259   if (*s == '\015')
260   {
261     done (w, event, argv, argc);
262     return;
263   }
264
265   i = j = strlen (typed_passwd);
266   typed_passwd[i] = *s;
267   s[++i] = 0;
268   while (i--)
269     s[i] = '*';
270   XmTextFieldSetString (passwd_text, s);
271   XmTextFieldSetInsertionPosition (passwd_text, j + 1);
272 }
273
274 static void
275 backspace (w, event, argv, argc)
276      Widget w;
277      XEvent *event;
278      String *argv;
279      Cardinal *argc;
280 {
281   char s[sizeof (typed_passwd)];
282   int i = strlen (typed_passwd);
283   int j = i;
284
285   if (i == 0)
286     return;
287   typed_passwd[--i] = 0;
288   s[i] = 0;
289   while (i--)
290     s[i] = '*';
291   XmTextFieldSetString (passwd_text, s);
292   XmTextFieldSetInsertionPosition (passwd_text, j + 1);
293 }
294
295 static void
296 kill_line (w, event, argv, argc)
297      Widget w;
298      XEvent *event;
299      String *argv;
300      Cardinal *argc;
301 {
302   memset (typed_passwd, 0, sizeof (typed_passwd));
303   XmTextFieldSetString (passwd_text, "");
304 }
305
306 static void
307 done (w, event, argv, argc)
308      Widget w;
309      XEvent *event;
310      String *argv;
311      Cardinal *argc;
312 {
313   passwd_done_cb (w, 0, 0);
314 }
315
316 #endif /* !VERIFY_CALLBACK_WORKS */
317
318 static void
319 format_into_label (widget, string)
320      Widget widget;
321      char *string;
322 {
323   char *label;
324   char buf[255];
325   XmString xm_label = 0;
326   XmString new_xm_label;
327   Arg av[10];
328   int ac = 0;
329
330   XtSetArg (av[ac], XmNlabelString, &xm_label);
331   ac++;
332   XtGetValues (widget, av, ac);
333   XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
334   if (!strcmp (label, XtName (widget)))
335     strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
336   else
337     sprintf (buf, label, string);
338   new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
339   ac = 0;
340   XtSetArg (av[ac], XmNlabelString, new_xm_label);
341   ac++;
342   XtSetValues (widget, av, ac);
343   XmStringFree (new_xm_label);
344   XtFree (label);
345 }
346
347 #if __STDC__
348 extern void skull (Display *, Window, GC, GC, int, int, int, int);
349
350 #endif
351
352 static void
353 roger (button, client_data, call_data)
354      Widget button;
355      XtPointer client_data, call_data;
356 {
357   Display *dpy = XtDisplay (button);
358   Screen *screen = XtScreen (button);
359   Window window = XtWindow (button);
360   Arg av[10];
361   int ac = 0;
362   XGCValues gcv;
363   Colormap cmap;
364   GC draw_gc, erase_gc;
365   unsigned int fg, bg;
366   int x, y, size;
367   XWindowAttributes xgwa;
368
369   XGetWindowAttributes (dpy, window, &xgwa);
370   cmap = xgwa.colormap;
371   if (xgwa.width > xgwa.height)
372     size = xgwa.height;
373   else
374     size = xgwa.width;
375   if (size > 40)
376     size -= 30;
377   x = (xgwa.width - size) / 2;
378   y = (xgwa.height - size) / 2;
379   XtSetArg (av[ac], XmNforeground, &fg);
380   ac++;
381   XtSetArg (av[ac], XmNbackground, &bg);
382   ac++;
383   XtGetValues (button, av, ac);
384   /* if it's black on white, swap it cause it looks better (hack hack) */
385   if (fg == BlackPixelOfScreen (screen) && bg == WhitePixelOfScreen (screen))
386     fg = WhitePixelOfScreen (screen), bg = BlackPixelOfScreen (screen);
387   gcv.foreground = bg;
388   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
389   gcv.foreground = fg;
390   draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
391   XFillRectangle (dpy, window, erase_gc, 0, 0, xgwa.width, xgwa.height);
392   skull (dpy, window, draw_gc, erase_gc, x, y, size, size);
393   XFreeGC (dpy, draw_gc);
394   XFreeGC (dpy, erase_gc);
395 }
396
397 static void
398 make_passwd_dialog (parent)
399      Widget parent;
400 {
401   struct passwd *pw;
402
403   create_passwd_dialog (parent);
404
405   XtAddCallback (passwd_done, XmNactivateCallback, passwd_done_cb, 0);
406   XtAddCallback (passwd_cancel, XmNactivateCallback, passwd_cancel_cb, 0);
407   XtAddCallback (roger_label, XmNexposeCallback, roger, 0);
408
409 #ifdef VERIFY_CALLBACK_WORKS
410   XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
411   XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
412 #else
413   XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
414   XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
415 #endif
416
417   pw = getpwuid (getuid ());
418   format_into_label (passwd_label3, (pw->pw_name ? pw->pw_name : "???"));
419   format_into_label (passwd_label1, screensaver_version);
420 }
421
422 extern void idle_timer ();
423
424 static int passwd_idle_timer_tick;
425 static XtIntervalId id;
426
427 static void
428 passwd_idle_timer (junk1, junk2)
429      void *junk1;
430      XtPointer junk2;
431 {
432   Display *dpy = XtDisplay (passwd_form);
433   Window window = XtWindow (passwd_form);
434   static Dimension x, y, d, s, ss;
435   static GC gc = 0;
436   int max = passwd_timeout / 1000;
437
438   idle_timer (junk1, junk2);
439
440   if (passwd_idle_timer_tick == max)    /* first time */
441   {
442     Arg av[10];
443     int ac = 0;
444     XGCValues gcv;
445     unsigned long fg, bg;
446
447     XtSetArg (av[ac], XmNheight, &d);
448     ac++;
449     XtGetValues (passwd_done, av, ac);
450     ac = 0;
451     XtSetArg (av[ac], XmNwidth, &x);
452     ac++;
453     XtSetArg (av[ac], XmNheight, &y);
454     ac++;
455     XtSetArg (av[ac], XmNforeground, &fg);
456     ac++;
457     XtSetArg (av[ac], XmNbackground, &bg);
458     ac++;
459     XtGetValues (passwd_form, av, ac);
460     x -= d;
461     y -= d;
462     d -= 4;
463     gcv.foreground = fg;
464     if (gc)
465       XFreeGC (dpy, gc);
466     gc = XCreateGC (dpy, window, GCForeground, &gcv);
467     s = 360 * 64 / (passwd_idle_timer_tick - 1);
468     ss = 90 * 64;
469     XFillArc (dpy, window, gc, x, y, d, d, 0, 360 * 64);
470     XSetForeground (dpy, gc, bg);
471     x += 1;
472     y += 1;
473     d -= 2;
474   }
475
476   if (--passwd_idle_timer_tick)
477   {
478     id = XtAppAddTimeOut (app, 1000, (XtTimerCallbackProc) passwd_idle_timer, 0);
479     XFillArc (dpy, window, gc, x, y, d, d, ss, s);
480     ss += s;
481   }
482 }
483
484 extern void pop_up_dialog_box ();
485 extern int BadWindow_ehandler ();
486
487 static Bool
488 pop_passwd_dialog (parent)
489      Widget parent;
490 {
491   Display *dpy = XtDisplay (passwd_dialog);
492   Window focus;
493   int revert_to;
494
495   typed_passwd[0] = 0;
496   passwd_state = pw_read;
497   XmTextFieldSetString (passwd_text, "");
498
499   XGetInputFocus (dpy, &focus, &revert_to);
500 #ifndef DESTROY_WORKS
501   /* This fucker blows up if we destroy the widget.  I can't figure
502      out why.  The second destroy phase dereferences freed memory...
503      So we just keep it around; but unrealizing or unmanaging it
504      doesn't work right either, so we hack the window directly. FMH.
505    */
506   if (XtWindow (passwd_form))
507     XMapRaised (dpy, XtWindow (passwd_dialog));
508 #endif
509   pop_up_dialog_box (passwd_dialog, passwd_form, 2);
510
511   XtManageChild (passwd_form);
512   XSetInputFocus (dpy, XtWindow (passwd_dialog), revert_to, CurrentTime);
513
514   /* #### This doesn't work in 1.2.1... Check return code... */
515   XmProcessTraversal (passwd_text, 0);
516
517   passwd_idle_timer_tick = passwd_timeout / 1000;
518   id = XtAppAddTimeOut (app, 1000, (XtTimerCallbackProc) passwd_idle_timer, 0);
519
520   XGrabServer (dpy);            /* ############ DANGER! */
521
522   while (passwd_state == pw_read)
523   {
524     XEvent event;
525
526     XtAppNextEvent (app, &event);
527     /* wait for timer event */
528     if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
529       passwd_state = pw_time;
530     XtDispatchEvent (&event);
531   }
532   XUngrabServer (dpy);
533   XSync (dpy, False);           /* ###### (danger over) */
534
535   if (passwd_state != pw_time)
536     XtRemoveTimeOut (id);
537
538   if (passwd_state != pw_ok)
539   {
540     char *lose;
541
542     switch (passwd_state)
543     {
544     case pw_time:
545       lose = "Timed out!";
546       break;
547     case pw_fail:
548       lose = "Sorry!";
549       break;
550     case pw_cancel:
551       lose = 0;
552       break;
553     default:
554       abort ();
555     }
556     XmProcessTraversal (passwd_cancel, 0);      /* turn off I-beam */
557     if (lose)
558     {
559       XmTextFieldSetString (passwd_text, lose);
560       XmTextFieldSetInsertionPosition (passwd_text, strlen (lose) + 1);
561       passwd_idle_timer_tick = 1;
562       id = XtAppAddTimeOut (app, 3000, (XtTimerCallbackProc) passwd_idle_timer, 0);
563       while (1)
564       {
565         XEvent event;
566
567         XtAppNextEvent (app, &event);
568         if (event.xany.type == 0 &&     /* wait for timer event */
569             passwd_idle_timer_tick == 0)
570           break;
571         XtDispatchEvent (&event);
572       }
573     }
574   }
575   memset (typed_passwd, 0, sizeof (typed_passwd));
576   XmTextFieldSetString (passwd_text, "");
577   XtSetKeyboardFocus (parent, None);
578
579 #ifdef DESTROY_WORKS
580   XtDestroyWidget (passwd_dialog);
581   passwd_dialog = 0;
582 #else
583   XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
584 #endif
585   {
586     int (*old_handler) ();
587
588     old_handler = XSetErrorHandler (BadWindow_ehandler);
589     /* I don't understand why this doesn't refocus on the old selected
590        window when MWM is running in click-to-type mode.  The value of
591        `focus' seems to be correct. */
592     XSetInputFocus (dpy, focus, revert_to, CurrentTime);
593     XSync (dpy, False);
594     XSetErrorHandler (old_handler);
595   }
596
597   return (passwd_state == pw_ok ? True : False);
598 }
599
600 Bool
601 unlock_p (parent)
602      Widget parent;
603 {
604   static Bool initted = False;
605
606   if (!initted)
607   {
608 #ifndef VERIFY_CALLBACK_WORKS
609     XtAppAddActions (app, actions, XtNumber (actions));
610 #endif
611     passwd_dialog = 0;
612     initted = True;
613   }
614   if (!passwd_dialog)
615     make_passwd_dialog (parent);
616   return pop_passwd_dialog (parent);
617 }
618
619 #endif /* !NO_LOCKING */