Subject: 30.0.50; ERC 5.5: /MOTD should not trigger erc-network hooks

From: "J.P." <[email-masked]>
Date: Sun, 12 Mar 2023 12:34:08 -0700

Tags: patch

When you issue an /motd command, ERC 5.5 prints a misleading warning. In
prior versions, MOTD hook members defined in erc-networks would still
run unnecessarily, but they'd do so without bothering the user. While
unwelcome from a UX perspective, this new behavior also marks a (likely
benign) functional regression because the purpose of the logic in
question has evolved to initializing and auditing core aspects of the
client model. So rather than suppress the warning, we ought to inhibit
these hook members from running, if only for peace of mind. This patch
offers one way to do that, but if anyone has a better idea, please
share.

(Also, I don't think it's worth creating a patch release for this issue
alone, but its changes could possibly accompany a more critical fix
should one emerge in the coming months.)

Thanks to incal for raising this issue on Libera.

In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
 3.24.35, cairo version 1.17.6) of 2023-03-11 built on localhost
Repository revision: b8e7061232f9a5b06af70031dcc4b48c6575a364
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12014000
System Description: Fedora Linux 36 (Workstation Edition)

Configured using:
 'configure --enable-check-lisp-object-type --enable-checking=yes,glyphs
 'CFLAGS=-O0 -g3'
 PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES NOTIFY
INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS WEBP X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  value of $XMODIFIERS: @im=ibus
  locale-coding-system: utf-8-unix

Major mode: Lisp Interaction

Minor modes in effect:
  tooltip-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug message mailcap yank-media puny dired
dired-loaddefs rfc822 mml mml-sec epa epg rfc6068 epg-config gnus-util
text-property-search mm-decode mm-bodies mm-encode mail-parse rfc2231
mailabbrev gmm-utils mailheader sendmail rfc2047 rfc2045 ietf-drums
mm-util mail-prsvr mail-utils erc-goodies erc derived iso8601 time-date
thingatpt pp format-spec erc-backend erc-networks easy-mmode erc-common
inline erc-compat pcase rx url-parse auth-source cl-seq eieio eieio-core
cl-macs password-cache json map byte-opt gv bytecomp byte-compile
url-vars subr-x cl-loaddefs cl-lib erc-loaddefs rmc iso-transl tooltip
cconv eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type
elisp-mode mwheel term/x-win x-win term/common-win x-dnd tool-bar dnd
fontset image regexp-opt fringe tabulated-list replace newcomment
text-mode lisp-mode prog-mode register page tab-bar menu-bar rfn-eshadow
isearch easymenu timer select scroll-bar mouse jit-lock font-lock syntax
font-core term/tty-colors frame minibuffer nadvice seq simple cl-generic
indonesian philippine cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads dbusbind inotify lcms2
dynamic-setting system-font-setting font-render-setting cairo
move-toolbar gtk x-toolkit xinput2 x multi-tty make-network-process
emacs)

Memory information:
((conses 16 118811 8756)
 (symbols 48 9966 0)
 (strings 32 25111 1673)
 (string-bytes 1 826194)
 (vectors 16 13831)
 (vector-slots 8 199722 5994)
 (floats 8 24 56)
 (intervals 56 231 0)
 (buffers 976 10))

>From e75c461b34282d10d02a80006271f5bb584df8a7 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Sat, 11 Mar 2023 09:25:24 -0800
Subject: [PATCH] [5.6] Add MOTD command to ERC

* lisp/erc/erc-backend.el (erc-server-402, erc-server-402-functions):
Add new response handler and hook.
* lisp/erc/erc.el (erc-cmd-MOTD): New function.
(erc-message-english-s402): Define new ERR_NOSUCHSERVER message
template.
* test/lisp/erc/erc-scenarios-base-misc-regressions.el: New file.
* test/lisp/erc/resources/base/commands/motd.eld: New file.
---
 lisp/erc/erc-backend.el                       |  7 ++-
 lisp/erc/erc.el                               | 17 +++++++
 .../erc-scenarios-base-misc-regressions.el    | 44 +++++++++++++++++
 .../lisp/erc/resources/base/commands/motd.eld | 48 +++++++++++++++++++
 4 files changed, 115 insertions(+), 1 deletion(-)
 create mode 100644 test/lisp/erc/resources/base/commands/motd.eld

diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 567443f5329..bf3c2b5b308 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -2236,6 +2236,11 @@ erc-server-322-message
     (erc-display-message parsed '(notice error) 'active
                          's401 ?n nick/channel)))
 
+(define-erc-response-handler (402)
+  "No such server." nil
+  (erc-display-message parsed '(notice error) 'active
+                       's402 ?c (cadr (erc-response.command-args parsed))))
+
 (define-erc-response-handler (403)
   "No such channel." nil
   (erc-display-message parsed '(notice error) 'active
@@ -2383,7 +2388,7 @@ erc-server-322-message
 ;; (define-erc-response-handler (323 364 365 381 382 392 393 394 395
 ;;                               200 201 202 203 204 205 206 208 209 211 212 213
 ;;                               214 215 216 217 218 219 241 242 243 244 249 261
-;;                               262 302 342 351 402 407 409 411 413 414 415
+;;                               262 302 342 351 407 409 411 413 414 415
 ;;                               423 424 436 441 443 444 467 471 472 473 KILL)
 ;;   nil nil
 ;;   (ignore proc parsed))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 69bdb5d71b1..1d65e518076 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -4046,6 +4046,22 @@ erc-cmd-TIME
    (t (erc-server-send "TIME"))))
 (defalias 'erc-cmd-DATE #'erc-cmd-TIME)
 
+(defun erc-cmd-MOTD (&optional target)
+  "Ask server to send the current MOTD.
+Some IRCds simply ignore TARGET."
+  (letrec ((oneoff (lambda (proc parsed)
+                     (with-current-buffer (erc-server-buffer)
+                       (cl-assert (eq (current-buffer) (process-buffer proc)))
+                       (remove-hook 'erc-server-402-functions h402 t)
+                       (remove-hook 'erc-server-376-functions h376 t)
+                       (remove-hook 'erc-server-422-functions h422 t))
+                     (erc-server-MOTD proc parsed)
+                     t))
+           (h402 (erc-once-with-server-event 402 oneoff))
+           (h376 (erc-once-with-server-event 376 oneoff))
+           (h422 (erc-once-with-server-event 422 oneoff)))
+    (erc-server-send (concat "MOTD" (and target " ") target))))
+
 (defun erc-cmd-TOPIC (topic)
   "Set or request the topic for a channel.
 LINE has the format: \"#CHANNEL TOPIC\", \"#CHANNEL\", \"TOPIC\"
@@ -7131,6 +7147,7 @@ erc-define-catalog
    (s379   . "%c: Forwarded to %f")
    (s391   . "The time at %s is %t")
    (s401   . "%n: No such nick/channel")
+   (s402   . "%c: No such server")
    (s403   . "%c: No such channel")
    (s404   . "%c: Cannot send to channel")
    (s405   . "%c: You have joined too many channels")
diff --git a/test/lisp/erc/erc-scenarios-base-misc-regressions.el b/test/lisp/erc/erc-scenarios-base-misc-regressions.el
index 16b2cb355d1..c1915d088a0 100644
--- a/test/lisp/erc/erc-scenarios-base-misc-regressions.el
+++ b/test/lisp/erc/erc-scenarios-base-misc-regressions.el
@@ -124,4 +124,48 @@ erc-scenarios-base-channel-buffer-revival
       (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan"))
         (erc-d-t-search-for 10 "and be prosperous")))))
 
+;; This defends against a partial regression in which an /MOTD caused
+;; 376 and 422 handlers in erc-networks to run.
+
+(ert-deftest erc-cmd-MOTD ()
+  :tags '(:expensive-test)
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/commands")
+       (erc-server-flood-penalty 0.1)
+       (dumb-server (erc-d-run "localhost" t 'motd))
+       (port (process-contact dumb-server :service))
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to server")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :full-name "tester")
+        (funcall expect 10 "This is the default Ergo MOTD")
+        (funcall expect 10 "debug mode")))
+
+    (ert-info ("Send plain MOTD")
+      (with-current-buffer "foonet"
+        (erc-cmd-MOTD)
+        (funcall expect -0.2 "Unexpected state detected")
+        (funcall expect 10 "This is the default Ergo MOTD")))
+
+    (ert-info ("Send MOTD with known target")
+      (with-current-buffer "foonet"
+        (erc-scenarios-common-say "/MOTD irc1.foonet.org")
+        (funcall expect -0.2 "Unexpected state detected")
+        (funcall expect 10 "This is the default Ergo MOTD")))
+
+    (ert-info ("Send MOTD with erroneous target")
+      (with-current-buffer "foonet"
+        (erc-scenarios-common-say "/MOTD fake.foonet.org")
+        (funcall expect -0.2 "Unexpected state detected")
+        (funcall expect 10 "No such server")
+        ;; Message may show up before the handler runs.
+        (erc-d-t-wait-for 10
+            (not (local-variable-p 'erc-server-402-functions)))
+        (should-not (local-variable-p 'erc-server-376-functions))
+        (should-not (local-variable-p 'erc-server-422-functions))
+        (erc-cmd-QUIT "")))))
+
 ;;; erc-scenarios-base-misc-regressions.el ends here
diff --git a/test/lisp/erc/resources/base/commands/motd.eld b/test/lisp/erc/resources/base/commands/motd.eld
new file mode 100644
index 00000000000..6d10ee122e2
--- /dev/null
+++ b/test/lisp/erc/resources/base/commands/motd.eld
@@ -0,0 +1,48 @@
+;; -*- mode: lisp-data; -*-
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
+ (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1")
+ (0.01 ":irc.foonet.org 003 tester :This server was created Sun, 12 Mar 2023 02:30:29 UTC")
+ (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server")
+ (0.01 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server")
+ (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server")
+ (0.00 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections")
+ (0.00 ":irc.foonet.org 254 tester 1 :channels formed")
+ (0.00 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers")
+ (0.00 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0.00 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0.00 ":irc.foonet.org 375 tester :- irc.foonet.org Message of the day - ")
+ (0.00 ":irc.foonet.org 372 tester :- This is the default Ergo MOTD.")
+ (0.01 ":irc.foonet.org 372 tester :- ")
+ (0.02 ":irc.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md")
+ (0.00 ":irc.foonet.org 376 tester :End of MOTD command"))
+
+((mode 10 "MODE tester +i")
+ (0.00 ":irc.foonet.org 221 tester +i")
+ (0.00 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")
+ (0.05 ":irc.foonet.org 221 tester +i"))
+
+((motd-1 10 "MOTD")
+ (0.08 ":irc.foonet.org 375 tester :- irc.foonet.org Message of the day - ")
+ (0.02 ":irc.foonet.org 372 tester :- This is the default Ergo MOTD.")
+ (0.01 ":irc.foonet.org 372 tester :- ")
+ (0.00 ":irc.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md")
+ (0.00 ":irc.foonet.org 376 tester :End of MOTD command"))
+
+((motd-2 10 "MOTD irc1.foonet.org")
+ (0.08 ":irc1.foonet.org 375 tester :- irc1.foonet.org Message of the day - ")
+ (0.02 ":irc1.foonet.org 372 tester :- This is the default Ergo MOTD.")
+ (0.01 ":irc1.foonet.org 372 tester :- ")
+ (0.00 ":irc1.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md")
+ (0.00 ":irc1.foonet.org 376 tester :End of MOTD command"))
+
+((motd-3 10 "MOTD fake.foonet.org")
+ (0.00 ":irc.foonet.org 402 tester fake.foonet.org :No such server"))
+
+((quit 10 "QUIT :\2ERC\2")
+ (0.07 ":[email-masked] QUIT :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)")
+ (0.01 "ERROR :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)"))
-- 
2.39.2