Normalize ISUPPORT params with empty values in ERC

* lisp/erc/erc-backend.el (erc-server-parameters)
(erc--isupport-params): Mention parsing and storage behavior regarding
nonstandard "FOO=" tokens.
(erc--parse-isupport-value): Move comment closer to code.
(erc--get-isupport-entry): Treat the empty string as truly null, as
prescribed by the Brocklesby draft cited in the top-level comment.
* test/lisp/erc/erc-tests.el (erc--get-isupport-entry): Add case for
the empty string appearing as a value for an `erc-server-parameters'
item.
(erc-server-005): Assert compat-related behavior of retaining the
empty string as a valid value from a raw "FOO=" token.
(Bug#67220)
This commit is contained in:
F. Jason Park 2024-02-11 17:15:14 -08:00
parent d7c18a7b4f
commit 25d15391f2
2 changed files with 31 additions and 16 deletions

View file

@ -254,6 +254,11 @@ Entries are of the form:
or
(PARAMETER) if no value is provided.
where PARAMETER is a string and VALUE is a string or nil. For
compatibility, a raw parameter of the form \"FOO=\" becomes
(\"FOO\" . \"\") even though it's equivalent to the preferred
canonical form \"FOO\" and its lisp representation (\"FOO\").
Some examples of possible parameters sent by servers:
CHANMODES=b,k,l,imnpst - list of supported channel modes
CHANNELLEN=50 - maximum length of channel names
@ -273,7 +278,8 @@ WALLCHOPS - supports sending messages to all operators in a channel")
(defvar-local erc--isupport-params nil
"Hash map of \"ISUPPORT\" params.
Keys are symbols. Values are lists of zero or more strings with hex
escapes removed.")
escapes removed. ERC normalizes incoming parameters of the form
\"FOO=\" to (FOO).")
;;; Server and connection state
@ -2150,10 +2156,6 @@ Then display the welcome message."
;;
;; > The server SHOULD send "X", not "X="; this is the normalized form.
;;
;; Note: for now, assume the server will only send non-empty values,
;; possibly with printable ASCII escapes. Though in practice, the
;; only two escapes we're likely to see are backslash and space,
;; meaning the pattern is too liberal.
(let (case-fold-search)
(mapcar
(lambda (v)
@ -2164,7 +2166,9 @@ Then display the welcome message."
(string-match "[\\]x[0-9A-F][0-9A-F]" v start))
(setq m (substring v (+ 2 (match-beginning 0)) (match-end 0))
c (string-to-number m 16))
(if (<= ?\ c ?~)
;; In practice, this range is too liberal. The only
;; escapes we're likely to see are ?\\, ?=, and ?\s.
(if (<= ?\s c ?~)
(setq v (concat (substring v 0 (match-beginning 0))
(string c)
(substring v (match-end 0)))
@ -2189,8 +2193,9 @@ primitive value."
(or erc-server-parameters
(erc-with-server-buffer
erc-server-parameters)))))
(if (cdr v)
(erc--parse-isupport-value (cdr v))
(if-let ((val (cdr v))
((not (string-empty-p val))))
(erc--parse-isupport-value val)
'--empty--)))))
(pcase value
('--empty-- (unless single (list key)))

View file

@ -1054,7 +1054,8 @@
(ert-deftest erc--get-isupport-entry ()
(let ((erc--isupport-params (make-hash-table))
(erc-server-parameters '(("FOO" . "1") ("BAR") ("BAZ" . "A,B,C")))
(erc-server-parameters '(("FOO" . "1") ("BAR") ("BAZ" . "A,B,C")
("SPAM" . "")))
(items (lambda ()
(cl-loop for k being the hash-keys of erc--isupport-params
using (hash-values v) collect (cons k v)))))
@ -1075,7 +1076,9 @@
(should (equal (erc--get-isupport-entry 'FOO) '(FOO "1")))
(should (equal (funcall items)
'((BAR . --empty--) (BAZ "A" "B" "C") (FOO "1"))))))
'((BAR . --empty--) (BAZ "A" "B" "C") (FOO "1"))))
(should (equal (erc--get-isupport-entry 'SPAM) '(SPAM)))
(should-not (erc--get-isupport-entry 'SPAM 'single))))
(ert-deftest erc-server-005 ()
(let* ((hooked 0)
@ -1093,34 +1096,41 @@
(lambda (_ _ _ line) (push line calls))))
(ert-info ("Baseline")
(setq args '("tester" "BOT=B" "EXCEPTS" "PREFIX=(ov)@+" "are supp...")
(setq args '("tester" "BOT=B" "CHANTYPES=" "EXCEPTS" "PREFIX=(ov)@+"
"are supp...")
parsed (make-erc-response :command-args args :command "005"))
(setq verify
(lambda ()
(should (equal erc-server-parameters
'(("PREFIX" . "(ov)@+") ("EXCEPTS")
;; Should be ("CHANTYPES") but
;; retained for compatibility.
("CHANTYPES" . "")
("BOT" . "B"))))
(should (zerop (hash-table-count erc--isupport-params)))
(should (equal "(ov)@+" (erc--get-isupport-entry 'PREFIX t)))
(should (equal '(EXCEPTS) (erc--get-isupport-entry 'EXCEPTS)))
(should (equal "B" (erc--get-isupport-entry 'BOT t)))
(should (string= (pop calls)
"BOT=B EXCEPTS PREFIX=(ov)@+ are supp..."))
(should (string=
(pop calls)
"BOT=B CHANTYPES= EXCEPTS PREFIX=(ov)@+ are supp..."))
(should (equal args (erc-response.command-args parsed)))))
(erc-call-hooks nil parsed))
(ert-info ("Negated, updated")
(setq args '("tester" "-EXCEPTS" "-FAKE" "PREFIX=(ohv)@%+" "are su...")
(setq args '("tester" "-EXCEPTS" "-CHANTYPES" "-FAKE" "PREFIX=(ohv)@%+"
"are su...")
parsed (make-erc-response :command-args args :command "005"))
(setq verify
(lambda ()
(should (equal erc-server-parameters
'(("PREFIX" . "(ohv)@%+") ("BOT" . "B"))))
(should (string= (pop calls)
"-EXCEPTS -FAKE PREFIX=(ohv)@%+ are su..."))
(should (string-prefix-p
"-EXCEPTS -CHANTYPES -FAKE PREFIX=(ohv)@%+ "
(pop calls)))
(should (equal "(ohv)@%+" (erc--get-isupport-entry 'PREFIX t)))
(should (equal "B" (erc--get-isupport-entry 'BOT t)))
(should-not (erc--get-isupport-entry 'EXCEPTS))