X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Flock.c;h=7c92be60d6375bda6dbb69c47191d1ab833919ef;hb=6afd6db0ae9396cd7ff897ade597cd5483f49b0e;hp=bba326b0be97f10e1e4568ac9e13e0fa9b715bad;hpb=c494fd2e6b3b25582375d62e40f4f5cc984ca424;p=xscreensaver diff --git a/driver/lock.c b/driver/lock.c index bba326b0..7c92be60 100644 --- a/driver/lock.c +++ b/driver/lock.c @@ -1,5 +1,5 @@ /* lock.c --- handling the password dialog for locking-mode. - * xscreensaver, Copyright (c) 1993-2007 Jamie Zawinski + * xscreensaver, Copyright (c) 1993-2014 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -30,10 +30,6 @@ #ifndef NO_LOCKING /* (mostly) whole file */ -#ifdef HAVE_SYSLOG -# include -#endif /* HAVE_SYSLOG */ - #ifdef HAVE_XHPDISABLERESET # include static void hp_lock_reset (saver_info *si, Bool lock_p); @@ -49,6 +45,9 @@ static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p); #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */ +#ifdef HAVE_RANDR +# include +#endif /* HAVE_RANDR */ #ifdef _VROOT_H_ ERROR! You must not include vroot.h in this file. @@ -84,12 +83,23 @@ vms_passwd_valid_p(char *pw, Bool verbose_p) typedef struct info_dialog_data info_dialog_data; + +#define MAX_BYTES_PER_CHAR 8 /* UTF-8 uses no more than 3, I think */ +#define MAX_PASSWD_CHARS 128 /* Longest possible passphrase */ + struct passwd_dialog_data { saver_screen_info *prompt_screen; int previous_mouse_x, previous_mouse_y; - char typed_passwd [80]; + /* "Characters" in the password may be a variable number of bytes long. + typed_passwd contains the raw bytes. + typed_passwd_char_size indicates the size in bytes of each character, + so that we can make backspace work. + */ + char typed_passwd [MAX_PASSWD_CHARS * MAX_BYTES_PER_CHAR]; + char typed_passwd_char_size [MAX_PASSWD_CHARS]; + XtIntervalId timer; int i_beam; @@ -112,6 +122,7 @@ struct passwd_dialog_data { char *date_label; char *passwd_string; Bool passwd_changed_p; /* Whether the user entry field needs redrawing */ + Bool caps_p; /* Whether we saw a keypress with caps-lock on */ char *unlock_label; char *login_label; char *uname_label; @@ -128,6 +139,7 @@ struct passwd_dialog_data { Pixel foreground; Pixel background; + Pixel border; Pixel passwd_foreground; Pixel passwd_background; Pixel thermo_foreground; @@ -182,7 +194,7 @@ static void restore_background (saver_info *si); extern void xss_authenticate(saver_info *si, Bool verbose_p); -static void +static int new_passwd_window (saver_info *si) { passwd_dialog_data *pw; @@ -193,7 +205,7 @@ new_passwd_window (saver_info *si) pw = (passwd_dialog_data *) calloc (1, sizeof(*pw)); if (!pw) - return; + return -1; /* Display the button only if the "newLoginCommand" pref is non-null. */ @@ -203,9 +215,6 @@ new_passwd_window (saver_info *si) pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow); pw->prompt_screen = ssi; - if (si->prefs.verbose_p) - fprintf (stderr, "%s: %d: creating password dialog.\n", - blurb(), pw->prompt_screen->number); screen = pw->prompt_screen->screen; cmap = DefaultColormapOfScreen (screen); @@ -311,6 +320,9 @@ new_passwd_window (saver_info *si) pw->background = get_pixel_resource (si->dpy, cmap, "passwd.background", "Dialog.Background" ); + pw->border = get_pixel_resource (si->dpy, cmap, + "passwd.borderColor", + "Dialog.borderColor"); if (pw->foreground == pw->background) { @@ -333,10 +345,10 @@ new_passwd_window (saver_info *si) "Dialog.Button.Background" ); pw->thermo_foreground = get_pixel_resource (si->dpy, cmap, "passwd.thermometer.foreground", - "Dialog.Thermometer.Foreground" ); + "Dialog.Thermometer.Foreground"); pw->thermo_background = get_pixel_resource ( si->dpy, cmap, "passwd.thermometer.background", - "Dialog.Thermometer.Background" ); + "Dialog.Thermometer.Background"); pw->shadow_top = get_pixel_resource ( si->dpy, cmap, "passwd.topShadowColor", "Dialog.Foreground" ); @@ -344,13 +356,16 @@ new_passwd_window (saver_info *si) "passwd.bottomShadowColor", "Dialog.Background" ); - pw->preferred_logo_width = get_integer_resource (si->dpy, "passwd.logo.width", + pw->preferred_logo_width = get_integer_resource (si->dpy, + "passwd.logo.width", "Dialog.Logo.Width"); - pw->preferred_logo_height = get_integer_resource (si->dpy, "passwd.logo.height", + pw->preferred_logo_height = get_integer_resource (si->dpy, + "passwd.logo.height", "Dialog.Logo.Height"); pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width", "Dialog.Thermometer.Width"); - pw->internal_border = get_integer_resource (si->dpy, "passwd.internalBorderWidth", + pw->internal_border = get_integer_resource (si->dpy, + "passwd.internalBorderWidth", "Dialog.InternalBorderWidth"); pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness", "Dialog.ShadowThickness"); @@ -389,10 +404,9 @@ new_passwd_window (saver_info *si) } /* Before mapping the window, save a pixmap of the current screen. - When we lower the window, we - restore these bits. This works, because the running screenhack - has already been sent SIGSTOP, so we know nothing else is drawing - right now! */ + When we lower the window, we restore these bits. This works, + because the running screenhack has already been sent SIGSTOP, so + we know nothing else is drawing right now! */ { XGCValues gcv; GC gc; @@ -412,13 +426,17 @@ new_passwd_window (saver_info *si) } si->pw_data = pw; + return 0; } +Bool debug_passwd_window_p = False; /* used only by test-passwd.c */ + + /** * info_msg and prompt may be NULL. */ -static void +static int make_passwd_window (saver_info *si, const char *info_msg, const char *prompt, @@ -434,18 +452,23 @@ make_passwd_window (saver_info *si, cleanup_passwd_window (si); + if (! ssi) /* WTF? Trying to prompt while no screens connected? */ + return -1; + if (!si->pw_data) - new_passwd_window (si); + if (new_passwd_window (si) < 0) + return -1; if (!(pw = si->pw_data)) - return; + return -1; pw->ratio = 1.0; pw->prompt_screen = ssi; if (si->prefs.verbose_p) - fprintf (stderr, "%s: %d: creating password dialog.\n", - blurb(), pw->prompt_screen->number); + fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n", + blurb(), pw->prompt_screen->number, + info_msg ? info_msg : ""); screen = pw->prompt_screen->screen; cmap = DefaultColormapOfScreen (screen); @@ -464,6 +487,11 @@ make_passwd_window (saver_info *si, * room for the dialog to grow without going off the edge of the screen. */ max_string_width_px *= 0.75; + if (!info_msg && senescent_p()) + info_msg = ("\n" + "This version of XScreenSaver\n" + "is very old! Please upgrade!\n"); + pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label, pw->label_font, max_string_width_px); @@ -504,7 +532,8 @@ make_passwd_window (saver_info *si, h2 += ascent + descent; /* Measure the info_label. */ - if (pw->info_label->overall_width > pw->width) pw->width = pw->info_label->overall_width; + if (pw->info_label->overall_width > pw->width) + pw->width = pw->info_label->overall_width; h2 += pw->info_label->overall_height; /* Measure the user string. */ @@ -529,9 +558,11 @@ make_passwd_window (saver_info *si, /* Measure the prompt_label. */ max_string_width_px -= w3; - pw->prompt_label = mlstring_new(prompt, pw->label_font, max_string_width_px); + pw->prompt_label = mlstring_new (prompt, pw->label_font, + max_string_width_px); - if (pw->prompt_label->overall_width > w2) w2 = pw->prompt_label->overall_width; + if (pw->prompt_label->overall_width > w2) + w2 = pw->prompt_label->overall_width; h2 += pw->prompt_label->overall_height; @@ -587,7 +618,9 @@ make_passwd_window (saver_info *si, /* Use (2 * shadow_width) spacing between the buttons. Another (2 * shadow_width) is required to account for button shadows. */ - w2 = MAX (w2, button_w + pw->unlock_button_width + (pw->shadow_width * 4)); + w2 = MAX (w2, + button_w + pw->unlock_button_width + + (pw->shadow_width * 4)); } if (w2 > pw->width) pw->width = w2; @@ -611,6 +644,9 @@ make_passwd_window (saver_info *si, attrmask |= CWOverrideRedirect; attrs.override_redirect = True; + if (debug_passwd_window_p) + attrs.override_redirect = False; /* kludge for test-passwd.c */ + attrmask |= CWEventMask; attrs.event_mask = (ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask); @@ -619,10 +655,11 @@ make_passwd_window (saver_info *si, actually be visible; this takes into account virtual viewports as well as Xinerama. */ { - int x, y, w, h; - get_screen_viewport (pw->prompt_screen, &x, &y, &w, &h, - pw->previous_mouse_x, pw->previous_mouse_y, - si->prefs.verbose_p); + saver_screen_info *ssi = &si->screens [mouse_screen (si)]; + int x = ssi->x; + int y = ssi->y; + int w = ssi->width; + int h = ssi->height; if (si->prefs.debug_p) w /= 2; pw->x = x + ((w + pw->width) / 2) - pw->width; pw->y = y + ((h + pw->height) / 2) - pw->height; @@ -644,6 +681,7 @@ make_passwd_window (saver_info *si, DefaultVisualOfScreen(screen), attrmask, &attrs); XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background); + XSetWindowBorder (si->dpy, si->passwd_dialog, pw->border); /* We use the default visual, not ssi->visual, so that the logo pixmap's visual matches that of the si->passwd_dialog window. */ @@ -683,6 +721,8 @@ make_passwd_window (saver_info *si, if (cmap) XInstallColormap (si->dpy, cmap); draw_passwd_window (si); + + return 0; } @@ -713,7 +753,7 @@ draw_passwd_window (saver_info *si) pw->date_font->ascent + pw->date_font->descent); if ((strlen(pw->uname_label)) && pw->show_uname_p) - height += (pw->uname_font->ascent + pw->uname_font->descent); /* for uname */ + height += (pw->uname_font->ascent + pw->uname_font->descent); height += ((pw->button_font->ascent + pw->button_font->descent) * 2 + 2 * pw->shadow_width); @@ -814,7 +854,7 @@ draw_passwd_window (saver_info *si) */ if (pw->prompt_label) { - y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2); + y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2); pw->passwd_field_x = x2 - pw->shadow_width; pw->passwd_field_y = y1 - (pw->passwd_font->ascent + @@ -836,7 +876,7 @@ draw_passwd_window (saver_info *si) if (pw->prompt_label) { - y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2); + y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2); draw_shaded_rectangle (si->dpy, si->passwd_dialog, x1, y1, x2, y2, pw->shadow_width, @@ -915,7 +955,9 @@ draw_passwd_window (saver_info *si) XSetForeground (si->dpy, gc1, pw->foreground); XSetBackground (si->dpy, gc1, pw->background); XSetClipMask (si->dpy, gc1, pw->logo_clipmask); - XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2)); + XSetClipOrigin (si->dpy, gc1, + x1 + ((x2 - (int)w) / 2), + y1 + ((y2 - (int)h) / 2)); if (d == 1) XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1, 0, 0, w, h, @@ -1076,9 +1118,10 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) pw->user_entry_pixmap = 0; } - pw->user_entry_pixmap = XCreatePixmap(si->dpy, si->passwd_dialog, - rects[0].width, rects[0].height, pw->prompt_screen->current_depth); - + pw->user_entry_pixmap = + XCreatePixmap (si->dpy, si->passwd_dialog, + rects[0].width, rects[0].height, + DefaultDepthOfScreen (pw->prompt_screen->screen)); XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2, 0, 0, rects[0].width, rects[0].height); @@ -1113,7 +1156,8 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) x = rects[0].x + rects[0].width - 1; XDrawLine (si->dpy, si->passwd_dialog, gc1, x, y, - x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1); + x, y + pw->passwd_font->ascent + + pw->passwd_font->descent-1); } pw->i_beam = (pw->i_beam + 1) % 4; @@ -1150,8 +1194,10 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) pw->unlock_button_x, pw->unlock_button_y, pw->unlock_button_width, pw->unlock_button_height, pw->shadow_width, - (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top), - (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom), + (pw->unlock_button_down_p ? pw->shadow_bottom : + pw->shadow_top), + (pw->unlock_button_down_p ? pw->shadow_top : + pw->shadow_bottom), pw->unlock_button_down_p); /* The "New Login" button @@ -1227,6 +1273,7 @@ cleanup_passwd_window (saver_info *si) } memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd)); + memset (pw->typed_passwd_char_size, 0, sizeof(pw->typed_passwd_char_size)); memset (pw->passwd_string, 0, strlen(pw->passwd_string)); if (pw->timer) @@ -1281,13 +1328,26 @@ destroy_passwd_window (saver_info *si) XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen), 0, 0, 0, 0, pw->previous_mouse_x, pw->previous_mouse_y); + XSync (si->dpy, False); while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event)) if (p->verbose_p) fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb()); +#ifdef HAVE_XINPUT + if (si->using_xinput_extension && si->xinput_DeviceMotionNotify) + while (XCheckTypedEvent (si->dpy, si->xinput_DeviceMotionNotify, &event)) + if (p->verbose_p) + fprintf (stderr, "%s: discarding DeviceMotionNotify event.\n", + blurb()); +#endif + if (si->passwd_dialog) { + if (si->prefs.verbose_p) + fprintf (stderr, "%s: %d: destroying password dialog.\n", + blurb(), pw->prompt_screen->number); + XDestroyWindow (si->dpy, si->passwd_dialog); si->passwd_dialog = 0; } @@ -1362,6 +1422,8 @@ destroy_passwd_window (saver_info *si) } +#if defined(HAVE_XF86MISCSETGRABKEYSSTATE) || defined(HAVE_XF86VMODE) + static Bool error_handler_hit_p = False; static int @@ -1371,6 +1433,8 @@ ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) return 0; } +#endif /* HAVE_XF86MISCSETGRABKEYSSTATE || HAVE_XF86VMODE */ + #ifdef HAVE_XHPDISABLERESET /* This function enables and disables the C-Sh-Reset hot-key, which @@ -1418,8 +1482,12 @@ xfree_lock_grab_smasher (saver_info *si, Bool lock_p) { saver_preferences *p = &si->prefs; int status; - + int event, error; XErrorHandler old_handler; + + if (!XF86MiscQueryExtension(si->dpy, &event, &error)) + return; + XSync (si->dpy, False); error_handler_hit_p = False; old_handler = XSetErrorHandler (ignore_all_errors_ehandler); @@ -1463,6 +1531,7 @@ xfree_lock_mode_switch (saver_info *si, Bool lock_p) static Bool any_mode_locked_p = False; saver_preferences *p = &si->prefs; int screen; + int real_nscreens = ScreenCount (si->dpy); int event, error; Bool status; XErrorHandler old_handler; @@ -1472,7 +1541,7 @@ xfree_lock_mode_switch (saver_info *si, Bool lock_p) if (!XF86VidModeQueryExtension (si->dpy, &event, &error)) return; - for (screen = 0; screen < (si->xinerama_p ? 1 : si->nscreens); screen++) + for (screen = 0; screen < real_nscreens; screen++) { XSync (si->dpy, False); old_handler = XSetErrorHandler (ignore_all_errors_ehandler); @@ -1509,12 +1578,13 @@ undo_vp_motion (saver_info *si) #ifdef HAVE_XF86VMODE saver_preferences *p = &si->prefs; int screen; + int real_nscreens = ScreenCount (si->dpy); int event, error; if (!XF86VidModeQueryExtension (si->dpy, &event, &error)) return; - for (screen = 0; screen < si->nscreens; screen++) + for (screen = 0; screen < real_nscreens; screen++) { saver_screen_info *ssi = &si->screens[screen]; int x, y; @@ -1667,85 +1737,129 @@ static void handle_passwd_key (saver_info *si, XKeyEvent *event) { passwd_dialog_data *pw = si->pw_data; - int pw_size = sizeof (pw->typed_passwd) - 1; - char *typed_passwd = pw->typed_passwd; - char s[2]; - char *stars = 0; - int i; - int size = XLookupString (event, s, 1, 0, compose_status); + unsigned char decoded [MAX_BYTES_PER_CHAR * 10]; /* leave some slack */ + KeySym keysym = 0; - if (size != 1) return; + /* XLookupString may return more than one character via XRebindKeysym; + and on some systems it returns multi-byte UTF-8 characters (contrary + to its documentation, which says it returns only Latin1.) - s[1] = 0; + It seems to only do so, however, if setlocale() has been called. + See the code inside ENABLE_NLS in xscreensaver.c. + */ + int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded), + &keysym, compose_status); +#if 0 + { + const char *ks = XKeysymToString (keysym); + int i; + fprintf(stderr, "## %-12s\t=> %d\t", (ks ? ks : "(null)"), decoded_size); + for (i = 0; i < decoded_size; i++) + fprintf(stderr, "%c", decoded[i]); + fprintf(stderr, "\t"); + for (i = 0; i < decoded_size; i++) + fprintf(stderr, "\\%03o", ((unsigned char *)decoded)[i]); + fprintf(stderr, "\n"); + } +#endif + + if (decoded_size > MAX_BYTES_PER_CHAR) + { + /* The multi-byte character returned is too large. */ + XBell (si->dpy, 0); + return; + } + + decoded[decoded_size] = 0; pw->passwd_changed_p = True; /* Add 10% to the time remaining every time a key is pressed. */ pw->ratio += 0.1; if (pw->ratio > 1) pw->ratio = 1; - switch (*s) + if (decoded_size == 1) /* Handle single-char commands */ { - case '\010': case '\177': /* Backspace */ - if (!*typed_passwd) - XBell (si->dpy, 0); - else - typed_passwd [strlen(typed_passwd)-1] = 0; - break; - - case '\025': case '\030': /* Erase line */ - memset (typed_passwd, 0, pw_size); - break; - - case '\012': case '\015': /* Enter */ - finished_typing_passwd(si, pw); - break; - - case '\033': /* Escape */ - si->unlock_state = ul_cancel; - break; - - default: - /* Though technically the only illegal characters in Unix passwords - are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry - fields that only let you type printable characters. So, people - who use funky characters in their passwords are already broken. - We follow that precedent. - */ - if (isprint ((unsigned char) *s)) + switch (*decoded) { - i = strlen (typed_passwd); - if (i >= pw_size-1) + case '\010': case '\177': /* Backspace */ + { + /* kludgey way to get the number of "logical" characters. */ + int nchars = strlen (pw->typed_passwd_char_size); + int nbytes = strlen (pw->typed_passwd); + if (nbytes <= 0) + XBell (si->dpy, 0); + else + { + int i; + for (i = pw->typed_passwd_char_size[nchars-1]; i >= 0; i--) + { + if (nbytes < 0) abort(); + pw->typed_passwd[nbytes--] = 0; + } + pw->typed_passwd_char_size[nchars-1] = 0; + } + } + break; + + case '\012': case '\015': /* Enter */ + finished_typing_passwd (si, pw); + break; + + case '\033': /* Escape */ + si->unlock_state = ul_cancel; + break; + + case '\025': case '\030': /* Erase line */ + memset (pw->typed_passwd, 0, sizeof (pw->typed_passwd)); + memset (pw->typed_passwd_char_size, 0, + sizeof (pw->typed_passwd_char_size)); + break; + + default: + if (*decoded < ' ' && *decoded != '\t') /* Other ctrl char */ XBell (si->dpy, 0); else - { - typed_passwd [i] = *s; - typed_passwd [i+1] = 0; - } + goto SELF_INSERT; + break; } - else + } + else + { + int nbytes, nchars; + SELF_INSERT: + nbytes = strlen (pw->typed_passwd); + nchars = strlen (pw->typed_passwd_char_size); + if (nchars + 1 >= sizeof (pw->typed_passwd_char_size)-1 || + nbytes + decoded_size >= sizeof (pw->typed_passwd)-1) /* overflow */ XBell (si->dpy, 0); - break; + else + { + pw->typed_passwd_char_size[nchars] = decoded_size; + pw->typed_passwd_char_size[nchars+1] = 0; + memcpy (pw->typed_passwd + nbytes, decoded, decoded_size); + pw->typed_passwd[nbytes + decoded_size] = 0; + } } if (pw->echo_input) { - /* If the input is wider than the text box, only show the last portion. - * This simulates a horizontally scrolling text field. */ + /* If the input is wider than the text box, only show the last portion, + to simulate a horizontally-scrolling text field. */ int chars_in_pwfield = (pw->passwd_field_width / pw->passwd_font->max_bounds.width); - - if (strlen(typed_passwd) > chars_in_pwfield) - typed_passwd += (strlen(typed_passwd) - chars_in_pwfield); - - update_passwd_window(si, typed_passwd, pw->ratio); + const char *output = pw->typed_passwd; + if (strlen(output) > chars_in_pwfield) + output += (strlen(output) - chars_in_pwfield); + update_passwd_window (si, output, pw->ratio); } else if (pw->show_stars_p) { - i = strlen(typed_passwd); - stars = (char *) malloc(i+1); - memset (stars, '*', i); - stars[i] = 0; + int nchars = strlen (pw->typed_passwd_char_size); + char *stars = 0; + stars = (char *) malloc(nchars + 1); + memset (stars, '*', nchars); + stars[nchars] = 0; update_passwd_window (si, stars, pw->ratio); free (stars); } @@ -1761,31 +1875,78 @@ passwd_event_loop (saver_info *si) { saver_preferences *p = &si->prefs; char *msg = 0; - XEvent event; - unsigned int caps_p = 0; + + /* We have to go through this union bullshit because gcc-4.4.0 has + stricter struct-aliasing rules. Without this, the optimizer + can fuck things up. + */ + union { + XEvent x_event; +# ifdef HAVE_RANDR + XRRScreenChangeNotifyEvent xrr_event; +# endif /* HAVE_RANDR */ + } event; passwd_animate_timer ((XtPointer) si, 0); + reset_watchdog_timer (si, False); /* Disable watchdog while dialog up */ while (si->unlock_state == ul_read) { - XtAppNextEvent (si->app, &event); - if (event.xany.window == si->passwd_dialog && event.xany.type == Expose) + XtAppNextEvent (si->app, &event.x_event); + +#ifdef HAVE_RANDR + if (si->using_randr_extension && + (event.x_event.type == + (si->randr_event_number + RRScreenChangeNotify))) + { + /* The Resize and Rotate extension sends an event when the + size, rotation, or refresh rate of any screen has changed. */ + + if (p->verbose_p) + { + /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */ + int screen = XRRRootToScreen(si->dpy, event.xrr_event.window); + fprintf (stderr, "%s: %d: screen change event received\n", + blurb(), screen); + } + +#ifdef RRScreenChangeNotifyMask + /* Inform Xlib that it's ok to update its data structures. */ + XRRUpdateConfiguration(&event.x_event); /* Xrandr.h 1.9, 2002/09/29*/ +#endif /* RRScreenChangeNotifyMask */ + + /* Resize the existing xscreensaver windows and cached ssi data. */ + if (update_screen_layout (si)) + { + if (p->verbose_p) + { + fprintf (stderr, "%s: new layout:\n", blurb()); + describe_monitor_layout (si); + } + resize_screensaver_window (si); + } + } + else +#endif /* HAVE_RANDR */ + + if (event.x_event.xany.window == si->passwd_dialog && + event.x_event.xany.type == Expose) draw_passwd_window (si); - else if (event.xany.type == KeyPress) + else if (event.x_event.xany.type == KeyPress) { - handle_passwd_key (si, &event.xkey); - caps_p = (event.xkey.state & LockMask); + handle_passwd_key (si, &event.x_event.xkey); + si->pw_data->caps_p = (event.x_event.xkey.state & LockMask); } - else if (event.xany.type == ButtonPress || - event.xany.type == ButtonRelease) + else if (event.x_event.xany.type == ButtonPress || + event.x_event.xany.type == ButtonRelease) { si->pw_data->button_state_changed_p = True; - handle_unlock_button (si, &event); + handle_unlock_button (si, &event.x_event); if (si->pw_data->login_button_p) - handle_login_button (si, &event); + handle_login_button (si, &event.x_event); } else - XtDispatchEvent (&event); + XtDispatchEvent (&event.x_event); } switch (si->unlock_state) @@ -1796,73 +1957,15 @@ passwd_event_loop (saver_info *si) default: msg = 0; break; } - if (si->unlock_state == ul_fail) - si->unlock_failures++; - if (p->verbose_p) - switch (si->unlock_state) - { - case ul_fail: - fprintf (stderr, "%s: auth/input incorrect!%s\n", blurb(), - (caps_p ? " (CapsLock)" : "")); - break; - case ul_cancel: - fprintf (stderr, "%s: input cancelled.\n", blurb()); break; - case ul_time: - fprintf (stderr, "%s: input timed out.\n", blurb()); break; - case ul_finished: - fprintf (stderr, "%s: input finished.\n", blurb()); break; - default: break; - } - -#ifdef HAVE_SYSLOG - if (si->unlock_state == ul_fail) - { - /* If they typed a password (as opposed to just hitting return) and - the password was invalid, log it. - */ - struct passwd *pw = getpwuid (getuid ()); - char *d = DisplayString (si->dpy); - char *u = (pw && pw->pw_name ? pw->pw_name : "???"); - int opt = 0; - int fac = 0; - -# ifdef LOG_PID - opt = LOG_PID; -# endif - -# if defined(LOG_AUTHPRIV) - fac = LOG_AUTHPRIV; -# elif defined(LOG_AUTH) - fac = LOG_AUTH; -# else - fac = LOG_DAEMON; -# endif - - if (!d) d = ""; - openlog (progname, opt, fac); - syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"", - si->unlock_failures, d, u); - closelog (); - } -#endif /* HAVE_SYSLOG */ - - if (si->unlock_state == ul_fail) - XBell (si->dpy, False); - - if (si->unlock_state == ul_success && si->unlock_failures != 0) - { - if (si->unlock_failures == 1) - fprintf (real_stderr, - "%s: WARNING: 1 failed attempt to unlock the screen.\n", - blurb()); - else - fprintf (real_stderr, - "%s: WARNING: %d failed attempts to unlock the screen.\n", - blurb(), si->unlock_failures); - fflush (real_stderr); - - si->unlock_failures = 0; + switch (si->unlock_state) { + case ul_cancel: + fprintf (stderr, "%s: input cancelled.\n", blurb()); break; + case ul_time: + fprintf (stderr, "%s: input timed out.\n", blurb()); break; + case ul_finished: + fprintf (stderr, "%s: input finished.\n", blurb()); break; + default: break; } if (msg) @@ -1870,7 +1973,6 @@ passwd_event_loop (saver_info *si) si->pw_data->i_beam = 0; update_passwd_window (si, msg, 0.0); XSync (si->dpy, False); - sleep (1); /* Swallow all pending KeyPress/KeyRelease events. */ { @@ -1879,6 +1981,8 @@ passwd_event_loop (saver_info *si) ; } } + + reset_watchdog_timer (si, True); /* Re-enable watchdog */ } @@ -1898,6 +2002,13 @@ handle_typeahead (saver_info *si) memcpy (pw->typed_passwd, si->unlock_typeahead, i); pw->typed_passwd [i] = 0; + { + int j; + char *c = pw->typed_passwd_char_size; + for (j = 0; j < i; j++) + *c++ = 1; + *c = 0; + } memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead)); si->unlock_typeahead[i] = 0; @@ -1926,11 +2037,10 @@ remove_trailing_whitespace(const char *str) len = strlen(str); newstr = malloc(len + 1); - (void) strcpy(newstr, str); - if (!newstr) return NULL; + (void) strcpy(newstr, str); chr = newstr + len; while (isspace(*--chr) && chr >= newstr) *chr = '\0'; @@ -1946,7 +2056,7 @@ remove_trailing_whitespace(const char *str) * passwd dialog. A message sequence of info or error followed by a prompt will * be reduced into a single dialog window. * - * Returns 0 on success or -1 if some problem occurred (cancelled auth, OOM, ...) + * Returns 0 on success or -1 if some problem occurred (cancelled, OOM, etc.) */ int gui_auth_conv(int num_msg, @@ -1958,6 +2068,12 @@ gui_auth_conv(int num_msg, const char *info_msg, *prompt; struct auth_response *responses; + if (si->unlock_state == ul_cancel || + si->unlock_state == ul_time) + /* If we've already cancelled or timed out in this PAM conversation, + don't prompt again even if PAM asks us to! */ + return -1; + if (!(responses = calloc(num_msg, sizeof(struct auth_response)))) goto fail; @@ -1992,9 +2108,11 @@ gui_auth_conv(int num_msg, info_msg_trimmed = remove_trailing_whitespace(info_msg); prompt_trimmed = remove_trailing_whitespace(prompt); - make_passwd_window(si, info_msg_trimmed, prompt_trimmed, - auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO - ? True : False); + if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed, + auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO + ? True : False) + < 0) + goto fail; if (info_msg_trimmed) free(info_msg_trimmed); @@ -2034,6 +2152,7 @@ gui_auth_conv(int num_msg, fail: if (compose_status) free (compose_status); + compose_status = 0; if (responses) { @@ -2050,23 +2169,65 @@ fail: void auth_finished_cb (saver_info *si) { - if (si->unlock_state == ul_fail) + char buf[1024]; + const char *s; + + /* If we have something to say, put the dialog back up for a few seconds + to display it. Otherwise, don't bother. + */ + + if (si->unlock_state == ul_fail && /* failed with caps lock on */ + si->pw_data && si->pw_data->caps_p) + s = "Authentication failed (Caps Lock?)"; + else if (si->unlock_state == ul_fail) /* failed without caps lock */ + s = "Authentication failed!"; + else if (si->unlock_state == ul_success && /* good, but report failures */ + si->unlock_failures > 0) { - make_passwd_window (si, "Authentication failed!", NULL, True); - sleep (2); /* Not very nice, I know */ + if (si->unlock_failures == 1) + s = "There has been\n1 failed login attempt."; + else + { + sprintf (buf, "There have been\n%d failed login attempts.", + si->unlock_failures); + s = buf; + } + si->unlock_failures = 0; - /* Swallow any keyboard or mouse events that were received while the - * dialog was up */ - { - XEvent e; - long mask = (KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask); - while (XCheckMaskEvent (si->dpy, mask, &e)) - ; - } + /* ignore failures if they all were too recent */ + if (time((time_t *) 0) - si->unlock_failure_time + < si->prefs.auth_warning_slack) + goto END; } + else /* good, with no failures, */ + goto END; /* or timeout, or cancel. */ + + make_passwd_window (si, s, NULL, True); + XSync (si->dpy, False); + + { + int secs = 4; + time_t start = time ((time_t *) 0); + XEvent event; + while (time ((time_t *) 0) < start + secs) + if (XPending (si->dpy)) + { + XNextEvent (si->dpy, &event); + if (event.xany.window == si->passwd_dialog && + event.xany.type == Expose) + draw_passwd_window (si); + else if (event.xany.type == ButtonPress || + event.xany.type == KeyPress) + break; + XSync (si->dpy, False); + } + else + usleep (250000); /* 1/4 second */ + } - destroy_passwd_window (si); + END: + if (si->pw_data) + destroy_passwd_window (si); } @@ -2083,9 +2244,6 @@ unlock_p (saver_info *si) raise_window (si, True, True, True); - if (p->verbose_p) - fprintf (stderr, "%s: prompting for password.\n", blurb()); - xss_authenticate(si, p->verbose_p); return (si->unlock_state == ul_success);