Compare commits

...

13 commits

Author SHA1 Message Date
Gabriel Simmer faeb46d188
Add Containerfile 2024-07-12 20:28:32 +01:00
Simon Ser bb234c8348 conn: document conn.ReadMessage 2024-07-08 23:16:14 +02:00
Conrad Hoffmann 07502541e4 conn: fix goroutine leak
As is, soju leaks goroutines on client disconnects, because the closure
started as goroutine in newConn never finishes. It gets stuck in the
for loop annotated as "draining the outgoing channel", because the
outgoing channel is in fact never closed.

This commit fixes the issue by calling conn.Close rather than
conn.conn.Close, which closes not only the underlying net.Conn (in
c.conn), but also the channel.
2024-07-08 23:04:52 +02:00
Simon Ser 3667102e72 conn: return net.ErrClosed in conn.Close 2024-07-08 20:37:00 +02:00
Simon Ser d65c1654b8 Stop dereferencing *conn
The *conn pointer returned by newConn is dereferenced in
newDownstreamConn and connectToUpstream (a premature optimization).
As a result, the whole struct is copied and the internal newConn
goroutine works with a different chunk of memory than
downstreamConn and upstreamConn.
2024-07-08 20:35:20 +02:00
Simon Ser a3716dc2d2 downstream: reflect AWAY status in self-WHO reply without upstream 2024-07-08 08:28:50 +02:00
Simon Ser 4682bbef66 downstream: drop TODO about WHOX mask2
This is not part of the IRCv3 spec.
2024-07-08 08:24:34 +02:00
Simon Ser 9a36e6730d contrib/clients: remove note about chat.sr.ht
This document is not a good place for SourceHut-specific
recommendations.
2024-07-04 21:34:23 +02:00
Simon Ser 29bdc1aa45 Migrate to Codeberg 2024-07-04 21:28:11 +02:00
Simon Ser 0ced56a155 doc: use openssl -verify_quiet instead of discarding stderr
stderr prints useful information when openssl fails to connect to
the server. Use -verify_quiet to reduce chatter a bit and still
retain error messages.
2024-07-01 15:13:08 +02:00
Simon Ser d5dd194b01 downstream: fix WHO membership prefix order without server-specific flags
Gregory noticed that my last-minute edit was wrong [1]. Indeed,
when i == -1, that means that Flags only contains 'H'/'G'/'*' and
nothing else. We need to append the membership prefix in that case.

[1]: https://lists.sr.ht/~emersion/soju-dev/%3C20240630213249.13061-2-greg@gpanders.com%3E#%3CD2DP18U4PP40.DBYWGA8WM2KN@gpanders.com%3E

Fixes: ae203388e1 ("Fix channel membership prefixes in cached WHO replies")
Reported-by: Gregory Anders <greg@gpanders.com>
2024-07-01 00:14:06 +02:00
Gregory Anders ae203388e1 Fix channel membership prefixes in cached WHO replies
Channel membership prefixes in WHO replies (RPL_WHOREPLY and
RPL_WHOSPCRPL) were cached in the user's flags, which meant those same
prefixes were returned on future cache hits, even though the flags are
channel specific.

Strip the channel membership prefixes from the user's flags before
adding a user to the cache and add the prefixes back when reading from
the cache (using the membership info from the NAMES reply).
2024-06-30 23:50:50 +02:00
Simon Ser 965ce9cdb9 xirc: fix chunking in GenerateSASL
The SASL response needs to be encoded into base64, then split into
400 byte chunks. We were doing it in reverse order.
2024-06-30 23:42:31 +02:00
37 changed files with 134 additions and 91 deletions

View file

@ -1,2 +0,0 @@
[b4]
send-series-to = ~emersion/soju-dev@lists.sr.ht

View file

@ -4,7 +4,7 @@ packages:
- scdoc - scdoc
- postgresql - postgresql
sources: sources:
- https://git.sr.ht/~emersion/soju - https://codeberg.org/emersion/soju.git
tasks: tasks:
- build: | - build: |
cd soju cd soju

19
Containerfile Normal file
View file

