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