aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorF. Jason Park2022-12-29 06:43:19 -0800
committerF. Jason Park2023-04-08 14:23:51 -0700
commit0f7fc5cfdf97a8280ea8f012e50af9e615e8c6ef (patch)
tree27b9b6f57a917207c20a7051a71baf856d611362
parent8dd209eea47f3b8e1fce6dc12c13d33da1154d89 (diff)
downloademacs-0f7fc5cfdf97a8280ea8f012e50af9e615e8c6ef.tar.gz
emacs-0f7fc5cfdf97a8280ea8f012e50af9e615e8c6ef.zip
Be smarter about switching to TLS from M-x erc
* lisp/erc/erc.el (erc--warn-unencrypted): Remove unused internal function. (erc-select-read-args): Offer to use TLS when user runs M-x erc and opts for default server and port or provides the well-known IANA TLS port or enters an ircs:// URL at the server prompt. For the last two, do this immediately instead of calling `erc-tls' interactively and imposing a review of just-chosen values. Also remove error warnings and ensure `erc-tls' still works by setting `erc-server-connect-function' to `erc-open-tls-stream' when appropriate. Include the word "URL" in server prompt. (erc--with-entrypoint-environment): Add new macro for empowering an entry point's interactive form to bind special variables in their command's body without shadowing them in the lambda list. (erc, erc-tls): Add internal keyword argument for interactive use, but don't make it `keywordp' or advertise its presence. Also use new helper macro, `erc--with-entrypoint-environment', to temporarily bind special vars given by interactive helper `erc-select-read-args'. * test/lisp/erc/erc-tests.el (erc--with-entrypoint-environment): Add new test. (erc-select-read-args): Modify return values to expect additional internal keyword argument where appropriate. (erc-tls): Make assertions about environment. (erc--interactive): New test. (Bug#60428.)
-rw-r--r--lisp/erc/erc.el79
-rw-r--r--test/lisp/erc/erc-tests.el159
2 files changed, 184 insertions, 54 deletions
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index ea581c17661..e1abfee9ba3 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -2241,29 +2241,12 @@ parameters SERVER and NICK."
2241 (setq input (concat "irc://" input))) 2241 (setq input (concat "irc://" input)))
2242 input) 2242 input)
2243 2243
2244;; A temporary means of addressing the problem of ERC's namesake entry
2245;; point defaulting to a non-TLS connection with its default server
2246;; (bug#60428).
2247(defun erc--warn-unencrypted ()
2248 ;; Remove unconditionally to avoid wrong context due to races from
2249 ;; simultaneous dialing or aborting (e.g., via `keybaord-quit').
2250 (remove-hook 'erc--server-post-connect-hook #'erc--warn-unencrypted)
2251 (when (and (process-contact erc-server-process :nowait)
2252 (equal erc-session-server erc-default-server)
2253 (eql erc-session-port erc-default-port))
2254 ;; FIXME use the autoloaded `info' instead of `Info-goto-node' in
2255 ;; `erc-button-alist'.
2256 (require 'info nil t)
2257 (erc-display-error-notice
2258 nil (concat "This connection is unencrypted. Please use `erc-tls'"
2259 " from now on. See Info:\"(erc) connecting\" for more."))))
2260
2261;;;###autoload 2244;;;###autoload
2262(defun erc-select-read-args () 2245(defun erc-select-read-args ()
2263 "Prompt the user for values of nick, server, port, and password." 2246 "Prompt the user for values of nick, server, port, and password."
2264 (require 'url-parse) 2247 (require 'url-parse)
2265 (let* ((input (let ((d (erc-compute-server))) 2248 (let* ((input (let ((d (erc-compute-server)))
2266 (read-string (format "Server (default is %S): " d) 2249 (read-string (format "Server or URL (default is %S): " d)
2267 nil 'erc-server-history-list d))) 2250 nil 'erc-server-history-list d)))
2268 ;; For legacy reasons, also accept a URL without a scheme. 2251 ;; For legacy reasons, also accept a URL without a scheme.
2269 (url (url-generic-parse-url (erc--ensure-url input))) 2252 (url (url-generic-parse-url (erc--ensure-url input)))
@@ -2286,15 +2269,32 @@ parameters SERVER and NICK."
2286 (m (if p 2269 (m (if p
2287 (format "Server password (default is %S): " p) 2270 (format "Server password (default is %S): " p)
2288 "Server password (optional): "))) 2271 "Server password (optional): ")))
2289 (if erc-prompt-for-password (read-passwd m nil p) p)))) 2272 (if erc-prompt-for-password (read-passwd m nil p) p)))
2273 (opener (and (or sp (eql port erc-default-port-tls)
2274 (and (equal server erc-default-server)
2275 (not (string-prefix-p "irc://" input))
2276 (eql port erc-default-port)
2277 (y-or-n-p "Connect using TLS instead? ")
2278 (setq port erc-default-port-tls)))
2279 #'erc-open-tls-stream))
2280 env)
2281 (when opener
2282 (push `(erc-server-connect-function . ,opener) env))
2290 (when (and passwd (string= "" passwd)) 2283 (when (and passwd (string= "" passwd))
2291 (setq passwd nil)) 2284 (setq passwd nil))
2292 (when (and (equal server erc-default-server) 2285 `( :server ,server :port ,port :nick ,nick
2293 (eql port erc-default-port) 2286 ,@(and passwd `(:password ,passwd))
2294 (not (eql port erc-default-port-tls)) ; not `erc-tls' 2287 ,@(and env `(&interactive-env ,env)))))
2295 (not (string-prefix-p "irc://" input))) ; not yanked URL 2288
2296 (add-hook 'erc--server-post-connect-hook #'erc--warn-unencrypted)) 2289(defmacro erc--with-entrypoint-environment (env &rest body)
2297 (list :server server :port port :nick nick :password passwd))) 2290 "Run BODY with bindings from ENV alist."
2291 (declare (indent 1))
2292 (let ((syms (make-symbol "syms"))
2293 (vals (make-symbol "vals")))
2294 `(let (,syms ,vals)
2295 (pcase-dolist (`(,k . ,v) ,env) (push k ,syms) (push v ,vals))
2296 (cl-progv ,syms ,vals
2297 ,@body))))
2298 2298
2299;;;###autoload 2299;;;###autoload
2300(cl-defun erc (&key (server (erc-compute-server)) 2300(cl-defun erc (&key (server (erc-compute-server))
@@ -2303,7 +2303,9 @@ parameters SERVER and NICK."
2303 (user (erc-compute-user)) 2303 (user (erc-compute-user))
2304 password 2304 password
2305 (full-name (erc-compute-full-name)) 2305 (full-name (erc-compute-full-name))
2306 id) 2306 id
2307 ;; Used by interactive form
2308 ((&interactive-env --interactive-env--)))
2307 "ERC is a powerful, modular, and extensible IRC client. 2309 "ERC is a powerful, modular, and extensible IRC client.
2308This function is the main entry point for ERC. 2310This function is the main entry point for ERC.
2309 2311
@@ -2326,9 +2328,12 @@ then the server and full-name will be set to those values,
2326whereas `erc-compute-port' and `erc-compute-nick' will be invoked 2328whereas `erc-compute-port' and `erc-compute-nick' will be invoked
2327for the values of the other parameters. 2329for the values of the other parameters.
2328 2330
2329See `erc-tls' for the meaning of ID." 2331See `erc-tls' for the meaning of ID.
2332
2333\(fn &key SERVER PORT NICK USER PASSWORD FULL-NAME ID)"
2330 (interactive (erc-select-read-args)) 2334 (interactive (erc-select-read-args))
2331 (erc-open server port nick full-name t password nil nil nil nil user id)) 2335 (erc--with-entrypoint-environment --interactive-env--
2336 (erc-open server port nick full-name t password nil nil nil nil user id)))
2332 2337
2333;;;###autoload 2338;;;###autoload
2334(defalias 'erc-select #'erc) 2339(defalias 'erc-select #'erc)
@@ -2342,7 +2347,9 @@ See `erc-tls' for the meaning of ID."
2342 password 2347 password
2343 (full-name (erc-compute-full-name)) 2348 (full-name (erc-compute-full-name))
2344 client-certificate 2349 client-certificate
2345 id) 2350 id
2351 ;; Used by interactive form
2352 ((&interactive-env --interactive-env--)))
2346 "ERC is a powerful, modular, and extensible IRC client. 2353 "ERC is a powerful, modular, and extensible IRC client.
2347This function is the main entry point for ERC over TLS. 2354This function is the main entry point for ERC over TLS.
2348 2355
@@ -2386,10 +2393,20 @@ When present, ID should be a symbol or a string to use for naming
2386the server buffer and identifying the connection unequivocally. 2393the server buffer and identifying the connection unequivocally.
2387See Info node `(erc) Network Identifier' for details. Like USER 2394See Info node `(erc) Network Identifier' for details. Like USER
2388and CLIENT-CERTIFICATE, this parameter cannot be specified 2395and CLIENT-CERTIFICATE, this parameter cannot be specified
2389interactively." 2396interactively.
2397
2398\(fn &key SERVER PORT NICK USER PASSWORD FULL-NAME CLIENT-CERTIFICATE ID)"
2390 (interactive (let ((erc-default-port erc-default-port-tls)) 2399 (interactive (let ((erc-default-port erc-default-port-tls))
2391 (erc-select-read-args))) 2400 (erc-select-read-args)))
2392 (let ((erc-server-connect-function 'erc-open-tls-stream)) 2401 ;; Bind `erc-server-connect-function' to `erc-open-tls-stream'
2402 ;; around `erc-open' when a non-default value hasn't been specified
2403 ;; by the user or the interactive form. And don't bother checking
2404 ;; for advice, indirect functions, autoloads, etc.
2405 (unless (or (assq 'erc-server-connect-function --interactive-env--)
2406 (not (eq erc-server-connect-function #'erc-open-network-stream)))
2407 (push '(erc-server-connect-function . erc-open-tls-stream)
2408 --interactive-env--))
2409 (erc--with-entrypoint-environment --interactive-env--
2393 (erc-open server port nick full-name t password 2410 (erc-open server port nick full-name t password
2394 nil nil nil client-certificate user id))) 2411 nil nil nil client-certificate user id)))
2395 2412
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index ae19b7d0aad..c5905ab4f67 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1064,32 +1064,62 @@
1064 (should (string-match erc--server-connect-dumb-ipv6-regexp 1064 (should (string-match erc--server-connect-dumb-ipv6-regexp
1065 (concat "[" a "]"))))) 1065 (concat "[" a "]")))))
1066 1066
1067(ert-deftest erc--with-entrypoint-environment ()
1068 (let ((env '((erc-join-buffer . foo)
1069 (erc-server-connect-function . bar))))
1070 (erc--with-entrypoint-environment env
1071 (should (eq erc-join-buffer 'foo))
1072 (should (eq erc-server-connect-function 'bar)))))
1073
1067(ert-deftest erc-select-read-args () 1074(ert-deftest erc-select-read-args ()
1068 1075
1069 (ert-info ("Does not default to TLS") 1076 (ert-info ("Prompts for switch to TLS by default")
1070 (should (equal (ert-simulate-keys "\r\r\r\r" 1077 (should (equal (ert-simulate-keys "\r\r\r\ry\r"
1071 (erc-select-read-args)) 1078 (erc-select-read-args))
1072 (list :server "irc.libera.chat" 1079 (list :server "irc.libera.chat"
1073 :port 6667 1080 :port 6697
1081 :nick (user-login-name)
1082 '&interactive-env '((erc-server-connect-function
1083 . erc-open-tls-stream))))))
1084
1085 (ert-info ("Switches to TLS when port matches default TLS port")
1086 (should (equal (ert-simulate-keys "irc.gnu.org\r6697\r\r\r"
1087 (erc-select-read-args))
1088 (list :server "irc.gnu.org"
1089 :port 6697
1090 :nick (user-login-name)
1091 '&interactive-env '((erc-server-connect-function
1092 . erc-open-tls-stream))))))
1093
1094 (ert-info ("Switches to TLS when URL is ircs://")
1095 (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
1096 (erc-select-read-args))
1097 (list :server "irc.gnu.org"
1098 :port 6697
1074 :nick (user-login-name) 1099 :nick (user-login-name)
1075 :password nil)))) 1100 '&interactive-env '((erc-server-connect-function
1101 . erc-open-tls-stream))))))
1102
1103 (ert-info ("Opt out of non-TLS warning manually")
1104 (should (equal (ert-simulate-keys "\r\r\r\rn\r"
1105 (erc-select-read-args))
1106 (list :server "irc.libera.chat"
1107 :port 6667
1108 :nick (user-login-name)))))
1076 1109
1077 (ert-info ("Override default TLS") 1110 (ert-info ("Override default TLS")
1078 (should (equal (ert-simulate-keys "irc://irc.libera.chat\r\r\r\r" 1111 (should (equal (ert-simulate-keys "irc://irc.libera.chat\r\r\r\r"
1079 (erc-select-read-args)) 1112 (erc-select-read-args))
1080 (list :server "irc.libera.chat" 1113 (list :server "irc.libera.chat"
1081 :port 6667 1114 :port 6667
1082 :nick (user-login-name) 1115 :nick (user-login-name)))))
1083 :password nil))))
1084 1116
1085 (ert-info ("Address includes port") 1117 (ert-info ("Address includes port")
1086 (should (equal (ert-simulate-keys 1118 (should (equal (ert-simulate-keys "localhost:6667\rnick\r\r"
1087 "localhost:6667\rnick\r\r"
1088 (erc-select-read-args)) 1119 (erc-select-read-args))
1089 (list :server "localhost" 1120 (list :server "localhost"
1090 :port 6667 1121 :port 6667
1091 :nick "nick" 1122 :nick "nick"))))
1092 :password nil))))
1093 1123
1094 (ert-info ("Address includes nick, password skipped via option") 1124 (ert-info ("Address includes nick, password skipped via option")
1095 (should (equal (ert-simulate-keys "nick@localhost:6667\r" 1125 (should (equal (ert-simulate-keys "nick@localhost:6667\r"
@@ -1097,8 +1127,7 @@
1097 (erc-select-read-args))) 1127 (erc-select-read-args)))
1098 (list :server "localhost" 1128 (list :server "localhost"
1099 :port 6667 1129 :port 6667
1100 :nick "nick" 1130 :nick "nick"))))
1101 :password nil))))
1102 1131
1103 (ert-info ("Address includes nick and password") 1132 (ert-info ("Address includes nick and password")
1104 (should (equal (ert-simulate-keys "nick:sesame@localhost:6667\r\r" 1133 (should (equal (ert-simulate-keys "nick:sesame@localhost:6667\r\r"
@@ -1113,37 +1142,40 @@
1113 (erc-select-read-args)) 1142 (erc-select-read-args))
1114 (list :server "[::1]" 1143 (list :server "[::1]"
1115 :port 6667 1144 :port 6667
1116 :nick (user-login-name) 1145 :nick (user-login-name)))))
1117 :password nil))))
1118 1146
1119 (ert-info ("IPv6 address with port") 1147 (ert-info ("IPv6 address with port")
1120 (should (equal (ert-simulate-keys "[::1]:6667\r\r\r" 1148 (should (equal (ert-simulate-keys "[::1]:6667\r\r\r"
1121 (erc-select-read-args)) 1149 (erc-select-read-args))
1122 (list :server "[::1]" 1150 (list :server "[::1]"
1123 :port 6667 1151 :port 6667
1124 :nick (user-login-name) 1152 :nick (user-login-name)))))
1125 :password nil))))
1126 1153
1127 (ert-info ("IPv6 address includes nick") 1154 (ert-info ("IPv6 address includes nick")
1128 (should (equal (ert-simulate-keys "nick@[::1]:6667\r\r" 1155 (should (equal (ert-simulate-keys "nick@[::1]:6667\r\r"
1129 (erc-select-read-args)) 1156 (erc-select-read-args))
1130 (list :server "[::1]" 1157 (list :server "[::1]"
1131 :port 6667 1158 :port 6667
1132 :nick "nick" 1159 :nick "nick")))))
1133 :password nil)))))
1134 1160
1135(ert-deftest erc-tls () 1161(ert-deftest erc-tls ()
1136 (let (calls) 1162 (let (calls env)
1137 (cl-letf (((symbol-function 'user-login-name) 1163 (cl-letf (((symbol-function 'user-login-name)
1138 (lambda (&optional _) "tester")) 1164 (lambda (&optional _) "tester"))
1139 ((symbol-function 'erc-open) 1165 ((symbol-function 'erc-open)
1140 (lambda (&rest r) (push r calls)))) 1166 (lambda (&rest r)
1167 (push `((erc-server-connect-function
1168 ,erc-server-connect-function))
1169 env)
1170 (push r calls))))
1141 1171
1142 (ert-info ("Defaults") 1172 (ert-info ("Defaults")
1143 (erc-tls) 1173 (erc-tls)
1144 (should (equal (pop calls) 1174 (should (equal (pop calls)
1145 '("irc.libera.chat" 6697 "tester" "unknown" t 1175 '("irc.libera.chat" 6697 "tester" "unknown" t
1146 nil nil nil nil nil "user" nil)))) 1176 nil nil nil nil nil "user" nil)))
1177 (should (equal (pop env)
1178 '((erc-server-connect-function erc-open-tls-stream)))))
1147 1179
1148 (ert-info ("Full") 1180 (ert-info ("Full")
1149 (erc-tls :server "irc.gnu.org" 1181 (erc-tls :server "irc.gnu.org"
@@ -1156,7 +1188,9 @@
1156 :id 'GNU.org) 1188 :id 'GNU.org)
1157 (should (equal (pop calls) 1189 (should (equal (pop calls)
1158 '("irc.gnu.org" 7000 "bob" "Bob's Name" t 1190 '("irc.gnu.org" 7000 "bob" "Bob's Name" t
1159 "bob:changeme" nil nil nil t "bobo" GNU.org)))) 1191 "bob:changeme" nil nil nil t "bobo" GNU.org)))
1192 (should (equal (pop env)
1193 '((erc-server-connect-function erc-open-tls-stream)))))
1160 1194
1161 ;; Values are often nil when called by lisp code, which leads to 1195 ;; Values are often nil when called by lisp code, which leads to
1162 ;; null params. This is why `erc-open' recomputes almost 1196 ;; null params. This is why `erc-open' recomputes almost
@@ -1172,7 +1206,86 @@
1172 :password "bob:changeme")) 1206 :password "bob:changeme"))
1173 (should (equal (pop calls) 1207 (should (equal (pop calls)
1174 '(nil 7000 nil "Bob's Name" t 1208 '(nil 7000 nil "Bob's Name" t
1175 "bob:changeme" nil nil nil nil "bobo" nil))))))) 1209 "bob:changeme" nil nil nil nil "bobo" nil)))
1210 (should (equal (pop env)
1211 '((erc-server-connect-function erc-open-tls-stream)))))
1212
1213 (ert-info ("Interactive")
1214 (ert-simulate-keys "nick:sesame@localhost:6667\r\r"
1215 (call-interactively #'erc-tls))
1216 (should (equal (pop calls)
1217 '("localhost" 6667 "nick" "unknown" t "sesame"
1218 nil nil nil nil "user" nil)))
1219 (should (equal (pop env)
1220 '((erc-server-connect-function
1221 erc-open-tls-stream)))))
1222
1223 (ert-info ("Custom connect function")
1224 (let ((erc-server-connect-function 'my-connect-func))
1225 (erc-tls)
1226 (should (equal (pop calls)
1227 '("irc.libera.chat" 6697 "tester" "unknown" t
1228 nil nil nil nil nil "user" nil)))
1229 (should (equal (pop env)
1230 '((erc-server-connect-function my-connect-func))))))
1231
1232 (ert-info ("Advised default function overlooked") ; intentional
1233 (advice-add 'erc-server-connect-function :around #'ignore
1234 '((name . erc-tests--erc-tls)))
1235 (erc-tls)
1236 (should (equal (pop calls)
1237 '("irc.libera.chat" 6697 "tester" "unknown" t
1238 nil nil nil nil nil "user" nil)))
1239 (should (equal (pop env)
1240 '((erc-server-connect-function erc-open-tls-stream))))
1241 (advice-remove 'erc-server-connect-function 'erc-tests--erc-tls))
1242
1243 (ert-info ("Advised non-default function honored")
1244 (let ((f (lambda (&rest r) (ignore r))))
1245 (cl-letf (((symbol-value 'erc-server-connect-function) f))
1246 (advice-add 'erc-server-connect-function :around #'ignore
1247 '((name . erc-tests--erc-tls)))
1248 (erc-tls)
1249 (should (equal (pop calls)
1250 '("irc.libera.chat" 6697 "tester" "unknown" t
1251 nil nil nil nil nil "user" nil)))
1252 (should (equal (pop env) `((erc-server-connect-function ,f))))
1253 (advice-remove 'erc-server-connect-function
1254 'erc-tests--erc-tls)))))))
1255
1256;; See `erc-select-read-args' above for argument parsing.
1257;; This only tests the "hidden" arguments.
1258
1259(ert-deftest erc--interactive ()
1260 (let (calls env)
1261 (cl-letf (((symbol-function 'user-login-name)
1262 (lambda (&optional _) "tester"))
1263 ((symbol-function 'erc-open)
1264 (lambda (&rest r)
1265 (push `((erc-server-connect-function
1266 ,erc-server-connect-function))
1267 env)
1268 (push r calls))))
1269
1270 (ert-info ("Default click-through accept TLS upgrade")
1271 (ert-simulate-keys "\r\r\r\ry\r"
1272 (call-interactively #'erc))
1273 (should (equal (pop calls)
1274 '("irc.libera.chat" 6697 "tester" "unknown" t nil
1275 nil nil nil nil "user" nil)))
1276 (should (equal (pop env)
1277 '((erc-server-connect-function erc-open-tls-stream)))))
1278
1279 (ert-info ("Nick supplied, decline TLS upgrade")
1280 (ert-simulate-keys "\r\rdummy\r\rn\r"
1281 (call-interactively #'erc))
1282 (should (equal (pop calls)
1283 '("irc.libera.chat" 6667 "dummy" "unknown" t nil
1284 nil nil nil nil "user" nil)))
1285 (should (equal (pop env)
1286 '(
1287 (erc-server-connect-function
1288 erc-open-network-stream))))))))
1176 1289
1177(defun erc-tests--make-server-buf (name) 1290(defun erc-tests--make-server-buf (name)
1178 (with-current-buffer (get-buffer-create name) 1291 (with-current-buffer (get-buffer-create name)