Subject: bug#73768: 'read-passwd-mode' can clobber user defined mode lines

From: martin rudalics <[email-masked]>
Date: Mon, 14 Oct 2024 16:56:29 +0200

> Thanks! With this recipe, I can reproduce the problem. And your patch
 > fixes it :-)
 >
 > Please install in the emacs-30 branch.

Hold your horses.  First try another recipe: With emacs -Q do C-x 5 2
followed by C-x b to show *Messages* in either of the frames, read a
password, do C-x 5 o and type C-g.  One of the two buffers has the eye
icon the other has the botched mode line.  I'm using the more accurate
patch attached now to hopefully fix that too.  Note that whenever one
switches frames, the mode line showing the eye will always stay with the
initially chosen buffer and not the one where the minibuffer window is.

But there are other scenarios that don't seem right.  While reading one
password ask for reading another one.  You now should see two eye icons
which, however, cannot be attributed to any of the two read operations.
Clicking on either of them will toggle visibility of the password.  If
you don't want to show the password for the first operation but you do
want to show it for the second operation, then when the second operation
terminates you will see the password for the first one.  Same for typing
TAB in the minibuffer.  Resetting 'read-passwd--hide-password' when
terminating any read password operation might fix that.

Likely password visibility should be a property of the specific reading
process (aka as buffer-local value of 'read-passwd--hide-password') and
not something global.  But then we would also have to change the first
icon of the mode line too and doing that is more tricky.  Currently, all
eye icons are the same because 'read-passwd--mode-line-icon' has the
same value for them.

Probably it might be better to have only one eye icon on any mode line
informing about the visibility of the innermost minibuffer's password
status.  But this becomes tricky when terminating a password operation
because we would have to internally (on the C-level) scan all
minibuffers to find out whether there's another password operation in
progress and remove the eye icon only if there's none.

Otherwise we could try giving only the first eye icon the dynamic eval
property and have all other icons statically have the hide password
property.  Less tricky - we'd only have to hide a password when asking
for another one.

Obviously, we could also inhibit asking for a second password.  But I
suppose this was always possible and would imply an incompatible change.

If anyone has a good idea what to do here, please speak up.

Thanks, martin

diff --git a/lisp/auth-source.el b/lisp/auth-source.el
index 90b58f560c0..f71393721cb 100644
--- a/lisp/auth-source.el
+++ b/lisp/auth-source.el
@@ -2474,7 +2474,7 @@ read-passwd--mode-line-icon
   "Propertized mode line icon for showing/hiding passwords.")
 
 (defvar read-passwd--hide-password t
-  "Toggle whether password should be hidden in minubuffer.")
+  "Toggle whether password should be hidden in minibuffer.")
 
 (defun read-passwd--hide-password ()
   "Make password in minibuffer hidden or visible."
@@ -2534,13 +2534,14 @@ read-passwd-mode
   :keymap read-passwd-map
   :version "30.1"
 
-  (setq read-passwd--hide-password nil
-        ;; Stolen from `eldoc-minibuffer-message'.
-        read-passwd--mode-line-buffer
-        (window-buffer
-         (or (window-in-direction 'above (minibuffer-window))
-	     (minibuffer-selected-window)
-	     (get-largest-window))))
+  (setq read-passwd--hide-password nil)
+  ;; Stolen from `eldoc-minibuffer-message'.
+  (unless (buffer-live-p read-passwd--mode-line-buffer)
+    (setq read-passwd--mode-line-buffer
+          (window-buffer
+           (or (window-in-direction 'above (minibuffer-window))
+	       (minibuffer-selected-window)
+	       (get-largest-window)))))
 
   (if read-passwd-mode
       (with-current-buffer read-passwd--mode-line-buffer
@@ -2551,8 +2552,10 @@ read-passwd-mode
 	              mode-line-format))))
     (with-current-buffer read-passwd--mode-line-buffer
       ;; Remove `read-passwd--mode-line-icon'.
-      (when (listp mode-line-format)
-        (setq mode-line-format (cdr mode-line-format)))))
+      (when (equal (car mode-line-format)
+		   '(:eval read-passwd--mode-line-icon))
+        (setq mode-line-format (cdr mode-line-format))))
+    (setq read-passwd--mode-line-buffer nil))
 
   (when read-passwd-mode
     (read-passwd-toggle-visibility)))