aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorF. Jason Park2022-07-11 05:14:57 -0700
committerF. Jason Park2022-11-16 21:34:36 -0800
commited5022b4eec676bd6c0509615a1f939b796b942b (patch)
tree0ad5489bdd8e7ea7b27cd110d559cbe347b1a682
parent535cc4c81a91d0661418ce59be951dda9e233a2e (diff)
downloademacs-ed5022b4eec676bd6c0509615a1f939b796b942b.tar.gz
emacs-ed5022b4eec676bd6c0509615a1f939b796b942b.zip
Improve new connections in erc-handle-irc-url
* doc/misc/erc.texi: Add new Integrations section to the info manual under Advanced Usage. * etc/ERC-NEWS: Add new section mentioning improved UX when clicking on irc:// links. * lisp/erc/erc.el (erc-handle-irc-url): Add optional "scheme" parameter. Fix `erc-open' invocation so that the server buffer is named correctly by deferring to a new customizable opener. Arrange for JOINing a channel in a manner similar to ERC's autojoin module. (erc-url-connect-function): Add new option for creating a new ERC connection based on info parsed from a URL. (erc--url-default-connect-function): New function to serve as an interactive-only fallback when a user hasn't specified a URL connect function. * lisp/erc/erc-compat.el (erc-compat--29-browse-url--irc): Add new compatibility function for `browse-url-irc' and include it in `browse-url-default-handlers' on Emacs versions below 29. * test/lisp/erc/erc-tests.el (erc-tests--make-server-buf, erc-tests--make-client-buf): Add helpers for creating dummy ERC buffers. (erc-handle-irc-url): Add test. * test/lisp/erc/erc-scenarios-misc.el (erc-scenarios-handle-irc-url): Add new test. * test/lisp/erc/resources/join/legacy/foonet.eld: Relax timeout. (Bug#56514.)
-rw-r--r--doc/misc/erc.texi28
-rw-r--r--etc/ERC-NEWS7
-rw-r--r--lisp/erc/erc-compat.el32
-rw-r--r--lisp/erc/erc.el92
-rw-r--r--test/lisp/erc/erc-scenarios-misc.el28
-rw-r--r--test/lisp/erc/erc-tests.el94
-rw-r--r--test/lisp/erc/resources/join/legacy/foonet.eld2
7 files changed, 263 insertions, 20 deletions
diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index ad35b78f0ed..0d807e323e6 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -79,6 +79,7 @@ Advanced Usage
79 79
80* Connecting:: Ways of connecting to an IRC server. 80* Connecting:: Ways of connecting to an IRC server.
81* Sample Configuration:: An example configuration file. 81* Sample Configuration:: An example configuration file.
82* Integrations:: Integrations available for ERC.
82* Options:: Options that are available for ERC. 83* Options:: Options that are available for ERC.
83 84
84@end detailmenu 85@end detailmenu
@@ -526,6 +527,7 @@ Translate morse code in messages
526@menu 527@menu
527* Connecting:: Ways of connecting to an IRC server. 528* Connecting:: Ways of connecting to an IRC server.
528* Sample Configuration:: An example configuration file. 529* Sample Configuration:: An example configuration file.
530* Integrations:: Integrations available for ERC.
529* Options:: Options that are available for ERC. 531* Options:: Options that are available for ERC.
530@end menu 532@end menu
531 533
@@ -991,6 +993,32 @@ stuff, to the current ERC buffer."
991;; (setq erc-kill-server-buffer-on-quit t) 993;; (setq erc-kill-server-buffer-on-quit t)
992@end lisp 994@end lisp
993 995
996@node Integrations
997@section Integrations
998@cindex integrations
999
1000@subheading URL
1001For anything to work, you'll want to set @code{url-irc-function} to
1002@code{url-irc-erc}. As a rule of thumb, libraries relying directly on
1003@code{url-retrieve} should be fine out the box from Emacs 29.1 onward.
1004On older versions of Emacs, you may need to @code{(require 'erc)}
1005beforehand. @pxref{Retrieving URLs,,, url, URL}.
1006
1007For other apps and libraries, such as those relying on the
1008higher-level @code{browse-url}, you'll oftentimes be asked to specify
1009a pattern, sometimes paired with a function that accepts a string URL
1010as a first argument. For example, with EWW, you may need to tack
1011something like @code{"\\|\\`irc6?s?:"} onto the end of
1012@code{eww-use-browse-url}. But with @code{gnus-button-alist}, you'll
1013need a function as well:
1014
1015@lisp
1016 '("\\birc6?s?://[][a-z0-9.,@@_:+%?&/#-]+" 0 t browse-url-irc 0)
1017@end lisp
1018
1019@noindent
1020Users on Emacs 28 and below may need to use @code{browse-url} instead.
1021
994@node Options 1022@node Options
995@section Options 1023@section Options
996@cindex options 1024@cindex options
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 5cabb9b015c..f638d4717a1 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -77,6 +77,13 @@ blanks when 'erc-send-whitespace-lines' is active. New options have
77also been added for warning when input spans multiple lines. Although 77also been added for warning when input spans multiple lines. Although
78off by default, new users are encouraged to enable them. 78off by default, new users are encouraged to enable them.
79 79
80** URL handling has improved.
81Clicking on 'irc://' and 'ircs://' links elsewhere in Emacs now does
82the right thing most of the time. However, for security reasons,
83users are now prompted to confirm connection parameters prior to lift
84off. See the new '(erc) Integrations' section in the Info manual to
85override this.
86
80** Miscellaneous behavioral changes impacting the user experience. 87** Miscellaneous behavioral changes impacting the user experience.
81A bug has been fixed that saw prompts being mangled, doubled, or 88A bug has been fixed that saw prompts being mangled, doubled, or
82erased in server buffers upon disconnection. Instead, input prompts 89erased in server buffers upon disconnection. Instead, input prompts
diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el
index 5b54a0587a1..d23703394be 100644
--- a/lisp/erc/erc-compat.el
+++ b/lisp/erc/erc-compat.el
@@ -32,8 +32,7 @@
32;;; Code: 32;;; Code:
33 33
34(require 'compat nil 'noerror) 34(require 'compat nil 'noerror)
35(eval-when-compile (require 'cl-lib)) 35(eval-when-compile (require 'cl-lib) (require 'url-parse))
36
37 36
38;;;###autoload(autoload 'erc-define-minor-mode "erc-compat") 37;;;###autoload(autoload 'erc-define-minor-mode "erc-compat")
39(define-obsolete-function-alias 'erc-define-minor-mode 38(define-obsolete-function-alias 'erc-define-minor-mode
@@ -285,6 +284,35 @@ If START or END is negative, it counts from the end."
285 `(cl--generic-with-memoization ,table ,@forms)) 284 `(cl--generic-with-memoization ,table ,@forms))
286 (t `(progn ,@forms)))) 285 (t `(progn ,@forms))))
287 286
287(defvar url-irc-function)
288
289(defun erc-compat--29-browse-url-irc (string &rest _)
290 (require 'url-irc)
291 (let* ((url (url-generic-parse-url string))
292 (url-irc-function
293 (if (function-equal url-irc-function 'url-irc-erc)
294 (lambda (host port chan user pass)
295 (erc-handle-irc-url host port chan user pass (url-type url)))
296 url-irc-function)))
297 (url-irc url)))
298
299(cond ((fboundp 'browse-url-irc)) ; 29
300 ((boundp 'browse-url-default-handlers) ; 28
301 (cl-pushnew '("\\`irc6?s?://" . erc-compat--29-browse-url-irc)
302 browse-url-default-handlers))
303 ((boundp 'browse-url-browser-function) ; 27
304 (require 'browse-url)
305 (let ((existing browse-url-browser-function))
306 (setq browse-url-browser-function
307 (if (functionp existing)
308 (lambda (u &rest r)
309 (apply (if (string-match-p "\\`irc6?s?://" u)
310 #'erc-compat--29-browse-url-irc
311 existing)
312 u r))
313 (cons '("\\`irc6?s?://" . erc-compat--29-browse-url-irc)
314 existing))))))
315
288(provide 'erc-compat) 316(provide 'erc-compat)
289 317
290;;; erc-compat.el ends here 318;;; erc-compat.el ends here
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 897357e16be..2312246e3ee 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -7186,25 +7186,83 @@ This function should be on `erc-kill-channel-hook'."
7186;; Teach url.el how to open irc:// URLs with ERC. 7186;; Teach url.el how to open irc:// URLs with ERC.
7187;; To activate, customize `url-irc-function' to `url-irc-erc'. 7187;; To activate, customize `url-irc-function' to `url-irc-erc'.
7188 7188
7189;; FIXME change user to nick, and use API to find server buffer 7189(defcustom erc-url-connect-function nil
7190 "When non-nil, a function used to connect to an IRC URL.
7191Called with a string meant to represent a URL scheme, like
7192\"ircs\", followed by any number of keyword arguments recognized
7193by `erc' and `erc-tls'."
7194 :group 'erc
7195 :package-version '(ERC . "5.4.1") ; FIXME increment on release
7196 :type '(choice (const nil) function))
7197
7198(defun erc--url-default-connect-function (scheme &rest plist)
7199 (let* ((ircsp (if scheme
7200 (string-suffix-p "s" scheme)
7201 (or (eql 6697 (plist-get plist :port))
7202 (yes-or-no-p "Connect using TLS? "))))
7203 (erc-server (plist-get plist :server))
7204 (erc-port (or (plist-get plist :port)
7205 (and ircsp (erc-normalize-port 'ircs-u))
7206 erc-port))
7207 (erc-nick (or (plist-get plist :nick) erc-nick))
7208 (erc-password (plist-get plist :password))
7209 (args (erc-select-read-args)))
7210 (unless ircsp
7211 (setq ircsp (eql 6697 erc-port)))
7212 (apply (if ircsp #'erc-tls #'erc) args)))
7213
7190;;;###autoload 7214;;;###autoload
7191(defun erc-handle-irc-url (host port channel user password) 7215(defun erc-handle-irc-url (host port channel nick password &optional scheme)
7192 "Use ERC to IRC on HOST:PORT in CHANNEL as USER with PASSWORD. 7216 "Use ERC to IRC on HOST:PORT in CHANNEL.
7193If ERC is already connected to HOST:PORT, simply /join CHANNEL. 7217If ERC is already connected to HOST:PORT, simply /join CHANNEL.
7194Otherwise, connect to HOST:PORT as USER and /join CHANNEL." 7218Otherwise, connect to HOST:PORT as NICK and /join CHANNEL.
7195 (let ((server-buffer 7219
7196 (car (erc-buffer-filter 7220Beginning with ERC 5.5, new connections require human intervention.
7197 (lambda () 7221Customize `erc-url-connect-function' to override this."
7198 (and (string-equal erc-session-server host) 7222 (when (eql port 0) (setq port nil))
7199 (= erc-session-port port) 7223 (let* ((net (erc-networks--determine host))
7200 (erc-open-server-buffer-p))))))) 7224 (server-buffer
7201 (with-current-buffer (or server-buffer (current-buffer)) 7225 ;; Viable matches may slip through the cracks for unknown
7202 (if (and server-buffer channel) 7226 ;; networks. Additional passes could likely improve things.
7203 (erc-cmd-JOIN channel) 7227 (car (erc-buffer-filter
7204 (erc-open host port (or user (erc-compute-nick)) (erc-compute-full-name) 7228 (lambda ()
7205 (not server-buffer) password nil channel 7229 (and (not erc--target)
7206 (when server-buffer 7230 (erc-server-process-alive)
7207 (get-buffer-process server-buffer))))))) 7231 ;; Always trust a matched network.
7232 (or (and net (eq net (erc-network)))
7233 (and (string-equal erc-session-server host)
7234 ;; Ports only matter when dialed hosts
7235 ;; match and we have sufficient info.
7236 (or (not port)
7237 (= (erc-normalize-port erc-session-port)
7238 port)))))))))
7239 key deferred)
7240 (unless server-buffer
7241 (setq deferred t
7242 server-buffer (apply (or erc-url-connect-function
7243 #'erc--url-default-connect-function)
7244 scheme
7245 :server host
7246 `(,@(and port (list :port port))
7247 ,@(and nick (list :nick nick))
7248 ,@(and password `(:password ,password))))))
7249 (when channel
7250 ;; These aren't percent-decoded by default
7251 (when (string-prefix-p "%" channel)
7252 (setq channel (url-unhex-string channel)))
7253 (cl-multiple-value-setq (channel key) (split-string channel "[?]"))
7254 (if deferred
7255 ;; Alternatively, we could make this a defmethod, so when
7256 ;; autojoin is loaded, it can do its own thing. Also, as
7257 ;; with `erc-once-with-server-event', it's fine to set local
7258 ;; hooks here because they're killed when reconnecting.
7259 (with-current-buffer server-buffer
7260 (letrec ((f (lambda (&rest _)
7261 (remove-hook 'erc-after-connect f t)
7262 (erc-cmd-JOIN channel key))))
7263 (add-hook 'erc-after-connect f nil t)))
7264 (with-current-buffer server-buffer
7265 (erc-cmd-JOIN channel key))))))
7208 7266
7209(provide 'erc) 7267(provide 'erc)
7210 7268
diff --git a/test/lisp/erc/erc-scenarios-misc.el b/test/lisp/erc/erc-scenarios-misc.el
index ded620ccc1d..8557a779069 100644
--- a/test/lisp/erc/erc-scenarios-misc.el
+++ b/test/lisp/erc/erc-scenarios-misc.el
@@ -177,4 +177,32 @@
177 (erc-scenarios-common-say "Hi") 177 (erc-scenarios-common-say "Hi")
178 (funcall expect 10 "Hola"))))) 178 (funcall expect 10 "Hola")))))
179 179
180(defvar url-irc-function)
181
182(ert-deftest erc-scenarios-handle-irc-url ()
183 :tags '(:expensive-test)
184 (erc-scenarios-common-with-cleanup
185 ((erc-scenarios-common-dialog "join/legacy")
186 (dumb-server (erc-d-run "localhost" t 'foonet))
187 (port (process-contact dumb-server :service))
188 (expect (erc-d-t-make-expecter))
189 (url-irc-function 'url-irc-erc)
190 (erc-url-connect-function
191 (lambda (scheme &rest r)
192 (ert-info ("Connect to foonet")
193 (should (equal scheme "irc"))
194 (with-current-buffer (apply #'erc `(:full-name "tester" ,@r))
195 (should (string= (buffer-name)
196 (format "127.0.0.1:%d" port)))
197 (current-buffer))))))
198
199 (with-temp-buffer
200 (insert (format ";; irc://tester:changeme@127.0.0.1:%d/#chan" port))
201 (goto-char 10)
202 (browse-url-at-point))
203
204 (ert-info ("Connected")
205 (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
206 (funcall expect 10 "welcome")))))
207
180;;; erc-scenarios-misc.el ends here 208;;; erc-scenarios-misc.el ends here
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index db54cb4889f..a5100ec1556 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1084,4 +1084,98 @@
1084 '(nil 7000 nil "Bob's Name" t 1084 '(nil 7000 nil "Bob's Name" t
1085 "bob:changeme" nil nil nil nil "bobo" nil))))))) 1085 "bob:changeme" nil nil nil nil "bobo" nil)))))))
1086 1086
1087(defun erc-tests--make-server-buf (name)
1088 (with-current-buffer (get-buffer-create name)
1089 (erc-mode)
1090 (setq erc-server-process (start-process "sleep" (current-buffer)
1091 "sleep" "1")
1092 erc-session-server (concat "irc." name ".org")
1093 erc-session-port 6667
1094 erc-network (intern name))
1095 (set-process-query-on-exit-flag erc-server-process nil)
1096 (current-buffer)))
1097
1098(defun erc-tests--make-client-buf (server name)
1099 (unless (bufferp server)
1100 (setq server (get-buffer server)))
1101 (with-current-buffer (get-buffer-create name)
1102 (erc-mode)
1103 (setq erc--target (erc--target-from-string name))
1104 (dolist (v '(erc-server-process
1105 erc-session-server
1106 erc-session-port
1107 erc-network))
1108 (set v (buffer-local-value v server)))
1109 (current-buffer)))
1110
1111(ert-deftest erc-handle-irc-url ()
1112 (let* (calls
1113 rvbuf
1114 erc-networks-alist
1115 erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook
1116 (erc-url-connect-function
1117 (lambda (&rest r)
1118 (push r calls)
1119 (if (functionp rvbuf) (funcall rvbuf) rvbuf))))
1120
1121 (cl-letf (((symbol-function 'erc-cmd-JOIN)
1122 (lambda (&rest r) (push r calls))))
1123
1124 (with-current-buffer (erc-tests--make-server-buf "foonet")
1125 (setq rvbuf (current-buffer)))
1126 (erc-tests--make-server-buf "barnet")
1127 (erc-tests--make-server-buf "baznet")
1128
1129 (ert-info ("Unknown network")
1130 (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil "irc")
1131 (should (equal '("#chan" nil) (pop calls)))
1132 (should-not calls))
1133
1134 (ert-info ("Unknown network, no port")
1135 (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil "irc")
1136 (should (equal '("#chan" nil) (pop calls)))
1137 (should-not calls))
1138
1139 (ert-info ("Known network, no port")
1140 (setq erc-networks-alist '((foonet "irc.foonet.org")))
1141 (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil "irc")
1142 (should (equal '("#chan" nil) (pop calls)))
1143 (should-not calls))
1144
1145 (ert-info ("Known network, different port")
1146 (erc-handle-irc-url "irc.foonet.org" 6697 "#chan" nil nil "irc")
1147 (should (equal '("#chan" nil) (pop calls)))
1148 (should-not calls))
1149
1150 (ert-info ("Known network, existing chan with key")
1151 (erc-tests--make-client-buf "foonet" "#chan")
1152 (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil "irc")
1153 (should (equal '("#chan" "sec") (pop calls)))
1154 (should-not calls))
1155
1156 (ert-info ("Unknown network, connect, no chan")
1157 (erc-handle-irc-url "irc.gnu.org" nil nil nil nil "irc")
1158 (should (equal '("irc" :server "irc.gnu.org") (pop calls)))
1159 (should-not calls))
1160
1161 (ert-info ("Unknown network, connect, chan")
1162 (with-current-buffer "foonet"
1163 (should-not (local-variable-p 'erc-after-connect)))
1164 (setq rvbuf (lambda () (erc-tests--make-server-buf "gnu")))
1165 (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil "irc")
1166 (should (equal '("irc" :server "irc.gnu.org") (pop calls)))
1167 (should-not calls)
1168 (with-current-buffer "gnu"
1169 (should (local-variable-p 'erc-after-connect))
1170 (funcall (car erc-after-connect))
1171 (should (equal '("#spam" nil) (pop calls)))
1172 (should-not (local-variable-p 'erc-after-connect)))
1173 (should-not calls))))
1174
1175 (when noninteractive
1176 (kill-buffer "foonet")
1177 (kill-buffer "barnet")
1178 (kill-buffer "baznet")
1179 (kill-buffer "#chan")))
1180
1087;;; erc-tests.el ends here 1181;;; erc-tests.el ends here
diff --git a/test/lisp/erc/resources/join/legacy/foonet.eld b/test/lisp/erc/resources/join/legacy/foonet.eld
index 344ba7c1daf..4025094a59c 100644
--- a/test/lisp/erc/resources/join/legacy/foonet.eld
+++ b/test/lisp/erc/resources/join/legacy/foonet.eld
@@ -1,5 +1,5 @@
1;; -*- mode: lisp-data; -*- 1;; -*- mode: lisp-data; -*-
2((pass 1 "PASS :changeme")) 2((pass 10 "PASS :changeme"))
3((nick 1 "NICK tester")) 3((nick 1 "NICK tester"))
4((user 1 "USER user 0 * :tester") 4((user 1 "USER user 0 * :tester")
5 (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") 5 (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")