bug#56514: 29.0.50; Improve ERC's URI scheme integration for irc:// links
"J.P." <[email protected]>
| Newsgroups | gmane.emacs.bugs,gmane.emacs.erc.general |
|---|---|
| Message-ID | <[email protected]> |
Stefan Kangas <[email protected]> writes: > "J.P." <[email protected]> writes: > >> Questions (for anyone): >> >> 1. I added a couple autoloads in lisp/url/url-irc.el to avoid having >> to create a url-ircs.el (or even a url-irc6{,s}.el). Is there a >> better alternate means of getting `url-scheme-get-property' to >> discover handlers that doesn't rely on autoloads? > > I'm hoping someone else will weigh in about this. > >> 2. In the function `url-irc', I bind `url-current-object' around the >> call to `url-irc-function' to avoid adding another parameter to the >> latter's interface (which mainly benefits ERC). Any obvious problem >> with borrowing `url-current-object' for this purpose? > > No real opinion here. It feels slightly cleaner to add it as a proper > argument, if we expect that other IRC clients than ERC would be > interested in its value. Thinking about this more, I guess it's fine if a modern ERC running on an older Emacs uses the port alone to decide whether to connect over TLS. As such, I've abandoned the whole `url-irc-function' thing in favor of adding a proper argument, as suggested. The small fraction of users with their own `url-irc-function' (if any) may feel some churn though. >> 3. In browse-url, I basically ignore what looks like the favored >> practice for adding handlers, namely, registering an internal >> function with `browse-url-default-handlers' that calls a public >> function assigned to a user option. An example of this pattern is: >> >> internal: browse-url--mailto >> option: browse-url-mailto-function >> public: browse-url-mail >> >> The reason for sidestepping the intervening indirection and adding >> a public function directly to `browse-url-default-handlers' is that >> I figure users wishing to override this can already do so via >> `browse-url-handlers'. Is that misguided somehow? > > You do have a point, but I think it's better to have the user option for > consistency, and for ease of customization. Customizing an alist with > customize is always going to be harder than customizing a single-value > user option. Makes sense. I have thus added the missing ingredients and wired them up. >> 4. Are any of these non-ERC changes newsworthy enough for etc/NEWS? > > I think teaching browse-url to recognize irc URLs is NEWS-worthy. Added. > I also added some notes inline below: Much appreciated! >> From 0d191d30b15ea2d5b6042f51c6cf421b82feb7e5 Mon Sep 17 00:00:00 2001 >> From: "F. Jason Park" <[email protected]> >> Date: Wed, 13 Jul 2022 01:54:19 -0700 >> Subject: [PATCH 1/6] Teach thing-at-point to recognize bracketed IPv6 URLs > > I suggest pushing this patch so that we're sure to have it in Emacs 29. > > I don't think it's NEWS-worthy, as it's more of a bug fix. Installed. >> From 6fd2f75707f123abfbcfae2d4f2837efed5b7adc Mon Sep 17 00:00:00 2001 >> From: "F. Jason Park" <[email protected]> >> Date: Mon, 11 Jul 2022 05:14:57 -0700 >> Subject: [PATCH 2/6] Accommodate ircs:// URLs in url-irc and browse-url > [...] >> +;;;; ircs:// >> + >> +;; The function `url-scheme-get-property' tries and fails to load the >> +;; nonexistent url-ircs.el but falls back to using the following: >> + >> +;;;###autoload >> +(defconst url-ircs-default-port 6697 "Default port for IRCS connections.") >> + >> +;;;###autoload >> +(defalias 'url-ircs 'url-irc) > > This change (support for ircs) should probably be in NEWS. Added. > What about `irc6' and `irc6s'? Should they have aliases? I guess I was trying to avoid growing lisp/loaddefs.el on account of a couple URL schemes that haven't caught on in the wild (and don't seem poised to). Still, I might as well ask around with some IRC folk just to be sure. >> From a9b47f5a6079fb3030c9e1514b4cbbda86dafff8 Mon Sep 17 00:00:00 2001 >> From: "F. Jason Park" <[email protected]> >> Date: Mon, 11 Jul 2022 05:14:57 -0700 >> Subject: [PATCH 4/6] Default to TLS port when calling erc-tls from lisp >> >> * lisp/erc/erc.el (erc-legacy-port-names, erc-normalize-port): Add >> standard IANA port-name mappings for 6667 and 6697, as well as an >> option to opt in for saner but nonstandard behavior. >> (erc-open): Add note to doc string explaining that params `connect' >> and `channel' are mutually exclusive. >> (erc-tls): Call `erc-compute-port' with override. >> (erc-compute-port): Call `erc-normalize-port' with result'. >> >> * test/lisp/erc/erc-tests.el (erc-tls): Add simplistic test focusing >> on default parameters. > > This belongs in NEWS. Right, I definitely plan on mentioning this and most other ERC changes in some fashion. >> @@ -1550,8 +1564,16 @@ erc-normalize-port >> (cond >> ((> port-nr 0) >> port-nr) >> - ((string-equal port "irc") >> - 194) >> + ((string-equal port "ircu") 6667) >> + ((string-equal port "ircs-u") 6697) >> + ((member port '("irc" "ircs")) >> + (when (eq erc-legacy-port-names 'legacy) >> + (lwarn 'ERC 'warning >> + (concat "`erc-legacy-port-names' will default to nil " >> + "in a future version of ERC."))) > > Warning about the default seems unfortunate. Then every user will be > warned until they customize this. > > I think we should either disable the warning, or flip the default to > nil. I've removed the option entirely because I've come to realize it's unlikely a new user would ever set a port parameter to an IANA name in the first place (via :port, `erc-port', or whatever). And existing users accustomed to doing so obviously already know what to expect (namely, quasi-obsolete port numbers, like 194). >> From 3658e89614cbe3b5b27f09271b7bc738a1c7ec38 Mon Sep 17 00:00:00 2001 >> From: "F. Jason Park" <[email protected]> >> Date: Mon, 11 Jul 2022 05:14:57 -0700 >> Subject: [PATCH 6/6] Improve new connections in erc-handle-irc-url > [...] >> + >> +@noindent >> +Keep in mind that when fiddling with these options, it may be easier >> +(and more polite) to connect to a local server or a test network, like >> +@samp{ircs://testnet.ergo.chat/#test}, since these generally don't >> +require authentication. > > Why would that be more polite? It seems to me that, sure, if you're > developing an IRC client I can see why you'd want to use a test network. > > But it seems like overkill just for user customization. Agreed! That's basically nonsense (and so removed). >> +;; The current spec, unlike the 2003 Butcher draft, doesn't explicitly >> +;; allow for an auth[:password]@ component (or trailing ,flags or >> +;; &options). >> +;; >> +;; https://www.iana.org/assignments/uri-schemes >> +;; https://datatracker.ietf.org/doc/html/draft-butcher-irc-url#section-6 >> + > > This is a breaking change, no? I think it should be in NEWS, even if it > is only to make ERC more standards compliant. ERC has always supported the auth:pass@ stuff, but my comment was confusing, so I've deleted it. Thanks so much for looking at these changes!
0000-v4-v5.diff
(text/x-patch, 15.3 KB)
From 7def5db8d6272380acb4bd871becfeb0e96ce4de Mon Sep 17 00:00:00 2001 From: "F. Jason Park" <[email protected]> Date: Wed, 9 Nov 2022 00:16:50 -0800 Subject: [PATCH 0/6] *** NOT A PATCH *** *** BLURB HERE *** F. Jason Park (6): Teach thing-at-point to recognize bracketed IPv6 URLs Accommodate ircs:// URLs in url-irc and browse-url Refactor erc-select-read-args Default to TLS port when calling erc-tls from lisp Add optional server param to erc-networks--determine Improve new connections in erc-handle-irc-url doc/misc/erc.texi | 39 +++++ etc/NEWS | 20 +++ lisp/erc/erc-backend.el | 6 + lisp/erc/erc-compat.el | 15 ++ lisp/erc/erc-networks.el | 9 +- lisp/erc/erc.el | 200 ++++++++++++++++-------- lisp/net/browse-url.el | 24 +++ lisp/thingatpt.el | 2 +- lisp/url/url-irc.el | 32 +++- test/lisp/erc/erc-networks-tests.el | 17 +++ test/lisp/erc/erc-tests.el | 226 ++++++++++++++++++++++++++++ test/lisp/net/browse-url-tests.el | 9 ++ test/lisp/thingatpt-tests.el | 3 + 13 files changed, 527 insertions(+), 75 deletions(-) Interdiff: diff --git a/etc/NEWS b/etc/NEWS index ab64eff74e..500ac5e50b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -438,6 +438,12 @@ The user options 'url-gateway-rlogin-host', 'url-gateway-rlogin-parameters', and 'url-gateway-rlogin-user-name' are also obsolete. +--- +** The user function 'url-irc-function' now takes a 'scheme' argument. +The user option 'url-irc-function' is now called with a sixth argument +corresponding to the scheme portion of the target URL. For example, +this would be "ircs" for a URL like "ircs://irc.libera.chat". + --- ** The linum.el library is now obsolete. We recommend using either the built-in 'display-line-numbers-mode', or @@ -2616,6 +2622,17 @@ This user option decides which URL scheme that 'browse-url' and related functions will use by default. For example, you could customize this to "https" to always prefer HTTPS URLs. +--- +*** New user option 'browse-url-irc-function'. +This option specifies a function for opening irc:// links. It +defaults to the new function 'browse-url-irc'. + +--- +*** New function 'browse-url-irc'. +This multipurpose autoloaded function can be used for opening irc:// +and ircs:// URLS by any caller that passes a URL string as an initial +arg. + --- *** Support for the Netscape web browser has been removed. This support has been obsolete since Emacs 25.1. The final version of @@ -2842,6 +2859,9 @@ remote host are shown. Alternatively, the user option *** 'outlineify-sticky' command is renamed to 'allout-outlinify-sticky'. The old name is still available as an obsolete function alias. +--- +*** The url-irc library now understands ircs:// links. + --- *** New command 'world-clock-copy-time-as-kill' for 'M-x world-clock'. It copies the current line into the kill ring. diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 3c9293e28a..51a97c8de1 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1534,15 +1534,6 @@ erc-reuse-buffers (make-obsolete-variable 'erc-reuse-buffers "old behavior when t now permanent" "29.1") -(defcustom erc-legacy-port-names 'legacy - "Interpret \"irc\" and \"ircs\" using IANA service mappings. -When non-nil, this yields 194 and 994 instead of 6667 and 6697. -When set to `legacy', it also emits a warning saying that the -default will change to nil in the future." - :group 'erc - :package-version '(ERC . "5.4.1") ; FIXME increment on ELPA release - :type '(choice (const nil) (const legacy) (const t))) - (defun erc-normalize-port (port) "Normalize the port specification PORT to integer form. PORT may be an integer, a string or a symbol. If it is a string or a @@ -1551,8 +1542,8 @@ erc-normalize-port * ircs -> 994 * ircd -> 6667 * ircd-dalnet -> 7000" - ;; These were updated in 2022 to reflect modern standards and - ;; practices. See also: + ;; These were updated somewhat in 2022 to reflect modern standards + ;; and practices. See also: ;; ;; https://datatracker.ietf.org/doc/html/rfc7194#section-1 ;; https://www.iana.org/assignments/service-names-port-numbers @@ -1564,20 +1555,14 @@ erc-normalize-port (cond ((> port-nr 0) port-nr) - ((string-equal port "ircu") 6667) - ((string-equal port "ircs-u") 6697) - ((member port '("irc" "ircs")) - (when (eq erc-legacy-port-names 'legacy) - (lwarn 'ERC 'warning - (concat "`erc-legacy-port-names' will default to nil " - "in a future version of ERC."))) - (if (string= port "irc") - (if erc-legacy-port-names 194 6667) - (if erc-legacy-port-names 994 6697))) + ((string-equal port "irc") + 194) ((string-equal port "ircs") 994) - ((string-equal port "ircd") + ((string-equal port "ircu") 6667) ; 6665-6669 + ((string-equal port "ircd") ; nonstandard (irc-serv is 529) 6667) + ((string-equal port "ircs-u") 6697) ((string-equal port "ircd-dalnet") 7000) (t @@ -7186,16 +7171,15 @@ erc-get-parsed-vector-type (defcustom erc-url-connect-function nil "When non-nil, a function used to connect to an IRC URL. -Called with any number of keyword arguments recognized by `erc' -and `erc-tls'. The variable `url-current-object', if non-nil, -can be used to help determine whether to connect using TLS." +Called with a string meant to represent a URL scheme, like +\"ircs\", followed by any number of keyword arguments recognized +by `erc' and `erc-tls'." :group 'erc :package-version '(ERC . "5.4.1") ; FIXME increment on release :type '(choice (const nil) function)) -(defun erc--url-default-connect-function (&rest plist) - (let* ((scheme (and url-current-object (url-type url-current-object))) - (ircsp (if scheme +(defun erc--url-default-connect-function (scheme &rest plist) + (let* ((ircsp (if scheme (string-suffix-p "s" scheme) (or (eql 6697 (plist-get plist :port)) (yes-or-no-p "Connect using TLS? ")))) @@ -7210,15 +7194,8 @@ erc--url-default-connect-function (setq ircsp (eql 6697 erc-port))) (apply (if ircsp #'erc-tls #'erc) args))) -;; The current spec, unlike the 2003 Butcher draft, doesn't explicitly -;; allow for an auth[:password]@ component (or trailing ,flags or -;; &options). -;; -;; https://www.iana.org/assignments/uri-schemes -;; https://datatracker.ietf.org/doc/html/draft-butcher-irc-url#section-6 - ;;;###autoload -(defun erc-handle-irc-url (host port channel nick password) +(defun erc-handle-irc-url (host port channel nick password scheme) "Use ERC to IRC on HOST:PORT in CHANNEL. If ERC is already connected to HOST:PORT, simply /join CHANNEL. Otherwise, connect to HOST:PORT as NICK and /join CHANNEL. @@ -7247,6 +7224,7 @@ erc-handle-irc-url (setq deferred t server-buffer (apply (or erc-url-connect-function #'erc--url-default-connect-function) + scheme :server host `(,@(and port (list :port port)) ,@(and nick (list :nick nick)) diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el index 8d95c0667b..7ac6396d31 100644 --- a/lisp/net/browse-url.el +++ b/lisp/net/browse-url.el @@ -222,6 +222,14 @@ browse-url-man-function (function :tag "Other function")) :version "26.1") +(defcustom browse-url-irc-function 'browse-url-irc + "Function to open an irc:// link." + :type '(choice + (function-item :tag "Emacs IRC" :value browse-url-irc) + (const :tag "None" nil) + (function :tag "Other function")) + :version "29.1") + (defcustom browse-url-button-regexp (concat "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|gemini\\|" @@ -547,6 +555,11 @@ browse-url--browser-kind-man (function-put 'browse-url--man 'browse-url-browser-kind #'browse-url--browser-kind-man) +(defun browse-url--irc (url &rest args) + "Call `browse-url-irc-function' with URL and ARGS." + (funcall browse-url-irc-function url args)) +(function-put 'browse-url--irc 'browse-url-browser-kind 'internal) + (defun browse-url--browser (url &rest args) "Call `browse-url-browser-function' with URL and ARGS." (funcall browse-url-browser-function url args)) @@ -565,7 +578,7 @@ browse-url--non-html-file-url-p (defvar browse-url-default-handlers '(("\\`mailto:" . browse-url--mailto) ("\\`man:" . browse-url--man) - ("\\`irc6?s?://" . browse-url-irc) + ("\\`irc6?s?://" . browse-url--irc) (browse-url--non-html-file-url-p . browse-url-emacs)) "Like `browse-url-handlers' but populated by Emacs and packages. diff --git a/lisp/url/url-irc.el b/lisp/url/url-irc.el index 0dd25b7f49..f97b6de6fe 100644 --- a/lisp/url/url-irc.el +++ b/lisp/url/url-irc.el @@ -39,15 +39,12 @@ url-irc-function CHANNEL - What channel on the server to visit right away (can be nil) USER - What username to use PASSWORD - What password to use. - -The variable `url-current-object' is bound to the parsed `url' -struct, but its members may not match the positional args above, -which should take precedence. For example, `:portspec' may be -nil while PORT is 6667." + SCHEME - a URI scheme, such as \"irc\" or \"ircs\"" :type '(choice (const :tag "rcirc" :value url-irc-rcirc) (const :tag "ERC" :value url-irc-erc) (const :tag "ZEN IRC" :value url-irc-zenirc) (function :tag "Other")) + :version "29.1" ; Added SCHEME :group 'url) ;; External. @@ -56,7 +53,7 @@ url-irc-function (defvar zenirc-server-alist) (defvar zenirc-buffer-name) -(defun url-irc-zenirc (host port channel user password) +(defun url-irc-zenirc (host port channel user password _) (let ((zenirc-buffer-name (if (and user host port) (format "%s@%s:%d" user host port) (format "%s:%d" host port))) @@ -70,14 +67,14 @@ url-irc-zenirc (insert "/join " channel) (zenirc-send-line)))) -(defun url-irc-rcirc (host port channel user password) +(defun url-irc-rcirc (host port channel user password _) (let ((chan (when channel (concat "#" channel)))) (rcirc-connect host port user nil nil (when chan (list chan)) password) (when chan (switch-to-buffer (concat chan "@" host))))) -(defun url-irc-erc (host port channel user password) - (erc-handle-irc-url host port channel user password)) +(defun url-irc-erc (host port channel user password scheme) + (erc-handle-irc-url host port channel user password scheme)) ;;;###autoload (defun url-irc (url) @@ -86,14 +83,18 @@ url-irc (pass (url-password url)) (user (url-user url)) (chan (url-filename url)) - (url-current-object url)) + (type (url-type url)) + (compatp (eql 5 (cdr (func-arity url-irc-function))))) (if (url-target url) (setq chan (concat chan "#" (url-target url)))) (if (string-match "^/" chan) (setq chan (substring chan 1 nil))) (if (= (length chan) 0) (setq chan nil)) - (funcall url-irc-function host port chan user pass) + (when compatp + (lwarn 'url :error "Obsolete value for `url-irc-function'")) + (apply url-irc-function + host port chan user pass (unless compatp (list type))) nil)) ;;;; ircs:// diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index e097090e5d..f83e8c8717 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -1069,7 +1069,8 @@ erc-tls "bob:changeme" nil nil nil t "bobo" GNU.org)))) ;; Values are often nil when called by lisp code, which leads to - ;; null params. This is why `erc-open' recomputes everything. + ;; null params. This is why `erc-open' recomputes almost + ;; everything. (ert-info ("Fallback") (let ((erc-nick "bob") (erc-server "irc.gnu.org") @@ -1126,43 +1127,43 @@ erc-handle-irc-url (erc-tests--make-server-buf "baznet") (ert-info ("Unknown network") - (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil) + (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil "irc") (should (equal '("#chan" nil) (pop calls))) (should-not calls)) (ert-info ("Unknown network, no port") - (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil) + (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil "irc") (should (equal '("#chan" nil) (pop calls))) (should-not calls)) (ert-info ("Known network, no port") (setq erc-networks-alist '((foonet "irc.foonet.org"))) - (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil) + (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil "irc") (should (equal '("#chan" nil) (pop calls))) (should-not calls)) (ert-info ("Known network, different port") - (erc-handle-irc-url "irc.foonet.org" 6697 "#chan" nil nil) + (erc-handle-irc-url "irc.foonet.org" 6697 "#chan" nil nil "irc") (should (equal '("#chan" nil) (pop calls))) (should-not calls)) (ert-info ("Known network, existing chan with key") (erc-tests--make-client-buf "foonet" "#chan") - (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil) + (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil "irc") (should (equal '("#chan" "sec") (pop calls))) (should-not calls)) (ert-info ("Unknown network, connect, no chan") - (erc-handle-irc-url "irc.gnu.org" nil nil nil nil) - (should (equal '(:server "irc.gnu.org") (pop calls))) + (erc-handle-irc-url "irc.gnu.org" nil nil nil nil "irc") + (should (equal '("irc" :server "irc.gnu.org") (pop calls))) (should-not calls)) (ert-info ("Unknown network, connect, chan") (with-current-buffer "foonet" (should-not (local-variable-p 'erc-after-connect))) (setq rvbuf (lambda () (erc-tests--make-server-buf "gnu"))) - (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil) - (should (equal '(:server "irc.gnu.org") (pop calls))) + (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil "irc") + (should (equal '("irc" :server "irc.gnu.org") (pop calls))) (should-not calls) (with-current-buffer "gnu" (should (local-variable-p 'erc-after-connect)) diff --git a/test/lisp/net/browse-url-tests.el b/test/lisp/net/browse-url-tests.el index cf917802e0..dc81976821 100644 --- a/test/lisp/net/browse-url-tests.el +++ b/test/lisp/net/browse-url-tests.el @@ -58,12 +58,12 @@ browse-url-tests-select-handler-man (ert-deftest browse-url-tests-select-handler-irc () (should (eq (browse-url-select-handler "irc://localhost" 'internal) - 'browse-url-irc)) + 'browse-url--irc)) (should-not (browse-url-select-handler "irc://localhost" 'external)) (should (eq (browse-url-select-handler "irc6://localhost") - 'browse-url-irc)) + 'browse-url--irc)) (should (eq (browse-url-select-handler "ircs://[email protected]/#chan") - 'browse-url-irc))) + 'browse-url--irc))) (ert-deftest browse-url-tests-select-handler-file () (should (eq (browse-url-select-handler "file://foo.txt") -- 2.38.1
0002-Accommodate-ircs-URLs-in-url-irc-and-browse-url.patch
(text/x-patch, 8.9 KB)
From 929465942c10a1434ac6333ba6f3df9a110b0199 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" <[email protected]> Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 2/6] Accommodate ircs:// URLs in url-irc and browse-url * lisp/url/url-irc.el (url-irc-function): Change signature of function interface to expect a final "scheme" argument, such as "ircs". (url-irc): Call `url-irc-function' with new positional argument, the scheme extracted via `url-type' from the input URL. (url-irc-erc, url-irc-rcirc, url-irc-zenirc): Accept a URL scheme as a sixth positional arg. (url-ircs-default-port, url-ircs): Add new autoloaded constant and alias for `url-scheme-get-property' to recognize. Do this to avoid having to add another file. * lisp/net/browse-url.el (browse-url-irc-function): Add new option. (browse-url--irc): Add new function to call `browse-url-irc-function'. (browse-url-default-handlers): Add "irc://" entry. (browse-url-irc): Add new function to serve as general handler for "irc://" URLS. Accept trailing variadic args to accommodate non-browse-url interfaces as well. * test/lisp/net/browse-url-tests.el (browse-url-tests-select-handler-irc): Add test for "irc://" URL pattern. * etc/NEWS: Mention select browse-url and url-irc changes. Bug#56514. --- etc/NEWS | 20 +++++++++++++++++++ lisp/net/browse-url.el | 24 +++++++++++++++++++++++ lisp/url/url-irc.el | 32 ++++++++++++++++++++++++------- test/lisp/net/browse-url-tests.el | 9 +++++++++ 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index ab64eff74e..500ac5e50b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -438,6 +438,12 @@ The user options 'url-gateway-rlogin-host', 'url-gateway-rlogin-parameters', and 'url-gateway-rlogin-user-name' are also obsolete. +--- +** The user function 'url-irc-function' now takes a 'scheme' argument. +The user option 'url-irc-function' is now called with a sixth argument +corresponding to the scheme portion of the target URL. For example, +this would be "ircs" for a URL like "ircs://irc.libera.chat". + --- ** The linum.el library is now obsolete. We recommend using either the built-in 'display-line-numbers-mode', or @@ -2616,6 +2622,17 @@ This user option decides which URL scheme that 'browse-url' and related functions will use by default. For example, you could customize this to "https" to always prefer HTTPS URLs. +--- +*** New user option 'browse-url-irc-function'. +This option specifies a function for opening irc:// links. It +defaults to the new function 'browse-url-irc'. + +--- +*** New function 'browse-url-irc'. +This multipurpose autoloaded function can be used for opening irc:// +and ircs:// URLS by any caller that passes a URL string as an initial +arg. + --- *** Support for the Netscape web browser has been removed. This support has been obsolete since Emacs 25.1. The final version of @@ -2842,6 +2859,9 @@ remote host are shown. Alternatively, the user option *** 'outlineify-sticky' command is renamed to 'allout-outlinify-sticky'. The old name is still available as an obsolete function alias. +--- +*** The url-irc library now understands ircs:// links. + --- *** New command 'world-clock-copy-time-as-kill' for 'M-x world-clock'. It copies the current line into the kill ring. diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el index 1597f3651a..7ac6396d31 100644 --- a/lisp/net/browse-url.el +++ b/lisp/net/browse-url.el @@ -222,6 +222,14 @@ browse-url-man-function (function :tag "Other function")) :version "26.1") +(defcustom browse-url-irc-function 'browse-url-irc + "Function to open an irc:// link." + :type '(choice + (function-item :tag "Emacs IRC" :value browse-url-irc) + (const :tag "None" nil) + (function :tag "Other function")) + :version "29.1") + (defcustom browse-url-button-regexp (concat "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|gemini\\|" @@ -547,6 +555,11 @@ browse-url--browser-kind-man (function-put 'browse-url--man 'browse-url-browser-kind #'browse-url--browser-kind-man) +(defun browse-url--irc (url &rest args) + "Call `browse-url-irc-function' with URL and ARGS." + (funcall browse-url-irc-function url args)) +(function-put 'browse-url--irc 'browse-url-browser-kind 'internal) + (defun browse-url--browser (url &rest args) "Call `browse-url-browser-function' with URL and ARGS." (funcall browse-url-browser-function url args)) @@ -565,6 +578,7 @@ browse-url--non-html-file-url-p (defvar browse-url-default-handlers '(("\\`mailto:" . browse-url--mailto) ("\\`man:" . browse-url--man) + ("\\`irc6?s?://" . browse-url--irc) (browse-url--non-html-file-url-p . browse-url-emacs)) "Like `browse-url-handlers' but populated by Emacs and packages. @@ -1510,6 +1524,16 @@ browse-url-text-emacs (function-put 'browse-url-text-emacs 'browse-url-browser-kind 'internal) +;; --- irc --- + +;;;###autoload +(defun browse-url-irc (url &rest _) + "Call `url-irc' directly after parsing URL. +This function is a fit for options like `gnus-button-alist'." + (url-irc (url-generic-parse-url url))) + +(function-put 'browse-url-irc 'browse-url-browser-kind 'internal) + ;; --- mailto --- (autoload 'rfc6068-parse-mailto-url "rfc6068") diff --git a/lisp/url/url-irc.el b/lisp/url/url-irc.el index 9161f7d13e..f97b6de6fe 100644 --- a/lisp/url/url-irc.el +++ b/lisp/url/url-irc.el @@ -38,11 +38,13 @@ url-irc-function PORT - the port number of the IRC server to contact CHANNEL - What channel on the server to visit right away (can be nil) USER - What username to use -PASSWORD - What password to use" +PASSWORD - What password to use. + SCHEME - a URI scheme, such as \"irc\" or \"ircs\"" :type '(choice (const :tag "rcirc" :value url-irc-rcirc) (const :tag "ERC" :value url-irc-erc) (const :tag "ZEN IRC" :value url-irc-zenirc) (function :tag "Other")) + :version "29.1" ; Added SCHEME :group 'url) ;; External. @@ -51,7 +53,7 @@ url-irc-function (defvar zenirc-server-alist) (defvar zenirc-buffer-name) -(defun url-irc-zenirc (host port channel user password) +(defun url-irc-zenirc (host port channel user password _) (let ((zenirc-buffer-name (if (and user host port) (format "%s@%s:%d" user host port) (format "%s:%d" host port))) @@ -65,14 +67,14 @@ url-irc-zenirc (insert "/join " channel) (zenirc-send-line)))) -(defun url-irc-rcirc (host port channel user password) +(defun url-irc-rcirc (host port channel user password _) (let ((chan (when channel (concat "#" channel)))) (rcirc-connect host port user nil nil (when chan (list chan)) password) (when chan (switch-to-buffer (concat chan "@" host))))) -(defun url-irc-erc (host port channel user password) - (erc-handle-irc-url host port channel user password)) +(defun url-irc-erc (host port channel user password scheme) + (erc-handle-irc-url host port channel user password scheme)) ;;;###autoload (defun url-irc (url) @@ -80,16 +82,32 @@ url-irc (port (url-port url)) (pass (url-password url)) (user (url-user url)) - (chan (url-filename url))) + (chan (url-filename url)) + (type (url-type url)) + (compatp (eql 5 (cdr (func-arity url-irc-function))))) (if (url-target url) (setq chan (concat chan "#" (url-target url)))) (if (string-match "^/" chan) (setq chan (substring chan 1 nil))) (if (= (length chan) 0) (setq chan nil)) - (funcall url-irc-function host port chan user pass) + (when compatp + (lwarn 'url :error "Obsolete value for `url-irc-function'")) + (apply url-irc-function + host port chan user pass (unless compatp (list type))) nil)) +;;;; ircs:// + +;; The function `url-scheme-get-property' tries and fails to load the +;; nonexistent url-ircs.el but falls back to using the following: + +;;;###autoload +(defconst url-ircs-default-port 6697 "Default port for IRCS connections.") + +;;;###autoload +(defalias 'url-ircs 'url-irc) + (provide 'url-irc) ;;; url-irc.el ends here diff --git a/test/lisp/net/browse-url-tests.el b/test/lisp/net/browse-url-tests.el index 1c993958b8..dc81976821 100644 --- a/test/lisp/net/browse-url-tests.el +++ b/test/lisp/net/browse-url-tests.el @@ -56,6 +56,15 @@ browse-url-tests-select-handler-man 'browse-url--man)) (should-not (browse-url-select-handler "man:ls" 'external))) +(ert-deftest browse-url-tests-select-handler-irc () + (should (eq (browse-url-select-handler "irc://localhost" 'internal) + 'browse-url--irc)) + (should-not (browse-url-select-handler "irc://localhost" 'external)) + (should (eq (browse-url-select-handler "irc6://localhost") + 'browse-url--irc)) + (should (eq (browse-url-select-handler "ircs://[email protected]/#chan") + 'browse-url--irc))) + (ert-deftest browse-url-tests-select-handler-file () (should (eq (browse-url-select-handler "file://foo.txt") 'browse-url-emacs)) -- 2.38.1
0003-Refactor-erc-select-read-args.patch
(text/x-patch, 10.8 KB)
From 4043399073ac746074157d0e15fa46f99df41833 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" <[email protected]> Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 3/6] Refactor erc-select-read-args * lisp/erc/erc-backend.el (erc--server-connect-dumb-ipv6-regexp): Add liberal pattern for matching bracketed IPv6 addresses. (erc-server-connect): Remove brackets from IPv6 hosts before connecting. * lisp/erc/erc.el (erc--ensure-url): Add compat adapter to massage partial URLs given as input that may be missing the scheme:// portion. (erc-select-read-args): Keep bracketed IPv6 hosts intact. Make this function fully URL-aware (was only partially so). Accept optional `input' argument. * lisp/erc/erc-tests.el (erc-tests--ipv6-examples, erc--server-connect-dumb-ipv6-regexp, erc-select-read-args): Add test reading user input during interactive invocations of entry points. Bug#56514. --- lisp/erc/erc-backend.el | 6 +++ lisp/erc/erc.el | 83 ++++++++++++++++++----------------- test/lisp/erc/erc-tests.el | 89 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 42 deletions(-) diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 026b34849a..1cb0876367 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -625,12 +625,18 @@ erc-open-network-stream (let ((p (plist-put parameters :nowait t))) (apply #'open-network-stream name buffer host service p))) +(defvar erc--server-connect-dumb-ipv6-regexp + ;; Not for validation (gives false positives). + (rx bot "[" (group (+ (any xdigit digit ":.")) (? "%" (+ alnum))) "]" eot)) + (defun erc-server-connect (server port buffer &optional client-certificate) "Perform the connection and login using the specified SERVER and PORT. We will store server variables in the buffer given by BUFFER. CLIENT-CERTIFICATE may optionally be used to specify a TLS client certificate to use for authentication when connecting over TLS (see `erc-session-client-certificate' for more details)." + (when (string-match erc--server-connect-dumb-ipv6-regexp server) + (setq server (match-string 1 server))) (let ((msg (erc-format-message 'connect ?S server ?p port)) process (args `(,(format "erc-%s-%s" server port) nil ,server ,port))) (when client-certificate diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 6b14cf87e2..7f25afa8c5 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -70,7 +70,7 @@ (require 'auth-source) (require 'time-date) (require 'iso8601) -(eval-when-compile (require 'subr-x)) +(eval-when-compile (require 'subr-x) (require 'url-parse)) (defconst erc-version "5.4.1" "This version of ERC.") @@ -2094,52 +2094,51 @@ erc-after-connect :group 'erc-hooks :type '(repeat function)) +(defun erc--ensure-url (input) + (unless (string-match (rx bot "irc" (? "6") (? "s") "://") input) + (when (and (string-match (rx (? (+ any) "@") + (or (group (* (not "[")) ":" (* any)) + (+ any)) + ":" (+ (not (any ":]"))) eot) + input) + (match-beginning 1)) + (setq input (concat "[" (substring input (match-beginning 1)) "]"))) + (setq input (concat "irc://" input))) + input) + ;;;###autoload (defun erc-select-read-args () "Prompt the user for values of nick, server, port, and password." - (let (user-input server port nick passwd) - (setq user-input (read-string - "IRC server: " - (erc-compute-server) 'erc-server-history-list)) - - (if (string-match "\\(.*\\):\\(.*\\)\\'" user-input) - (setq port (erc-string-to-port (match-string 2 user-input)) - user-input (match-string 1 user-input)) - (setq port - (erc-string-to-port (read-string - "IRC port: " (erc-port-to-string - (erc-compute-port)))))) - - (if (string-match "\\`\\(.*\\)@\\(.*\\)" user-input) - (setq nick (match-string 1 user-input) - user-input (match-string 2 user-input)) - (setq nick - (if (erc-already-logged-in server port nick) - (read-string - (erc-format-message 'nick-in-use ?n nick) - nick 'erc-nick-history-list) - (read-string - "Nickname: " (erc-compute-nick nick) - 'erc-nick-history-list)))) - - (setq server user-input) - - (setq passwd (if erc-prompt-for-password - (read-passwd "Server password: ") - (with-suppressed-warnings ((obsolete erc-password)) - erc-password))) + (require 'url-parse) + (let* ((input (let ((d (erc-compute-server))) + (read-string (format "Server (default is %S): " d) + nil 'erc-server-history-list d))) + ;; For legacy reasons, also accept a URL without a scheme. + (url (url-generic-parse-url (erc--ensure-url input))) + (server (url-host url)) + (sp (and (or (string-suffix-p "s" (url-type url)) + (and (equal server erc-default-server) + (not (string-prefix-p "irc://" input)))) + 'ircs-u)) + (port (or (url-portspec url) + (erc-compute-port + (let ((d (erc-compute-port sp))) ; may be a string + (read-string (format "Port (default is %s): " d) + nil nil d))))) + ;; Trust the user not to connect twice accidentally. We + ;; can't use `erc-already-logged-in' to check for an existing + ;; connection without modifying it to consider USER and PASS. + (nick (or (url-user url) + (let ((d (erc-compute-nick))) + (read-string (format "Nickname (default is %S): " d) + nil 'erc-nick-history-list d)))) + (passwd (or (url-password url) + (if erc-prompt-for-password + (read-passwd "Server password (optional): ") + (with-suppressed-warnings ((obsolete erc-password)) + erc-password))))) (when (and passwd (string= "" passwd)) (setq passwd nil)) - - (while (erc-already-logged-in server port nick) - ;; hmm, this is a problem when using multiple connections to a bnc - ;; with the same nick. Currently this code prevents using more than one - ;; bnc with the same nick. actually it would be nice to have - ;; bncs transparent, so that erc-compute-buffer-name displays - ;; the server one is connected to. - (setq nick (read-string - (erc-format-message 'nick-in-use ?n nick) - nick 'erc-nick-history-list))) (list :server server :port port :nick nick :password passwd))) ;;;###autoload diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index c88dd9888d..f72db816af 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -953,4 +953,93 @@ erc-message (kill-buffer "ExampleNet") (kill-buffer "#chan"))) +(defvar erc-tests--ipv6-examples + '("1:2:3:4:5:6:7:8" + "::ffff:10.0.0.1" "::ffff:1.2.3.4" "::ffff:0.0.0.0" + "1:2:3:4:5:6:77:88" "::ffff:255.255.255.255" + "fe08::7:8" "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + "1:2:3:4:5:6:7:8" "1::" "1:2:3:4:5:6:7::" "1::8" + "1:2:3:4:5:6::8" "1:2:3:4:5:6::8" "1::7:8" "1:2:3:4:5::7:8" + "1:2:3:4:5::8" "1::6:7:8" "1:2:3:4::6:7:8" "1:2:3:4::8" + "1::5:6:7:8" "1:2:3::5:6:7:8" "1:2:3::8" "1::4:5:6:7:8" + "1:2::4:5:6:7:8" "1:2::8" "1::3:4:5:6:7:8" "1::3:4:5:6:7:8" + "1::8" "::2:3:4:5:6:7:8" "::2:3:4:5:6:7:8" "::8" + "::" "fe08::7:8%eth0" "fe08::7:8%1" "::255.255.255.255" + "::ffff:255.255.255.255" "::ffff:0:255.255.255.255" + "2001:db8:3:4::192.0.2.33" "64:ff9b::192.0.2.33")) + +(ert-deftest erc--server-connect-dumb-ipv6-regexp () + (dolist (a erc-tests--ipv6-examples) + (should-not (string-match erc--server-connect-dumb-ipv6-regexp a)) + (should (string-match erc--server-connect-dumb-ipv6-regexp + (concat "[" a "]"))))) + +(ert-deftest erc-select-read-args () + + (ert-info ("Defaults to TLS") + (should (equal (ert-simulate-keys "\r\r\r\r" + (erc-select-read-args)) + (list :server "irc.libera.chat" + :port 6697 + :nick (user-login-name) + :password nil)))) + + (ert-info ("Override default TLS") + (should (equal (ert-simulate-keys "irc://irc.libera.chat\r\r\r\r" + (erc-select-read-args)) + (list :server "irc.libera.chat" + :port 6667 + :nick (user-login-name) + :password nil)))) + + (ert-info ("Address includes port") + (should (equal (ert-simulate-keys + "localhost:6667\rnick\r\r" + (erc-select-read-args)) + (list :server "localhost" + :port 6667 + :nick "nick" + :password nil)))) + + (ert-info ("Address includes nick, password skipped via option") + (should (equal (ert-simulate-keys "nick@localhost:6667\r" + (let (erc-prompt-for-password) + (erc-select-read-args))) + (list :server "localhost" + :port 6667 + :nick "nick" + :password nil)))) + + (ert-info ("Addresss includes nick and password") + (should (equal (ert-simulate-keys "nick:sesame@localhost:6667\r" + (erc-select-read-args)) + (list :server "localhost" + :port 6667 + :nick "nick" + :password "sesame")))) + + (ert-info ("IPv6 address plain") + (should (equal (ert-simulate-keys "::1\r\r\r\r" + (erc-select-read-args)) + (list :server "[::1]" + :port 6667 + :nick (user-login-name) + :password nil)))) + + (ert-info ("IPv6 address with port") + (should (equal (ert-simulate-keys "[::1]:6667\r\r\r" + (erc-select-read-args)) + (list :server "[::1]" + :port 6667 + :nick (user-login-name) + :password nil)))) + + (ert-info ("IPv6 address includes nick") + (should (equal (ert-simulate-keys "nick@[::1]:6667\r\r" + (erc-select-read-args)) + (list :server "[::1]" + :port 6667 + :nick "nick" + :password nil))))) + ;;; erc-tests.el ends here -- 2.38.1
0004-Default-to-TLS-port-when-calling-erc-tls-from-lisp.patch
(text/x-patch, 4.7 KB)
From 6aaeb39c3655829598d8d6cf843e27e4720dd136 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" <[email protected]> Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 4/6] Default to TLS port when calling erc-tls from lisp * lisp/erc/erc.el (erc-normalize-port): Add standard IANA port-name mappings for 6667 and 6697. (erc-open): Add note to doc string explaining that params `connect' and `channel' are mutually exclusive. (erc-tls): Call `erc-compute-port' with override. (erc-compute-port): Call `erc-normalize-port' with result'. * test/lisp/erc/erc-tests.el (erc-tls): Add simplistic test focusing on default parameters. Bug#56514. --- lisp/erc/erc.el | 17 +++++++++++---- test/lisp/erc/erc-tests.el | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 7f25afa8c5..28370d7724 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1542,6 +1542,11 @@ erc-normalize-port * ircs -> 994 * ircd -> 6667 * ircd-dalnet -> 7000" + ;; These were updated somewhat in 2022 to reflect modern standards + ;; and practices. See also: + ;; + ;; https://datatracker.ietf.org/doc/html/rfc7194#section-1 + ;; https://www.iana.org/assignments/service-names-port-numbers (cond ((symbolp port) (erc-normalize-port (symbol-name port))) @@ -1554,8 +1559,10 @@ erc-normalize-port 194) ((string-equal port "ircs") 994) - ((string-equal port "ircd") + ((string-equal port "ircu") 6667) ; 6665-6669 + ((string-equal port "ircd") ; nonstandard (irc-serv is 529) 6667) + ((string-equal port "ircs-u") 6697) ((string-equal port "ircd-dalnet") 7000) (t @@ -1924,7 +1931,9 @@ erc-open If CONNECT is non-nil, connect to the server. Otherwise assume already connected and just create a separate buffer for the new -target CHANNEL. +target given by CHANNEL, meaning these parameters are mutually +exclusive. Note that CHANNEL may also be a query; its name has +been retained for historical reasons. Use PASSWD as user password on the server. If TGT-LIST is non-nil, use it to initialize `erc-default-recipients'. @@ -2183,7 +2192,7 @@ 'erc-ssl ;;;###autoload (cl-defun erc-tls (&key (server (erc-compute-server)) - (port (erc-compute-port)) + (port (erc-compute-port 'ircs-u)) (nick (erc-compute-nick)) (user (erc-compute-user)) password @@ -6390,7 +6399,7 @@ erc-compute-port - PORT (the argument passed to this function) - The `erc-port' option - The `erc-default-port' variable" - (or port erc-port erc-default-port)) + (erc-normalize-port (or port erc-port erc-default-port))) ;; time routines diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index f72db816af..db54cb4889 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -1042,4 +1042,46 @@ erc-select-read-args :nick "nick" :password nil))))) +(ert-deftest erc-tls () + (let (calls) + (cl-letf (((symbol-function 'user-login-name) + (lambda (&optional _) "tester")) + ((symbol-function 'erc-open) + (lambda (&rest r) (push r calls)))) + + (ert-info ("Defaults") + (erc-tls) + (should (equal (pop calls) + '("irc.libera.chat" 6697 "tester" "unknown" t + nil nil nil nil nil "user" nil)))) + + (ert-info ("Full") + (erc-tls :server "irc.gnu.org" + :port 7000 + :user "bobo" + :nick "bob" + :full-name "Bob's Name" + :password "bob:changeme" + :client-certificate t + :id 'GNU.org) + (should (equal (pop calls) + '("irc.gnu.org" 7000 "bob" "Bob's Name" t + "bob:changeme" nil nil nil t "bobo" GNU.org)))) + + ;; Values are often nil when called by lisp code, which leads to + ;; null params. This is why `erc-open' recomputes almost + ;; everything. + (ert-info ("Fallback") + (let ((erc-nick "bob") + (erc-server "irc.gnu.org") + (erc-email-userid "bobo") + (erc-user-full-name "Bob's Name")) + (erc-tls :server nil + :port 7000 + :nick nil + :password "bob:changeme")) + (should (equal (pop calls) + '(nil 7000 nil "Bob's Name" t + "bob:changeme" nil nil nil nil "bobo" nil))))))) + ;;; erc-tests.el ends here -- 2.38.1
0005-Add-optional-server-param-to-erc-networks-determine.patch
(text/x-patch, 2.9 KB)
From b2edeb5efffb7b7951245483af75567967ccc403 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" <[email protected]> Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 5/6] Add optional server param to erc-networks--determine * lisp/erc/erc-networks.el (erc-networks--determine): Accept optional `server' argument. * test/lisp/erc/erc-networks-tests.el (erc-networks--determine): Add test. Bug#56514. --- lisp/erc/erc-networks.el | 9 +++++---- test/lisp/erc/erc-networks-tests.el | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el index dba6ead073..b3e5fcf1a3 100644 --- a/lisp/erc/erc-networks.el +++ b/lisp/erc/erc-networks.el @@ -1256,14 +1256,15 @@ erc-set-network-name (defconst erc-networks--name-missing-sentinel (gensym "Unknown ") "Value to cover rare case of a literal NETWORK=nil.") -(defun erc-networks--determine () +(defun erc-networks--determine (&optional server) "Return the name of the network as a symbol. -Search `erc-networks-alist' for a known entity matching +Search `erc-networks-alist' for a known entity matching SERVER or `erc-server-announced-name'. If that fails, use the display name given by the `RPL_ISUPPORT' NETWORK parameter." (or (cl-loop for (name matcher) in erc-networks-alist - when (and matcher (string-match (concat matcher "\\'") - erc-server-announced-name)) + when (and matcher + (string-match (concat matcher "\\'") + (or server erc-server-announced-name))) return name) (and-let* ((vanity (erc--get-isupport-entry 'NETWORK 'single)) ((intern vanity)))) diff --git a/test/lisp/erc/erc-networks-tests.el b/test/lisp/erc/erc-networks-tests.el index 32bdfa11ff..fc12bf7ce3 100644 --- a/test/lisp/erc/erc-networks-tests.el +++ b/test/lisp/erc/erc-networks-tests.el @@ -1704,4 +1704,21 @@ erc-networks--update-server-identity--triple-new (erc-networks-tests--clean-bufs)) +(ert-deftest erc-networks--determine () + (should (eq (erc-networks--determine "irc.libera.chat") 'Libera.Chat)) + (should (eq (erc-networks--determine "irc.oftc.net") 'OFTC)) + (should (eq (erc-networks--determine "irc.dal.net") 'DALnet)) + + (let ((erc-server-announced-name "zirconium.libera.chat")) + (should (eq (erc-networks--determine) 'Libera.Chat))) + (let ((erc-server-announced-name "weber.oftc.net")) + (should (eq (erc-networks--determine) 'OFTC))) + (let ((erc-server-announced-name "redemption.ix.us.dal.net")) + (should (eq (erc-networks--determine) 'DALnet))) + + ;; Failure + (let ((erc-server-announced-name "irc-us2.alphachat.net")) + (should (eq (erc-networks--determine) + erc-networks--name-missing-sentinel)))) + ;;; erc-networks-tests.el ends here -- 2.38.1
0006-Improve-new-connections-in-erc-handle-irc-url.patch
(text/x-patch, 13.7 KB)
From 7def5db8d6272380acb4bd871becfeb0e96ce4de Mon Sep 17 00:00:00 2001 From: "F. Jason Park" <[email protected]> Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 6/6] Improve new connections in erc-handle-irc-url * lisp/erc/erc.el (erc-handle-irc-url): 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. (erc-browse-url-handler): Add autoloaded function. * lisp/erc/erc-compat.el (erc-compat--browse-url--irc): Add new compat function for `browse-url-irc'. Also add it to `browse-url-default-handlers' on Emacs versions below 29. * 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. Bug#56514. --- doc/misc/erc.texi | 39 +++++++++++++++ lisp/erc/erc-compat.el | 15 ++++++ lisp/erc/erc.el | 100 ++++++++++++++++++++++++++++++------- test/lisp/erc/erc-tests.el | 95 +++++++++++++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 17 deletions(-) diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi index 3db83197f9..d01eab1bbb 100644 --- a/doc/misc/erc.texi +++ b/doc/misc/erc.texi @@ -79,6 +79,7 @@ Top * Connecting:: Ways of connecting to an IRC server. * Sample Configuration:: An example configuration file. +* Integrations:: Integrations available for ERC. * Options:: Options that are available for ERC. @end detailmenu @@ -526,6 +527,7 @@ Advanced Usage @menu * Connecting:: Ways of connecting to an IRC server. * Sample Configuration:: An example configuration file. +* Integrations:: Integrations available for ERC. * Options:: Options that are available for ERC. @end menu @@ -990,6 +992,43 @@ Sample Configuration ;; (setq erc-kill-server-buffer-on-quit t) @end lisp +@node Integrations +@section Integrations +@cindex integrations + +@subheading URL +For anything to work, you'll want to set @code{url-irc-function} to +@code{url-irc-erc}. As a rule of thumb, libraries that rely directly +on @code{url-retrieve} should be good to go out the box from Emacs +29.1 onward. On older versions of Emacs, you may need to +@code{(require 'erc)} beforehand. @pxref{Retrieving URLs,,, url, URL}. + +For other apps and libraries, such as those relying on the +higher-level @code{browse-url}, you'll oftentimes be asked to specify +a pattern, sometimes paired with a function that accepts a string URL +as a first argument. For example, with EWW, you may need to tack +something like @code{"\\|\\`irc6?s?:"} onto the end of +@code{eww-use-browse-url}. But with @code{gnus-button-alist}, you'll +need a function as well: + +@lisp + '("\\birc6?s?://[][a-z0-9.,@@_:+%?&/#-]+" + 0 t erc-browse-url-handler 0) +@end lisp + +@defun erc-browse-url-handler url &rest args +An autoloaded convenience function for use in options like those +mentioned above. @var{url} must be a string. In Emacs 29 and above, +the function @code{browse-url-irc} can be used instead. +@end defun + +@noindent +Keep in mind that when fiddling with these options, it may be easier +(and more polite) to connect to a local server or a test network, like +@samp{ircs://testnet.ergo.chat/#test}, since these generally don't +require authentication. + + @node Options @section Options @cindex options diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el index 03bd8f1352..683d19dfc7 100644 --- a/lisp/erc/erc-compat.el +++ b/lisp/erc/erc-compat.el @@ -168,6 +168,21 @@ erc-compat--with-memoization `(cl--generic-with-memoization ,table ,@forms)) (t `(progn ,@forms)))) +(declare-function browse-url-irc "browse-url" (url &rest _)) + +(defun erc-compat--browse-url-irc (string &rest _) + "Parse STRING and call `url-irc'." + (require 'url-irc) + (if (< emacs-major-version 29) + ;; `url-irc' binds this in Emacs 29+. + (let ((url-current-object (url-generic-parse-url string))) + (url-irc url-current-object)) + (browse-url-irc string))) + +(when (< emacs-major-version 29) + (add-to-list 'browse-url-default-handlers + '("\\`irc6?s?://" . erc-compat--browse-url-irc))) + (provide 'erc-compat) ;;; erc-compat.el ends here diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 28370d7724..51a97c8de1 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -7169,25 +7169,91 @@ erc-get-parsed-vector-type ;; Teach url.el how to open irc:// URLs with ERC. ;; To activate, customize `url-irc-function' to `url-irc-erc'. -;; FIXME change user to nick, and use API to find server buffer +(defcustom erc-url-connect-function nil + "When non-nil, a function used to connect to an IRC URL. +Called with a string meant to represent a URL scheme, like +\"ircs\", followed by any number of keyword arguments recognized +by `erc' and `erc-tls'." + :group 'erc + :package-version '(ERC . "5.4.1") ; FIXME increment on release + :type '(choice (const nil) function)) + +(defun erc--url-default-connect-function (scheme &rest plist) + (let* ((ircsp (if scheme + (string-suffix-p "s" scheme) + (or (eql 6697 (plist-get plist :port)) + (yes-or-no-p "Connect using TLS? ")))) + (erc-server (plist-get plist :server)) + (erc-port (or (plist-get plist :port) + (and ircsp (erc-normalize-port 'ircs-u)) + erc-port)) + (erc-nick (or (plist-get plist :nick) erc-nick)) + (erc-password (plist-get plist :password)) + (args (erc-select-read-args))) + (unless ircsp + (setq ircsp (eql 6697 erc-port))) + (apply (if ircsp #'erc-tls #'erc) args))) + ;;;###autoload -(defun erc-handle-irc-url (host port channel user password) - "Use ERC to IRC on HOST:PORT in CHANNEL as USER with PASSWORD. +(defun erc-handle-irc-url (host port channel nick password scheme) + "Use ERC to IRC on HOST:PORT in CHANNEL. If ERC is already connected to HOST:PORT, simply /join CHANNEL. -Otherwise, connect to HOST:PORT as USER and /join CHANNEL." - (let ((server-buffer - (car (erc-buffer-filter - (lambda () - (and (string-equal erc-session-server host) - (= erc-session-port port) - (erc-open-server-buffer-p))))))) - (with-current-buffer (or server-buffer (current-buffer)) - (if (and server-buffer channel) - (erc-cmd-JOIN channel) - (erc-open host port (or user (erc-compute-nick)) (erc-compute-full-name) - (not server-buffer) password nil channel - (when server-buffer - (get-buffer-process server-buffer))))))) +Otherwise, connect to HOST:PORT as NICK and /join CHANNEL. + +Beginning with ERC 5.5, new connections require human intervention. +Customize `erc-url-connect-function' to override this." + (when (eql port 0) (setq port nil)) + (let* ((net (erc-networks--determine host)) + (server-buffer + ;; Viable matches may slip through the cracks for unknown + ;; networks. Additional passes could likely improve things. + (car (erc-buffer-filter + (lambda () + (and (not erc--target) + (erc-server-process-alive) + ;; Always trust a matched network. + (or (and net (eq net (erc-network))) + (and (string-equal erc-session-server host) + ;; Ports only matter when dialed hosts + ;; match and we have sufficient info. + (or (not port) + (= (erc-normalize-port erc-session-port) + port))))))))) + key deferred) + (unless server-buffer + (setq deferred t + server-buffer (apply (or erc-url-connect-function + #'erc--url-default-connect-function) + scheme + :server host + `(,@(and port (list :port port)) + ,@(and nick (list :nick nick)) + ,@(and password `(:password ,password)))))) + (when channel + ;; These aren't percent-decoded by default + (when (string-prefix-p "%" channel) + (setq channel (url-unhex-string channel))) + (cl-multiple-value-setq (channel key) (split-string channel "[?]")) + (if deferred + ;; Alternatively, we could make this a defmethod, so when + ;; autojoin is loaded, it can do its own thing. Also, as + ;; with `erc-once-with-server-event', it's fine to set local + ;; hooks here because they're killed when reconnecting. + (with-current-buffer server-buffer + (letrec ((f (lambda (&rest _) + (remove-hook 'erc-after-connect f t) + (erc-cmd-JOIN channel key)))) + (add-hook 'erc-after-connect f nil t))) + (with-current-buffer server-buffer + (erc-cmd-JOIN channel key)))))) + +(defvar url-irc-function) + +;;;###autoload +(defun erc-browse-url-handler (url &rest _) + "Launch an ERC session when given an irc:// URL." + (let ((url-irc-function 'url-irc-erc)) + (erc-compat--browse-url-irc url))) (provide 'erc) diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index db54cb4889..f83e8c8717 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -1084,4 +1084,99 @@ erc-tls '(nil 7000 nil "Bob's Name" t "bob:changeme" nil nil nil nil "bobo" nil))))))) +(defun erc-tests--make-server-buf (name) + (with-current-buffer (get-buffer-create name) + (erc-mode) + (setq erc-server-process (start-process "sleep" (current-buffer) + "sleep" "1") + erc-session-server (concat "irc." name ".org") + erc-session-port 6667 + erc-network (intern name)) + (set-process-query-on-exit-flag erc-server-process nil) + (current-buffer))) + +(defun erc-tests--make-client-buf (server name) + (unless (bufferp server) + (setq server (get-buffer server))) + (with-current-buffer (get-buffer-create name) + (erc-mode) + (setq erc--target (erc--target-from-string name)) + (dolist (v '(erc-server-process + erc-session-server + erc-session-port + erc-network)) + (set v (buffer-local-value v server))) + (current-buffer))) + +(ert-deftest erc-handle-irc-url () + (let* (calls + rvbuf + erc-networks-alist + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook + (erc-url-connect-function + (lambda (&rest r) + (push r calls) + (if (functionp rvbuf) (funcall rvbuf) rvbuf)))) + + (cl-letf (((symbol-function 'erc-cmd-JOIN) + (lambda (&rest r) (push r calls)))) + + (with-current-buffer (erc-tests--make-server-buf "foonet") + (setq rvbuf (current-buffer))) + (erc-tests--make-server-buf "barnet") + (erc-tests--make-server-buf "baznet") + + (ert-info ("Unknown network") + (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil "irc") + (should (equal '("#chan" nil) (pop calls))) + (should-not calls)) + + (ert-info ("Unknown network, no port") + (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil "irc") + (should (equal '("#chan" nil) (pop calls))) + (should-not calls)) + + (ert-info ("Known network, no port") + (setq erc-networks-alist '((foonet "irc.foonet.org"))) + (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil "irc") + (should (equal '("#chan" nil) (pop calls))) + (should-not calls)) + + (ert-info ("Known network, different port") + (erc-handle-irc-url "irc.foonet.org" 6697 "#chan" nil nil "irc") + (should (equal '("#chan" nil) (pop calls))) + (should-not calls)) + + (ert-info ("Known network, existing chan with key") + (erc-tests--make-client-buf "foonet" "#chan") + (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil "irc") + (should (equal '("#chan" "sec") (pop calls))) + (should-not calls)) + + (ert-info ("Unknown network, connect, no chan") + (erc-handle-irc-url "irc.gnu.org" nil nil nil nil "irc") + (should (equal '("irc" :server "irc.gnu.org") (pop calls))) + (should-not calls)) + + (ert-info ("Unknown network, connect, chan") + (with-current-buffer "foonet" + (should-not (local-variable-p 'erc-after-connect))) + (setq rvbuf (lambda () (erc-tests--make-server-buf "gnu"))) + (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil "irc") + (should (equal '("irc" :server "irc.gnu.org") (pop calls))) + (should-not calls) + (with-current-buffer "gnu" + (should (local-variable-p 'erc-after-connect)) + (funcall (car erc-after-connect)) + (should (equal '("#spam" nil) (pop calls))) + (should-not erc-after-connect) + (should-not (local-variable-p 'erc-after-connect))) + (should-not calls)))) + + (when noninteractive + (kill-buffer "foonet") + (kill-buffer "barnet") + (kill-buffer "baznet") + (kill-buffer "#chan"))) + ;;; erc-tests.el ends here -- 2.38.1