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 13, 2024
1 parent 50e3ac0 commit a9b8a8a
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 22 deletions.
62 changes: 40 additions & 22 deletions lib/irc/handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,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 @@ -301,6 +305,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 @@ -341,30 +361,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 @@ -789,11 +790,28 @@ 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)
|> Enum.join(" ")

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
20 changes: 20 additions & 0 deletions test/irc/handler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,26 @@ defmodule M51.IrcConn.HandlerTest do
Logger.add_backend(:console)
end

test "post-registration CAP LS", %{handler: handler} do
do_connection_registration(handler)

send(handler, cmd("CAP LS 302"))
assert_line(@cap_ls_302)

send(handler, cmd("CAP LS"))
assert_line(@cap_ls)
end

test "post-registration CAP LIST", %{handler: handler} do
caps_requested = ["draft/multiline", "extended-join", "message-tags", "server-time"]
caps_expected = Enum.join(["batch", "labeled-response", "sasl"] ++ caps_requested, " ")

do_connection_registration(handler, caps_requested)

send(handler, cmd("CAP LIST"))
assert_line(":server. CAP * LIST :" <> caps_expected <> "\r\n")
end

test "labeled response", %{handler: handler} do
do_connection_registration(handler)

Expand Down

0 comments on commit a9b8a8a

Please sign in to comment.