Commit Graph

169 Commits

Author SHA1 Message Date
Simon Ser
637b989a47 Add downstreamConn.updateCasemapping()
We weren't correctly updating nickCM.
2023-03-01 12:55:10 +01:00
Simon Ser
05a0775658 Silence "Web Push subscription expired" errors
These are expected to happen from time to time, nothing to worry
about.
2023-02-23 13:48:02 +01:00
delthas
1ccc7ce6d2 Add a database store for messages
This adds a new config option, `logs db`, which enables storing chat
logs in the soju database.

Regular store options, CHATHISTORY options, and SEARCH operations are
supported, like the fs logs backend.

Messages are stored in a new table, Message. In order to track the list
of targets we have messages for in an optimized manner, another database
is used: MessageTarget.

All new requests are backend by indexes so should be fast even with
hundreds of thousands of messages.

A contrib script is provided for migrating existing logs fs chat logs to
the database. It can be run with eg:

  go run ./contrib/migrate-logs/ logs/ sqlite3:soju.db

Co-authored-by: Simon Ser <contact@emersion.fr>
2023-02-17 14:13:43 +01:00
delthas
b920facdff service: Return the error rather than printing it
This enables callers to make the difference between a successful
service call and a failed one.
2023-02-06 15:15:09 +01:00
delthas
d17c7d57f2 service: Enable running service commands without users
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.
2023-02-06 15:15:09 +01:00
delthas
4bd600c651 service: Store the admin capability independently the user
We can acquire admin contexts independently of the user.

This is mainly for a future commit that will introduce events
without a user.
2023-02-06 15:15:09 +01:00
Simon Ser
db49bc120f Add enable-user-on-auth config directive 2023-01-26 19:51:35 +01:00
Simon Ser
9df9880301 Add disable-inactive-user config option
This can be used to automatically disable users if they don't
actively use the bouncer for a while.
2023-01-26 19:42:29 +01:00
Simon Ser
57f5ee8d6f Add user downstream interaction time 2023-01-26 19:42:29 +01:00
Simon Ser
d7d9d45b45 Add a flag to disable users
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.
2023-01-26 18:33:55 +01:00
Simon Ser
bbf234d441 user: take context in user.stop 2023-01-26 17:12:58 +01:00
delthas
b29c9ef09a service: add user run
This enables to run commands as other users, like sudo.

This is useful for eg fixing a user networks on their behalf.
2023-01-18 15:12:33 +01:00
Simon Ser
ec2c0685dd Add WHO cache
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>
2022-12-01 15:50:33 +01:00
delthas
ac578823dc Fix clearing webpush targets after any MARKREAD
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.
2022-11-28 17:55:19 +01:00
delthas
897c21dbb4 Truncate message times to the second when using the FS message store
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.
2022-11-28 17:54:13 +01:00
Simon Ser
106d40dcd4 Upgrade to gopkg.in/irc.v4 2022-11-14 12:06:58 +01:00
Simon Ser
8a2f544806 downstream: drop downstreamConn.marshalEntity
This is a no-op.
2022-09-26 16:57:05 +02:00
Simon Ser
eba7aa38d2 downstream: drop downstreamConn.isMultiUpstream
This is now always false.
2022-09-26 16:57:05 +02:00
Simon Ser
65f0b2367e Broadcast Web Push subscriptions in a new goroutine 2022-08-17 16:09:12 +02:00
Simon Ser
af95bc3a4f upstream: regain desired nick when MONITOR is missing
We already have logic to regain our desired nick when the upstream
server supports MONITOR. However some networks (e.g. OFTC, Rizon)
don't support MONITOR. Also try to regain our desired nick in that
case, by periodically sending NICK commands.

Closes: https://todo.sr.ht/~emersion/soju/197
2022-07-14 12:25:47 +02:00
Simon Ser
dc0a847240 Add per-user default nickname
The soju username is immutable. Add a separate nickname setting so
that users can change their nickname for all networks.

References: https://todo.sr.ht/~emersion/soju/110
2022-07-08 18:01:05 +02:00
Simon Ser
14cbd63412 Send MARKREAD push notifications
Allows clients to dismiss notifications when another client marks
the conversation as read.
2022-07-08 16:55:29 +02:00
Simon Ser
7db1be44fa upstream: compare service nick with case-mapping 2022-07-08 14:47:43 +02:00
Simon Ser
f0db261fc0 database: add missing user column to WebPushSubscription table
Some WebPushSubscription entries aren't tried to a network, in
which case the "network" column is NULL. But then all users share
the same row. Oops.

Fortunately network-less subscriptions aren't used for anything
yet, they're just stored. So the impact should be minimal.
2022-06-16 19:33:39 +02:00
Simon Ser
3863b8cb6b Add webpush extension
References: https://github.com/ircv3/ircv3-specifications/pull/471
Co-authored-by: delthas <delthas@dille.cc>
2022-06-14 16:16:12 +02:00
Simon Ser
09f2cf8489 Don't provide name in channel casemapMap Set and ForEach
The name is already provided in the struct.
2022-06-06 10:05:31 +02:00
Simon Ser
657e25b25c Make casemapMap more type-safe
In addition to a type-safe getter, also define type-safe setters
and iterators.

