diff options
| author | EuAndreh <eu@euandre.org> | 2026-04-29 17:10:23 -0300 |
|---|---|---|
| committer | EuAndreh <eu@euandre.org> | 2026-04-29 17:10:23 -0300 |
| commit | 5f101f4166dc26cb8a4b823c94a4d175cff2514c (patch) | |
| tree | 3d58b3381c31a77244d3853410b935b029d5ce0b /src/papod.clj | |
| parent | Fix mute extban exemption, list query, ISUPPORT split, line limit (diff) | |
| download | papod-5f101f4166dc26cb8a4b823c94a4d175cff2514c.tar.gz papod-5f101f4166dc26cb8a4b823c94a4d175cff2514c.tar.xz | |
Implement draft/metadata-2 before-connect optional behavior
Clients with draft/metadata-2 + batch can now METADATA * SET/GET/LIST/
CLEAR before completing registration. Pre-registration values are
buffered on the client atom and replayed in a metadata BATCH at the
end of the welcome burst, then merged into the global metadata atom
under the registered nick.
The value-suffix form ("draft/metadata-2=before-connect") is only sent
in CAP LS 302 replies; CAP LS without a 302 version still emits the
bare name to avoid IRCv3.1 clients seeing name=value capabilities.
Diffstat (limited to 'src/papod.clj')
| -rw-r--r-- | src/papod.clj | 116 |
1 files changed, 112 insertions, 4 deletions
diff --git a/src/papod.clj b/src/papod.clj index d96bb87..e20bec8 100644 --- a/src/papod.clj +++ b/src/papod.clj @@ -1225,9 +1225,28 @@ " :You have " (count memos) " unread memo(s)." " Use /msg MemoServ LIST")]) - rejoins (auto-rejoin-replies client components)] + rejoins (auto-rejoin-replies client components) + pending (:pending-metadata @client) + meta-burst + (when (seq pending) + (let [bid (str (java.util.UUID/randomUUID)) + nlow (string/lower-case nick)] + (when-let [meta-atom (:metadata components)] + (swap! meta-atom update nlow + (fnil into {}) pending)) + (swap! client dissoc :pending-metadata) + (concat + [(str "BATCH +" bid " metadata " nick)] + (map + (fn [[k v]] + (str "@batch=" bid + " :" +server-name+ + " METADATA " nick " " k + " * :" v)) + (sort-by first pending)) + [(str "BATCH -" bid)])))] (notify-monitors! components nick "730") - (vec (concat welcome memo-line rejoins))))))) + (vec (concat welcome memo-line meta-burst rejoins))))))) (defn- valid-nick? [s] @@ -1439,7 +1458,7 @@ reg-cap " draft/channel-rename" " draft/extended-isupport" - " draft/metadata-2" + " draft/metadata-2=before-connect" " draft/typing") (str "sasl message-tags server-time" " echo-message batch" @@ -3800,8 +3819,96 @@ (client-target client) requested)) +(defn- pending-metadata-handler + "Handles METADATA SET/GET/LIST/CLEAR before registration completes + (draft/metadata-2 before-connect). Values are buffered in the + client atom until the welcome burst replays them." + [params client] + (let [target (first params) + subcmd (when (> (count params) 1) + (string/upper-case (second params))) + rest-args (drop 2 params)] + (cond + (or (nil? target) (string/blank? target)) + [(numeric-reply client "461" + "METADATA :Not enough parameters")] + + (nil? subcmd) + [(numeric-reply client "461" + "METADATA :Not enough parameters")] + + ;; Only "*" target makes sense pre-registration. + (not= target "*") + [(str "FAIL METADATA INVALID_TARGET " + target " :Invalid target")] + + :else + (case subcmd + "GET" + (let [keys-asked (vec rest-args) + cur (or (:pending-metadata @client) {})] + (mapv + (fn [k] + (if-let [v (get cur k)] + (numeric-reply client "761" + (str "* " k " * :" v)) + (numeric-reply client "766" + (str "* " k + " :no matching key")))) + keys-asked)) + + "LIST" + (let [cur (or (:pending-metadata @client) {})] + (mapv + (fn [[k v]] + (numeric-reply client "761" + (str "* " k " * :" v))) + (sort-by first cur))) + + "SET" + (let [k (first rest-args) + v-parts (rest rest-args) + raw-v (when (seq v-parts) + (string/join " " v-parts)) + v (when raw-v + (cond-> raw-v + (string/starts-with? raw-v ":") (subs 1))) + v-bytes (when v + (alength (.getBytes ^String v "UTF-8")))] + (cond + (nil? k) + [(numeric-reply client "461" + "METADATA :Not enough parameters")] + + (and v-bytes (> v-bytes 360)) + [(str "FAIL METADATA VALUE_INVALID :Value of " + k " is too long")] + + (or (nil? v) (string/blank? v)) + (do (swap! client update :pending-metadata + (fnil dissoc {}) k) + [(numeric-reply client "766" + (str "* " k " :no matching key"))]) + + :else + (do (swap! client update :pending-metadata + (fnil assoc {}) k v) + [(numeric-reply client "761" + (str "* " k " * :" v))]))) + + "CLEAR" + (do (swap! client assoc :pending-metadata {}) + [(numeric-reply client "762" + "* :end of metadata")]) + + ;; SUB/UNSUB/SUBS pre-reg: not useful. + [(numeric-reply client "461" + "METADATA :Not enough parameters")])))) + (defn- handle-metadata [params client components] + (if-not (:registered? @client) + (pending-metadata-handler params client) (let [nick (client-target client) target (first params) subcmd (when (> (count params) 1) @@ -3954,7 +4061,7 @@ ":none"))]) [(str "FAIL METADATA INVALID_SUBCOMMAND " - subcmd " :Unknown subcommand")])))) + subcmd " :Unknown subcommand")]))))) (defn- handle-markread [params client components] @@ -4936,6 +5043,7 @@ "CAP" (handle-cap params client components) "AUTHENTICATE" (handle-authenticate params client components) "REGISTER" (handle-register params client components) + "METADATA" (handle-metadata params client components) "BATCH" (let [ref (first params)] (cond (and ref (string/starts-with? ref "+")) |
