diff options
| author | EuAndreh <eu@euandre.org> | 2026-04-26 17:44:01 -0300 |
|---|---|---|
| committer | EuAndreh <eu@euandre.org> | 2026-04-26 17:44:01 -0300 |
| commit | fbb40946e666c6240b141f58180ce52b4cff6ed9 (patch) | |
| tree | 30e8d1d6cd2d905864278b9fc5b2b7646ccac127 /src | |
| parent | Implement KILL command (oper-only) (diff) | |
| download | papod-fbb40946e666c6240b141f58180ce52b4cff6ed9.tar.gz papod-fbb40946e666c6240b141f58180ce52b4cff6ed9.tar.xz | |
MODE channel: validate target/channel/key/limit, strip leading colon
- Channel must exist (403) before checking op status (482).
- MODE +o now validates that the target nick exists (401) and is on
the channel (441) before changing ops.
- MODE +k now rejects empty / whitespace keys with 696
ERR_INVALIDMODEPARAM and a placeholder '*' so clients see a
5-param numeric.
- MODE +l now rejects non-positive / non-numeric values with 696
(same 5-param shape) and accepts only positive integers.
- The trailing-form mode argument (e.g. ':0') has its leading colon
stripped before parsing/validation, so MODE #chan +l :0 no longer
emits a malformed reply.
Diffstat (limited to 'src')
| -rw-r--r-- | src/papod.clj | 177 |
1 files changed, 121 insertions, 56 deletions
diff --git a/src/papod.clj b/src/papod.clj index f68c430..a63e5b5 100644 --- a/src/papod.clj +++ b/src/papod.clj @@ -3212,13 +3212,24 @@ :else (let [nick (client-target client) mode-str (second params) - mode-arg (nth params 2 nil) + raw-arg (nth params 2 nil) + mode-arg (when raw-arg + (cond-> raw-arg + (string/starts-with? + raw-arg ":") + (subs 1))) adding? (or (string/starts-with? mode-str "+") (not (string/starts-with? mode-str "-"))) mode-char (last mode-str)] (cond + ;; Channel must exist + (and channels + (empty? (get @channels target))) + [(numeric-reply client "403" + (str target " :No such channel"))] + ;; Need op for mode changes (and channels (not (is-op? components target nick))) @@ -3228,9 +3239,23 @@ ;; +o/-o: operator mode (= \o mode-char) - (if-not mode-arg + (cond + (not mode-arg) [(numeric-reply client "461" "MODE :Not enough parameters")] + (and clients + (not (get @clients mode-arg))) + [(numeric-reply client "401" + (str mode-arg + " :No such nick/channel"))] + (and channels + (not (contains? + (get @channels target) + mode-arg))) + [(numeric-reply client "441" + (str mode-arg " " target + " :They aren't on that channel"))] + :else (let [line (str ":" nick " MODE " target " " mode-str " " mode-arg)] (if adding? @@ -3280,35 +3305,58 @@ ;; +l/-l: limit (= \l mode-char) - (let [cur (or (get @(:chan-modes components) - target) - "+nt") - flag "l" - new (if adding? - (if (string/includes? cur flag) - cur - (str cur flag)) - (string/replace cur flag "")) - line (str ":" nick " MODE " target - " " mode-str - (when (and adding? mode-arg) - (str " " mode-arg)))] - (when (:chan-modes components) - (swap! (:chan-modes components) - assoc target new)) - (when-let [limits (:chan-limits components)] - (if (and adding? mode-arg) - (try - (swap! limits assoc target - (Integer/parseInt mode-arg)) - (catch NumberFormatException _ nil)) - (swap! limits dissoc target))) - (when (and clients channels) - (doseq [mn (get @channels target) - :let [m (get @clients mn)] - :when m] - (deliver-to-client! (:w m) line))) - []) + (let [parsed (when (and adding? mode-arg) + (try + (Integer/parseInt + (string/trim mode-arg)) + (catch NumberFormatException _ + nil))) + valid? (or (not adding?) + (and parsed (pos? parsed)))] + (cond + (and adding? (not mode-arg)) + [(numeric-reply client "461" + "MODE :Not enough parameters")] + (not valid?) + (let [shown (if (and mode-arg + (seq mode-arg)) + mode-arg + "*")] + [(numeric-reply client "696" + target "l" shown + ":Invalid limit value")]) + :else + (let [cur (or (get @(:chan-modes + components) + target) + "+nt") + flag "l" + new (if adding? + (if (string/includes? + cur flag) + cur + (str cur flag)) + (string/replace cur flag "")) + line (str ":" nick " MODE " target + " " mode-str + (when (and adding? + mode-arg) + (str " " mode-arg)))] + (when (:chan-modes components) + (swap! (:chan-modes components) + assoc target new)) + (when-let [limits + (:chan-limits components)] + (if adding? + (swap! limits assoc target parsed) + (swap! limits dissoc target))) + (when (and clients channels) + (doseq [mn (get @channels target) + :let [m (get @clients mn)] + :when m] + (deliver-to-client! + (:w m) line))) + []))) ;; Simple flag modes (n/t/m/i/s/p/c) ;; Iterate every char in the mode-str so @@ -3346,32 +3394,49 @@ ;; +k/-k: channel key (= \k mode-char) - (let [cur (or (get @(:chan-modes components) + (let [valid-key? + (and adding? + mode-arg + (seq mode-arg) + (not (string/includes? mode-arg + " ")))] + (cond + (and adding? (not valid-key?)) + [(numeric-reply client "696" + target "k" "*" + ":Invalid key value")] + :else + (let [cur (or (get + @(:chan-modes components) target) - "+nt") - flag "k" - new (if adding? - (if (string/includes? cur flag) - cur - (str cur flag)) - (string/replace cur flag "")) - line (str ":" nick " MODE " target - " " mode-str - (when (and adding? mode-arg) - (str " " mode-arg)))] - (when (:chan-modes components) - (swap! (:chan-modes components) - assoc target new)) - (when-let [keys (:chan-keys components)] - (if (and adding? mode-arg) - (swap! keys assoc target mode-arg) - (swap! keys dissoc target))) - (when (and clients channels) - (doseq [mn (get @channels target) - :let [m (get @clients mn)] - :when m] - (deliver-to-client! (:w m) line))) - []) + "+nt") + flag "k" + new (if adding? + (if (string/includes? + cur flag) + cur + (str cur flag)) + (string/replace cur flag "")) + line (str ":" nick " MODE " target + " " mode-str + (when (and adding? + mode-arg) + (str " " mode-arg)))] + (when (:chan-modes components) + (swap! (:chan-modes components) + assoc target new)) + (when-let [keys + (:chan-keys components)] + (if adding? + (swap! keys assoc target mode-arg) + (swap! keys dissoc target))) + (when (and clients channels) + (doseq [mn (get @channels target) + :let [m (get @clients mn)] + :when m] + (deliver-to-client! + (:w m) line))) + []))) ;; +b: ban list query (and (= \b mode-char) (nil? mode-arg)) |
