Subject: erc-services.el: Auth-source support for passwords

From: Leon Vack <[email-masked]>
Date: Sun, 20 Dec 2020 10:54:03 GMT

Adds an option to erc-services.el to query auth-source for NickServ
passwords.

This is my first patch to emacs, so please let me know if I missed any
standards.  I am about to start the process of assigning the copyright
to the FSF.

>From 367593cc4cbfdfbdac778fd15ceb79fd61d7f64c Mon Sep 17 00:00:00 2001
From: Leon Vack <[email-masked]>
Date: Sun, 20 Dec 2020 10:53:33 +0100
Subject: [PATCH] erc-services.el: Auth-source support for passwords

* lisp/etc/erc-services.el (erc-nickserv-passwords): Document that
the passwords are only used when erc-prompt-for-nickserv-password is
nil.
* lisp/etc/erc-services.el
(erc-use-auth-source-for-nickserv-password): New customizable
variable to enable checking auth-source for NickServ passwords.
* lisp/etc/erc-services.el (etc-nickserv-get-password): New function
to handle the lookup of the NickServ password from both auth-source
and the erc-nickserv-passwords variable.
* lisp/etc/erc-services.el (erc-nickserv-call-identify-function):
Use new erc-nickserv-get-password function to lookup NickServ
passwords.
* lisp/etc/erc-services.el (erc-nickserv-identify-autodetect)
(erc-nickserv-identify-on-connect)
(erc-nickserv-identify-on-nick-change): Call
erc-nickserv-call-identify-function when
erc-use-auth-source-for-nickserv-password is set.
---
 etc/NEWS                 |  4 ++++
 lisp/erc/erc-services.el | 50 ++++++++++++++++++++++++++++++++--------
 2 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 4a8e70e6a6..c9e0fa7459 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1283,6 +1283,10 @@ https://www.w3.org/TR/xml/#charsets).  Now it rejects such strings.
 
 ** erc
 
+*** erc-services.el now supports NickServ passwords from auth-source.
+The 'erc-use-auth-source-for-nickserv-password' enables querying
+auth-source for NickServ passwords.
+
 ---
 *** The '/ignore' command will now ask for a timeout to stop ignoring the user.
 Allowed inputs are seconds or ISO8601-like periods like "1h" or "4h30m".
diff --git a/lisp/erc/erc-services.el b/lisp/erc/erc-services.el
index c0011f9808..0710813e9d 100644
--- a/lisp/erc/erc-services.el
+++ b/lisp/erc/erc-services.el
@@ -168,9 +168,23 @@ erc-prompt-for-nickserv-password
   :group 'erc-services
   :type 'boolean)
 
+(defcustom erc-use-auth-source-for-nickserv-password nil
+  "Query auth-source for a password when identifiying to NickServ.
+
+
+This option has an no effect `erc-prompt-for-nickserv-password'
+is not nil and passwords from `erc-nickserv-passwords' take
+precedence."
+  :version "28.1"
+  :group 'erc-services
+  :type 'boolean)
+
 (defcustom erc-nickserv-passwords nil
   "Passwords used when identifying to NickServ automatically.
 
+`erc-prompt-for-nickserv-password' must be nil for these
+passwords to be used.
+
 Example of use:
   (setq erc-nickserv-passwords
         \\='((freenode ((\"nick-one\" . \"password\")
@@ -199,7 +213,7 @@ erc-nickserv-passwords
 			(cons :tag "Identity"
 			      (string :tag "Nick")
 			      (string :tag "Password"
-                                      :secret ?*))))))
+				      :secret ?*))))))
 
 ;; Variables:
 
@@ -375,7 +389,8 @@ erc-nickserv-identify-autodetect
 If `erc-prompt-for-nickserv-password' is non-nil, prompt the user for the
 password for this nickname, otherwise try to send it automatically."
   (unless (and (null erc-nickserv-passwords)
-	       (null erc-prompt-for-nickserv-password))
+	       (null erc-prompt-for-nickserv-password)
+	       (null erc-use-auth-source-for-nickserv-password))
     (let* ((network (erc-network))
 	   (sender (erc-nickserv-alist-sender network))
 	   (identify-regex (erc-nickserv-alist-regexp network))
@@ -394,7 +409,8 @@ erc-nickserv-identify-autodetect
 (defun erc-nickserv-identify-on-connect (_server nick)
   "Identify to Nickserv after the connection to the server is established."
   (unless (or (and (null erc-nickserv-passwords)
-		   (null erc-prompt-for-nickserv-password))
+		   (null erc-prompt-for-nickserv-password)
+		   (null erc-use-auth-source-for-nickserv-password))
 	      (and (eq erc-nickserv-identify-mode 'both)
 		   (erc-nickserv-alist-regexp (erc-network))))
     (erc-nickserv-call-identify-function nick)))
@@ -402,22 +418,38 @@ erc-nickserv-identify-on-connect
 (defun erc-nickserv-identify-on-nick-change (nick _old-nick)
   "Identify to Nickserv whenever your nick changes."
   (unless (or (and (null erc-nickserv-passwords)
-		   (null erc-prompt-for-nickserv-password))
+		   (null erc-prompt-for-nickserv-password)
+		   (null erc-use-auth-source-for-nickserv-password))
 	      (and (eq erc-nickserv-identify-mode 'both)
 		   (erc-nickserv-alist-regexp (erc-network))))
     (erc-nickserv-call-identify-function nick)))
 
+(defun erc-nickserv-get-password (nickname)
+  "Return the password for NICKNAME from configured sources.
+
+It uses `erc-nickserv-passwords' and additionally auth-source
+when `erc-use-auth-source-for-nickserv-password' is not nil."
+  (or (when erc-nickserv-passwords
+	(cdr (assoc nickname
+		    (nth 1 (assoc (erc-network)
+				  erc-nickserv-passwords)))))
+      (when erc-use-auth-source-for-nickserv-password
+	(let* ((secret (nth 0 (auth-source-search
+			       :max 1 :require '(:secret)
+			       :host (erc-with-server-buffer erc-session-server)
+			       :port (format ; ensure we have a string
+				      "%s" (erc-with-server-buffer erc-session-port))
+			       :user nickname))))
+	  (when secret (let ((passwd (plist-get secret :secret)))
+			 (if (functionp passwd) (funcall passwd) passwd)))))))
+
 (defun erc-nickserv-call-identify-function (nickname)
   "Call `erc-nickserv-identify'.
 Either call it interactively or run it with NICKNAME's password,
 depending on the value of `erc-prompt-for-nickserv-password'."
   (if erc-prompt-for-nickserv-password
       (call-interactively 'erc-nickserv-identify)
-    (when erc-nickserv-passwords
-      (erc-nickserv-identify
-       (cdr (assoc nickname
-		   (nth 1 (assoc (erc-network)
-				 erc-nickserv-passwords))))))))
+    (when password (erc-nickserv-identify (erc-nickserv-get-password nickname)))))
 
 (defvar erc-auto-discard-away)
 
-- 
2.28.0