From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: "J.P." Newsgroups: gmane.emacs.bugs Subject: bug#56514: 29.0.50; Improve ERC's URI scheme integration for irc:// links Date: Wed, 13 Jul 2022 07:44:48 -0700 Message-ID: <874jzl2hsv.fsf__1893.52188478864$1657723580$gmane$org@neverwas.me> References: <87pmiabvd5.fsf@neverwas.me> <87edyqzeag.fsf@gnus.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="8281"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux) Cc: 56514@debbugs.gnu.org, emacs-erc@gnu.org To: Lars Ingebrigtsen Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Wed Jul 13 16:46:13 2022 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1oBdcy-0001zj-QP for geb-bug-gnu-emacs@m.gmane-mx.org; Wed, 13 Jul 2022 16:46:13 +0200 Original-Received: from localhost ([::1]:46658 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oBdcx-0006it-FW for geb-bug-gnu-emacs@m.gmane-mx.org; Wed, 13 Jul 2022 10:46:11 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:45470) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oBdcp-0006ii-Ps for bug-gnu-emacs@gnu.org; Wed, 13 Jul 2022 10:46:03 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:53578) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1oBdco-0007Ap-IW for bug-gnu-emacs@gnu.org; Wed, 13 Jul 2022 10:46:03 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1oBdco-0001qu-HM for bug-gnu-emacs@gnu.org; Wed, 13 Jul 2022 10:46:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: "J.P." Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Wed, 13 Jul 2022 14:46:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 56514 X-GNU-PR-Package: emacs Original-Received: via spool by 56514-submit@debbugs.gnu.org id=B56514.16577235247069 (code B ref 56514); Wed, 13 Jul 2022 14:46:02 +0000 Original-Received: (at 56514) by debbugs.gnu.org; 13 Jul 2022 14:45:24 +0000 Original-Received: from localhost ([127.0.0.1]:47475 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oBdby-0001pd-Sr for submit@debbugs.gnu.org; Wed, 13 Jul 2022 10:45:24 -0400 Original-Received: from mail-108-mta133.mxroute.com ([136.175.108.133]:42029) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oBdbr-0001oF-P5 for 56514@debbugs.gnu.org; Wed, 13 Jul 2022 10:45:09 -0400 Original-Received: from filter006.mxroute.com ([140.82.40.27] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta133.mxroute.com (ZoneMTA) with ESMTPSA id 181f8051c1a0000261.001 for <56514@debbugs.gnu.org> (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256); Wed, 13 Jul 2022 14:44:53 +0000 X-Zone-Loop: ccf4e34993a8ed02d222b5838dc04e9a7b1333292b7a X-Originating-IP: [140.82.40.27] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:In-Reply-To:Date:References: Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=8ignJVO9cfihlQ3muR8OLApEIvIb1dUxWoZExFCKVJY=; b=PGPXKNpID2b1r19BInw9j5O8bD 5p/NEMF+pxV2Lk/6hsV4wrvsLO67tg4EJ3Ewd9qjpt84mMyWrGP0e6ZecB68+n1SJMSNV2NEstXaj n/DDqAGZO/r+/C9N8cK7UydYSC1y0xR6m/8rITgUo6Xj1euFNT9Xb1IQOPvhZzIZfwBcq91mXFQ2q m2GoPCdaLcU2WXCUx6sYa6spaB5f9/EonkQRI0IFoP+sX1LKm2sWfxaVpXA3MdOydAxRUtjmCJ+zB PkvswFRPM8PQoQTLScAdQU/LVi8SNSaAC/IjAha/txqSwFNXSKwEJ6tG/0ZRwGR25otusDiYTnWM9 JF5SNLOQ==; In-Reply-To: <87edyqzeag.fsf@gnus.org> (Lars Ingebrigtsen's message of "Tue, 12 Jul 2022 14:49:43 +0200") X-AuthUser: masked@neverwas.me X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:236903 Archived-At: --=-=-= Content-Type: text/plain Lars Ingebrigtsen writes: > "J.P." writes: > >> In the end, I'm hoping other folks will step forward who may be more >> familiar with the libraries mentioned so that nicer renditions can >> emerge. > > It's a large patch series -- perhaps asking questions about specific > libraries would be more productive. Hm, right. Seems I'll have to take my entitled prima dona act elsewhere. Questions, then (TIA, people): 1. The first patch strays outside ERC's turf. Should I open a separate bug report for it? [1]. 2. With how I have things now, we'd use `browse-url-default-handlers' to sidestep url.el's loader-finding logic, as performed by `url-scheme-get-property'. But that feels a little hacky since my new, generalized handler is just a wrapper that calls `url-irc' (the loader), which massages the arguments and then calls our original (somewhat revamped) handler. A cleaner way might be to perhaps make url-irc.el aware of the new handler. But for that we'd need `url-scheme-get-property' to map all the scheme variants we're interested in, like ircs, irc6, etc., to that same loader. OTOH, that file's pretty ancient, so perhaps it's better to just leave it be? 3. Should I include the actual setup code for the integrations? If so, where would that go? My initial plan was to just have it all live in the docs, perhaps under a new Info node. BTW (re integrations), I also threw in a .desktop file [2], knowing full well that folks may just perceive that as more clutter polluting the Emacs tree. Should I drop it? People wanting one can just make their own. 4. I'll approach the Org people separately for this stuff, but just as a preview: my main question for them deals with their nonstandard "/user" variant of the URI syntax. Specifically, I'd like to know how it's supposed to work when a "?key" or multiple comma-separated channels (also nonstandard) are present in the URL. I'd also like to know how important it is we preserve this feature and how amenable they'd be to it (rapidly) going extinct [3]. Other, minor questions remain, some internal to ERC [4], but I'll spare everyone the trouble for now. Thanks! [1] On a system for which `browse-url-can-use-xdg-open' returns non-nil, and with point somewhere over some text like "http://[::1]", do M-x browse-url-at-point RET What happens is that `browse-url-url-at-point' returns "http://http://" because `thing-at-point-url-at-point' doesn't seem to like IPv6 URLs. This ultimately leads to a (call-process "xdg-open" nil 0 nil "http://http://") which exits nonzero. [2] For anyone interested, if your OS supports XDG desktop stuff, you can try the included etc/emacs-irc.desktop by doing something like a. Change the Exec directive to launch a local emacs -Q -Exec=emacs -l erc -f erc--handle-ircs-url %u +Exec=/home/me/emacs/master/src/emacs -Q -l erc -f ... b. $ desktop-file-install --rebuild-mime-info-cache \ --dir=~/.local/share/applications etc/emacs-irc.desktop c. $ gtk-launch emacs-irc 'ircs://testnet.ergo.chat/#test' [3] Not that it matters, but it took a fair bit of surgery across four patches to make "/user" behave as originally intended (according to my possibly warped impression). However, I'm pretty convinced it can only ever work reliably in conjunction with an IRC extension that's not (yet) widely adopted by servers and that ERC doesn't yet implement (although bug#49860 has something coming down the pike). [4] There's also the matter of duplicate functionality WRT the autojoin module and URL-triggered channel joining. It'd be nice to find a way to defer to existing code when a URL specifies a channel. Obviously, when a connection already exists or autojoin is already enabled, this won't be an issue. But when that's not the case, what's the right move? (Disabling autojoin seems mighty popular.) One option is just to refuse to open a new connection without autojoin. Or, we could prompt the user for permission to enable it. Somewhat complicating this is the fact that autojoin (like all modules) is only designed to be enabled globally. (I have a patch to address this, but it's only aimed at defining new modules as local to a network context.) --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0000-v1-v2.diff >From 1ef37c2aeff57b5d81a6ebd64a5a0d505203d923 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Wed, 13 Jul 2022 05:50:50 -0700 Subject: [PATCH 00/10] *** NOT A PATCH *** *** BLURB HERE *** F. Jason Park (10): Teach thing-at-point to recognize bracketed IPv6 URLs Accept bracketed IPv6 hosts in ERC Default to TLS port when calling erc-tls in lisp code Add optional server param to erc-networks--determine Improve new connections in erc-handle-irc-url [POC] Make erc-once-with-server-event more nimble [POC] Support one-off JOIN handlers in ERC [POC] Use erc--join-with-callback in URL handler [POC] Demo improved ol-irc integration [POC] * etc/emacs-irc.desktop: New file. etc/emacs-irc.desktop | 13 ++ lisp/erc/erc-backend.el | 8 + lisp/erc/erc-networks.el | 9 +- lisp/erc/erc.el | 268 ++++++++++++++++++++++++---- lisp/thingatpt.el | 2 +- test/lisp/erc/erc-networks-tests.el | 17 ++ test/lisp/erc/erc-tests.el | 136 ++++++++++++++ test/lisp/thingatpt-tests.el | 1 + 8 files changed, 415 insertions(+), 39 deletions(-) create mode 100644 etc/emacs-irc.desktop Interdiff: diff --git a/etc/emacs-irc.desktop b/etc/emacs-irc.desktop index ed13e918d2..ebdcda3a07 100644 --- a/etc/emacs-irc.desktop +++ b/etc/emacs-irc.desktop @@ -5,7 +5,6 @@ Keywords=ERC;extensible;chat;IRC;client; Categories=Network;Chat;IRCClient; Comment=GNU Emacs is an extensible, customizable text editor - ERC is a powerful, modular, and extensible IRC client for Emacs # FIXME update command line and name once autoloaded -# Also check if shell-quoting %u is needed, since it likely includes a # Exec=emacs -l erc -f erc--handle-ircs-url %u Icon=emacs MimeType=x-scheme-handler/irc;x-scheme-handler/ircs; diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index bc7a7d14dc..2ead0c9ba5 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -532,12 +532,20 @@ 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 + ;; Likely gives false positives and false negatives + (rx bot "[" + (group (+ (or (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 efa88bfff5..7137a7b401 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -2364,8 +2364,11 @@ erc-select-read-args (setq user-input (read-string "IRC server: " (erc-compute-server) 'erc-server-history-list)) - - (if (string-match "\\(.*\\):\\(.*\\)\\'" user-input) + (if (and (string-match (rx (or (: (* (not "[")) ":" (* any)) + (group (+ any))) + ":" (group (+ (not (any ":]")))) eot) + user-input) + (match-string 1 user-input)) (setq port (erc-string-to-port (match-string 2 user-input)) user-input (match-string 1 user-input)) (setq port @@ -7481,10 +7484,9 @@ erc-handle-irc-url If ERC is already connected to HOST:PORT, simply /join CHANNEL. Otherwise, connect to HOST:PORT as NICK and /join CHANNEL. -Note: calling this function with NICK and/or PASSWORD is -deprecated and results in a warning. Moreover, ERC no longer -attempts to establish new connections without human intervention, -although opting in may eventually be allowed." +Note that ERC no longer attempts to establish new connections +without human intervention, although opting in may eventually be +allowed." (when (eql port 0) (setq port nil)) (let* ((net (erc-networks--determine host)) (server-buffer @@ -7503,9 +7505,6 @@ erc-handle-irc-url (= (erc-normalize-port erc-session-port) port))))))))) key deferred) - (when (or nick password) - (display-warning 'erc (concat "Calling `erc-handle-irc-url' with a nick " - "or a password argument is deprecated."))) (unless server-buffer (unless connect-fn (user-error "Existing session for %s not found." host)) @@ -7518,7 +7517,7 @@ erc-handle-irc-url ;; 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 "?")) + (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 @@ -7532,37 +7531,44 @@ erc-handle-irc-url (with-current-buffer server-buffer (erc--join-with-callback channel key on-join)))))) -;; XXX ERASE ME -;; -;; The final spec was simplified from the 2003 Butcher draft and -;; doesn't allow an auth@ component or trailing ,flags or &options. -;; Because of this, we shouldn't just connect and risk exposing -;; whatever's returned by `user-login-name'. +;; XXX ERASE ME (possibly use as basis for new section in info doc) ;; ;; For now, as a demo, users must require erc and do something like: ;; ;; (add-to-list 'browse-url-default-handlers -;; '("\\`ircs?://" . erc--handle-ircs-url)) +;; '("\\`irc6?s?://" . erc--handle-ircs-url)) ;; ;; Libraries that optionally depend on browse-url, like eww, etc. need ;; an extra hand as well: ;; ;; (setq eww-use-browse-url -;; (concat eww-use-browse-url "\\|\\`ircs?:")) +;; (concat eww-use-browse-url "\\|\\`irc6?s?:")) ;; ;; Those that don't use browse-url get the same handler: ;; -;; (push '("\\bircs?://[a-z.@_+0-9%=?&/#-]+" -;; 0 t erc--handle-ircs-url 0) -;; gnus-button-alist) +;; (add-to-list 'gnus-button-alist +;; '("\\birc6?s?://[][a-z0-9.,@_:+%?&/#-]+" +;; 0 t erc--handle-ircs-url 0)) ;; ;; Finally, insert something like "ircs://testnet.ergo.chat/#test" ;; where appropriate and perform a suitable action. + +;; The two variables below are contenders for exporting as user +;; options. The rationale for separate functions here instead of, +;; say, a single option granting ERC permission to connect +;; automatically is that, since ERC doesn't lacks any concept of +;; configured server profiles, it has no idea what values to give for +;; connection parameters, like nick, user, etc. +;; +;; Also, the current spec was simplified from the 2003 Butcher draft +;; and doesn't explicitly allow for an auth[:password]@ component (or +;; trailing ,flags or &options, for that matter). Regardless, even +;; when provided, we shouldn't just connect and risk exposing +;; whatever's returned by `user-login-name', right? ;; ;; https://www.iana.org/assignments/uri-schemes ;; https://datatracker.ietf.org/doc/html/draft-butcher-irc-url#section-6 -;; Contenders for exporting as user options. (defvar erc--url-irc-connect-function nil) (defvar erc--url-ircs-connect-function nil) @@ -7586,7 +7592,7 @@ erc--handle-ircs-url (require 'url-parse) (unless (url-p url) (setq url (url-generic-parse-url url))) - (let* ((ircsp (string-match "ircs" (url-type url))) + (let* ((ircsp (string-suffix-p "s" (url-type url))) (fn (or (if ircsp erc--url-ircs-connect-function erc--url-irc-connect-function) @@ -7637,6 +7643,7 @@ erc--handle-url-org-follow-ircs ;; present. The following is only for demo purposes. (defun erc--org-init () + ;; TODO also add irc6 and irc6s (possibly nonstandard) (require 'ol-irc) (org-link-set-parameters "irc" diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el index b3dca5890f..5e597df6ff 100644 --- a/lisp/thingatpt.el +++ b/lisp/thingatpt.el @@ -430,7 +430,7 @@ thing-at-point-bounds-of-url-at-point ;; Otherwise, find the bounds within which a URI may exist. The ;; method is similar to `ffap-string-at-point'. Note that URIs ;; may contain parentheses but may not contain spaces (RFC3986). - (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'") + (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'[]") (skip-before "^[0-9a-zA-Z]") (skip-after ":;.,!?'") (pt (point)) diff --git a/test/lisp/thingatpt-tests.el b/test/lisp/thingatpt-tests.el index b6d0b1446a..b5f4ea8cdc 100644 --- a/test/lisp/thingatpt-tests.el +++ b/test/lisp/thingatpt-tests.el @@ -44,6 +44,7 @@ thing-at-point-test-data ;; Non alphanumeric characters can be found in URIs ("ftp://example.net/~foo!;#bar=baz&goo=bob" 3 url "ftp://example.net/~foo!;#bar=baz&goo=bob") ("bzr+ssh://user@example.net:5/a%20d,5" 34 url "bzr+ssh://user@example.net:5/a%20d,5") + ("http://[::1]:8000/foo" 10 url "http://[::1]:8000/foo") ;; markup ("Url: ..." 8 url "foo://1.example.com") ("Url: ..." 30 url "foo://2.example.com") -- 2.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Teach-thing-at-point-to-recognize-bracketed-IPv6-URL.patch >From 1467585c21d1bebbb3823d4c71b9265c358e1b46 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Wed, 13 Jul 2022 01:54:19 -0700 Subject: [PATCH 01/10] Teach thing-at-point to recognize bracketed IPv6 URLs * lisp/thingatpt.el (thing-at-point-bounds-of-url-at-point): Allow brackets. * test/lisp/thingatpt-tests.el (thing-at-point-test-data): Add case for IPv6 URL. --- lisp/thingatpt.el | 2 +- test/lisp/thingatpt-tests.el | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el index b3dca5890f..5e597df6ff 100644 --- a/lisp/thingatpt.el +++ b/lisp/thingatpt.el @@ -430,7 +430,7 @@ thing-at-point-bounds-of-url-at-point ;; Otherwise, find the bounds within which a URI may exist. The ;; method is similar to `ffap-string-at-point'. Note that URIs ;; may contain parentheses but may not contain spaces (RFC3986). - (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'") + (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'[]") (skip-before "^[0-9a-zA-Z]") (skip-after ":;.,!?'") (pt (point)) diff --git a/test/lisp/thingatpt-tests.el b/test/lisp/thingatpt-tests.el index b6d0b1446a..b5f4ea8cdc 100644 --- a/test/lisp/thingatpt-tests.el +++ b/test/lisp/thingatpt-tests.el @@ -44,6 +44,7 @@ thing-at-point-test-data ;; Non alphanumeric characters can be found in URIs ("ftp://example.net/~foo!;#bar=baz&goo=bob" 3 url "ftp://example.net/~foo!;#bar=baz&goo=bob") ("bzr+ssh://user@example.net:5/a%20d,5" 34 url "bzr+ssh://user@example.net:5/a%20d,5") + ("http://[::1]:8000/foo" 10 url "http://[::1]:8000/foo") ;; markup ("Url: ..." 8 url "foo://1.example.com") ("Url: ..." 30 url "foo://2.example.com") -- 2.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-Accept-bracketed-IPv6-hosts-in-ERC.patch >From eb3c13c5a09634c2ae71bf3dc97bd32bcf1bef0f Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Wed, 13 Jul 2022 02:48:29 -0700 Subject: [PATCH 02/10] Accept bracketed IPv6 hosts in ERC * 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-select-read-args): Keep bracketed IPv6 hosts intact. --- lisp/erc/erc-backend.el | 8 ++++++++ lisp/erc/erc.el | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index bc7a7d14dc..2ead0c9ba5 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -532,12 +532,20 @@ 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 + ;; Likely gives false positives and false negatives + (rx bot "[" + (group (+ (or (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 0a16831fba..a7114a4bcf 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -2348,8 +2348,11 @@ erc-select-read-args (setq user-input (read-string "IRC server: " (erc-compute-server) 'erc-server-history-list)) - - (if (string-match "\\(.*\\):\\(.*\\)\\'" user-input) + (if (and (string-match (rx (or (: (* (not "[")) ":" (* any)) + (group (+ any))) + ":" (group (+ (not (any ":]")))) eot) + user-input) + (match-string 1 user-input)) (setq port (erc-string-to-port (match-string 2 user-input)) user-input (match-string 1 user-input)) (setq port -- 2.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0003-Default-to-TLS-port-when-calling-erc-tls-in-lisp-cod.patch >From 74d61e3d1e8c34cb31efe764e79ac64417b06b47 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 03/10] Default to TLS port when calling erc-tls in lisp code * 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): When `erc-port' hasn't been set and the port param is a string, ask `erc-normalize-port' to look it up before falling back to `erc-default-port'. * test/lisp/erc/erc-tests.el (erc-tls): Add simplistic test focusing on default parameters. --- lisp/erc/erc.el | 18 ++++++++++++++--- test/lisp/erc/erc-tests.el | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index a7114a4bcf..f09720fc79 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1743,6 +1743,11 @@ erc-normalize-port * ircs -> 994 * ircd -> 6667 * ircd-dalnet -> 7000" + ;; These were updated 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))) @@ -1751,6 +1756,8 @@ erc-normalize-port (cond ((> port-nr 0) port-nr) + ((string-equal port "ircu") 6667) + ((string-equal port "ircs-u") 6697) ((string-equal port "irc") 194) ((string-equal port "ircs") @@ -2171,7 +2178,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'. @@ -2434,7 +2443,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 @@ -6656,7 +6665,10 @@ 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)) + (cond ((numberp port) port) + (erc-port (erc-normalize-port erc-port)) + (port (erc-normalize-port port)) + (t erc-default-port))) ;; time routines diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index 4971d0e194..be95a2f8e0 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -893,4 +893,45 @@ erc-process-input-line (should-not calls)))))) +(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 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.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0004-Add-optional-server-param-to-erc-networks-determine.patch >From 1bd05c2ced90a2bdc339bdd8cfc76dd67918afc5 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 04/10] 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. --- 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 091b8aa92d..95338e5f1e 100644 --- a/lisp/erc/erc-networks.el +++ b/lisp/erc/erc-networks.el @@ -1232,14 +1232,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 66a334b709..88b9c3ca04 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.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0005-Improve-new-connections-in-erc-handle-irc-url.patch >From 45afa0d04d2306b78cedfdf6eb2e04b13bdda2ba Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 05/10] 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. Arrange for JOINing a channel in a manner similar to ERC's autojoin module. (erc--handle-irc-url-connect-function, erc--handle-ircs-url-connect-function): Add placeholders for possible future options allowing a user to connect when clicking an IRC link without being prompted. (erc--handle-url-connect-function): New function to serve as an interactive-only fallback when a user hasn't specified a connect function. (erc-url-ircs): Add function conforming to browse-url, and possibly other library interfaces that offer URI integration. --- lisp/erc/erc.el | 145 ++++++++++++++++++++++++++++++++----- test/lisp/erc/erc-tests.el | 95 ++++++++++++++++++++++++ 2 files changed, 223 insertions(+), 17 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index f09720fc79..f06bbc6ab0 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -7450,25 +7450,136 @@ 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 +;; FIXME update comment above once the URL business is fully settled. +;; Also: the function `url-retrieve-internal' finds a "loader" by +;; looking for a library providing a feature named "url-", but +;; no such file currently exists for "ircs". + ;;;###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 + &optional connect-fn) + "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. + +Note that ERC no longer attempts to establish new connections +without human intervention, although opting in may eventually be +allowed." + (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 + (unless connect-fn + (user-error "Existing session for %s not found." host)) + (setq deferred t + server-buffer (apply connect-fn :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)))))) + +;; XXX ERASE ME (possibly use as basis for new section in info doc) +;; +;; For now, as a demo, users must require erc and do something like: +;; +;; (add-to-list 'browse-url-default-handlers +;; '("\\`irc6?s?://" . erc--handle-ircs-url)) +;; +;; Libraries that optionally depend on browse-url, like eww, etc. need +;; an extra hand as well: +;; +;; (setq eww-use-browse-url +;; (concat eww-use-browse-url "\\|\\`irc6?s?:")) +;; +;; Those that don't use browse-url get the same handler: +;; +;; (add-to-list 'gnus-button-alist +;; '("\\birc6?s?://[][a-z0-9.,@_:+%?&/#-]+" +;; 0 t erc--handle-ircs-url 0)) +;; +;; Finally, insert something like "ircs://testnet.ergo.chat/#test" +;; where appropriate and perform a suitable action. + +;; The two variables below are contenders for exporting as user +;; options. The rationale for separate functions here instead of, +;; say, a single option granting ERC permission to connect +;; automatically is that, since ERC doesn't lacks any concept of +;; configured server profiles, it has no idea what values to give for +;; connection parameters, like nick, user, etc. +;; +;; Also, the current spec was simplified from the 2003 Butcher draft +;; and doesn't explicitly allow for an auth[:password]@ component (or +;; trailing ,flags or &options, for that matter). Regardless, even +;; when provided, we shouldn't just connect and risk exposing +;; whatever's returned by `user-login-name', right? +;; +;; https://www.iana.org/assignments/uri-schemes +;; https://datatracker.ietf.org/doc/html/draft-butcher-irc-url#section-6 + +(defvar erc--url-irc-connect-function nil) +(defvar erc--url-ircs-connect-function nil) + +(defun erc--url-default-connect-function (ircs &rest plist) + (let ((erc-server (plist-get plist :server)) + (erc-port (or (plist-get plist :port) + (and ircs (erc-normalize-port 'ircs-u)) + erc-port)) + (erc-nick (or (plist-get plist :nick) erc-nick))) + (call-interactively (if ircs #'erc-tls #'erc)))) + +(defvar url-irc-function) +(declare-function url-type "url-parse.el" (url) t) +(declare-function url-p "url-parse.el" (url) t) + +;; FIXME rename this and autoload it +(defun erc--handle-ircs-url (&optional url &rest _) + (unless url + (setq url (pop command-line-args-left)) + (cl-assert url)) + (require 'url-parse) + (unless (url-p url) + (setq url (url-generic-parse-url url))) + (let* ((ircsp (string-suffix-p "s" (url-type url))) + (fn (or (if ircsp + erc--url-ircs-connect-function + erc--url-irc-connect-function) + (apply-partially #'erc--url-default-connect-function ircsp))) + (url-irc-function (lambda (&rest r) + (apply #'erc-handle-irc-url `(,@r ,fn))))) + ;; Amazingly, this does TRT for /&chan, /#chan, /##chan, /#&chan + (url-irc url))) + (provide 'erc) diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index be95a2f8e0..f68a7debed 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -934,4 +934,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 () + (should-error (erc-handle-irc-url "irc.gnu.org" 6667 nil nil nil)) + (let* (calls + rvbuf + erc-networks-alist + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook + (connect (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 connect) + (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 connect) + (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 connect) + (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 connect) + (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 connect) + (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 connect) + (should (equal '(: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 connect) + (should (equal '(: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.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0006-POC-Make-erc-once-with-server-event-more-nimble.patch >From ef467e6e378b1098bfe60ac2ab25270b035a63c0 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 06/10] [POC] Make erc-once-with-server-event more nimble * lisp/erc/erc.el (erc-once-with-server-event, erc-once-more): Allow ephemeral callbacks to indicate a need to postpone cleanup and go another round by signaling the new custom error called `erc-once-again'. Also add new optional `depth' argument to let caller specify a hook depth. --- lisp/erc/erc.el | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index f06bbc6ab0..39ec11c94b 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1484,7 +1484,9 @@ erc--default-target (when erc--target (erc--target-string erc--target))) -(defun erc-once-with-server-event (event f) +(define-error 'erc-once-again "Untracked server event" 'error) + +(defun erc-once-with-server-event (event f &optional depth) "Run function F the next time EVENT occurs in the `current-buffer'. You should make sure that `current-buffer' is a server buffer. @@ -1507,11 +1509,16 @@ erc-once-with-server-event (hook (erc-get-hook event))) (put fun 'erc-original-buffer (current-buffer)) (fset fun (lambda (proc parsed) - (with-current-buffer (get fun 'erc-original-buffer) - (remove-hook hook fun t)) - (fmakunbound fun) - (funcall f proc parsed))) - (add-hook hook fun nil t) + (let (rv again) + (condition-case _err + (setq rv (funcall f proc parsed)) + (erc-once-again (setq again t))) + (unless again + (with-current-buffer (get fun 'erc-original-buffer) + (remove-hook hook fun t)) + (fmakunbound fun)) + rv))) + (add-hook hook fun depth t) fun)) (define-inline erc-log (string) -- 2.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0007-POC-Support-one-off-JOIN-handlers-in-ERC.patch >From a46f0bc6f452aec9be43b1534f5dc385dc39e72e Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 07/10] [POC] Support one-off JOIN handlers in ERC * lisp/erc/erc.el (erc--join-with-callback, erc-cmd-JOIN): Factor out joining logic for use in things like URL handlers for external integrations. Accept a callback to run when channel is joined. --- lisp/erc/erc.el | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 39ec11c94b..371612b085 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -3542,6 +3542,26 @@ erc--valid-local-channel-p (string-search "&" chan-types) (string-match-p "&" chan-types)))))) +(defun erc--join-with-callback (chnl key on-join) + (if-let* ((existing (erc-get-buffer chnl erc-server-process)) + ((with-current-buffer existing + (erc-get-channel-user (erc-current-nick))))) + (progn (switch-to-buffer existing) + (when on-join (funcall on-join))) + (let ((callback + (and on-join + (lambda (_ parsed) + (unless (equal chnl + (car (erc-response.command-args parsed))) + (signal 'erc-once-again nil)) + (with-current-buffer (erc-get-buffer chnl erc-server-process) + (funcall on-join)) + nil)))) + (setq erc--server-last-reconnect-count 0) + (when callback + (erc-once-with-server-event 'JOIN callback 90)) + (erc-server-join-channel nil chnl key)))) + (defun erc-cmd-JOIN (channel &optional key) "Join the channel given in CHANNEL, optionally with KEY. If CHANNEL is specified as \"-invite\", join the channel to which you @@ -3554,12 +3574,7 @@ erc-cmd-JOIN (setq chnl (erc-ensure-channel-name channel))) (when chnl ;; Prevent double joining of same channel on same server. - (if-let* ((existing (erc-get-buffer chnl erc-server-process)) - ((with-current-buffer existing - (erc-get-channel-user (erc-current-nick))))) - (switch-to-buffer existing) - (setq erc--server-last-reconnect-count 0) - (erc-server-join-channel nil chnl key)))) + (erc--join-with-callback chnl key nil))) t) (defalias 'erc-cmd-CHANNEL #'erc-cmd-JOIN) -- 2.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0008-POC-Use-erc-join-with-callback-in-URL-handler.patch >From 596835a935222f4625820340bf8d86a487646ace Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 08/10] [POC] Use erc--join-with-callback in URL handler * lisp/erc/erc.el (erc-handle-irc-url): Accept new `on-join' one-off JOIN handler and pass it to `erc--join-with-callback'. * test/lisp/erc/erc-tests.el (erc-handle-irc-url): Use `erc--join-with-callback' instead of `erc-cmd-JOIN'. --- lisp/erc/erc.el | 6 +++--- test/lisp/erc/erc-tests.el | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 371612b085..fd91441828 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -7479,7 +7479,7 @@ erc-get-parsed-vector-type ;;;###autoload (defun erc-handle-irc-url (host port channel nick password - &optional connect-fn) + &optional connect-fn on-join) "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. @@ -7526,10 +7526,10 @@ erc-handle-irc-url (with-current-buffer server-buffer (letrec ((f (lambda (&rest _) (remove-hook 'erc-after-connect f t) - (erc-cmd-JOIN channel key)))) + (erc--join-with-callback channel key on-join)))) (add-hook 'erc-after-connect f nil t))) (with-current-buffer server-buffer - (erc-cmd-JOIN channel key)))))) + (erc--join-with-callback channel key on-join)))))) ;; XXX ERASE ME (possibly use as basis for new section in info doc) ;; diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index f68a7debed..947b45e1dc 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -968,8 +968,8 @@ erc-handle-irc-url (push r calls) (if (functionp rvbuf) (funcall rvbuf) rvbuf)))) - (cl-letf (((symbol-function 'erc-cmd-JOIN) - (lambda (&rest r) (push r calls)))) + (cl-letf (((symbol-function 'erc--join-with-callback) + (lambda (&rest r) (push (butlast r) calls)))) (with-current-buffer (erc-tests--make-server-buf "foonet") (setq rvbuf (current-buffer))) -- 2.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0009-POC-Demo-improved-ol-irc-integration.patch >From 35c018604c93d7f7f4a52393c7e55dab185c0f90 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Jul 2022 05:14:57 -0700 Subject: [PATCH 09/10] [POC] Demo improved ol-irc integration * lisp/erc/erc.el (erc--org-init, erc--handle-url-org-visit, erc--handle-url-org-visit-irc, erc--handle-url-org-visit-ircs): Add various functions to demo org link integration. --- lisp/erc/erc.el | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index fd91441828..7137a7b401 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -7602,6 +7602,58 @@ erc--handle-ircs-url ;; Amazingly, this does TRT for /&chan, /#chan, /##chan, /#&chan (url-irc url))) +;; ERASE ME +;; +;; Org's ol-irc.el is pretty elaborate. But a lot of things have to +;; go perfectly for joining and prompting to work as intended. + +(defun erc--handle-url-org-visit (ircsp link) + ;; The dispatcher that calls `org-irc-visit' strips the scheme and + ;; colon, leaving only "//irc.gnu.org/#chan", which becomes + ;; (("irc.gnu.org") "#chan") when parsed by `org-irc-parse-link'. + (pcase-let* + ((`((,server ,port) ,channel ,nick) link) + (oj (and nick + (lambda () + (cl-assert nick) + ;; Channel may not be populated yet + (unless (erc-get-server-user nick) + (erc-error "%s not found in %s" nick (erc-default-target))) + (goto-char erc-input-marker) + (insert (concat nick ": "))))) + (fn (or (if ircsp + erc--url-ircs-connect-function + erc--url-irc-connect-function) + (apply-partially #'erc--url-default-connect-function + (and ircsp t))))) + (erc-handle-irc-url server port channel nil nil fn oj))) + +(declare-function org-irc-parse-link "ol-irc" (link)) +(declare-function org-link-get-parameter "ol" (type key)) +(declare-function org-link-set-parameters "ol" (type &rest parameters)) + +(defun erc--handle-url-org-follow-irc (link _) + (erc--handle-url-org-visit nil (org-irc-parse-link link))) + +(defun erc--handle-url-org-follow-ircs (link _) + (erc--handle-url-org-visit t (org-irc-parse-link link))) + +;; Eventually, we should petition for `org-irc-visit-erc' to call our +;; stuff to do the heavy lifting, assuming a new enough Emacs is +;; present. The following is only for demo purposes. + +(defun erc--org-init () + ;; TODO also add irc6 and irc6s (possibly nonstandard) + (require 'ol-irc) + (org-link-set-parameters + "irc" + :follow #'erc--handle-url-org-follow-irc) + (org-link-set-parameters + "ircs" + :follow #'erc--handle-url-org-follow-ircs + :store (org-link-get-parameter "irc" :store) + :export (org-link-get-parameter "irc" :export))) + (provide 'erc) -- 2.36.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0010-POC-etc-emacs-irc.desktop-New-file.patch >From 1ef37c2aeff57b5d81a6ebd64a5a0d505203d923 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 11 Jul 2022 22:07:08 -0700 Subject: [PATCH 10/10] [POC] * etc/emacs-irc.desktop: New file. --- etc/emacs-irc.desktop | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 etc/emacs-irc.desktop diff --git a/etc/emacs-irc.desktop b/etc/emacs-irc.desktop new file mode 100644 index 0000000000..ebdcda3a07 --- /dev/null +++ b/etc/emacs-irc.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Name=Emacs (IRC) +GenericName=Chat client +Keywords=ERC;extensible;chat;IRC;client; +Categories=Network;Chat;IRCClient; +Comment=GNU Emacs is an extensible, customizable text editor - ERC is a powerful, modular, and extensible IRC client for Emacs +# FIXME update command line and name once autoloaded +Exec=emacs -l erc -f erc--handle-ircs-url %u +Icon=emacs +MimeType=x-scheme-handler/irc;x-scheme-handler/ircs; +NoDisplay=true +Terminal=false +Type=Application -- 2.36.1 --=-=-=--