diff options
| author | EuAndreh <eu@euandre.org> | 2026-04-30 09:50:19 -0300 |
|---|---|---|
| committer | EuAndreh <eu@euandre.org> | 2026-04-30 09:50:19 -0300 |
| commit | c41848e3fa3eed149fedc56c1cd62a977905baaa (patch) | |
| tree | 9e19ae2435c84f25bbcf5c8708f7bbcdd557ec79 /src/papod.clj | |
| parent | Honor CAP REQ -cap and ignore unknown batch refs (diff) | |
| download | papod-c41848e3fa3eed149fedc56c1cd62a977905baaa.tar.gz papod-c41848e3fa3eed149fedc56c1cd62a977905baaa.tar.xz | |
Implement VERSION, ISON, and CHGHOST
- VERSION: emit 351 with version + server name, followed by
the same three-line 005 ISUPPORT burst we send on welcome.
- ISON: reply with 303, listing only those of the requested
targets that have a live client entry.
- CHGHOST (oper-only): introduce a per-client :host field
(defaulting to 'localhost' when unset) and update both
client-prefix and nuh-for-nick to use it. CHGHOST <target>
<newuser> <newhost> mutates the target's :user :username
and :host, replies 396 to the target itself, and broadcasts
':oldprefix CHGHOST newuser newhost' to peers in shared
channels that negotiated the chghost cap. Non-opers get
481; missing params get 461; bad chars get 501.
Was previously surfaced as gaps by the chat.papo.im
integration suite; all three commands now respond with the
RFC-mandated numerics.
Diffstat (limited to 'src/papod.clj')
| -rw-r--r-- | src/papod.clj | 133 |
1 files changed, 129 insertions, 4 deletions
diff --git a/src/papod.clj b/src/papod.clj index 35b9b49..e89f7cc 100644 --- a/src/papod.clj +++ b/src/papod.clj @@ -765,8 +765,9 @@ [client] (let [nick (or (:nick @client) "*") u (:user @client) - user (or (:username u) nick)] - (str nick "!" user "@localhost"))) + user (or (:username u) nick) + host (or (:host @client) "localhost")] + (str nick "!" user "@" host))) (defn- numeric-reply [client code & parts] @@ -2578,8 +2579,9 @@ (when-let [m (get @clients nick)] (when-let [c (some-> (:client-atom m) deref)] (let [u (:user c) - ident (or (:username u) nick)] - (str nick "!" ident "@localhost"))))) + ident (or (:username u) nick) + host (or (:host c) "localhost")] + (str nick "!" ident "@" host))))) (str nick "!" nick "@localhost"))) (defn- nick-mask-match? @@ -5880,6 +5882,51 @@ [(numeric-reply client "391" (str +server-name+ " :" (iso-time)))] + (= command "VERSION") + [(numeric-reply client "351" + (str +version+ " " +server-name+ " :papod")) + (numeric-reply client "005" + (str "ACCOUNTEXTBAN=a" + " AWAYLEN=200 BOT=B" + " CASEMAPPING=ascii" + " CHANLIMIT=#:100" + " CHANMODES=beI,k,l,Cnt" + " CHANNELLEN=64 CHANTYPES=#" + " ELIST= EXCEPTS=e" + " EXTBAN=~,am HOSTLEN=64" + " :are supported by this server")) + (numeric-reply client "005" + (str "INVEX=I KICKLEN=300" + " MAXLIST=b:50,e:50,I:50" + " MAXTARGETS=4 MODES=4" + " MONITOR=100 NETWORK=papod" + " NICKLEN=30 PREFIX=(ov)@+" + " STATUSMSG=@+" + " :are supported by this server")) + (numeric-reply client "005" + (str "TARGMAX=KICK:1,LIST:,WHOIS:1,PRIVMSG:4,NAMES:" + " TOPICLEN=390 USERLEN=18" + " UTF8ONLY WHOX" + " :are supported by this server"))] + + (= command "ISON") + (let [{:keys [clients]} components + online (when clients + (for [target params + :when (and (string? target) + (seq target)) + :let [low (string/lower-case target) + m (some + (fn [[k m]] + (when (= (string/lower-case k) + low) + m)) + @clients)] + :when m] + target))] + [(numeric-reply client "303" + (str ":" (string/join " " (or online []))))]) + (= command "OPER") (cond (< (count params) 2) @@ -5962,6 +6009,84 @@ (deliver-to-client! (:w m) line))) []))) + ;; CHGHOST: oper-only, mutates target's user@host and + ;; broadcasts ":old-prefix CHGHOST newuser newhost" to + ;; peers in shared chans that negotiated the chghost cap. + (= command "CHGHOST") + (let [{:keys [clients channels]} components + target (first params) + newuser (second params) + newhost (when (>= (count params) 3) + (let [h (nth params 2)] + (cond-> h + (string/starts-with? h ":") + (subs 1)))) + nick (client-target client)] + (cond + (< (count params) 3) + [(numeric-reply client "461" + "CHGHOST :Not enough parameters")] + + (not (:oper? @client)) + [(numeric-reply client "481" + (str ":Permission Denied- You're not" + " an IRC operator"))] + + (or (string/blank? newuser) + (string/blank? newhost) + (string/includes? newuser " ") + (string/includes? newhost " ")) + [(numeric-reply client "501" + ":Invalid CHGHOST parameters")] + + :else + (let [m (and clients (get @clients target)) + ca (when m (:client-atom m))] + (cond + (not ca) + [(numeric-reply client "401" + (str target " :No such nick"))] + + :else + (let [old-prefix + (let [c @ca + tnick (or (:nick c) target) + u (:user c) + ident (or (:username u) tnick) + host (or (:host c) "localhost")] + (str tnick "!" ident "@" host)) + line (str ":" old-prefix + " CHGHOST " newuser + " " newhost)] + (swap! ca (fn [s] + (-> s + (assoc-in [:user :username] + newuser) + (assoc :host newhost)))) + ;; Notify the target itself. + (deliver-to-client! (:w m) line) + ;; Notify peers in shared channels that + ;; negotiated chghost. + (when (and clients channels) + (let [seen (atom #{target})] + (doseq [[ch members] @channels + :when (contains? members target) + mn members + :when (not (contains? @seen mn)) + :let [pm (get @clients mn) + pca (:client-atom pm) + pcaps (or (some-> pca + deref :caps) + #{})] + :when (and pm + (contains? + pcaps "chghost"))] + (deliver-to-client! (:w pm) line) + (swap! seen conj mn)))) + [(numeric-reply client "396" + (str newuser "@" newhost + " :is now your visible host"))]))))) + (or (= command "HELP") (= command "HELPOP")) (let [subject (or (first params) "*") nick (client-target client)] |
