c87597832039ee11f32fb5f8ca0dfa1312f7b25b
[xscreensaver] / driver / lock.c
1 /* lock.c --- handling the password dialog for locking-mode.
2  * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* Athena locking code contributed by Jon A. Christopher <jac8782@tamu.edu> */
14 /* Copyright 1997, with the same permissions as above. */
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19
20 #ifndef NO_LOCKING   /* whole file */
21
22 #include <X11/Intrinsic.h>
23 #include "xscreensaver.h"
24 #include "resources.h"
25
26 #ifdef HAVE_SYSLOG
27 # include <syslog.h>
28 #endif /* HAVE_SYSLOG */
29
30 #ifdef _VROOT_H_
31 ERROR!  You must not include vroot.h in this file.
32 #endif
33
34 #ifndef VMS
35 # include <pwd.h>
36 #else /* VMS */
37
38 extern char *getenv(const char *name);
39 extern int validate_user(char *name, char *password);
40
41 static Bool
42 vms_passwd_valid_p(char *pw)
43 {
44   return (validate_user (getenv("USER"), typed_passwd) == 1);
45 }
46 # undef passwd_valid_p
47 # define passwd_valid_p vms_passwd_valid_p
48
49 #endif /* VMS */
50
51
52 #undef MAX
53 #define MAX(a,b) ((a)>(b)?(a):(b))
54
55 enum passwd_state { pw_read, pw_ok, pw_null, pw_fail, pw_cancel, pw_time };
56
57 struct passwd_dialog_data {
58
59   enum passwd_state state;
60   char typed_passwd [80];
61   XtIntervalId timer;
62   int i_beam;
63
64   float ratio;
65   Dimension width;
66   Dimension height;
67
68   char *heading_label;
69   char *body_label;
70   char *user_label;
71   char *passwd_label;
72   char *user_string;
73   char *passwd_string;
74
75   XFontStruct *heading_font;
76   XFontStruct *body_font;
77   XFontStruct *label_font;
78   XFontStruct *passwd_font;
79
80   Pixel foreground;
81   Pixel background;
82   Pixel passwd_foreground;
83   Pixel passwd_background;
84   Pixel logo_foreground;
85   Pixel logo_background;
86   Pixel shadow_top;
87   Pixel shadow_bottom;
88
89   Dimension logo_width;
90   Dimension logo_height;
91   Dimension thermo_width;
92   Dimension internal_border;
93   Dimension shadow_width;
94
95   Dimension passwd_field_x, passwd_field_y;
96   Dimension passwd_field_width, passwd_field_height;
97
98   Dimension thermo_field_x, thermo_field_y;
99   Dimension thermo_field_height;
100 };
101
102
103 void
104 make_passwd_window (saver_info *si)
105 {
106   struct passwd *p = getpwuid (getuid ());
107   int x, y, bw;
108   XSetWindowAttributes attrs;
109   unsigned long attrmask = 0;
110   Screen *screen = si->default_screen->screen;
111   passwd_dialog_data *pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
112   Colormap cmap = DefaultColormapOfScreen (screen);
113   char *f;
114
115   pw->ratio = 1.0;
116
117   pw->heading_label = get_string_resource ("passwd.heading.label",
118                                            "Dialog.Label.Label");
119   pw->body_label = get_string_resource ("passwd.body.label",
120                                         "Dialog.Label.Label");
121   pw->user_label = get_string_resource ("passwd.user.label",
122                                         "Dialog.Label.Label");
123   pw->passwd_label = get_string_resource ("passwd.passwd.label",
124                                           "Dialog.Label.Label");
125
126   if (!pw->heading_label)
127     pw->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
128   if (!pw->body_label)
129     pw->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
130   if (!pw->user_label) pw->user_label = strdup("ERROR");
131   if (!pw->passwd_label) pw->passwd_label = strdup("ERROR");
132
133   /* Put the version number in the label. */
134   {
135     char *s = (char *) malloc (strlen(pw->heading_label) + 20);
136     sprintf(s, pw->heading_label, si->version);
137     free (pw->heading_label);
138     pw->heading_label = s;
139   }
140
141   pw->user_string = (p->pw_name ? p->pw_name : "???");
142   pw->passwd_string = strdup("");
143
144   f = get_string_resource ("passwd.headingFont", "Dialog.Font");
145   pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
146   if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
147   if (f) free (f);
148
149   f = get_string_resource("passwd.bodyFont", "Dialog.Font");
150   pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
151   if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
152   if (f) free (f);
153
154   f = get_string_resource("passwd.labelFont", "Dialog.Font");
155   pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
156   if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
157   if (f) free (f);
158
159   f = get_string_resource("passwd.passwdFont", "Dialog.Font");
160   pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
161   if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
162   if (f) free (f);
163
164   pw->foreground = get_pixel_resource ("passwd.foreground",
165                                        "Dialog.Foreground",
166                                        si->dpy, cmap);
167   pw->background = get_pixel_resource ("passwd.background",
168                                        "Dialog.Background",
169                                        si->dpy, cmap);
170
171   if (pw->foreground == pw->background)
172     {
173       /* Make sure the error messages show up. */
174       pw->foreground = BlackPixelOfScreen (screen);
175       pw->background = WhitePixelOfScreen (screen);
176     }
177
178   pw->passwd_foreground = get_pixel_resource ("passwd.text.foreground",
179                                               "Dialog.Text.Foreground",
180                                               si->dpy, cmap);
181   pw->passwd_background = get_pixel_resource ("passwd.text.background",
182                                               "Dialog.Text.Background",
183                                               si->dpy, cmap);
184   pw->logo_foreground = get_pixel_resource ("passwd.logo.foreground",
185                                             "Dialog.Logo.Foreground",
186                                             si->dpy, cmap);
187   pw->logo_background = get_pixel_resource ("passwd.logo.background",
188                                             "Dialog.Logo.Background",
189                                             si->dpy, cmap);
190   pw->shadow_top = get_pixel_resource ("passwd.topShadowColor",
191                                        "Dialog.Foreground",
192                                        si->dpy, cmap);
193   pw->shadow_bottom = get_pixel_resource ("passwd.bottomShadowColor",
194                                           "Dialog.Background",
195                                           si->dpy, cmap);
196
197   pw->logo_width = get_integer_resource ("passwd.logo.width",
198                                          "Dialog.Logo.Width");
199   pw->logo_height = get_integer_resource ("passwd.logo.height",
200                                           "Dialog.Logo.Height");
201   pw->thermo_width = get_integer_resource ("passwd.thermometer.width",
202                                            "Dialog.Thermometer.Width");
203   pw->internal_border = get_integer_resource ("passwd.internalBorderWidth",
204                                               "Dialog.InternalBorderWidth");
205   pw->shadow_width = get_integer_resource ("passwd.shadowThickness",
206                                            "Dialog.ShadowThickness");
207
208   if (pw->logo_width == 0)  pw->logo_width = 150;
209   if (pw->logo_height == 0) pw->logo_height = 150;
210   if (pw->internal_border == 0) pw->internal_border = 15;
211   if (pw->shadow_width == 0) pw->shadow_width = 4;
212   if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
213
214   {
215     int direction, ascent, descent;
216     XCharStruct overall;
217
218     pw->width = 0;
219     pw->height = 0;
220
221     /* Measure the heading_label. */
222     XTextExtents (pw->heading_font,
223                   pw->heading_label, strlen(pw->heading_label),
224                   &direction, &ascent, &descent, &overall);
225     if (overall.width > pw->width) pw->width = overall.width;
226     pw->height += ascent + descent;
227
228     /* Measure the body_label. */
229     XTextExtents (pw->body_font,
230                   pw->body_label, strlen(pw->body_label),
231                   &direction, &ascent, &descent, &overall);
232     if (overall.width > pw->width) pw->width = overall.width;
233     pw->height += ascent + descent;
234
235     {
236       Dimension w2 = 0, w3 = 0;
237       Dimension h2 = 0, h3 = 0;
238       const char *passwd_string = "MMMMMMMMMMMM";
239
240       /* Measure the user_label. */
241       XTextExtents (pw->label_font,
242                     pw->user_label, strlen(pw->user_label),
243                     &direction, &ascent, &descent, &overall);
244       if (overall.width > w2)  w2 = overall.width;
245       h2 += ascent + descent;
246
247       /* Measure the passwd_label. */
248       XTextExtents (pw->label_font,
249                     pw->passwd_label, strlen(pw->passwd_label),
250                     &direction, &ascent, &descent, &overall);
251       if (overall.width > w2)  w2 = overall.width;
252       h2 += ascent + descent;
253
254       /* Measure the user_string. */
255       XTextExtents (pw->passwd_font,
256                     pw->user_string, strlen(pw->user_string),
257                     &direction, &ascent, &descent, &overall);
258       overall.width += (pw->shadow_width * 4);
259       ascent += (pw->shadow_width * 4);
260       if (overall.width > w3)  w3 = overall.width;
261       h3 += ascent + descent;
262
263       /* Measure the (maximally-sized, dummy) passwd_string. */
264       XTextExtents (pw->passwd_font,
265                     passwd_string, strlen(passwd_string),
266                     &direction, &ascent, &descent, &overall);
267       overall.width += (pw->shadow_width * 4);
268       ascent += (pw->shadow_width * 4);
269       if (overall.width > w3)  w3 = overall.width;
270       h3 += ascent + descent;
271
272       w2 = w2 + w3 + (pw->shadow_width * 2);
273       h2 = MAX (h2, h3);
274
275       if (w2 > pw->width)  pw->width  = w2;
276       pw->height += h2;
277     }
278
279     pw->width  += (pw->internal_border * 2);
280     pw->height += (pw->internal_border * 4);
281
282     pw->width += pw->thermo_width + (pw->shadow_width * 3);
283
284     if (pw->logo_height > pw->height)
285       pw->height = pw->logo_height;
286     else if (pw->height > pw->logo_height)
287       pw->logo_height = pw->height;
288
289     pw->logo_width = pw->logo_height;
290
291     pw->width += pw->logo_width;
292   }
293
294   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
295   attrmask |= CWEventMask; attrs.event_mask = ExposureMask|KeyPressMask;
296
297   {
298     Dimension w = WidthOfScreen(screen);
299     Dimension h = HeightOfScreen(screen);
300     if (si->prefs.debug_p) w /= 2;
301     x = ((w + pw->width) / 2) - pw->width;
302     y = ((h + pw->height) / 2) - pw->height;
303     if (x < 0) x = 0;
304     if (y < 0) y = 0;
305   }
306
307   bw = get_integer_resource ("passwd.borderWidth", "Dialog.BorderWidth");
308
309   si->passwd_dialog =
310     XCreateWindow (si->dpy,
311                    RootWindowOfScreen(screen),
312                    x, y, pw->width, pw->height, bw,
313                    DefaultDepthOfScreen (screen), InputOutput,
314                    DefaultVisualOfScreen(screen),
315                    attrmask, &attrs);
316   XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
317
318   XMapRaised (si->dpy, si->passwd_dialog);
319   XSync (si->dpy, False);
320
321   si->pw_data = pw;
322
323   draw_passwd_window (si);
324   XSync (si->dpy, False);
325 }
326
327
328 void
329 draw_passwd_window (saver_info *si)
330 {
331   passwd_dialog_data *pw = si->pw_data;
332   XGCValues gcv;
333   GC gc1, gc2;
334   int spacing, height;
335   int x1, x2, x3, y1, y2;
336   int sw;
337   int tb_height;
338
339   height = (pw->heading_font->ascent + pw->heading_font->descent +
340             pw->body_font->ascent + pw->body_font->descent +
341             (2 * MAX ((pw->label_font->ascent + pw->label_font->descent),
342                       (pw->passwd_font->ascent + pw->passwd_font->descent +
343                        (pw->shadow_width * 4)))));
344   spacing = ((pw->height - (2 * pw->shadow_width) -
345               pw->internal_border - height)) / 8;
346   if (spacing < 0) spacing = 0;
347
348   gcv.foreground = pw->foreground;
349   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
350   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
351   x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
352   x3 = pw->width - (pw->shadow_width * 2);
353   y1 = (pw->shadow_width * 2) + spacing + spacing;
354
355   /* top heading
356    */
357   XSetFont (si->dpy, gc1, pw->heading_font->fid);
358   sw = string_width (pw->heading_font, pw->heading_label);
359   x2 = (x1 + ((x3 - x1 - sw) / 2));
360   y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
361   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
362                pw->heading_label, strlen(pw->heading_label));
363
364   /* text below top heading
365    */
366   XSetFont (si->dpy, gc1, pw->body_font->fid);
367   y1 += spacing + pw->body_font->ascent + pw->body_font->descent;
368   sw = string_width (pw->body_font, pw->body_label);
369   x2 = (x1 + ((x3 - x1 - sw) / 2));
370   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
371                pw->body_label, strlen(pw->body_label));
372
373
374   tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
375                (pw->shadow_width * 4));
376
377   /* the "User:" prompt
378    */
379   y1 += spacing;
380   y2 = y1;
381   XSetForeground (si->dpy, gc1, pw->foreground);
382   XSetFont (si->dpy, gc1, pw->label_font->fid);
383   y1 += (spacing + tb_height);
384   x2 = (x1 + pw->internal_border +
385         MAX(string_width (pw->label_font, pw->user_label),
386             string_width (pw->label_font, pw->passwd_label)));
387   XDrawString (si->dpy, si->passwd_dialog, gc1,
388                x2 - string_width (pw->label_font, pw->user_label),
389                y1,
390                pw->user_label, strlen(pw->user_label));
391
392   /* the "Password:" prompt
393    */
394   y1 += (spacing + tb_height);
395   XDrawString (si->dpy, si->passwd_dialog, gc1,
396                x2 - string_width (pw->label_font, pw->passwd_label),
397                y1,
398                pw->passwd_label, strlen(pw->passwd_label));
399
400
401   XSetForeground (si->dpy, gc2, pw->passwd_background);
402
403   /* the "user name" text field
404    */
405   y1 = y2;
406   XSetForeground (si->dpy, gc1, pw->passwd_foreground);
407   XSetFont (si->dpy, gc1, pw->passwd_font->fid);
408   y1 += (spacing + tb_height);
409   x2 += (pw->shadow_width * 4);
410
411   pw->passwd_field_width = x3 - x2 - pw->internal_border;
412   pw->passwd_field_height = (pw->passwd_font->ascent +
413                              pw->passwd_font->descent +
414                              pw->shadow_width);
415
416   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
417                   x2 - pw->shadow_width,
418                   y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
419                   pw->passwd_field_width, pw->passwd_field_height);
420   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
421                pw->user_string, strlen(pw->user_string));
422
423   /* the "password" text field
424    */
425   y1 += (spacing + tb_height);
426
427   pw->passwd_field_x = x2 - pw->shadow_width;
428   pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
429                              pw->passwd_font->descent);
430
431   /* The shadow around the text fields
432    */
433   y1 = y2;
434   y1 += (spacing + (pw->shadow_width * 3));
435   x1 = x2 - (pw->shadow_width * 2);
436   x2 = pw->passwd_field_width + (pw->shadow_width * 2);
437   y2 = pw->passwd_field_height + (pw->shadow_width * 2);
438
439   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
440                          x1, y1, x2, y2,
441                          pw->shadow_width,
442                          pw->shadow_bottom, pw->shadow_top);
443
444   y1 += (spacing + pw->passwd_font->ascent + pw->passwd_font->descent +
445          (pw->shadow_width * 4));
446   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
447                          x1, y1, x2, y2,
448                          pw->shadow_width,
449                          pw->shadow_bottom, pw->shadow_top);
450
451   /* the logo
452    */
453   XSetForeground (si->dpy, gc1, pw->logo_foreground);
454   XSetForeground (si->dpy, gc2, pw->logo_background);
455
456   x1 = pw->shadow_width * 3;
457   y1 = pw->shadow_width * 3;
458   x2 = pw->logo_width - (pw->shadow_width * 6);
459   y2 = pw->logo_height - (pw->shadow_width * 6);
460
461   XFillRectangle (si->dpy, si->passwd_dialog, gc2, x1, y1, x2, y2);
462   skull (si->dpy, si->passwd_dialog, gc1, gc2,
463          x1 + pw->shadow_width, y1 + pw->shadow_width,
464          x2 - (pw->shadow_width * 2), y2 - (pw->shadow_width * 2));
465
466   /* The thermometer
467    */
468   pw->thermo_field_x = pw->logo_width + pw->shadow_width;
469   pw->thermo_field_y = pw->shadow_width * 3;
470   pw->thermo_field_height = pw->height - (pw->shadow_width * 6);
471
472   /* Solid border inside the logo box. */
473   XSetForeground (si->dpy, gc1, pw->foreground);
474   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
475
476   /* The shadow around the logo
477    */
478   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
479                          pw->shadow_width * 2,
480                          pw->shadow_width * 2,
481                          pw->logo_width - (pw->shadow_width * 4),
482                          pw->logo_height - (pw->shadow_width * 4),
483                          pw->shadow_width,
484                          pw->shadow_bottom, pw->shadow_top);
485
486   /* The shadow around the thermometer
487    */
488   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
489                          pw->logo_width,
490                          pw->shadow_width * 2,
491                          pw->thermo_width + (pw->shadow_width * 2),
492                          pw->height - (pw->shadow_width * 4),
493                          pw->shadow_width,
494                          pw->shadow_bottom, pw->shadow_top);
495
496   /* Solid border inside the thermometer. */
497   XSetForeground (si->dpy, gc1, pw->foreground);
498   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, 
499                   pw->logo_width + pw->shadow_width,
500                   pw->shadow_width * 3,
501                   pw->thermo_width - 1,
502                   pw->height - (pw->shadow_width * 6) - 1);
503
504   /* The shadow around the whole window
505    */
506   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
507                          0, 0, pw->width, pw->height, pw->shadow_width,
508                          pw->shadow_top, pw->shadow_bottom);
509
510   XFreeGC (si->dpy, gc1);
511   XFreeGC (si->dpy, gc2);
512
513   update_passwd_window (si, pw->passwd_string, pw->ratio);
514 }
515
516
517 void
518 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
519 {
520   passwd_dialog_data *pw = si->pw_data;
521   XGCValues gcv;
522   GC gc1, gc2;
523   int x, y;
524
525   pw->ratio = ratio;
526   gcv.foreground = pw->passwd_foreground;
527   gcv.font = pw->passwd_font->fid;
528   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
529   gcv.foreground = pw->passwd_background;
530   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
531
532   if (printed_passwd)
533     {
534       char *s = strdup (printed_passwd);
535       if (pw->passwd_string) free (pw->passwd_string);
536       pw->passwd_string = s;
537     }
538
539   /* the "password" text field
540    */
541   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
542                   pw->passwd_field_x, pw->passwd_field_y,
543                   pw->passwd_field_width, pw->passwd_field_height);
544   XDrawString (si->dpy, si->passwd_dialog, gc1,
545                pw->passwd_field_x + pw->shadow_width,
546                pw->passwd_field_y + (pw->passwd_font->ascent +
547                                      pw->passwd_font->descent),
548                pw->passwd_string, strlen(pw->passwd_string));
549
550   /* The I-beam
551    */
552   if (pw->i_beam != 0)
553     {
554       x = (pw->passwd_field_x + pw->shadow_width +
555            string_width (pw->passwd_font, pw->passwd_string));
556       y = pw->passwd_field_y + pw->shadow_width;
557       XDrawLine (si->dpy, si->passwd_dialog, gc1, 
558                  x, y, x, y + pw->passwd_font->ascent);
559     }
560
561   pw->i_beam = (pw->i_beam + 1) % 4;
562
563
564   /* the thermometer
565    */
566   y = pw->thermo_field_height * (1.0 - pw->ratio);
567   if (y > 0)
568     {
569       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
570                       pw->thermo_field_x + 1,
571                       pw->thermo_field_y + 1,
572                       pw->thermo_width-2,
573                       y);
574       XSetForeground (si->dpy, gc1, pw->logo_foreground);
575       XFillRectangle (si->dpy, si->passwd_dialog, gc1,
576                       pw->thermo_field_x + 1,
577                       pw->thermo_field_y + 1 + y,
578                       pw->thermo_width-2,
579                       MAX (0, pw->thermo_field_height - y - 2));
580     }
581
582   XFreeGC (si->dpy, gc1);
583   XFreeGC (si->dpy, gc2);
584   XSync (si->dpy, False);
585 }
586
587
588 void
589 destroy_passwd_window (saver_info *si)
590 {
591   passwd_dialog_data *pw = si->pw_data;
592   Screen *screen = si->default_screen->screen;
593   Colormap cmap = DefaultColormapOfScreen (screen);
594   Pixel black = BlackPixelOfScreen (screen);
595   Pixel white = WhitePixelOfScreen (screen);
596
597   if (pw->timer)
598     XtRemoveTimeOut (pw->timer);
599
600   if (si->passwd_dialog)
601     {
602       XDestroyWindow (si->dpy, si->passwd_dialog);
603       si->passwd_dialog = 0;
604     }
605   
606   if (pw->heading_label) free (pw->heading_label);
607   if (pw->body_label)    free (pw->body_label);
608   if (pw->user_label)    free (pw->user_label);
609   if (pw->passwd_label)  free (pw->passwd_label);
610
611   if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
612   if (pw->body_font)    XFreeFont (si->dpy, pw->body_font);
613   if (pw->label_font)   XFreeFont (si->dpy, pw->label_font);
614   if (pw->passwd_font)  XFreeFont (si->dpy, pw->passwd_font);
615
616   if (pw->foreground != black && pw->foreground != white)
617     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
618   if (pw->background != black && pw->background != white)
619     XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
620   if (pw->passwd_foreground != black && pw->passwd_foreground != white)
621     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
622   if (pw->passwd_background != black && pw->passwd_background != white)
623     XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
624   if (pw->logo_foreground != black && pw->logo_foreground != white)
625     XFreeColors (si->dpy, cmap, &pw->logo_foreground, 1, 0L);
626   if (pw->logo_background != black && pw->logo_background != white)
627     XFreeColors (si->dpy, cmap, &pw->logo_background, 1, 0L);
628   if (pw->shadow_top != black && pw->shadow_top != white)
629     XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
630   if (pw->shadow_bottom != black && pw->shadow_bottom != white)
631     XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
632
633   memset (pw, 0, sizeof(*pw));
634   free (pw);
635
636   si->pw_data = 0;
637 }
638
639 \f
640 /* Interactions
641  */
642
643 static void
644 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
645 {
646   saver_info *si = (saver_info *) closure;
647   int tick = 166;
648   passwd_dialog_data *pw = si->pw_data;
649
650   if (!pw) return;
651
652   pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
653   if (pw->ratio < 0)
654     {
655       pw->ratio = 0;
656       if (pw->state == pw_read)
657         pw->state = pw_time;
658     }
659
660   update_passwd_window (si, 0, pw->ratio);
661
662   if (pw->state == pw_read)
663     pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
664                                  (XtPointer) si);
665   else
666     pw->timer = 0;
667
668   idle_timer ((XtPointer) si, id);
669 }
670
671
672 static void
673 handle_passwd_key (saver_info *si, XKeyEvent *event)
674 {
675   passwd_dialog_data *pw = si->pw_data;
676   int pw_size = sizeof (pw->typed_passwd) - 1;
677   char *typed_passwd = pw->typed_passwd;
678   char s[2];
679   char *stars = 0;
680   int i;
681   int size = XLookupString (event, s, 1, 0, 0);
682
683   if (size != 1) return;
684
685   s[1] = 0;
686
687   switch (*s)
688     {
689     case '\010': case '\177':                           /* Backspace */
690       if (!*typed_passwd)
691         XBell (si->dpy, 0);
692       else
693         typed_passwd [strlen(typed_passwd)-1] = 0;
694       break;
695
696     case '\025': case '\030':                           /* Erase line */
697       memset (typed_passwd, 0, pw_size);
698       break;
699
700     case '\012': case '\015':                           /* Enter */
701       if (pw->state != pw_read)
702         ;  /* already done? */
703       else if (passwd_valid_p (typed_passwd))
704         pw->state = pw_ok;
705       else if (typed_passwd[0] == 0)
706         pw->state = pw_null;
707       else
708         pw->state = pw_fail;
709       break;
710
711     default:
712       i = strlen (typed_passwd);
713       if (i >= pw_size-1)
714         XBell (si->dpy, 0);
715       else
716         {
717           typed_passwd [i] = *s;
718           typed_passwd [i+1] = 0;
719         }
720       break;
721     }
722
723   i = strlen(typed_passwd);
724   stars = (char *) malloc(i+1);
725   memset (stars, '*', i);
726   stars[i] = 0;
727   update_passwd_window (si, stars, pw->ratio);
728   free (stars);
729 }
730
731
732 static void
733 passwd_event_loop (saver_info *si)
734 {
735   saver_preferences *p = &si->prefs;
736   char *msg = 0;
737   XEvent event;
738   passwd_animate_timer ((XtPointer) si, 0);
739
740   while (si->pw_data && si->pw_data->state == pw_read)
741     {
742       XtAppNextEvent (si->app, &event);
743       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
744         draw_passwd_window (si);
745       else if (event.xany.type == KeyPress)
746         handle_passwd_key (si, &event.xkey);
747       else
748         XtDispatchEvent (&event);
749     }
750
751   switch (si->pw_data->state)
752     {
753     case pw_ok:   msg = 0; break;
754     case pw_null: msg = ""; break;
755     case pw_time: msg = "Timed out!"; break;
756     default:      msg = "Sorry!"; break;
757     }
758
759   if (si->pw_data->state == pw_fail)
760     si->unlock_failures++;
761
762   if (p->verbose_p)
763     switch (si->pw_data->state)
764       {
765       case pw_ok:
766         fprintf (stderr, "%s: password correct.\n", blurb()); break;
767       case pw_fail:
768         fprintf (stderr, "%s: password incorrect!\n", blurb()); break;
769       case pw_null:
770       case pw_cancel:
771         fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break;
772       case pw_time:
773         fprintf (stderr, "%s: password entry timed out.\n", blurb()); break;
774       default: break;
775       }
776
777 #ifdef HAVE_SYSLOG
778   if (si->pw_data->state == pw_fail)
779     {
780       /* If they typed a password (as opposed to just hitting return) and
781          the password was invalid, log it.
782       */
783       struct passwd *pw = getpwuid (getuid ());
784       char *d = DisplayString (si->dpy);
785       char *u = (pw->pw_name ? pw->pw_name : "???");
786       int opt = 0;
787       int fac = 0;
788
789 # ifdef LOG_PID
790       opt = LOG_PID;
791 # endif
792
793 # if defined(LOG_AUTHPRIV)
794       fac = LOG_AUTHPRIV;
795 # elif defined(LOG_AUTH)
796       fac = LOG_AUTH;
797 # else
798       fac = LOG_DAEMON;
799 # endif
800
801       if (!d) d = "";
802       openlog (progname, opt, fac);
803       syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
804               si->unlock_failures, d, u);
805       closelog ();
806     }
807 #endif /* HAVE_SYSLOG */
808
809   if (si->pw_data->state == pw_ok && si->unlock_failures != 0)
810     {
811       if (si->unlock_failures == 1)
812         fprintf (real_stderr,
813                  "%s: WARNING: 1 failed attempt to unlock the screen.\n",
814                  blurb());
815       else
816         fprintf (real_stderr,
817                  "%s: WARNING: %d failed attempts to unlock the screen.\n",
818                  blurb(), si->unlock_failures);
819       fflush (real_stderr);
820
821       si->unlock_failures = 0;
822     }
823
824   if (msg)
825     {
826       si->pw_data->i_beam = 0;
827       update_passwd_window (si, msg, 0.0);
828       XBell (si->dpy, False);
829       XSync (si->dpy, False);
830       sleep (1);
831
832       /* Swallow all pending KeyPress/KeyRelease events. */
833       {
834         XEvent e;
835         while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
836           ;
837       }
838     }
839 }
840
841
842 Bool
843 unlock_p (saver_info *si)
844 {
845   saver_preferences *p = &si->prefs;
846   Screen *screen = si->default_screen->screen;
847   Colormap cmap = DefaultColormapOfScreen (screen);
848   Bool status;
849
850   if (p->verbose_p)
851     fprintf (stderr, "%s: prompting for password.\n", blurb());
852
853   if (si->pw_data || si->passwd_dialog)
854     destroy_passwd_window (si);
855
856   make_passwd_window (si);
857   if (cmap) XInstallColormap (si->dpy, cmap);
858
859   passwd_event_loop (si);
860
861   status = (si->pw_data->state == pw_ok);
862   destroy_passwd_window (si);
863
864   cmap = si->default_screen->cmap;
865   if (cmap) XInstallColormap (si->dpy, cmap);
866
867   return status;
868 }
869
870 #endif /* !NO_LOCKING -- whole file */