branch: elpa/base32
commit 93aa724afe2a43212d6d1233d17320cf60848de1
Author: Vivek Das Mohapatra <[email protected]>
Commit: Vivek Das Mohapatra <[email protected]>

    Improve the UI to display a constantly updated TOTP token
    
    M-x totp RET now pops up a token buffer which shows the label,
    a countdown to token expiry, and the current token.
---
 totp.el | 72 +++++++++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 52 insertions(+), 20 deletions(-)

diff --git a/totp.el b/totp.el
index 3bd3091f42..af5a38223c 100644
--- a/totp.el
+++ b/totp.el
@@ -366,33 +366,65 @@ and EXPIRY is the seconds after the epoch when the TOTP 
expires."
       (setq totp (format fmt totp)))
     (list totp ttl expiry)))
 
-(defun totp-display-token (token &optional label)
-  (let (ui-buffer title)  
-    (setq label     (or label "???")
-          expiry    (nth 2 token)
-          expiry    (format-time-string "%F %T %z" (seconds-to-time expiry))
-          title     (format "*TOTP %s (%s)*" label expiry )
-          ui-buffer (get-buffer-create title))
+(defvar totp-display-ttl    nil)
+(defvar totp-display-label  nil)
+(defvar totp-display-expiry nil)
+(defvar totp-display-secret nil)
+
+(defun totp-cancel-timer (fun buf)
+  "Cancel timers which call FUN with buffer BUF as the first argument."
+  (dolist (timer timer-list)
+    (if (and (eq (timer--function timer) fun)
+             (eq (car (timer--args timer)) buf))
+        (cancel-timer timer))))
+
+(defun totp-update-token-display (buf &optional otp token)
+  (if (buffer-live-p buf)
+      (with-current-buffer buf
+        (erase-buffer)
+        (if (or (not totp-display-ttl)
+                (not totp-display-expiry))
+            ;; metadata unset, need to generate TOTP
+            (setq otp                 (totp-generate-otp totp-display-secret)
+                  token               (nth 0 otp)
+                  totp-display-ttl    (nth 1 otp)
+                  totp-display-expiry (nth 2 otp))
+          ;; metadata already set, work out our new ttl:
+          (setq totp-display-ttl
+                (floor (- (time-to-seconds) totp-display-expiry))))
+        ;; regenerate metadata if the ttl is <= 0
+        (if (>= 0 totp-display-ttl)
+            (setq otp (totp-generate-otp totp-display-secret)
+                  token               (nth 0 otp)
+                  totp-display-ttl    (nth 1 otp)
+                  totp-display-expiry (nth 2 otp)))
+        (insert (format "TOTP %s [%02ds]: %s\n"
+                        totp-display-label totp-display-ttl token)))
+    (totp-cancel-timer #'totp-update-token-display buf)))
+
+(defun totp-display-token (secret &optional label)
+  (let (ui-buffer)
+    (or label
+        (setq label (totp-secret-make-label secret)))
+    (setq ui-buffer (get-buffer-create (format "*TOTP %s*" label)))
     (set-buffer ui-buffer)
-    (insert (format "%s:  %s\n" title (nth 0 token)))
-    (pop-to-buffer ui-buffer)))
+    (mapc 'make-local-variable '(totp-display-ttl
+                                 totp-display-label
+                                 totp-display-expiry
+                                 totp-display-secret))
+    (setq totp-display-label  label
+          totp-display-secret (cdr (assq :secret secret))
+          totp-display-ttl    nil
+          totp-display-expiry nil)
+    (pop-to-buffer ui-buffer)
+    (run-with-timer 0 1 #'totp-update-token-display ui-buffer)))
 
 (defun totp (&optional secret label)
   (interactive
    (let ((secrets (totp-secrets)) key)
      (setq key (completing-read "Generate TOTP: " secrets))
      (list (cdr (assoc key secrets)) key)))
-  (let ((token (totp-generate-otp (cdr (assq :secret secret))))
-        ttl expiry)
-    (if (not token)
-        (error "Could not generate TOTP for %s" label))
-    (setq ttl    (nth 1 token)
-          expiry (nth 2 token))
-    (if (>= ttl totp-minimum-ui-grace)
-        (totp-display-token token label)
-      (display-message-or-buffer
-       (format "Token %s expires in %d seconds…" label ttl)))
-    (nth 0 token)))
+  (totp-display-token secret label))
 
 (provide 'totp)
 

Reply via email to