Skip to content

Commit

Permalink
Send all enabled capabilities on CAP LIST
Browse files Browse the repository at this point in the history
Notably, this fixes an issue with weechat (and possibly some other
clients) where if you ever run /cap or /cap list after connection
registration, the client will see the "CAP * LIST sasl" reply as
downgrading the connection to drop all capabilities except sasl.

This also enables the use of CAP LS after connection registration,
although without post-hoc CAP REQ support this is of limited use.
  • Loading branch information
ToxicFrog committed Feb 12, 2024
1 parent bac0f5b commit 800afa0
Showing 1 changed file with 39 additions and 22 deletions.
61 changes: 39 additions & 22 deletions lib/irc/handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ defmodule M51.IrcConn.Handler do

@capabilities_ls Map.merge(@capabilities, @informative_capabilities)

@capability_names @capabilities
|> Enum.map(fn {name, {atom, _}} -> {atom, name} end)
|> Map.new()

@valid_batch_types ["draft/multiline"]

@doc """
Expand Down Expand Up @@ -300,6 +304,22 @@ defmodule M51.IrcConn.Handler do
end
end

defp cap_ls(is_302, send) do
caps = @capabilities_ls
|> Map.to_list()
|> Enum.sort_by(fn {k, _v} -> k end)
|> Enum.map(fn {k, {_, v}} ->
cond do
is_nil(v) -> k
!is_302 -> k
true -> k <> "=" <> v
end
end)
|> Enum.join(" ")

send.(%M51.Irc.Command{source: "server.", command: "CAP", params: ["*", "LS", caps]})
end

# Handles a connection registration command, ie. only NICK/USER/CAP/AUTHENTICATE.
# Returns nil, {:nick, new_nick}, {:user, new_gecos}, {:authenticate, user_id},
# :got_cap_ls, or :got_cap_end.
Expand Down Expand Up @@ -340,30 +360,11 @@ defmodule M51.IrcConn.Handler do
nil

{"CAP", ["LS", "302"]} ->
caps =
@capabilities_ls
|> Map.to_list()
|> Enum.sort_by(fn {k, _v} -> k end)
|> Enum.map(fn {k, {_, v}} ->
case v do
nil -> k
_ -> k <> "=" <> v
end
end)
|> Enum.join(" ")

send.(%M51.Irc.Command{source: "server.", command: "CAP", params: ["*", "LS", caps]})
cap_ls(true, send)
:got_cap_ls

{"CAP", ["LS" | _]} ->
caps =
@capabilities_ls
|> Map.to_list()
|> Enum.sort_by(fn {k, {_, _v}} -> k end)
|> Enum.map(fn {k, _v} -> k end)
|> Enum.join(" ")

send.(%M51.Irc.Command{source: "server.", command: "CAP", params: ["*", "LS", caps]})
cap_ls(false, send)
:got_cap_ls

{"CAP", ["LIST" | _]} ->
Expand Down Expand Up @@ -788,11 +789,27 @@ defmodule M51.IrcConn.Handler do
{"USER", _} ->
nil

{"CAP", ["LS", "302"]} ->
cap_ls(true, send)

{"CAP", ["LS" | _]} ->
cap_ls(false, send)

{"CAP", ["LIST" | _]} ->
send.(%M51.Irc.Command{source: "server.", command: "CAP", params: ["*", "LIST", "sasl"]})
caps =
M51.IrcConn.State.capabilities(state)
|> Enum.map(fn cap -> @capability_names[cap] end)
|> Enum.filter(fn cap -> !is_nil(cap) end)

send.(%M51.Irc.Command{
source: "server.",
command: "CAP",
params: ["*", "LIST"] ++ caps
})

{"CAP", [subcommand | _]} ->
# ERR_INVALIDCAPCMD
# TODO: support CAP REQ to turn caps on and off post-registration.
send_numeric.("410", [subcommand, "Invalid CAP subcommand"])

{"CAP", []} ->
Expand Down

0 comments on commit 800afa0

Please sign in to comment.