(let ((buf (erc-get-buffer chan erc-server-process)))
(unless (and buf (with-current-buffer buf
(erc--current-buffer-joined-p)))
- (erc-server-join-channel match chan)))))))
+ (erc-server-join-channel nil chan)))))))
(defun erc-autojoin-after-ident (_network _nick)
"Autojoin channels in `erc-autojoin-channels-alist'.
:version "28.1"
:type 'boolean)
+(defcustom erc-auth-source-services-function #'erc-auth-source-search
+ "Function to retrieve NickServ password from auth-source.
+Called with a subset of keyword parameters known to
+`auth-source-search' and relevant to authenticating to nickname
+services. In return, ERC expects a string to send as the
+password, or nil, to fall through to the next method, such as
+prompting. See info node `(erc) Connecting' for details."
+ :package-version '(ERC . "5.4.1") ; FIXME update when publishing to ELPA
+ :type '(choice (const erc-auth-source-search)
+ (const nil)
+ function))
+
(defcustom erc-nickserv-passwords nil
"Passwords used when identifying to NickServ automatically.
`erc-prompt-for-nickserv-password' must be nil for these
(const QuakeNet)
(const Rizon)
(const SlashNET)
- (symbol :tag "Network name"))
+ (symbol :tag "Network name or session ID"))
(repeat :tag "Nickname and password"
(cons :tag "Identity"
(string :tag "Nick")
lookups stops and this function returns it (or returns nil if it
is empty). Otherwise, no corresponding password was found, and
it returns nil."
- (let (network server port)
- ;; Fill in local vars, switching to the server buffer once only
- (erc-with-server-buffer
- (setq network erc-network
- server erc-session-server
- port erc-session-port))
- (let ((ret
- (or
- (when erc-nickserv-passwords
- (cdr (assoc nick
- (cl-second (assoc network
- erc-nickserv-passwords)))))
- (when erc-use-auth-source-for-nickserv-password
- (auth-source-pick-first-password
- :require '(:secret)
- :host server
- ;; Ensure a string for :port
- :port (format "%s" port)
- :user nick))
- (when erc-prompt-for-nickserv-password
- (read-passwd
- (format "NickServ password for %s on %s (RET to cancel): "
- nick network))))))
- (when (and ret (not (string= ret "")))
- ret))))
+ (when-let*
+ ((nid (erc-networks--id-symbol erc-networks--id))
+ (ret (or (when erc-nickserv-passwords
+ (assoc-default nick
+ (cadr (assq nid erc-nickserv-passwords))))
+ (when (and erc-use-auth-source-for-nickserv-password
+ erc-auth-source-services-function)
+ (funcall erc-auth-source-services-function :user nick))
+ (when erc-prompt-for-nickserv-password
+ (read-passwd
+ (format "NickServ password for %s on %s (RET to cancel): "
+ nick nid)))))
+ ((not (string-empty-p ret))))
+ ret))
(defvar erc-auto-discard-away)
"old behavior when t now permanent" "29.1")
(defvar erc-password nil
- "Password to use when authenticating to an IRC server.
-It is not strictly necessary to provide this, since ERC will
-prompt you for it.")
+ "Password to use when authenticating to an IRC server interactively.
+
+This variable only exists for legacy reasons. It's not customizable and
+is limited to a single server password. Users looking for similar
+functionality should consider auth-source instead. See info
+node `(auth) Top' and info node `(erc) Connecting'.")
+
+(make-obsolete-variable 'erc-password "use auth-source instead" "29.1")
(defcustom erc-user-mode "+i"
;; +i "Invisible". Hides user from global /who and /names.
(defcustom erc-prompt-for-password t
- "Asks before using the default password, or whether to enter a new one."
+ "Ask for a server password when invoking `erc-tls' interactively."
:group 'erc
:type 'boolean)
(setq erc-logged-in nil)
;; The local copy of `erc-nick' - the list of nicks to choose
(setq erc-default-nicks (if (consp erc-nick) erc-nick (list erc-nick)))
- ;; password stuff
- (setq erc-session-password
- (or passwd
- (auth-source-pick-first-password
- :host server
- :user nick
- ;; secrets.el wouldn’t accept a number
- :port (if (numberp port) (number-to-string port) port)
- :require '(:secret))))
;; client certificate (only useful if connecting over TLS)
(setq erc-session-client-certificate client-certificate)
(setq erc-networks--id (if connect
(erc-display-prompt)
(goto-char (point-max)))
- (erc-determine-parameters server port nick full-name user)
+ (erc-determine-parameters server port nick full-name user passwd)
;; Saving log file on exit
(run-hook-with-args 'erc-connect-pre-hook buffer)
(setq server user-input)
(setq passwd (if erc-prompt-for-password
- (if (and erc-password
- (y-or-n-p "Use the default password? "))
- erc-password
- (read-passwd "Password: "))
- erc-password))
+ (read-passwd "Server password: ")
+ (with-suppressed-warnings ((obsolete erc-password))
+ erc-password)))
(when (and passwd (string= "" passwd))
(setq passwd nil))
(defalias 'erc-cmd-H #'erc-cmd-HELP)
(put 'erc-cmd-HELP 'process-not-needed t)
+(defcustom erc-auth-source-server-function #'erc-auth-source-search
+ "Function to query auth-source for a server password.
+Called with a subset of keyword parameters known to
+`auth-source-search' and relevant to an opening \"PASS\" command,
+if any. In return, ERC expects a string to send as the server
+password, or nil, to skip the \"PASS\" command completely. An
+explicit `:password' argument to entry-point commands `erc' and
+`erc-tls' also inhibits lookup, as does setting this option to
+nil. See info node `(erc) Connecting' for details."
+ :package-version '(ERC . "5.4.1") ; FIXME update when publishing to ELPA
+ :group 'erc
+ :type '(choice (const erc-auth-source-search)
+ (const nil)
+ function))
+
+(defcustom erc-auth-source-join-function #'erc-auth-source-search
+ "Function to query auth-source on joining a channel.
+Called with a subset of keyword arguments known to
+`auth-source-search' and relevant to joining a password-protected
+channel. In return, ERC expects a string to use as the channel
+\"key\", or nil to just join the channel normally. Setting the
+option itself to nil tells ERC to always forgo consulting
+auth-source for channel keys. For more information, see info
+node `(erc) Connecting'."
+ :package-version '(ERC . "5.4.1") ; FIXME update when publishing to ELPA
+ :group 'erc
+ :type '(choice (const erc-auth-source-search)
+ (const nil)
+ function))
+
+(defun erc--auth-source-determine-params-defaults ()
+ (let* ((net (and-let* ((esid (erc-networks--id-symbol erc-networks--id))
+ ((symbol-name esid)))))
+ (localp (and erc--target (erc--target-channel-local-p erc--target)))
+ (hosts (if localp
+ (list erc-server-announced-name erc-session-server net)
+ (list net erc-server-announced-name erc-session-server)))
+ (ports (list (cl-typecase erc-session-port
+ (integer (number-to-string erc-session-port))
+ (string (and (string= erc-session-port "irc")
+ erc-session-port)) ; or nil
+ (t erc-session-port))
+ "irc")))
+ (list (cons :host (delq nil hosts))
+ (cons :port (delq nil ports))
+ (cons :require '(:secret)))))
+
+(defun erc--auth-source-determine-params-merge (&rest plist)
+ "Return a plist of merged keyword args to pass to `auth-source-search'.
+Combine items in PLIST with others derived from the current connection
+context, but prioritize the former. For keys not present in PLIST,
+favor a network ID over an announced server unless `erc--target' is a
+local channel. And treat the dialed server address as a fallback for
+the announced name in both cases."
+ (let ((defaults (erc--auth-source-determine-params-defaults)))
+ `(,@(cl-loop for (key value) on plist by #'cddr
+ for default = (assq key defaults)
+ do (when default (setq defaults (delq default defaults)))
+ append `(,key ,(delete-dups
+ `(,@(if (consp value) value (list value))
+ ,@(cdr default)))))
+ ,@(cl-loop for (k . v) in defaults append (list k v)))))
+
+(defun erc--auth-source-search (&rest defaults)
+ "Ask auth-source for a secret and return it if found.
+Use DEFAULTS as keyword arguments for querying auth-source and as a
+guide for narrowing results. Return a string if found or nil otherwise.
+The ordering of DEFAULTS influences how results are filtered, as does
+the ordering of the members of any individual composite values. If
+necessary, the former takes priority. For example, if DEFAULTS were to
+contain
+
+ :host (\"foo\" \"bar\") :port (\"123\" \"456\")
+
+the secret from an auth-source entry of host foo and port 456 would be
+chosen over another of host bar and port 123. However, if DEFAULTS
+looked like
+
+ :port (\"123\" \"456\") :host (\"foo\" \"bar\")
+
+the opposite would be true. In both cases, two entries with the same
+host but different ports would result in the one with port 123 getting
+the nod. Much the same would happen for entries sharing only a port:
+the one with host foo would win."
+ (when-let*
+ ((priority (map-keys defaults))
+ (test (lambda (a b)
+ (catch 'done
+ (dolist (key priority)
+ (let* ((d (plist-get defaults key))
+ (defval (if (listp d) d (list d)))
+ ;; featurep 'seq via auth-source > json > map
+ (p (seq-position defval (plist-get a key)))
+ (q (seq-position defval (plist-get b key))))
+ (unless (eql p q)
+ (throw 'done (when p (or (not q) (< p q))))))))))
+ (plist (copy-sequence defaults)))
+ (unless (plist-get plist :max)
+ (setq plist (plist-put plist :max 5000))) ; `auth-source-netrc-parse'
+ (unless (plist-get defaults :require)
+ (setq plist (plist-put plist :require '(:secret))))
+ (when-let* ((sorted (sort (apply #'auth-source-search plist) test))
+ (secret (plist-get (car sorted) :secret)))
+ (if (functionp secret) (funcall secret) secret))))
+
+(defun erc-auth-source-search (&rest plist)
+ "Call `auth-source-search', possibly with keyword params in PLIST."
+ ;; These exist as separate helpers in case folks should find them
+ ;; useful. If that's you, please request that they be exported.
+ (apply #'erc--auth-source-search
+ (apply #'erc--auth-source-determine-params-merge plist)))
+
(defun erc-server-join-channel (server channel &optional secret)
- (let ((password
- (or secret
- (auth-source-pick-first-password
- :host server
- :port "irc"
- :user channel))))
- (erc-log (format "cmd: JOIN: %s" channel))
- (erc-server-send (concat "JOIN " channel
- (if password
- (concat " " password)
- "")))))
+ "Join CHANNEL, optionally with SECRET.
+Without SECRET, consult auth-source, possibly passing SERVER as the
+`:host' query parameter."
+ (unless (or secret (not erc-auth-source-join-function))
+ (unless server
+ (when (and erc-server-announced-name
+ (erc--valid-local-channel-p channel))
+ (setq server erc-server-announced-name)))
+ (setq secret (apply erc-auth-source-join-function
+ `(,@(and server (list :host server)) :user ,channel))))
+ (erc-log (format "cmd: JOIN: %s" channel))
+ (erc-server-send (concat "JOIN " channel (and secret (concat " " secret)))))
(defun erc--valid-local-channel-p (channel)
"Non-nil when channel is server-local on a network that allows them."
(setq chnl (erc-ensure-channel-name channel)))
(when chnl
;; Prevent double joining of same channel on same server.
- (let* ((joined-channels
- (mapcar (lambda (chanbuf)
- (with-current-buffer chanbuf (erc-default-target)))
- (erc-channel-list erc-server-process)))
- (server (with-current-buffer (process-buffer erc-server-process)
- (or erc-session-server erc-server-announced-name)))
- (chnl-name (car (erc-member-ignore-case chnl joined-channels))))
- (if chnl-name
- (switch-to-buffer (if (get-buffer chnl-name)
- chnl-name
- (concat chnl-name "/" server)))
- (setq erc--server-last-reconnect-count 0)
- (erc-server-join-channel server chnl key)))))
+ (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))))
t)
(defalias 'erc-cmd-CHANNEL #'erc-cmd-JOIN)
;; connection properties' heuristics
-(defun erc-determine-parameters (&optional server port nick name user)
+(defun erc-determine-parameters (&optional server port nick name user passwd)
"Determine the connection and authentication parameters.
Sets the buffer local variables:
- `erc-session-port'
- `erc-session-user-full-name'
- `erc-session-username'
+- `erc-session-password'
- `erc-server-current-nick'"
(setq erc-session-connector erc-server-connect-function
erc-session-server (erc-compute-server server)
erc-session-port (or port erc-default-port)
erc-session-user-full-name (erc-compute-full-name name)
- erc-session-username (erc-compute-user user))
+ erc-session-username (erc-compute-user user)
+ erc-session-password (erc--compute-server-password passwd nick))
(erc-set-current-nick (erc-compute-nick nick)))
(defun erc-compute-server (&optional server)
(getenv "IRCNICK")
(user-login-name)))
+(defun erc--compute-server-password (password nick)
+ "Maybe provide a PASSWORD argument for the IRC \"PASS\" command.
+When `erc-auth-source-server-function' is non-nil, call it with NICK for
+the user field and use whatever it returns as the server password."
+ (or password (and erc-auth-source-server-function
+ (funcall erc-auth-source-server-function :user nick))))
(defun erc-compute-full-name (&optional full-name)
"Return user's full name.
--- /dev/null
+;;; erc-scenarios-auth-source.el --- auth-source scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+;;
+;; This file is part of GNU Emacs.
+;;
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see
+;; <https://www.gnu.org/licenses/>.
+
+;; Commentary:
+;;
+;; For practical reasons (mainly lack of imagination), this file
+;; contains tests for both server-password and NickServ contexts.
+
+(require 'ert-x)
+(eval-and-compile
+ (let ((load-path (cons (ert-resource-directory) load-path)))
+ (require 'erc-scenarios-common)))
+
+(eval-when-compile (require 'erc-join)
+ (require 'erc-services))
+
+(defun erc-scenarios-common--auth-source (id dialog &rest rest)
+ (push "machine GNU.chat port %d user \"#chan\" password spam" rest)
+ (erc-scenarios-common-with-cleanup
+ ((erc-scenarios-common-dialog "base/auth-source")
+ (dumb-server (erc-d-run "localhost" t dialog))
+ (port (process-contact dumb-server :service))
+ (ents `(,@(mapcar (lambda (fmt) (format fmt port)) rest)
+ "machine MyHost port irc password 123"))
+ (netrc-file (make-temp-file "auth-source-test" nil nil
+ (string-join ents "\n")))
+ (auth-sources (list netrc-file))
+ (auth-source-do-cache nil)
+ (erc-scenarios-common-extra-teardown (lambda ()
+ (delete-file netrc-file))))
+
+ (ert-info ("Connect")
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "tester"
+ :full-name "tester"
+ :id id)
+ (should (string= (buffer-name) (if id
+ (symbol-name id)
+ (format "127.0.0.1:%d" port))))
+ (erc-d-t-wait-for 5 (eq erc-network 'FooNet))))))
+
+(ert-deftest erc-scenarios-base-auth-source-server--dialed ()
+ :tags '(:expensive-test)
+ (erc-scenarios-common--auth-source
+ nil 'foonet
+ "machine GNU.chat port %d user tester password fake"
+ "machine FooNet port %d user tester password fake"
+ "machine 127.0.0.1 port %d user tester password changeme"
+ "machine 127.0.0.1 port %d user imposter password fake"))
+
+(ert-deftest erc-scenarios-base-auth-source-server--netid ()
+ :tags '(:expensive-test)
+ (erc-scenarios-common--auth-source
+ 'MySession 'foonet
+ "machine MySession port %d user tester password changeme"
+ "machine 127.0.0.1 port %d user tester password fake"
+ "machine FooNet port %d user tester password fake"))
+
+(ert-deftest erc-scenarios-base-auth-source-server--netid-custom ()
+ :tags '(:expensive-test)
+ (let ((erc-auth-source-server-function
+ (lambda (&rest _) (erc-auth-source-search :host "MyHost"))))
+ (erc-scenarios-common--auth-source
+ 'MySession 'foonet
+ "machine 127.0.0.1 port %d user tester password fake"
+ "machine MyHost port %d user tester password changeme"
+ "machine MySession port %d user tester password fake")))
+
+(ert-deftest erc-scenarios-base-auth-source-server--nopass ()
+ :tags '(:expensive-test)
+ (let (erc-auth-source-server-function)
+ (erc-scenarios-common--auth-source nil 'nopass)))
+
+(ert-deftest erc-scenarios-base-auth-source-server--nopass-netid ()
+ :tags '(:expensive-test)
+ (let (erc-auth-source-server-function)
+ (erc-scenarios-common--auth-source 'MySession 'nopass)))
+
+;; Identify via auth source with no initial password
+
+(defun erc-scenarios-common--services-auth-source (&rest rest)
+ (erc-scenarios-common-with-cleanup
+ ((erc-scenarios-common-dialog "services/auth-source")
+ (erc-server-flood-penalty 0.1)
+ (dumb-server (erc-d-run "localhost" t 'libera))
+ (port (process-contact dumb-server :service))
+ (ents `(,@(mapcar (lambda (fmt) (format fmt port)) rest)
+ "machine MyHost port irc password 123"))
+ (netrc-file (make-temp-file "auth-source-test" nil nil
+ (string-join ents "\n")))
+ (auth-sources (list netrc-file))
+ (auth-source-do-cache nil)
+ (erc-modules (cons 'services erc-modules))
+ (erc-use-auth-source-for-nickserv-password t) ; do consult for NickServ
+ (expect (erc-d-t-make-expecter))
+ (erc-scenarios-common-extra-teardown (lambda ()
+ (delete-file netrc-file))))
+
+ (cl-letf (((symbol-function 'read-passwd)
+ (lambda (&rest _) (error "Unexpected read-passwd call"))))
+ (ert-info ("Connect without password")
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "tester"
+ :full-name "tester")
+ (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
+ (erc-d-t-wait-for 8 (eq erc-network 'Libera.Chat))
+ (funcall expect 3 "This nickname is registered.")
+ (funcall expect 3 "You are now identified")
+ (funcall expect 3 "Last login from")
+ (erc-cmd-QUIT ""))))
+
+ (erc-services-mode -1)
+
+ (should-not (memq 'services erc-modules))))
+
+;; These tests are about authenticating to nick services
+
+(ert-deftest erc-scenarios-services-auth-source--network ()
+ :tags '(:expensive-test)
+ ;; Skip consulting auth-source for the server password (PASS).
+ (let (erc-auth-source-server-function)
+ (erc-scenarios-common--services-auth-source
+ "machine 127.0.0.1 port %d user tester password spam"
+ "machine zirconium.libera.chat port %d user tester password fake"
+ "machine Libera.Chat port %d user tester password changeme")))
+
+(ert-deftest erc-scenarios-services-auth-source--network-connect-lookup ()
+ :tags '(:expensive-test)
+ ;; Do consult auth-source for the server password (and find nothing)
+ (erc-scenarios-common--services-auth-source
+ "machine zirconium.libera.chat port %d user tester password fake"
+ "machine Libera.Chat port %d user tester password changeme"))
+
+(ert-deftest erc-scenarios-services-auth-source--announced ()
+ :tags '(:expensive-test)
+ (let (erc-auth-source-server-function)
+ (erc-scenarios-common--services-auth-source
+ "machine 127.0.0.1 port %d user tester password spam"
+ "machine zirconium.libera.chat port %d user tester password changeme")))
+
+(ert-deftest erc-scenarios-services-auth-source--dialed ()
+ :tags '(:expensive-test)
+ ;; Support legacy host -> domain name
+ ;; (likely most common in real configs)
+ (let (erc-auth-source-server-function)
+ (erc-scenarios-common--services-auth-source
+ "machine 127.0.0.1 port %d user tester password changeme")))
+
+(ert-deftest erc-scenarios-services-auth-source--custom ()
+ :tags '(:expensive-test)
+ (let (erc-auth-source-server-function
+ (erc-auth-source-services-function
+ (lambda (&rest _) (erc-auth-source-search :host "MyAccount"))))
+ (erc-scenarios-common--services-auth-source
+ "machine zirconium.libera.chat port %d user tester password spam"
+ "machine MyAccount port %d user tester password changeme"
+ "machine 127.0.0.1 port %d user tester password fake")))
+
+;;; erc-scenarios-auth-source.el ends here
erc-reuse-buffers)
(erc-scenarios-common--base-reuse-buffers-server-buffers nil))))
+;; This also asserts that `erc-cmd-JOIN' is no longer susceptible to a
+;; regression introduced in 28.1 (ERC 5.4) that caused phantom target
+;; buffers of the form target/server to be created via
+;; `switch-to-buffer' ("phantom" because they would go unused"). This
+;; would happen (in place of a JOIN being sent out) when a previously
+;; used (parted) target buffer existed and `erc-reuse-buffers' was
+;; nil.
+;;
+;; Note: All the `erc-get-channel-user' calls have to do with the fact
+;; that `erc-default-target' relies on the ambiguously defined
+;; `erc-default-recipients' (meaning it's overloaded in the sense of
+;; being used both for retrieving a target name and checking if a
+;; channel has been PARTed). While not ideal, `erc-get-channel-user'
+;; can (also) be used to detect the latter.
+
+(defun erc-scenarios-common--base-reuse-buffers-channel-buffers (port)
+ "The option `erc-reuse-buffers' is still respected when nil.
+Adapted from scenario clash-of-chans/uniquify described in Bug#48598:
+28.0.50; buffer-naming collisions involving bouncers in ERC."
+ (let* ((expect (erc-d-t-make-expecter))
+ (server-buffer-foo
+ (get-buffer (format "127.0.0.1:%d/127.0.0.1" port)))
+ (server-buffer-bar
+ (get-buffer (format "127.0.0.1:%d/127.0.0.1<2>" port)))
+ (chan-buffer-foo (get-buffer "#chan/127.0.0.1"))
+ (chan-buffer-bar (get-buffer "#chan/127.0.0.1<2>"))
+ (server-process-foo (with-current-buffer server-buffer-foo
+ erc-server-process))
+ (server-process-bar (with-current-buffer server-buffer-bar
+ erc-server-process)))
+
+ (ert-info ("Unique #chan buffers exist")
+ (let ((chan-bufs (erc-scenarios-common-buflist "#chan"))
+ (known (list chan-buffer-bar chan-buffer-foo)))
+ (should (memq (pop chan-bufs) known))
+ (should (memq (pop chan-bufs) known))
+ (should-not chan-bufs)))
+
+ (ert-info ("#chan@foonet is exclusive and not contaminated")
+ (with-current-buffer chan-buffer-foo
+ (funcall expect 1 "<bob>")
+ (erc-d-t-absent-for 0.1 "<joe>")
+ (funcall expect 1 "strength to climb")
+ (should (eq erc-server-process server-process-foo))))
+
+ (ert-info ("#chan@barnet is exclusive and not contaminated")
+ (with-current-buffer chan-buffer-bar
+ (funcall expect 1 "<joe>")
+ (erc-d-t-absent-for 0.1 "<bob>")
+ (funcall expect 1 "the loudest noise")
+ (should (eq erc-server-process server-process-bar))))
+
+ (ert-info ("Part #chan@foonet")
+ (with-current-buffer chan-buffer-foo
+ (erc-d-t-search-for 1 "shake my sword")
+ (erc-cmd-PART "#chan")
+ (funcall expect 3 "You have left channel #chan")
+ (erc-cmd-JOIN "#chan")))
+
+ (ert-info ("Part #chan@barnet")
+ (with-current-buffer chan-buffer-bar
+ (funcall expect 10 "Arm it in rags")
+ (should (erc-get-channel-user (erc-current-nick)))
+ (erc-cmd-PART "#chan")
+ (funcall expect 3 "You have left channel #chan")
+ (should-not (erc-get-channel-user (erc-current-nick)))
+ (erc-cmd-JOIN "#chan")))
+
+ (erc-d-t-wait-for 3 "New unique target buffer for #chan@foonet created"
+ (get-buffer "#chan/127.0.0.1<3>"))
+
+ (ert-info ("Activity continues in new, <n>-suffixed #chan@foonet buffer")
+ (with-current-buffer chan-buffer-foo
+ (should-not (erc-get-channel-user (erc-current-nick))))
+ (with-current-buffer "#chan/127.0.0.1<3>"
+ (should (erc-get-channel-user (erc-current-nick)))
+ (funcall expect 2 "You have joined channel #chan")
+ (funcall expect 2 "#chan was created on")
+ (funcall expect 2 "<alice>")
+ (should (eq erc-server-process server-process-foo))
+ (erc-d-t-absent-for 0.2 "<joe>")))
+
+ (sit-for 3)
+ (erc-d-t-wait-for 5 "New unique target buffer for #chan@barnet created"
+ (get-buffer "#chan/127.0.0.1<4>"))
+
+ (ert-info ("Activity continues in new, <n>-suffixed #chan@barnet buffer")
+ (with-current-buffer chan-buffer-bar
+ (should-not (erc-get-channel-user (erc-current-nick))))
+ (with-current-buffer "#chan/127.0.0.1<4>"
+ (funcall expect 2 "You have joined channel #chan")
+ (funcall expect 1 "Users on #chan: @mike joe tester")
+ (funcall expect 2 "<mike>")
+ (should (eq erc-server-process server-process-bar))
+ (erc-d-t-absent-for 0.2 "<bob>")))
+
+ (ert-info ("Two new chans created for a total of four")
+ (let* ((bufs (erc-scenarios-common-buflist "#chan"))
+ (names (sort (mapcar #'buffer-name bufs) #'string<)))
+ (should
+ (equal names (mapcar (lambda (f) (concat "#chan/127.0.0.1" f))
+ '("" "<2>" "<3>" "<4>"))))))
+
+ (ert-info ("All output sent")
+ (with-current-buffer "#chan/127.0.0.1<3>"
+ (funcall expect 10 "most lively"))
+ (with-current-buffer "#chan/127.0.0.1<4>"
+ (funcall expect 10 "soul black")))
+
+ ;; TODO ensure the exact <N>'s aren't reassigned during killing as
+ ;; they are when the option is on.
+ (ert-info ("Buffers are exempt from shortening")
+ (kill-buffer "#chan/127.0.0.1<4>")
+ (kill-buffer "#chan/127.0.0.1<3>")
+ (kill-buffer chan-buffer-bar)
+ (should-not (get-buffer "#chan"))
+ (should chan-buffer-foo))))
+
+(ert-deftest erc-scenarios-base-reuse-buffers-channel-buffers--disabled ()
+ :tags '(:expensive-test)
+ (with-suppressed-warnings ((obsolete erc-reuse-buffers))
+ (should erc-reuse-buffers)
+ (let ((erc-scenarios-common-dialog "base/reuse-buffers/channel")
+ (erc-server-flood-penalty 0.1)
+ erc-reuse-buffers)
+ (erc-scenarios-common--base-reuse-buffers-server-buffers
+ #'erc-scenarios-common--base-reuse-buffers-channel-buffers))))
+
;;; erc-scenarios-base-reuse-buffers.el ends here
--- /dev/null
+;;; erc-scenarios-join-auth-source.el --- join-auth-source scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+;;
+;; This file is part of GNU Emacs.
+;;
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see
+;; <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; TODO add another test with autojoin and channel keys
+
+(require 'ert-x)
+(eval-and-compile
+ (let ((load-path (cons (ert-resource-directory) load-path)))
+ (require 'erc-scenarios-common)))
+
+(ert-deftest erc-scenarios-join-auth-source--network ()
+ :tags '(:expensive-test)
+ (should erc-auth-source-join-function)
+ (erc-scenarios-common-with-cleanup
+ ((entries
+ '("machine 127.0.0.1 port %d login \"#foo\" password spam"
+ "machine irc.foonet.org port %d login tester password fake"
+ "machine irc.foonet.org login \"#spam\" password secret"
+ "machine foonet port %d login dummy password fake"
+ "machine 127.0.0.1 port %d login dummy password changeme"))
+ (erc-scenarios-common-dialog "join/auth-source")
+ (erc-server-flood-penalty 0.1)
+ (dumb-server (erc-d-run "localhost" t 'foonet))
+ (port (process-contact dumb-server :service))
+ (ents (mapcar (lambda (fmt) (format fmt port)) entries))
+ (netrc-file (make-temp-file "auth-source-test" nil nil
+ (string-join ents "\n")))
+ (auth-sources (list netrc-file))
+ (auth-source-do-cache nil)
+ (expect (erc-d-t-make-expecter))
+ (erc-scenarios-common-extra-teardown (lambda ()
+ (delete-file netrc-file))))
+
+ (ert-info ("Connect without password")
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "dummy"
+ :full-name "dummy")
+ (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
+ (erc-d-t-wait-for 8 (eq erc-network 'foonet))
+ (funcall expect 10 "user modes")
+ (erc-scenarios-common-say "/JOIN #spam")))
+
+ (ert-info ("Join #spam")
+ (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+ (funcall expect 10 "#spam was created on")))))
+
+;;; erc-scenarios-join-auth-source.el ends here
--- /dev/null
+;;; erc-services-tests.el --- Tests for erc-services. -*- lexical-binding:t -*-
+
+;; Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published
+;; by the Free Software Foundation, either version 3 of the License,
+;; or (at your option) any later version.
+;;
+;; GNU Emacs is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; TODO: move the auth-source tests somewhere else. They've been
+;; stashed here for pragmatic reasons.
+
+;;; Code:
+
+(require 'ert-x)
+(require 'erc-services)
+(require 'erc-compat)
+(require 'secrets)
+
+;;;; Core auth-source
+
+(ert-deftest erc--auth-source-determine-params-merge ()
+ (let ((erc-session-server "irc.gnu.org")
+ (erc-server-announced-name "my.gnu.org")
+ (erc-session-port 6697)
+ (erc-network 'fake)
+ (erc-server-current-nick "tester")
+ (erc-networks--id (erc-networks--id-create 'GNU.chat)))
+
+ (should (equal (erc--auth-source-determine-params-merge)
+ '(:host ("GNU.chat" "my.gnu.org" "irc.gnu.org")
+ :port ("6697" "irc")
+ :require (:secret))))
+
+ (should (equal (erc--auth-source-determine-params-merge :host "fake")
+ '(:host ("fake" "GNU.chat" "my.gnu.org" "irc.gnu.org")
+ :port ("6697" "irc")
+ :require (:secret))))
+
+ (should (equal (erc--auth-source-determine-params-merge
+ :host '("fake") :require :host)
+ '(:host ("fake" "GNU.chat" "my.gnu.org" "irc.gnu.org")
+ :require (:host :secret)
+ :port ("6697" "irc"))))
+
+ (should (equal (erc--auth-source-determine-params-merge
+ :host '("fake" "GNU.chat") :port "1234" :x "x")
+ '(:host ("fake" "GNU.chat" "my.gnu.org" "irc.gnu.org")
+ :port ("1234" "6697" "irc")
+ :x ("x")
+ :require (:secret))))))
+
+;; Some of the following may be related to bug#23438.
+
+(defun erc-services-tests--auth-source-standard (search)
+
+ (ert-info ("Session wins")
+ (let ((erc-session-server "irc.gnu.org")
+ (erc-server-announced-name "my.gnu.org")
+ (erc-session-port 6697)
+ (erc-network 'fake)
+ (erc-server-current-nick "tester")
+ (erc-networks--id (erc-networks--id-create 'GNU.chat)))
+ (should (string= (funcall search :user "#chan") "foo"))))
+
+ (ert-info ("Network wins")
+ (let* ((erc-session-server "irc.gnu.org")
+ (erc-server-announced-name "my.gnu.org")
+ (erc-session-port 6697)
+ (erc-network 'GNU.chat)
+ (erc-server-current-nick "tester")
+ (erc-networks--id (erc-networks--id-create nil)))
+ (should (string= (funcall search :user "#chan") "foo"))))
+
+ (ert-info ("Announced wins")
+ (let ((erc-session-server "irc.gnu.org")
+ (erc-server-announced-name "my.gnu.org")
+ (erc-session-port 6697)
+ erc-network
+ (erc-networks--id (erc-networks--id-create nil)))
+ (should (string= (funcall search :user "#chan") "baz")))))
+
+(defun erc-services-tests--auth-source-announced (search)
+ (let* ((erc--isupport-params (make-hash-table))
+ (erc-server-parameters '(("CHANTYPES" . "&#")))
+ (erc--target (erc--target-from-string "&chan")))
+
+ (ert-info ("Announced prioritized")
+
+ (ert-info ("Announced wins")
+ (let* ((erc-session-server "irc.gnu.org")
+ (erc-server-announced-name "my.gnu.org")
+ (erc-session-port 6697)
+ (erc-network 'GNU.chat)
+ (erc-server-current-nick "tester")
+ (erc-networks--id (erc-networks--id-create nil)))
+ (should (string= (funcall search :user "#chan") "baz"))))
+
+ (ert-info ("Peer next")
+ (let* ((erc-server-announced-name "irc.gnu.org")
+ (erc-session-port 6697)
+ (erc-network 'GNU.chat)
+ (erc-server-current-nick "tester")
+ (erc-networks--id (erc-networks--id-create nil)))
+ (should (string= (funcall search :user "#chan") "bar"))))
+
+ (ert-info ("Network used as fallback")
+ (let* ((erc-session-port 6697)
+ (erc-network 'GNU.chat)
+ (erc-server-current-nick "tester")
+ (erc-networks--id (erc-networks--id-create nil)))
+ (should (string= (funcall search :user "#chan") "foo")))))))
+
+(defun erc-services-tests--auth-source-overrides (search)
+ (let* ((erc-session-server "irc.gnu.org")
+ (erc-server-announced-name "my.gnu.org")
+ (erc-network 'GNU.chat)
+ (erc-server-current-nick "tester")
+ (erc-networks--id (erc-networks--id-create nil))
+ (erc-session-port 6667))
+
+ (ert-info ("Specificity and overrides")
+
+ (ert-info ("More specific port")
+ (let ((erc-session-port 6697))
+ (should (string= (funcall search :user "#chan") "spam"))))
+
+ (ert-info ("More specific user (network loses)")
+ (should (string= (funcall search :user '("#fsf")) "42")))
+
+ (ert-info ("Actual override")
+ (should (string= (funcall search :port "6667") "sesame")))
+
+ (ert-info ("Overrides don't interfere with post-processing")
+ (should (string= (funcall search :host "MyHost") "123"))))))
+
+;; auth-source netrc backend
+
+(defvar erc-services-tests--auth-source-entries
+ '("machine irc.gnu.org port irc user \"#chan\" password bar"
+ "machine my.gnu.org port irc user \"#chan\" password baz"
+ "machine GNU.chat port irc user \"#chan\" password foo"))
+
+;; FIXME explain what this is for
+(defun erc-services-tests--auth-source-shuffle (&rest extra)
+ (string-join `(,@(sort (append erc-services-tests--auth-source-entries extra)
+ (lambda (&rest _) (zerop (random 2))))
+ "")
+ "\n"))
+
+(ert-deftest erc--auth-source-search--netrc-standard ()
+ (ert-with-temp-file netrc-file
+ :prefix "erc--auth-source-search--standard"
+ :text (erc-services-tests--auth-source-shuffle)
+
+ (let ((auth-sources (list netrc-file))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-standard #'erc-auth-source-search))))
+
+(ert-deftest erc--auth-source-search--netrc-announced ()
+ (ert-with-temp-file netrc-file
+ :prefix "erc--auth-source-search--announced"
+ :text (erc-services-tests--auth-source-shuffle)
+
+ (let ((auth-sources (list netrc-file))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-announced #'erc-auth-source-search))))
+
+(ert-deftest erc--auth-source-search--netrc-overrides ()
+ (ert-with-temp-file netrc-file
+ :prefix "erc--auth-source-search--overrides"
+ :text (erc-services-tests--auth-source-shuffle
+ "machine GNU.chat port 6697 user \"#chan\" password spam"
+ "machine my.gnu.org port irc user \"#fsf\" password 42"
+ "machine irc.gnu.org port 6667 password sesame"
+ "machine MyHost port irc password 456"
+ "machine MyHost port 6667 password 123")
+
+ (let ((auth-sources (list netrc-file))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-overrides #'erc-auth-source-search))))
+
+;; auth-source plstore backend
+
+(defun erc-services-test--call-with-plstore (&rest args)
+ (advice-add 'epg-decrypt-string :override
+ (lambda (&rest r) (prin1-to-string (cadr r)))
+ '((name . erc--auth-source-plstore)))
+ (advice-add 'epg-find-configuration :override
+ (lambda (&rest _) "" '((program . "/bin/true")))
+ '((name . erc--auth-source-plstore)))
+ (unwind-protect
+ (apply #'erc-auth-source-search args)
+ (advice-remove 'epg-decrypt-string 'erc--auth-source-plstore)
+ (advice-remove 'epg-find-configuration 'erc--auth-source-plstore)))
+
+(defvar erc-services-tests--auth-source-plstore-standard-entries
+ '(("ba950d38118a76d71f9f0591bb373d6cb366a512"
+ :secret-secret t
+ :host "irc.gnu.org"
+ :user "#chan"
+ :port "irc")
+ ("7f17ca445d11158065e911a6d0f4cbf52ca250e3"
+ :secret-secret t
+ :host "my.gnu.org"
+ :user "#chan"
+ :port "irc")
+ ("fcd3c8bd6daf4509de0ad6ee98e744ce0fca9377"
+ :secret-secret t
+ :host "GNU.chat"
+ :user "#chan"
+ :port "irc")))
+
+(defvar erc-services-tests--auth-source-plstore-standard-secrets
+ '(("ba950d38118a76d71f9f0591bb373d6cb366a512" :secret "bar")
+ ("7f17ca445d11158065e911a6d0f4cbf52ca250e3" :secret "baz")
+ ("fcd3c8bd6daf4509de0ad6ee98e744ce0fca9377" :secret "foo")))
+
+(ert-deftest erc--auth-source-search--plstore-standard ()
+ (ert-with-temp-file plstore-file
+ :suffix ".plist"
+ :text (concat ";;; public entries -*- mode: plstore -*- \n"
+ (prin1-to-string
+ erc-services-tests--auth-source-plstore-standard-entries)
+ "\n;;; secret entries\n"
+ (prin1-to-string
+ erc-services-tests--auth-source-plstore-standard-secrets)
+ "\n")
+
+ (let ((auth-sources (list plstore-file))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-standard
+ #'erc-services-test--call-with-plstore))))
+
+(ert-deftest erc--auth-source-search--plstore-announced ()
+ (ert-with-temp-file plstore-file
+ :suffix ".plist"
+ :text (concat ";;; public entries -*- mode: plstore -*- \n"
+ (prin1-to-string
+ erc-services-tests--auth-source-plstore-standard-entries)
+ "\n;;; secret entries\n"
+ (prin1-to-string
+ erc-services-tests--auth-source-plstore-standard-secrets)
+ "\n")
+
+ (let ((auth-sources (list plstore-file))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-announced
+ #'erc-services-test--call-with-plstore))))
+
+(ert-deftest erc--auth-source-search--plstore-overrides ()
+ (ert-with-temp-file plstore-file
+ :suffix ".plist"
+ :text (concat
+ ";;; public entries -*- mode: plstore -*- \n"
+ (prin1-to-string
+ `(,@erc-services-tests--auth-source-plstore-standard-entries
+ ("1b3fab249a8dff77a4d8fe7eb4b0171b25cc711a"
+ :secret-secret t :host "GNU.chat" :user "#chan" :port "6697")
+ ("6cbcdc39476b8cfcca6f3e9a7876f41ec3f708cc"
+ :secret-secret t :host "my.gnu.org" :user "#fsf" :port "irc")
+ ("a33e2b3bd2d6f33995a4b88710a594a100c5e41d"
+ :secret-secret t :host "irc.gnu.org" :port "6667")
+ ("ab2fd349b2b7d6a9215bb35a92d054261b0b1537"
+ :secret-secret t :host "MyHost" :port "irc")
+ ("61a6bd552059494f479ff720e8de33e22574650a"
+ :secret-secret t :host "MyHost" :port "6667")))
+ "\n;;; secret entries\n"
+ (prin1-to-string
+ `(,@erc-services-tests--auth-source-plstore-standard-secrets
+ ("1b3fab249a8dff77a4d8fe7eb4b0171b25cc711a" :secret "spam")
+ ("6cbcdc39476b8cfcca6f3e9a7876f41ec3f708cc" :secret "42")
+ ("a33e2b3bd2d6f33995a4b88710a594a100c5e41d" :secret "sesame")
+ ("ab2fd349b2b7d6a9215bb35a92d054261b0b1537" :secret "456")
+ ("61a6bd552059494f479ff720e8de33e22574650a" :secret "123")))
+ "\n")
+
+ (let ((auth-sources (list plstore-file))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-overrides
+ #'erc-services-test--call-with-plstore))))
+
+;; auth-source JSON backend
+
+(defvar erc-services-tests--auth-source-json-standard-entries
+ [(:host "irc.gnu.org" :port "irc" :user "#chan" :secret "bar")
+ (:host "my.gnu.org" :port "irc" :user "#chan" :secret "baz")
+ (:host "GNU.chat" :port "irc" :user "#chan" :secret "foo")])
+
+(ert-deftest erc--auth-source-search--json-standard ()
+ (ert-with-temp-file json-store
+ :suffix ".json"
+ :text (let ((json-object-type 'plist))
+ (json-encode
+ erc-services-tests--auth-source-json-standard-entries))
+ (let ((auth-sources (list json-store))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-standard #'erc-auth-source-search))))
+
+(ert-deftest erc--auth-source-search--json-announced ()
+ (ert-with-temp-file plstore-file
+ :suffix ".json"
+ :text (let ((json-object-type 'plist))
+ (json-encode
+ erc-services-tests--auth-source-json-standard-entries))
+
+ (let ((auth-sources (list plstore-file))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-announced #'erc-auth-source-search))))
+
+(ert-deftest erc--auth-source-search--json-overrides ()
+ (ert-with-temp-file json-file
+ :suffix ".json"
+ :text (let ((json-object-type 'plist))
+ (json-encode
+ (vconcat
+ erc-services-tests--auth-source-json-standard-entries
+ [(:secret "spam" :host "GNU.chat" :user "#chan" :port "6697")
+ (:secret "42" :host "my.gnu.org" :user "#fsf" :port "irc")
+ (:secret "sesame" :host "irc.gnu.org" :port "6667")
+ (:secret "456" :host "MyHost" :port "irc")
+ (:secret "123" :host "MyHost" :port "6667")])))
+
+ (let ((auth-sources (list json-file))
+ (auth-source-do-cache nil))
+ (erc-services-tests--auth-source-overrides #'erc-auth-source-search))))
+
+;; auth-source-secrets backend
+
+(defvar erc-services-tests--auth-source-secrets-standard-entries
+ '(("#chan@irc.gnu.org:irc" ; label
+ (:host . "irc.gnu.org")
+ (:user . "#chan")
+ (:port . "irc")
+ (:xdg:schema . "org.freedesktop.Secret.Generic"))
+ ("#chan@my.gnu.org:irc"
+ (:host . "my.gnu.org")
+ (:user . "#chan")
+ (:port . "irc")
+ (:xdg:schema . "org.freedesktop.Secret.Generic"))
+ ("#chan@GNU.chat:irc"
+ (:host . "GNU.chat")
+ (:user . "#chan")
+ (:port . "irc")
+ (:xdg:schema . "org.freedesktop.Secret.Generic"))))
+
+(defvar erc-services-tests--auth-source-secrets-standard-secrets
+ '(("#chan@irc.gnu.org:irc" . "bar")
+ ("#chan@my.gnu.org:irc" . "baz")
+ ("#chan@GNU.chat:irc" . "foo")))
+
+(ert-deftest erc--auth-source-search--secrets-standard ()
+ (skip-unless (bound-and-true-p secrets-enabled))
+ (let ((auth-sources '("secrets:Test"))
+ (auth-source-do-cache nil)
+ (entries erc-services-tests--auth-source-secrets-standard-entries)
+ (secrets erc-services-tests--auth-source-secrets-standard-secrets))
+
+ (cl-letf (((symbol-function 'secrets-search-items)
+ (lambda (col &rest r)
+ (should (equal col "Test"))
+ (should (plist-get r :user))
+ (map-keys entries)))
+ ((symbol-function 'secrets-get-secret)
+ (lambda (col label)
+ (should (equal col "Test"))
+ (assoc-default label secrets)))
+ ((symbol-function 'secrets-get-attributes)
+ (lambda (col label)
+ (should (equal col "Test"))
+ (assoc-default label entries))))
+
+ (erc-services-tests--auth-source-standard #'erc-auth-source-search))))
+
+(ert-deftest erc--auth-source-search--secrets-announced ()
+ (skip-unless (bound-and-true-p secrets-enabled))
+ (let ((auth-sources '("secrets:Test"))
+ (auth-source-do-cache nil)
+ (entries erc-services-tests--auth-source-secrets-standard-entries)
+ (secrets erc-services-tests--auth-source-secrets-standard-secrets))
+
+ (cl-letf (((symbol-function 'secrets-search-items)
+ (lambda (col &rest r)
+ (should (equal col "Test"))
+ (should (plist-get r :user))
+ (map-keys entries)))
+ ((symbol-function 'secrets-get-secret)
+ (lambda (col label)
+ (should (equal col "Test"))
+ (assoc-default label secrets)))
+ ((symbol-function 'secrets-get-attributes)
+ (lambda (col label)
+ (should (equal col "Test"))
+ (assoc-default label entries))))
+
+ (erc-services-tests--auth-source-announced #'erc-auth-source-search))))
+
+(ert-deftest erc--auth-source-search--secrets-overrides ()
+ (skip-unless (bound-and-true-p secrets-enabled))
+ (let ((auth-sources '("secrets:Test"))
+ (auth-source-do-cache nil)
+ (entries `(,@erc-services-tests--auth-source-secrets-standard-entries
+ ("#chan@GNU.chat:6697"
+ (:host . "GNU.chat") (:user . "#chan") (:port . "6697")
+ (:xdg:schema . "org.freedesktop.Secret.Generic"))
+ ("#fsf@my.gnu.org:irc"
+ (:host . "my.gnu.org") (:user . "#fsf") (:port . "irc")
+ (:xdg:schema . "org.freedesktop.Secret.Generic"))
+ ("irc.gnu.org:6667"
+ (:host . "irc.gnu.org") (:port . "6667")
+ (:xdg:schema . "org.freedesktop.Secret.Generic"))
+ ("MyHost:irc"
+ (:host . "MyHost") (:port . "irc")
+ (:xdg:schema . "org.freedesktop.Secret.Generic"))
+ ("MyHost:6667"
+ (:host . "MyHost") (:port . "6667")
+ (:xdg:schema . "org.freedesktop.Secret.Generic"))))
+ (secrets `(,@erc-services-tests--auth-source-secrets-standard-secrets
+ ("#chan@GNU.chat:6697" . "spam")
+ ("#fsf@my.gnu.org:irc" . "42" )
+ ("irc.gnu.org:6667" . "sesame")
+ ("MyHost:irc" . "456")
+ ("MyHost:6667" . "123"))))
+
+ (cl-letf (((symbol-function 'secrets-search-items)
+ (lambda (col &rest _)
+ (should (equal col "Test"))
+ (map-keys entries)))
+ ((symbol-function 'secrets-get-secret)
+ (lambda (col label)
+ (should (equal col "Test"))
+ (assoc-default label secrets)))
+ ((symbol-function 'secrets-get-attributes)
+ (lambda (col label)
+ (should (equal col "Test"))
+ (assoc-default label entries))))
+
+ (erc-services-tests--auth-source-overrides #'erc-auth-source-search))))
+
+;; auth-source-pass backend
+
+(require 'auth-source-pass)
+
+;; `auth-source-pass--find-match-unambiguous' returns something like:
+;;
+;; (list :host "irc.gnu.org"
+;; :port "6697"
+;; :user "rms"
+;; :secret
+;; #[0 "\301\302\300\"\207"
+;; [((secret . "freedom")) auth-source-pass--get-attr secret] 3])
+;;
+;; This function gives ^ (faked here to avoid gpg and file IO). See
+;; `auth-source-pass--with-store' in ../auth-source-pass-tests.el
+(defun erc-services-tests--asp-parse-entry (store entry)
+ (when-let ((found (cl-find entry store :key #'car :test #'string=)))
+ (list (assoc 'secret (cdr found)))))
+
+(defvar erc-join-tests--auth-source-pass-entries
+ '(("irc.gnu.org:irc/#chan"
+ ("port" . "irc") ("user" . "#chan") (secret . "bar"))
+ ("my.gnu.org:irc/#chan"
+ ("port" . "irc") ("user" . "#chan") (secret . "baz"))
+ ("GNU.chat:irc/#chan"
+ ("port" . "irc") ("user" . "#chan") (secret . "foo"))))
+
+(ert-deftest erc--auth-source-search--pass-standard ()
+ (ert-skip "Pass backend not yet supported")
+ (let ((store erc-join-tests--auth-source-pass-entries)
+ (auth-sources '(password-store))
+ (auth-source-do-cache nil))
+
+ (cl-letf (((symbol-function 'auth-source-pass-parse-entry)
+ (apply-partially #'erc-services-tests--asp-parse-entry store))
+ ((symbol-function 'auth-source-pass-entries)
+ (lambda () (mapcar #'car store))))
+
+ (erc-services-tests--auth-source-standard #'erc-auth-source-search))))
+
+(ert-deftest erc--auth-source-search--pass-announced ()
+ (ert-skip "Pass backend not yet supported")
+ (let ((store erc-join-tests--auth-source-pass-entries)
+ (auth-sources '(password-store))
+ (auth-source-do-cache nil))
+
+ (cl-letf (((symbol-function 'auth-source-pass-parse-entry)
+ (apply-partially #'erc-services-tests--asp-parse-entry store))
+ ((symbol-function 'auth-source-pass-entries)
+ (lambda () (mapcar #'car store))))
+
+ (erc-services-tests--auth-source-announced #'erc-auth-source-search))))
+
+(ert-deftest erc--auth-source-search--pass-overrides ()
+ (ert-skip "Pass backend not yet supported")
+ (let ((store
+ `(,@erc-join-tests--auth-source-pass-entries
+ ("GNU.chat:6697/#chan"
+ ("port" . "6697") ("user" . "#chan") (secret . "spam"))
+ ("my.gnu.org:irc/#fsf"
+ ("port" . "irc") ("user" . "#fsf") (secret . "42"))
+ ("irc.gnu.org:6667"
+ ("port" . "6667") (secret . "sesame"))
+ ("MyHost:irc"
+ ("port" . "irc") (secret . "456"))
+ ("MyHost:6667"
+ ("port" . "6667") (secret . "123"))))
+ (auth-sources '(password-store))
+ (auth-source-do-cache nil))
+
+ (cl-letf (((symbol-function 'auth-source-pass-parse-entry)
+ (apply-partially #'erc-services-tests--asp-parse-entry store))
+ ((symbol-function 'auth-source-pass-entries)
+ (lambda () (mapcar #'car store))))
+
+ (erc-services-tests--auth-source-overrides #'erc-auth-source-search))))
+
+;;;; The services module
+
+(ert-deftest erc-nickserv-get-password ()
+ (should erc-prompt-for-nickserv-password)
+ (ert-with-temp-file netrc-file
+ :prefix "erc-nickserv-get-password"
+ :text (mapconcat 'identity
+ '("machine GNU/chat port 6697 user bob password spam"
+ "machine FSF.chat port 6697 user bob password sesame"
+ "machine MyHost port irc password 123")
+ "\n")
+
+ (let* ((auth-sources (list netrc-file))
+ (auth-source-do-cache nil)
+ (erc-nickserv-passwords '((FSF.chat (("alice" . "foo")
+ ("joe" . "bar")))))
+ (erc-use-auth-source-for-nickserv-password t)
+ (erc-session-server "irc.gnu.org")
+ (erc-server-announced-name "my.gnu.org")
+ (erc-network 'FSF.chat)
+ (erc-server-current-nick "tester")
+ (erc-networks--id (erc-networks--id-create nil))
+ (erc-session-port 6697))
+
+ (ert-info ("Lookup custom option")
+ (should (string= (erc-nickserv-get-password "alice") "foo")))
+
+ (ert-info ("Auth source")
+ (ert-info ("Network")
+ (should (string= (erc-nickserv-get-password "bob") "sesame")))
+
+ (ert-info ("Network ID")
+ (let ((erc-networks--id (erc-networks--id-create 'GNU/chat)))
+ (should (string= (erc-nickserv-get-password "bob") "spam")))))
+
+ (ert-info ("Read input")
+ (should (string=
+ (ert-simulate-keys "baz\r" (erc-nickserv-get-password "mike"))
+ "baz")))
+
+ (ert-info ("Failed")
+ (should-not (ert-simulate-keys "\r"
+ (erc-nickserv-get-password "fake")))))))
+
+
+;;; erc-services-tests.el ends here
--- /dev/null
+;; -*- mode: lisp-data; -*-
+((pass 1 "PASS :changeme"))
+((nick 1 "NICK tester"))
+((user 1 "USER user 0 * :tester")
+ (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16")
+ (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC")
+ (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
+ (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server")
+ (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server")
+ (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0 ":irc.foonet.org 253 tester 0 :unregistered connections")
+ (0 ":irc.foonet.org 254 tester 1 :channels formed")
+ (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers")
+ (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0 ":irc.foonet.org 422 tester :MOTD File is missing"))
+
+((mode-user 1.2 "MODE tester +i")
+ (0 ":irc.foonet.org 221 tester +i")
+ (0 ":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."))
--- /dev/null
+;; -*- mode: lisp-data; -*-
+((nick 1 "NICK tester"))
+((user 1 "USER user 0 * :tester")
+ (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16")
+ (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC")
+ (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
+ (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server")
+ (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server")
+ (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0 ":irc.foonet.org 253 tester 0 :unregistered connections")
+ (0 ":irc.foonet.org 254 tester 1 :channels formed")
+ (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers")
+ (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0 ":irc.foonet.org 422 tester :MOTD File is missing"))
+
+((mode-user 1.2 "MODE tester +i")
+ (0 ":irc.foonet.org 221 tester +i")
+ (0 ":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."))
--- /dev/null
+;; -*- mode: lisp-data; -*-
+((pass 3 "PASS :barnet:changeme"))
+((nick 3 "NICK tester"))
+((user 3 "USER user 0 * :tester")
+ (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester")
+ (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16")
+ (0 ":irc.barnet.org 003 tester :This server was created Wed, 05 May 2021 09:05:33 UTC")
+ (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
+ (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server")
+ (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server")
+ (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0 ":irc.barnet.org 252 tester 0 :IRC Operators online")
+ (0 ":irc.barnet.org 254 tester 1 :channels formed")
+ (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers")
+ (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0 ":irc.barnet.org 422 tester :MOTD File is missing"))
+
+((mode-user 10.2 "MODE tester +i")
+ ;; No mode answer
+ (0 ":irc.znc.in 306 tester :You have been marked as being away")
+ (0 ":tester!~u@wvys46tx8tpmk.irc JOIN #chan")
+ (0 ":irc.barnet.org 353 tester = #chan :joe @mike tester")
+ (0 ":irc.barnet.org 366 tester #chan :End of /NAMES list.")
+ (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:16] joe: Tush! none but minstrels like of sonneting.")
+ (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:19] mike: Prithee, nuncle, be contented; 'tis a naughty night to swim in. Now a little fire in a wide field were like an old lecher's heart; a small spark, all the rest on's body cold. Look! here comes a walking fire.")
+ (0 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:22] joe: My name is Edgar, and thy father's son.")
+ (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:26] mike: Good my lord, be good to me; your honour is accounted a merciful man; good my lord.")
+ (0 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:31] joe: Thy child shall live, and I will see it nourish'd.")
+ (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:33] mike: Quick, quick; fear nothing; I'll be at thy elbow.")
+ (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")
+ (0 ":irc.barnet.org NOTICE tester :[09:05:35] 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 ":irc.barnet.org 305 tester :You are no longer marked as being away"))
+
+((mode 3 "MODE #chan")
+ (0 ":irc.barnet.org 324 tester #chan +nt")
+ (0 ":irc.barnet.org 329 tester #chan 1620205534")
+ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: That will be given to the loudest noise we make.")
+ (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: If it please your honour, I am the poor duke's constable, and my name is Elbow: I do lean upon justice, sir; and do bring in here before your good honour two notorious benefactors.")
+ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Following the signs, woo'd but the sign of she.")
+ (0.5 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: That, sir, which I will not report after her.")
+ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Boyet, prepare: I will away to-night.")
+ (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: If the man be a bachelor, sir, I can; but if he be a married man, he is his wife's head, and I can never cut off a woman's head.")
+ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Thyself upon thy virtues, they on thee.")
+ (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: Arm it in rags, a pigmy's straw doth pierce it."))
+
+((part 5.1 "PART #chan :" quit)
+ (0 ":tester!~u@wvys46tx8tpmk.irc PART #chan :" quit))
+
+((join 10.1 "JOIN #chan")
+ (0 ":tester!~u@wvys46tx8tpmk.irc JOIN #chan")
+ (0 ":irc.barnet.org 353 tester = #chan :@mike joe tester")
+ (0 ":irc.barnet.org 366 tester #chan :End of NAMES list")
+ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :tester, welcome!")
+ (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :tester, welcome!"))
+
+((mode 1 "MODE #chan")
+ (0 ":irc.barnet.org 324 tester #chan +nt")
+ (0 ":irc.barnet.org 329 tester #chan 1620205534")
+ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Chi non te vede, non te pretia.")
+ (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: Well, if ever thou dost fall from this faith, thou wilt prove a notable argument.")
+ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Of heavenly oaths, vow'd with integrity.")
+ (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: These herblets shall, which we upon you strew.")
+ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Aaron will have his soul black like his face."))
+
+((linger 0.5 LINGER))
--- /dev/null
+;; -*- mode: lisp-data; -*-
+((pass 1 "PASS :foonet:changeme"))
+((nick 1 "NICK tester"))
+((user 1 "USER user 0 * :tester")
+ (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16")
+ (0 ":irc.foonet.org 003 tester :This server was created Wed, 05 May 2021 09:05:34 UTC")
+ (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
+ (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server")
+ (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server")
+ (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0 ":irc.foonet.org 254 tester 1 :channels formed")
+ (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers")
+ (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0 ":irc.foonet.org 422 tester :MOTD File is missing"))
+
+((mode-user 12 "MODE tester +i")
+ ;; No mode answer
+ (0 ":irc.znc.in 306 tester :You have been marked as being away")
+ (0 ":tester!~u@247eaxkrufj44.irc JOIN #chan")
+ (0 ":irc.foonet.org 353 tester = #chan :alice @bob tester")
+ (0 ":irc.foonet.org 366 tester #chan :End of /NAMES list.")
+ (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:07:19] bob: Is this; she hath bought the name of whore thus dearly.")
+ (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:07:24] alice: He sent to me, sir,Here he comes.")
+ (0 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:07:26] bob: Till I torment thee for this injury.")
+ (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:07:29] alice: There's an Italian come; and 'tis thought, one of Leonatus' friends.")
+ (0 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:09:33] bob: Ay, and the particular confirmations, point from point, to the full arming of the verity.")
+ (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:09:35] alice: Kneel in the streets and beg for grace in vain.")
+ (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")
+ (0 ":irc.foonet.org NOTICE tester :[09:06:05] 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 ":irc.foonet.org 305 tester :You are no longer marked as being away"))
+
+((mode 10 "MODE #chan")
+ (0 ":irc.foonet.org 324 tester #chan +nt")
+ (0 ":irc.foonet.org 329 tester #chan 1620205534")
+ (0.5 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: Nor I no strength to climb without thy help.")
+ (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: Nothing, but let him have thanks. Demand of him my condition, and what credit I have with the duke.")
+ (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: Show me this piece. I am joyful of your sights.")
+ (0.2 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: Whilst I can shake my sword or hear the drum."))
+
+((part 5 "PART #chan :" quit)
+ (0 ":tester!~u@247eaxkrufj44.irc PART #chan :" quit))
+
+((join 10 "JOIN #chan")
+ (0 ":tester!~u@247eaxkrufj44.irc JOIN #chan")
+ (0 ":irc.foonet.org 353 tester = #chan :@bob alice tester")
+ (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")
+ (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :tester, welcome!")
+ (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :tester, welcome!"))
+
+((mode 1 "MODE #chan")
+ (0 ":irc.foonet.org 324 tester #chan +nt")
+ (0 ":irc.foonet.org 329 tester #chan 1620205534")
+ (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: Thou desirest me to stop in my tale against the hair.")
+ (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: And dar'st not stand, nor look me in the face.")
+ (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: It should not be, by the persuasion of his new feasting.")
+ (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: It was not given me, nor I did not buy it.")
+ (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: He that would vouch it in any place but here.")
+ (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: In everything I wait upon his will.")
+ (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: Thou counterfeit'st most lively."))
+
+((linger 8 LINGER))
(erc-modules (copy-sequence erc-modules))
(inhibit-interaction t)
(auth-source-do-cache nil)
+ (erc-auth-source-parameters-join-function nil)
(erc-autojoin-channels-alist nil)
(erc-server-auto-reconnect nil)
(erc-d-linger-secs 10)
--- /dev/null
+;; -*- mode: lisp-data; -*-
+((pass 1 "PASS :changeme"))
+((nick 1 "NICK dummy"))
+((user 1 "USER user 0 * :dummy")
+ (0.00 ":irc.foonet.org 001 dummy :Welcome to the foonet IRC Network dummy")
+ (0.01 ":irc.foonet.org 002 dummy :Your host is irc.foonet.org, running version ergo-v2.8.0")
+ (0.00 ":irc.foonet.org 003 dummy :This server was created Tue, 24 May 2022 05:28:42 UTC")
+ (0.00 ":irc.foonet.org 004 dummy irc.foonet.org ergo-v2.8.0 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 dummy AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
+ (0.01 ":irc.foonet.org 005 dummy 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 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server")
+ (0.01 ":irc.foonet.org 005 dummy draft/CHATHISTORY=100 :are supported by this server")
+ (0.00 ":irc.foonet.org 251 dummy :There are 0 users and 4 invisible on 1 server(s)")
+ (0.00 ":irc.foonet.org 252 dummy 0 :IRC Operators online")
+ (0.00 ":irc.foonet.org 253 dummy 0 :unregistered connections")
+ (0.00 ":irc.foonet.org 254 dummy 2 :channels formed")
+ (0.00 ":irc.foonet.org 255 dummy :I have 4 clients and 0 servers")
+ (0.00 ":irc.foonet.org 265 dummy 4 4 :Current local users 4, max 4")
+ (0.00 ":irc.foonet.org 266 dummy 4 4 :Current global users 4, max 4")
+ (0.00 ":irc.foonet.org 422 dummy :MOTD File is missing"))
+
+((mode 6 "MODE dummy +i")
+ (0.00 ":irc.foonet.org 221 dummy +i")
+ (0.00 ":irc.foonet.org NOTICE dummy :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.02 ":irc.foonet.org 221 dummy +i"))
+
+((join 6.47 "JOIN #spam secret")
+ (0.03 ":dummy!~u@w9rfqveugz722.irc JOIN #spam"))
+
+((mode 1 "MODE #spam")
+ (0.01 ":irc.foonet.org 353 dummy = #spam :~tester dummy")
+ (0.00 ":irc.foonet.org 366 dummy #spam :End of NAMES list")
+ (0.01 ":irc.foonet.org 324 dummy #spam +knt secret")
+ (0.03 ":irc.foonet.org 329 dummy #spam 1653370308"))
--- /dev/null
+;; -*- mode: lisp-data; -*-
+((nick 1 "NICK tester"))
+((user 1 "USER user 0 * :tester")
+ (0.26 ":zirconium.libera.chat NOTICE * :*** Checking Ident")
+ (0.01 ":zirconium.libera.chat NOTICE * :*** Looking up your hostname...")
+ (0.01 ":zirconium.libera.chat NOTICE * :*** No Ident response")
+ (0.02 ":zirconium.libera.chat NOTICE * :*** Found your hostname: static-198-54-131-100.cust.tzulo.com")
+ (0.02 ":zirconium.libera.chat 001 tester :Welcome to the Libera.Chat Internet Relay Chat Network tester")
+ (0.01 ":zirconium.libera.chat 002 tester :Your host is zirconium.libera.chat[46.16.175.175/6697], running version solanum-1.0-dev")
+ (0.03 ":zirconium.libera.chat 003 tester :This server was created Wed Jun 9 2021 at 01:38:28 UTC")
+ (0.02 ":zirconium.libera.chat 004 tester zirconium.libera.chat solanum-1.0-dev DGQRSZaghilopsuwz CFILMPQSbcefgijklmnopqrstuvz bkloveqjfI")
+ (0.00 ":zirconium.libera.chat 005 tester ETRACE WHOX FNC MONITOR=100 SAFELIST ELIST=CTU CALLERID=g KNOCK CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstuz :are supported by this server")
+ (0.03 ":zirconium.libera.chat 005 tester CHANLIMIT=#:250 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=Libera.Chat STATUSMSG=@+ CASEMAPPING=rfc1459 NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server")
+ (0.02 ":zirconium.libera.chat 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz CLIENTVER=3.0 :are supported by this server")
+ (0.02 ":zirconium.libera.chat 251 tester :There are 68 users and 37640 invisible on 25 servers")
+ (0.00 ":zirconium.libera.chat 252 tester 36 :IRC Operators online")
+ (0.01 ":zirconium.libera.chat 253 tester 5 :unknown connection(s)")
+ (0.00 ":zirconium.libera.chat 254 tester 19341 :channels formed")
+ (0.01 ":zirconium.libera.chat 255 tester :I have 3321 clients and 1 servers")
+ (0.01 ":zirconium.libera.chat 265 tester 3321 4289 :Current local users 3321, max 4289")
+ (0.00 ":zirconium.libera.chat 266 tester 37708 38929 :Current global users 37708, max 38929")
+ (0.01 ":zirconium.libera.chat 250 tester :Highest connection count: 4290 (4289 clients) (38580 connections received)")
+ (0.21 ":zirconium.libera.chat 375 tester :- zirconium.libera.chat Message of the Day - ")
+ (0.00 ":zirconium.libera.chat 372 tester :- This server provided by Seeweb <https://www.seeweb.it/>")
+ (0.01 ":zirconium.libera.chat 372 tester :- Welcome to Libera Chat, the IRC network for")
+ (0.01 ":zirconium.libera.chat 372 tester :- free & open-source software and peer directed projects.")
+ (0.00 ":zirconium.libera.chat 372 tester :- ")
+ (0.00 ":zirconium.libera.chat 372 tester :- Use of Libera Chat is governed by our network policies.")
+ (0.00 ":zirconium.libera.chat 372 tester :- ")
+ (0.01 ":zirconium.libera.chat 372 tester :- Please visit us in #libera for questions and support.")
+ (0.01 ":zirconium.libera.chat 372 tester :- ")
+ (0.01 ":zirconium.libera.chat 372 tester :- Website and documentation: https://libera.chat")
+ (0.01 ":zirconium.libera.chat 372 tester :- Webchat: https://web.libera.chat")
+ (0.01 ":zirconium.libera.chat 372 tester :- Network policies: https://libera.chat/policies")
+ (0.01 ":zirconium.libera.chat 372 tester :- Email: support@libera.chat")
+ (0.00 ":zirconium.libera.chat 376 tester :End of /MOTD command."))
+
+((mode-user 1.2 "MODE tester +i")
+ (0.02 ":tester MODE tester :+Zi")
+ (0.02 ":NickServ!NickServ@services.libera.chat NOTICE tester :This nickname is registered. Please choose a different nickname, or identify via \2/msg NickServ IDENTIFY tester <password>\2"))
+
+((privmsg 2 "PRIVMSG NickServ :IDENTIFY changeme")
+ (0.96 ":NickServ!NickServ@services.libera.chat NOTICE tester :You are now identified for \2tester\2.")
+ (0.25 ":NickServ!NickServ@services.libera.chat NOTICE tester :Last login from: \2~tester@school.edu/tester\2 on Jun 18 01:15:56 2021 +0000."))
+
+((quit 5 "QUIT :\2ERC\2")
+ (0.19 ":tester!~user@static-198-54-131-100.cust.tzulo.com QUIT :Client Quit"))
+
+((linger 1 LINGER))