This new command enables communicating with the unix administrative
endpoint (unix+admin) that can be enabled on soju.
The syntax is just that of BouncerServ.
Examples:
sojuctl -config soju.config help
sojuctl -config soju.config user status
This preserves the previous behavior of *not* enabling any unix+admin
endpoint for developement purposes, but enables it in the default
configuration installed by the Makefile.
This adds support for listening on a Unix socket for administrative
connections, that then use a simple protocol for communicating with
the service (BouncerServ) as an administrator with a global context.
The wire format used by the Unix socket is IRC, but without
registration or overheads.
Example session:
>>> BOUNCERSERV
<<< 461 * BOUNCERSERV :Not enough parameters
>>> BOUNCERSERV :n s
<<< :gensou FAIL BOUNCERSERV :this command must be run as a user
>>> BOUNCERSERV :u s
<<< :gensou PRIVMSG * :marisa: 2 networks
<<< :gensou PRIVMSG * :alice: 1 networks
<<< :gensou BOUNCERSERV OK
This enables support for global service contexts, that are run
independently from a user context.
These contexts are considered to be admin. They only have access
to admin commands, because those are relevant in a global context.
This can be used to disable password authentication for a user.
This is useful to prevent a disabled user account from being
auto-enabled when enable-user-on-auth is on.
Add a new flag to disable users. This can be useful to temporarily
deactivate an account without erasing data.
The user goroutine is kept alive for simplicity's sake. Most of the
infrastructure assumes that each user always has a running goroutine.
A disabled user's goroutine is responsible for sending back an error
to downstream connections, and listening for potential events to
re-enable the account.
This implements sql.Scanner and sql/driver.Valuer, so that we can
load/store time values into SQLite with the format we want, and
properly handle NULL (which the go-sqlite3 package doesn't do
correctly).
Some IPv6 hostnames can start with a colon (eg '::1'). This breaks
the IRC line format.
To work around this issue, prefix the hostname with a '0'. This
changes the representation of the IP but not its value.
References: https://todo.sr.ht/~taiite/senpai/109
Co-authored-by: Simon Ser <contact@emersion.fr>
This lists all the users of the instances, along with the number
of networks they have.
Limited to 50 users to avoid flooding with thousands of messages
on large instances.
This avoids issues where a user misstypes the following message:
network update foobar enabled -false
This is obviously a typo of:
network update foobar -enabled false
But we currently accept it without failing, and ignore the trailing
parameter "false".
This fixes this behavior by failing on unexpected arguments.
We only want to store TAGMSG that should be persistent. +typing TAGMSG
should be dropped, but +react messages should be kept.
This introduces a whitelist for TAGMSG labels. We only store TAGMSG
having a least one tag in that whitelist.
This adds a new field to upstreams, members, which is a casemapped map
of upstream users known to the soju. The upstream users known to soju
are: self, any monitored user, and any user with whom we share a
channel.
The information stored for each upstream user corresponds to the info
that can be returned by a WHO/WHOX command.
We build the upstream user information both incrementally, capturing
information contained in JOIN and AWAY messages; and with the bulk user
information contained in WHO replies we receive.
This lets us build a user cache that can then be used to return
synthetic WHO responses to later WHO requests by downstreams.
This is useful because some networks (eg Libera) heavily throttle WHO
commands, and without this cache, any downstream connecting would send 1
WHO command per channel, so possibly more than a dozen WHO commands,
which soju then forwarded to the upstream as WHO commands.
With this cache most WHO commands can be cached and avoid sending
WHO commands to the upstream.
In order to cache the "flags" field, we synthetize the field from user
info we get from incremental messages: away status (H/G) and bot status
(B). This could result in incorrect values for proprietary user fields.
Support for the server-operator status (*) is also not supported.
Of note is that it is difficult to obtain a user "connected server"
field incrementally, so clients that want to maximize their WHO cache
hit ratio can use WHOX to only request fields they need, and in
particular not include the server field flag.
Co-authored-by: delthas <delthas@dille.cc>
Previously, we would clear webpush targets after any MARKREAD.
Consider the following scenario (ignore any typos, this is crafted by
hand):
<<< @time=2020-01-01T00:00:00Z PRIVMSG #foo :hi mark!
<<< @time=2020-01-02T00:00:00Z PRIVMSG #foo :hi again mark!
>>> MARKREAD #foo timestamp=2020-01-01T00:00:00Z
>>> MARKREAD #foo timestamp=2020-01-02T00:00:00Z
The push target was previously cleared on the first MARKREAD, which
means that the second MARKREAD was never broadcast to Firebase, and all
devices would keep the "hi again mark!" notification indefinitely.
This changes the webpush target map so that we store a timestamp of the
last highlight we sent. We only clear the push target when sending a
MARKREAD that is at or after the last message.
The FS message store truncates message times to the second.
This means that a message sent out as 2020-01-01T00:00:00.123Z could be
sent later as part of a CHATHISTORY batch as 2020-01-01T00:00:00.000Z,
which could cause issues in clients.
One such issue is a client sending a MARKREAD for
2020-01-01T00:00:00.000Z, with another client considering the
2020-01-01T00:00:00.123Z message it has as unread.
This fixes the issue by truncating all message times to the second when
using the FS message store.