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