@ -0,0 +1,19 @@
FROM golang:1.21.6-bookworm as build
ENV CGO_ENABLED=0 \
GOOS=linux \
GOFLAGS="-tags=moderncsqlite"
WORKDIR /build
COPY . /build
RUN make soju
FROM scratch
COPY --from=build /build/soju /app/soju
COPY --from=build /build/sojuctl /app/sojuctl
COPY --from=build /build/sojudb /app/sojudb
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
CMD ["/app/soju"]

View file

@ -11,8 +11,8 @@ RUNDIR ?= /run
sharedstatedir := /var/lib sharedstatedir := /var/lib
config_path := $(SYSCONFDIR)/soju/config config_path := $(SYSCONFDIR)/soju/config
admin_socket_path := $(RUNDIR)/soju/admin admin_socket_path := $(RUNDIR)/soju/admin
goldflags := -X 'git.sr.ht/~emersion/soju/config.DefaultPath=$(config_path)' \ goldflags := -X 'codeberg.org/emersion/soju/config.DefaultPath=$(config_path)' \
-X 'git.sr.ht/~emersion/soju/config.DefaultUnixAdminPath=$(admin_socket_path)' -X 'codeberg.org/emersion/soju/config.DefaultUnixAdminPath=$(admin_socket_path)'
goflags := $(GOFLAGS) -ldflags="$(goldflags)" goflags := $(GOFLAGS) -ldflags="$(goldflags)"
commands := soju sojuctl sojudb commands := soju sojuctl sojudb
man_pages := doc/soju.1 doc/sojuctl.1 man_pages := doc/soju.1 doc/sojuctl.1

View file