References: https://lists.sr.ht/~emersion/soju-dev/patches/32777
2022-06-06 09:58:39 +02:00
Simon Ser
c8f9728ff6 Drop size arg from newCasemapMap
It's unused.
2022-06-06 09:23:17 +02:00
Simon Ser
620a8789b0 Add msgstore package 2022-05-09 16:25:57 +02:00
Simon Ser
3a7dee8128 Introduce a database package 2022-05-09 15:08:04 +02:00
delthas
5ae1ec5381 Fix network.forEachDownstream exiting on first non-match
This fixes a serious bug where we stop executing forEachDownstream on
the first downstream that does not match the network. Instead we want to
simply continue; it's a basic filter.
2022-04-24 18:52:36 +02:00
Simon Ser
42b8dac44e Add user.notifyBouncerNetworkState
DRY.
2022-04-15 10:41:38 +02:00
Simon Ser
f3932ab500 Drop user.forEachDownstream
It's just a for loop.
2022-04-15 10:32:28 +02:00
Simon Ser
a6636a9b0d Add TODO for upstreamConn.register race 2022-04-15 09:53:30 +02:00
Simon Ser
9fb789ee26 Add timeout for upstream connection registration 2022-04-15 09:49:19 +02:00
Simon Ser
77460dfb97 Split upstream connection handling to separate function
Just a refactoring, no functional change.
2022-04-15 09:37:43 +02:00
delthas
f7a468194d bouncer-networks: Add a read-only error attribute
This is useful for clients to display additional info abotu why a
network is disconnected.
2022-04-11 08:38:31 +02:00
Simon Ser
a45f9c3274 Use SETNAME when updating user-wide realname 2022-03-30 15:11:25 +02:00
Simon Ser
78d9a84a6f Add support for RPL_VISIBLEHOST 2022-03-21 16:09:45 +01:00
delthas
f5b16dc00c Send any welcome error messages to the downstream
Currently, if we fail connecting to a new networking during welcome, we
send no error message to the client, and the connection remains open in
an undefined state.

Given the input:

  NICK nick
  USER user/invalid.xyz s e r
  PASS pass

soju will fail to connect, add a message to its own logs, but will
return no message to the downstream.

This fixes the issue by forwarding the error message if it is an IRC
error message (which it is for connecting to new networks).

We should probably also close the connection after the message is
written, because it leaves the connection in an undefined state. This is
TODO for now because we'd have to wait for the error message to be
written out first, which is non-trivial.
2022-03-21 09:49:37 +01:00
Simon Ser
74fd506fef Use capRegistry for downstreamConn 2022-03-14 19:25:49 +01:00
Simon Ser
dc58a70794 msgstore_fs: fix direct message targets
When fetching messages via draft/chathistory from a conversation
with another user, soju would send the following:

    :sender PRIVMSG sender :hey

instead of

    :sender PRIVMSG recipient :hey

because the file-system message store format doesn't contain the
original PRIVMSG target.

Fix this by doing some guesswork.
2022-02-25 21:05:10 +01:00
delthas
cae248f672 Add support for the wip soju.im/read capability and READ command
READ lets downstream clients share information between each other about
what messages have been read by other downstreams.

Each target/entity has an optional corresponding read receipt, which is
stored as a timestamp.

- When a downstream sends:
  READ #chan timestamp=2020-01-01T01:23:45.000Z
  the read receipt for that target is set to that date
- soju sends READ to downstreams:
  - on JOIN, if the client uses the soju.im/read capability
  - when the read receipt timestamp is set by any downstream

The read receipt date is clamped by the previous receipt date and the
current time.
2022-02-11 19:41:46 +01:00
Simon Ser
e72c896bb4 Add context to upstreamConn.register 2022-02-08 16:38:34 +01:00
Simon Ser
375ac53f74 Add context to upstreamConn.runUntilRegistered 2022-02-08 16:37:31 +01:00
Simon Ser
02dac11308 Forbid empty and flag-looking network names 2022-02-07 21:39:39 +01:00
Simon Ser
49b77d630a Ensure consistent network ordering
Right now there is no consistent ordering in the network list:
no ORDER BY in the DB, and network updates move entries to the end.

Let's always sort by network ID so that users don't see the entries
move around.

I've contemplated sorting by Network.GetName() instead, but:

- Clients have now way to figure out dynamic order changes, e.g.
  when renaming a network.
- Some clients might use ISUPPORT NETWORK when a user hasn't
  explicitly named a network, but soju won't use that for ordering,
  leading to non-alphabetic ordering in the client.

Let's leave it to clients to sort the networks by display name if
they want to.
2022-02-04 15:03:13 +01:00
Simon Ser
0b5da29916 Drop user.forEachNetwork
It's a trivial for loop.
2022-02-04 14:01:27 +01:00
Simon Ser
b6c47a517c Use more descriptive errors when aborting pending commands 2021-12-06 22:33:50 +01:00
Simon Ser
f75ee272a4 Add context to network.storeClientDeliveryReceipts 2021-12-02 23:32:12 +01:00