@ -1,7 +1,5 @@
# [soju] # [soju]
[![builds.sr.ht status](https://builds.sr.ht/~emersion/soju/commits/master.svg)](https://builds.sr.ht/~emersion/soju/commits/master?)
soju is a user-friendly IRC bouncer. soju connects to upstream IRC servers on soju is a user-friendly IRC bouncer. soju connects to upstream IRC servers on
behalf of the user to provide extra functionality. soju supports many features behalf of the user to provide extra functionality. soju supports many features
such as multiple users, numerous [IRCv3] extensions, chat history playback and such as multiple users, numerous [IRCv3] extensions, chat history playback and
@ -35,8 +33,8 @@ build with PAM authentication support, set `GOFLAGS="-tags=pam"`.
## Contributing ## Contributing
Send patches on the [mailing list] or on [GitHub], report bugs on the Send patches on [Codeberg] or on [GitHub], report bugs on the [issue tracker].
[issue tracker]. Discuss in [#soju on Libera Chat][IRC channel]. Discuss in [#soju on Libera Chat][IRC channel].
## License ## License
@ -47,7 +45,7 @@ Copyright (C) 2020 The soju Contributors
[soju]: https://soju.im [soju]: https://soju.im
[Getting started]: doc/getting-started.md [Getting started]: doc/getting-started.md
[Man page]: https://soju.im/doc/soju.1.html [Man page]: https://soju.im/doc/soju.1.html
[mailing list]: https://lists.sr.ht/~emersion/soju-dev [Codeberg]: https://codeberg.org/emersion/soju
[GitHub]: https://github.com/emersion/soju [GitHub]: https://github.com/emersion/soju
[issue tracker]: https://todo.sr.ht/~emersion/soju [issue tracker]: https://todo.sr.ht/~emersion/soju
[IRC channel]: ircs://irc.libera.chat/#soju [IRC channel]: ircs://irc.libera.chat/#soju

View file

@ -4,7 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
type Authenticator interface{} type Authenticator interface{}

View file

@ -4,7 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
type internal struct{} type internal struct{}

View file

@ -10,7 +10,7 @@ import (
"strings" "strings"
"time" "time"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
type oauth2 struct { type oauth2 struct {

View file

@ -8,7 +8,7 @@ import (
"github.com/msteinert/pam/v2" "github.com/msteinert/pam/v2"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
type pamAuth struct{} type pamAuth struct{}

View file

@ -22,12 +22,12 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"git.sr.ht/~emersion/soju" "codeberg.org/emersion/soju"
"git.sr.ht/~emersion/soju/auth" "codeberg.org/emersion/soju/auth"
"git.sr.ht/~emersion/soju/config" "codeberg.org/emersion/soju/config"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/fileupload" "codeberg.org/emersion/soju/fileupload"
"git.sr.ht/~emersion/soju/identd" "codeberg.org/emersion/soju/identd"
) )
// TCP keep-alive interval for downstream TCP connections // TCP keep-alive interval for downstream TCP connections

View file

@ -11,7 +11,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.sr.ht/~emersion/soju/config" "codeberg.org/emersion/soju/config"
) )
const usage = `usage: sojuctl [-config path] <command> const usage = `usage: sojuctl [-config path] <command>

View file

@ -11,8 +11,8 @@ import (
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
"git.sr.ht/~emersion/soju/config" "codeberg.org/emersion/soju/config"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
const usage = `usage: sojudb [-config path] <action> [options...] const usage = `usage: sojudb [-config path] <action> [options...]

View file

@ -3,7 +3,6 @@ package soju
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"strings" "strings"
@ -158,7 +157,7 @@ func newConn(srv *Server, ic ircConn, options *connOptions) *conn {
break break
} }
} }
if err := c.conn.Close(); err != nil && !errors.Is(err, net.ErrClosed) { if err := c.Close(); err != nil && !errors.Is(err, net.ErrClosed) {
c.logger.Printf("failed to close connection: %v", err) c.logger.Printf("failed to close connection: %v", err)
} else { } else {
c.logger.Debugf("connection closed") c.logger.Debugf("connection closed")
@ -185,7 +184,7 @@ func (c *conn) Close() error {
defer c.lock.Unlock() defer c.lock.Unlock()
if c.closed { if c.closed {
return fmt.Errorf("connection already closed") return net.ErrClosed
} }
err := c.conn.Close() err := c.conn.Close()
@ -195,6 +194,10 @@ func (c *conn) Close() error {
return err return err
} }
// Read reads an incoming message. It must be called from a single goroutine
// at a time.
//
// io.EOF is returned when there are no more messages to read.
func (c *conn) ReadMessage() (*irc.Message, error) { func (c *conn) ReadMessage() (*irc.Message, error) {
msg, err := c.conn.ReadMessage() msg, err := c.conn.ReadMessage()
if errors.Is(err, net.ErrClosed) { if errors.Is(err, net.ErrClosed) {

View file

@ -70,12 +70,6 @@ outgoing messages:
/set irc_reconnect_rejoin off /set irc_reconnect_rejoin off
/set net_throttle off /set net_throttle off
Older Hexchat versions (without the [hexchat password length fix]) do not
support long passwords, which include personal access tokens from sourcehut with
limited scope. To work around this issue for sourcehut, [generate a sourcehut
personal access token] without limiting the grant (by not selecting any
permissions).
# [irssi] # [irssi]
To connect irssi to a network, for example Libera Chat: To connect irssi to a network, for example Libera Chat:
@ -120,6 +114,5 @@ See `/help cap` for more information.
[read_marker.py]: https://weechat.org/scripts/source/read_marker.py.html/ [read_marker.py]: https://weechat.org/scripts/source/read_marker.py.html/
[Hexchat]: https://hexchat.github.io/ [Hexchat]: https://hexchat.github.io/
[hexchat password length fix]: https://github.com/hexchat/hexchat/commit/778047bc65e529804c3342ee0f3a8d5d7550fde5 [hexchat password length fix]: https://github.com/hexchat/hexchat/commit/778047bc65e529804c3342ee0f3a8d5d7550fde5
[generate a sourcehut personal access token]: https://meta.sr.ht/oauth2/personal-token
[Emacs]: https://www.gnu.org/software/emacs/ [Emacs]: https://www.gnu.org/software/emacs/
[irssi]: https://irssi.org/ [irssi]: https://irssi.org/

View file

@ -7,7 +7,7 @@ import (
"log" "log"
"strings" "strings"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
const usage = `usage: migrate-db <source database> <destination database> const usage = `usage: migrate-db <source database> <destination database>

View file

@ -14,9 +14,9 @@ import (
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/msgstore" "codeberg.org/emersion/soju/msgstore"
"git.sr.ht/~emersion/soju/msgstore/znclog" "codeberg.org/emersion/soju/msgstore/znclog"
) )
const usage = `usage: migrate-logs <source logs> <destination database> const usage = `usage: migrate-logs <source logs> <destination database>

View file

@ -12,8 +12,8 @@ import (
"strings" "strings"
"unicode" "unicode"
"git.sr.ht/~emersion/soju/config" "codeberg.org/emersion/soju/config"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
const usage = `usage: znc-import [options...] <znc config path> const usage = `usage: znc-import [options...] <znc config path>

View file

@ -10,7 +10,7 @@ import (
"strings" "strings"
"time" "time"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
promcollectors "github.com/prometheus/client_golang/prometheus/collectors" promcollectors "github.com/prometheus/client_golang/prometheus/collectors"

View file

@ -13,7 +13,7 @@ import (
"time" "time"
"unicode" "unicode"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
promcollectors "github.com/prometheus/client_golang/prometheus/collectors" promcollectors "github.com/prometheus/client_golang/prometheus/collectors"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"

View file

@ -296,7 +296,7 @@ character.
of an IRC server: of an IRC server:
``` ```
openssl s_client -connect irc.example.org:6697 </dev/null 2>/dev/null | openssl x509 -fingerprint -sha512 -noout -in /dev/stdin openssl s_client -connect irc.example.org:6697 -verify_quiet </dev/null | openssl x509 -fingerprint -sha512 -noout -in /dev/stdin
``` ```
*-nick* <nickname> *-nick* <nickname>
@ -556,7 +556,7 @@ character.
Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other
open-source contributors. For more information about soju development, see open-source contributors. For more information about soju development, see
<https://sr.ht/~emersion/soju>. <https://soju.im>.
# SEE ALSO # SEE ALSO

View file

@ -29,7 +29,7 @@ file. sojuctl needs to be run with write permissions on the soju admin socket.
Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other
open-source contributors. For more information about soju development, see open-source contributors. For more information about soju development, see
<https://sr.ht/~emersion/soju>. <https://soju.im>.
# SEE ALSO # SEE ALSO

View file

@ -17,10 +17,10 @@ import (
"github.com/emersion/go-sasl" "github.com/emersion/go-sasl"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/auth" "codeberg.org/emersion/soju/auth"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/msgstore" "codeberg.org/emersion/soju/msgstore"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
) )
type ircError struct { type ircError struct {
@ -328,7 +328,7 @@ func serverSASLMechanisms(srv *Server) []string {
} }
type downstreamConn struct { type downstreamConn struct {
conn *conn
id uint64 id uint64
@ -363,7 +363,7 @@ func newDownstreamConn(srv *Server, ic ircConn, id uint64) *downstreamConn {
options := connOptions{Logger: logger} options := connOptions{Logger: logger}
cm := xirc.CaseMappingASCII cm := xirc.CaseMappingASCII
dc := &downstreamConn{ dc := &downstreamConn{
conn: *newConn(srv, ic, &options), conn: newConn(srv, ic, &options),
id: id, id: id,
nick: "*", nick: "*",
nickCM: "*", nickCM: "*",
@ -2142,7 +2142,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
// Clients will use the first mask to match RPL_ENDOFWHO // Clients will use the first mask to match RPL_ENDOFWHO
endOfWhoToken := msg.Params[0] endOfWhoToken := msg.Params[0]
// TODO: add support for WHOX mask2
mask := msg.Params[0] mask := msg.Params[0]
var options string var options string
if len(msg.Params) > 1 { if len(msg.Params) > 1 {
@ -2154,8 +2153,10 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
// TODO: support mixed bouncer/upstream WHO queries // TODO: support mixed bouncer/upstream WHO queries
maskCM := dc.casemap(mask) maskCM := dc.casemap(mask)
if dc.network == nil && maskCM == dc.nickCM { if dc.network == nil && maskCM == dc.nickCM {
// TODO: support AWAY (H/G) in self WHO reply
flags := "H" flags := "H"
if dc.away != nil {
flags = "G"
}
if dc.user.Admin { if dc.user.Admin {
flags += "*" flags += "*"
} }
@ -2230,6 +2231,23 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
} }
if uc.isChannel(mask) { if uc.isChannel(mask) {
info.Channel = mask info.Channel = mask
// Set channel membership prefixes from cached NAMES reply
ch := uc.channels.Get(info.Channel)
memberships := ch.Members.Get(info.Nickname)
prefixes := formatMemberPrefix(*memberships, dc)
// Channel membership prefixes are listed after away status ('G'/'H')
// and optional server operator indicator ('*')
i := strings.IndexFunc(info.Flags, func(f rune) bool {
return f != 'G' && f != 'H' && f != '*'
})
if i == -1 {
info.Flags += prefixes
} else {
info.Flags = info.Flags[:i] + prefixes + info.Flags[i:]
}
} }
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info)) dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
} }

View file

@ -12,8 +12,8 @@ import (
"strings" "strings"
"time" "time"
"git.sr.ht/~emersion/soju/auth" "codeberg.org/emersion/soju/auth"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
const maxSize = 50 * 1024 * 1024 // 50 MiB const maxSize = 50 * 1024 * 1024 // 50 MiB

2
go.mod
View file

@ -1,4 +1,4 @@
module git.sr.ht/~emersion/soju module codeberg.org/emersion/soju
go 1.19 go 1.19

15
irc.go
View file

@ -9,7 +9,7 @@ import (
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
) )
// TODO: generalize and move helpers to the xirc package // TODO: generalize and move helpers to the xirc package
@ -194,6 +194,19 @@ func formatMemberPrefix(ms xirc.MembershipSet, dc *downstreamConn) string {
return string(prefixes) return string(prefixes)
} }
// Remove channel membership prefixes from flags
func stripMemberPrefixes(flags string, uc *upstreamConn) string {
return strings.Map(func(r rune) rune {
for _, v := range uc.availableMemberships {
if byte(r) == v.Prefix {
return -1
}
}
return r
}, flags)
}
func parseMessageParams(msg *irc.Message, out ...*string) error { func parseMessageParams(msg *irc.Message, out ...*string) error {
if len(msg.Params) < len(out) { if len(msg.Params) < len(out) {
return newNeedMoreParamsError(msg.Command) return newNeedMoreParamsError(msg.Command)

View file

@ -4,7 +4,7 @@ import (
"context" "context"
"time" "time"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~sircmpwn/go-bare" "git.sr.ht/~sircmpwn/go-bare"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
) )

View file

@ -14,9 +14,9 @@ import (
"git.sr.ht/~sircmpwn/go-bare" "git.sr.ht/~sircmpwn/go-bare"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/msgstore/znclog" "codeberg.org/emersion/soju/msgstore/znclog"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
) )
const ( const (

View file

@ -8,7 +8,7 @@ import (
"git.sr.ht/~sircmpwn/go-bare" "git.sr.ht/~sircmpwn/go-bare"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
const messageRingBufferCap = 4096 const messageRingBufferCap = 4096

View file

@ -10,7 +10,7 @@ import (
"git.sr.ht/~sircmpwn/go-bare" "git.sr.ht/~sircmpwn/go-bare"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
type LoadMessageOptions struct { type LoadMessageOptions struct {

View file

@ -7,8 +7,8 @@ import (
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
) )
var timestampPrefixLen = len("[01:02:03] ") var timestampPrefixLen = len("[01:02:03] ")

View file

@ -7,7 +7,7 @@ import (
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
) )
func MarshalLine(msg *irc.Message, t time.Time) string { func MarshalLine(msg *irc.Message, t time.Time) string {

View file

@ -20,11 +20,11 @@ import (
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"nhooyr.io/websocket" "nhooyr.io/websocket"
"git.sr.ht/~emersion/soju/auth" "codeberg.org/emersion/soju/auth"
"git.sr.ht/~emersion/soju/config" "codeberg.org/emersion/soju/config"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/fileupload" "codeberg.org/emersion/soju/fileupload"
"git.sr.ht/~emersion/soju/identd" "codeberg.org/emersion/soju/identd"
) )
var ( var (

View file

@ -10,8 +10,8 @@ import (
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
) )
var testServerPrefix = &irc.Prefix{Name: "soju-test-server"} var testServerPrefix = &irc.Prefix{Name: "soju-test-server"}

View file

@ -17,7 +17,7 @@ import (
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
) )
const serviceNick = "BouncerServ" const serviceNick = "BouncerServ"

View file

@ -21,8 +21,8 @@ import (
"github.com/emersion/go-sasl" "github.com/emersion/go-sasl"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
) )
// permanentUpstreamCaps is the static list of upstream capabilities always // permanentUpstreamCaps is the static list of upstream capabilities always
@ -191,7 +191,7 @@ type pendingUpstreamCommand struct {
} }
type upstreamConn struct { type upstreamConn struct {
conn *conn
network *network network *network
user *user user *user
@ -353,7 +353,7 @@ func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, er
cm := stdCaseMapping cm := stdCaseMapping
uc := &upstreamConn{ uc := &upstreamConn{
conn: *newConn(network.user.srv, newNetIRCConn(netConn), &options), conn: newConn(network.user.srv, newNetIRCConn(netConn), &options),
network: network, network: network,
user: network.user, user: network.user,
channels: xirc.NewCaseMappingMap[*upstreamChannel](cm), channels: xirc.NewCaseMappingMap[*upstreamChannel](cm),
@ -1541,7 +1541,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
Hostname: host, Hostname: host,
Server: server, Server: server,
Nickname: nick, Nickname: nick,
Flags: flags, Flags: stripMemberPrefixes(flags, uc),
Realname: realname, Realname: realname,
}) })
} }
@ -1564,13 +1564,14 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
if err != nil { if err != nil {
return err return err
} }
if uc.shouldCacheUserInfo(info.Nickname) { if uc.shouldCacheUserInfo(info.Nickname) {
uc.cacheUserInfo(info.Nickname, &upstreamUser{ uc.cacheUserInfo(info.Nickname, &upstreamUser{
Nickname: info.Nickname, Nickname: info.Nickname,
Username: info.Username, Username: info.Username,
Hostname: info.Hostname, Hostname: info.Hostname,
Server: info.Server, Server: info.Server,
Flags: info.Flags, Flags: stripMemberPrefixes(info.Flags, uc),
Account: info.Account, Account: info.Account,
Realname: info.Realname, Realname: info.Realname,
}) })

View file

@ -14,13 +14,13 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"git.sr.ht/~emersion/soju/xirc" "codeberg.org/emersion/soju/xirc"
"github.com/SherClockHolmes/webpush-go" "github.com/SherClockHolmes/webpush-go"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"git.sr.ht/~emersion/soju/database" "codeberg.org/emersion/soju/database"
"git.sr.ht/~emersion/soju/msgstore" "codeberg.org/emersion/soju/msgstore"
) )
type UserUpdateFunc func(record *database.User) error type UserUpdateFunc func(record *database.User) error

View file

@ -206,25 +206,25 @@ func GenerateNamesReply(channel string, status ChannelStatus, members []string)
} }
func GenerateSASL(resp []byte) []*irc.Message { func GenerateSASL(resp []byte) []*irc.Message {
encoded := base64.StdEncoding.EncodeToString(resp)
// <= instead of < because we need to send a final empty response if // <= instead of < because we need to send a final empty response if
// the last chunk is exactly 400 bytes long // the last chunk is exactly 400 bytes long
var msgs []*irc.Message var msgs []*irc.Message
for i := 0; i <= len(resp); i += MaxSASLLength { for i := 0; i <= len(encoded); i += MaxSASLLength {
j := i + MaxSASLLength j := i + MaxSASLLength
if j > len(resp) { if j > len(encoded) {
j = len(resp) j = len(encoded)
} }
chunk := resp[i:j] chunk := encoded[i:j]
if chunk == "" {
var respStr = "+" chunk = "+"
if len(chunk) != 0 {
respStr = base64.StdEncoding.EncodeToString(chunk)
} }
msgs = append(msgs, &irc.Message{ msgs = append(msgs, &irc.Message{
Command: "AUTHENTICATE", Command: "AUTHENTICATE",
Params: []string{respStr}, Params: []string{chunk},
}) })
} }
return msgs return msgs