Compare commits

...

10 commits

Author SHA1 Message Date
Gabriel Simmer 9bd3bebd19
Add Containerfile 2024-06-30 13:04:59 +01:00
Krystian Chachuła 0c86b96e86 Set IRC, WS and HTTP Unix sockets' mode to 775
It was 755 before, so a reverse proxy running as a different user didn't
have permission to connect.
2024-06-28 22:12:36 +02:00
Simon Ser 9e0d0b0ff4 doc: document username argument for "user status" 2024-06-09 22:36:23 +02:00
delthas 75a58cc2cb service: user status: accept username filter
This is useful for getting information about a particular user,
especially when the server has more than the max amount of users
returned by the command.
2024-06-09 20:43:33 +02:00
Simon Ser 3a4e54bc45 Upgrade dependencies 2024-05-22 17:04:46 +02:00
Simon Ser 6d0f7330e3 Remove RLIMIT_NOFILE bump
Starting with Go 1.19 [0] the file limit is increased by default.

[0]: 8427429c59
2024-05-09 09:02:16 +02:00
jacob1 0ecc9cd042 Include prefix in CAP messages again
Buggy clients like hexchat can't parse CAP otherwise, making it unable to connect at all. Prefix on CAP commands must be pretty ubiquitous if a major issue like that was never caught
2024-05-07 16:15:54 +02:00
Simon Ser c36140fb2a Drop source prefix from most non-numeric server messages
Clients are supposed to handle these just fine without a source
prefix.
2024-04-30 14:39:19 +02:00
Simon Ser 6b13ca5f34 Drop unnecessary prefixes for numerics
Previous commit populates the source prefix automatically for all
numerics. Drop the now-unnecessary explicit source prefix.
2024-04-30 14:38:20 +02:00
Simon Ser 51a12983d8 downstream: ensure numerics always carry a source prefix
From [1]:

> Distinct from a normal message, a numeric reply MUST contain a
> <source> and use a three-digit numeric as the command.

[1]: https://modern.ircdocs.horse/#numeric-replies
2024-04-30 14:23:49 +02:00
14 changed files with 116 additions and 178 deletions

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

@ -134,10 +134,6 @@ func main() {
cfg.Listen = []string{":6697"}
}
if err := bumpOpenedFileLimit(); err != nil {
log.Printf("failed to bump max number of opened files: %v", err)
}
db, err := database.Open(cfg.DB.Driver, cfg.DB.Source)
if err != nil {
log.Fatalf("failed to open database: %v", err)
@ -227,6 +223,9 @@ func main() {
log.Fatalf("failed to start listener on %q: %v", listen, err)
}
ln = proxyProtoListener(ln, srv)
if err := os.Chmod(u.Path, 0775); err != nil {
log.Printf("failed to chmod Unix IRC socket: %v", err)
}
go func() {
if err := srv.Serve(ln, srv.Handle); err != nil {
log.Printf("serving %q: %v", listen, err)
@ -280,6 +279,9 @@ func main() {
if err != nil {
log.Fatalf("failed to start listener on %q: %v", listen, err)
}
if err := os.Chmod(u.Path, 0775); err != nil {
log.Printf("failed to chmod Unix WS socket: %v", err)
}
go func() {
if err := http.Serve(ln, srv); err != nil {
log.Fatalf("serving %q: %v", listen, err)
@ -381,6 +383,9 @@ func main() {
if err != nil {
log.Fatalf("failed to start listener on %q: %v", listen, err)
}
if err := os.Chmod(u.Path, 0775); err != nil {
log.Printf("failed to chmod Unix HTTP socket: %v", err)
}
go func() {
if err := http.Serve(ln, httpMux); err != nil {
log.Fatalf("serving %q: %v", listen, err)

View file

@ -1,20 +0,0 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
package main
import (
"fmt"
"syscall"
)
func bumpOpenedFileLimit() error {
var rlimit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
return fmt.Errorf("failed to get RLIMIT_NOFILE: %v", err)
}
rlimit.Cur = rlimit.Max
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
return fmt.Errorf("failed to set RLIMIT_NOFILE: %v", err)
}
return nil
}

View file

@ -1,7 +0,0 @@
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris
package main
func bumpOpenedFileLimit() error {
return nil
}

View file

@ -483,9 +483,11 @@ character.
*-network* <name>
Select a network. By default, the current network is selected, if any.
*user status*
*user status* [username]
Show a list of users on this server. Only admins can query this information.
If _username_ is specified, statistics are only displayed for this user.
*user create* -username <username> -password <password> [options...]
Create a new soju user. Only admin users can create new accounts.
The _-username_ and _-password_ flags are mandatory.

View file

@ -530,6 +530,12 @@ func (dc *downstreamConn) SendMessage(ctx context.Context, msg *irc.Message) {
msg = &msgCopy
msg.Prefix = nil
}
if msg.Prefix == nil && isNumeric(msg.Command) {
// Numerics must always carry a source
msgCopy := *msg
msg = &msgCopy
msg.Prefix = dc.srv.prefix()
}
dc.srv.metrics.downstreamOutMessagesTotal.Inc()
dc.conn.SendMessage(ctx, msg)
@ -542,7 +548,6 @@ func (dc *downstreamConn) SendBatch(ctx context.Context, typ string, params []st
if dc.caps.IsEnabled("batch") {
dc.SendMessage(ctx, &irc.Message{
Tags: tags,
Prefix: dc.srv.prefix(),
Command: "BATCH",
Params: append([]string{"+" + ref, typ}, params...),
})
@ -552,7 +557,6 @@ func (dc *downstreamConn) SendBatch(ctx context.Context, typ string, params []st
if dc.caps.IsEnabled("batch") {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "BATCH",
Params: []string{"-" + ref},
})
@ -697,7 +701,6 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
if err != nil {
dc.logger.Printf("SASL %v authentication error for nick %q: %v", credentials.mechanism, dc.registration.nick, err)
dc.endSASL(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, authErrorReason(err)},
})
@ -753,7 +756,7 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
dc.away = nil
}
dc.SendMessage(ctx, generateAwayReply(dc.away != nil, dc.srv.prefix()))
dc.SendMessage(ctx, generateAwayReply(dc.away != nil))
default:
dc.logger.Debugf("unhandled message: %v", msg)
return newUnknownCommandError(msg.Command)
@ -905,21 +908,18 @@ func (dc *downstreamConn) handleAuthenticate(ctx context.Context, msg *irc.Messa
if !dc.caps.IsEnabled("sasl") {
return nil, ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
}}
}
if len(msg.Params) == 0 {
return nil, ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Missing AUTHENTICATE argument"},
}}
}
if msg.Params[0] == "*" {
return nil, ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLABORTED,
Params: []string{dc.nick, "SASL authentication aborted"},
}}
@ -952,7 +952,6 @@ func (dc *downstreamConn) handleAuthenticate(ctx context.Context, msg *irc.Messa
})
default:
return nil, ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Unsupported SASL mechanism"},
}}
@ -967,7 +966,6 @@ func (dc *downstreamConn) handleAuthenticate(ctx context.Context, msg *irc.Messa
if dc.sasl.pendingResp.Len()+len(chunk) > 10*1024 {
return nil, ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Response too long"},
}}
@ -982,7 +980,6 @@ func (dc *downstreamConn) handleAuthenticate(ctx context.Context, msg *irc.Messa
resp, err = base64.StdEncoding.DecodeString(dc.sasl.pendingResp.String())
if err != nil {
return nil, ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Invalid base64-encoded response"},
}}
@ -1004,7 +1001,6 @@ func (dc *downstreamConn) handleAuthenticate(ctx context.Context, msg *irc.Messa
// TODO: multi-line messages
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "AUTHENTICATE",
Params: []string{challengeStr},
})
@ -1023,7 +1019,6 @@ func (dc *downstreamConn) endSASL(ctx context.Context, msg *irc.Message) {
dc.SendMessage(ctx, msg)
} else {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_SASLSUCCESS,
Params: []string{dc.nick, "SASL authentication successful"},
})
@ -1204,13 +1199,11 @@ func (dc *downstreamConn) updateAccount(ctx context.Context) {
if account != "" {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_LOGGEDIN,
Params: []string{dc.nick, dc.prefix().String(), account, "You are logged in as " + account},
})
} else {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_LOGGEDOUT,
Params: []string{dc.nick, dc.prefix().String(), "You are logged out"},
})
@ -1281,7 +1274,6 @@ func (dc *downstreamConn) register(ctx context.Context) error {
if dc.sasl != nil {
dc.endSASL(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLABORTED,
Params: []string{dc.nick, "SASL authentication aborted"},
})
@ -1499,33 +1491,28 @@ func (dc *downstreamConn) welcome(ctx context.Context, user *user) error {
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_WELCOME,
Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_YOURHOST,
Params: []string{dc.nick, "Your host is " + dc.srv.Config().Hostname},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_MYINFO,
Params: []string{dc.nick, dc.srv.Config().Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
})
for _, msg := range xirc.GenerateIsupport(dc.srv.prefix(), isupport) {
for _, msg := range xirc.GenerateIsupport(isupport) {
dc.SendMessage(ctx, msg)
}
if uc := dc.upstream(); uc != nil {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_UMODEIS,
Params: []string{dc.nick, "+" + string(uc.modes)},
})
}
if dc.network == nil && dc.user.Admin {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_UMODEIS,
Params: []string{dc.nick, "+o"},
})
@ -1537,7 +1524,7 @@ func (dc *downstreamConn) welcome(ctx context.Context, user *user) error {
dc.updateCasemapping()
if motd := dc.user.srv.Config().MOTD; motd != "" && dc.network == nil {
for _, msg := range xirc.GenerateMOTD(dc.srv.prefix(), motd) {
for _, msg := range xirc.GenerateMOTD(motd) {
dc.SendMessage(ctx, msg)
}
} else {
@ -1546,7 +1533,6 @@ func (dc *downstreamConn) welcome(ctx context.Context, user *user) error {
motdHint = "Use /motd to read the message of the day"
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_NOMOTD,
Params: []string{dc.nick, motdHint},
})
@ -1559,7 +1545,6 @@ func (dc *downstreamConn) welcome(ctx context.Context, user *user) error {
attrs := getNetworkAttrs(network)
dc.SendMessage(ctx, &irc.Message{
Tags: irc.Tags{"batch": batchRef},
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"NETWORK", idStr, attrs.String()},
})
@ -1903,7 +1888,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if name == "" || strings.ContainsAny(name, illegalChanChars) {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_BADCHANMASK,
Params: []string{name, "Invalid channel name"},
})
@ -1911,7 +1895,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
if !uc.isChannel(name) {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_NOSUCHCHANNEL,
Params: []string{name, "Not a channel name"},
})
@ -2032,7 +2015,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_UMODEIS,
Params: []string{dc.nick, "+" + userMode},
})
@ -2078,13 +2060,11 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
params = append(params, modeParams...)
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_CHANNELMODEIS,
Params: params,
})
if ch.creationTime != "" {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: xirc.RPL_CREATIONTIME,
Params: []string{dc.nick, name, ch.creationTime},
})
@ -2131,7 +2111,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if len(msg.Params) == 0 {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFNAMES,
Params: []string{dc.nick, "*", "End of /NAMES list"},
})
@ -2154,7 +2133,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
case "WHO":
if len(msg.Params) == 0 {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{"*", "*", "End of /WHO list"},
})
@ -2191,9 +2169,8 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
Account: dc.user.Username,
Realname: dc.realname,
}
dc.SendMessage(ctx, xirc.GenerateWHOXReply(dc.srv.prefix(), fields, &info))
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{"*", endOfWhoToken, "End of /WHO list"},
})
@ -2218,9 +2195,8 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
Account: serviceNick,
Realname: serviceRealname,
}
dc.SendMessage(ctx, xirc.GenerateWHOXReply(dc.srv.prefix(), fields, &info))
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{"*", endOfWhoToken, "End of /WHO list"},
})
@ -2228,7 +2204,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
if dc.network == nil {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{"*", endOfWhoToken, "End of /WHO list"},
})
@ -2256,10 +2231,9 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if uc.isChannel(mask) {
info.Channel = mask
}
dc.SendMessage(ctx, xirc.GenerateWHOXReply(dc.srv.prefix(), fields, &info))
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{"*", endOfWhoToken, "End of /WHO list"},
})
@ -2288,29 +2262,24 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if dc.network == nil && dc.casemap(mask) == dc.nickCM {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_WHOISUSER,
Params: []string{dc.nick, dc.nick, dc.user.Username, dc.hostname, "*", dc.realname},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_WHOISSERVER,
Params: []string{dc.nick, dc.nick, dc.srv.Config().Hostname, "soju"},
})
if dc.user.Admin {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_WHOISOPERATOR,
Params: []string{dc.nick, dc.nick, "is a bouncer administrator"},
})
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: xirc.RPL_WHOISACCOUNT,
Params: []string{dc.nick, dc.nick, dc.user.Username, "is logged in as"},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHOIS,
Params: []string{dc.nick, dc.nick, "End of /WHOIS list"},
})
@ -2318,32 +2287,26 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
if dc.casemap(mask) == serviceNickCM {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_WHOISUSER,
Params: []string{dc.nick, serviceNick, servicePrefix.User, servicePrefix.Host, "*", serviceRealname},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_WHOISSERVER,
Params: []string{dc.nick, serviceNick, dc.srv.Config().Hostname, "soju"},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_WHOISOPERATOR,
Params: []string{dc.nick, serviceNick, "is the bouncer service"},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: xirc.RPL_WHOISACCOUNT,
Params: []string{dc.nick, serviceNick, serviceNick, "is logged in as"},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: xirc.RPL_WHOISBOT,
Params: []string{dc.nick, serviceNick, "is a bot"},
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHOIS,
Params: []string{dc.nick, serviceNick, "End of /WHOIS list"},
})
@ -2381,7 +2344,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
// hostname, broadcast the message to all bouncer users.
if !dc.user.Admin {
return ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_BADMASK,
Params: []string{dc.nick, name, "Permission denied to broadcast message to all bouncer users"},
}}
@ -2518,7 +2480,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if uc.saslClient != nil {
dc.endSASL(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Another authentication attempt is already in progress"},
})
@ -2545,7 +2506,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if err != nil {
dc.logger.Printf("failed to clear SASL credentials")
dc.endSASL(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Internal server error"},
})
@ -2555,7 +2515,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
dc.endSASL(ctx, nil)
default:
dc.endSASL(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Unsupported SASL authentication mechanism"},
})
@ -2584,7 +2543,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
dc.away = nil
}
dc.SendMessage(ctx, generateAwayReply(dc.away != nil, dc.srv.prefix()))
dc.SendMessage(ctx, generateAwayReply(dc.away != nil))
uc := dc.upstream()
if uc != nil {
@ -2636,7 +2595,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
// Hard limit, just to avoid having downstreams fill our map
if dc.monitored.Len() >= 1000 {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_MONLISTFULL,
Params: []string{dc.nick, "1000", target, "Bouncer monitor list is full"},
})
@ -2648,7 +2606,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if uc.network.casemap(target) == serviceNickCM {
// BouncerServ is never tired
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_MONONLINE,
Params: []string{dc.nick, target},
})
@ -2662,7 +2619,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: cmd,
Params: []string{dc.nick, target},
})
@ -2679,13 +2635,11 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
// TODO: be less lazy and pack the list
dc.monitored.ForEach(func(name string, _ struct{}) {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_MONLIST,
Params: []string{dc.nick, name},
})
})
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFMONLIST,
Params: []string{dc.nick, "End of MONITOR list"},
})
@ -2702,7 +2656,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: cmd,
Params: []string{dc.nick, target},
})
@ -2832,7 +2785,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
dc.SendMessage(ctx, &irc.Message{
Tags: irc.Tags{"batch": batchRef},
Prefix: dc.srv.prefix(),
Command: "CHATHISTORY",
Params: []string{"TARGETS", target.Name, xirc.FormatServerTime(target.LatestMessage)},
})
@ -3064,7 +3016,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
attrs := getNetworkAttrs(network)
dc.SendMessage(ctx, &irc.Message{
Tags: irc.Tags{"batch": batchRef},
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"NETWORK", idStr, attrs.String()},
})
@ -3099,7 +3050,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"ADDNETWORK", fmt.Sprintf("%v", network.ID)},
})
@ -3143,7 +3093,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"CHANGENETWORK", idStr},
})
@ -3170,7 +3119,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"DELNETWORK", idStr},
})
@ -3274,7 +3222,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "WEBPUSH",
Params: []string{"REGISTER", endpoint},
})
@ -3296,7 +3243,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
oldSub := findWebPushSubscription(subs, endpoint)
if oldSub == nil {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "WEBPUSH",
Params: []string{"UNREGISTER", endpoint},
})
@ -3312,7 +3258,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "WEBPUSH",
Params: []string{"UNREGISTER", endpoint},
})
@ -3440,21 +3385,18 @@ func forwardChannel(ctx context.Context, dc *downstreamConn, ch *upstreamChannel
func sendTopic(ctx context.Context, dc *downstreamConn, ch *upstreamChannel) {
if ch.Topic != "" {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_TOPIC,
Params: []string{dc.nick, ch.Name, ch.Topic},
})
if ch.TopicWho != nil {
topicTime := strconv.FormatInt(ch.TopicTime.Unix(), 10)
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: xirc.RPL_TOPICWHOTIME,
Params: []string{dc.nick, ch.Name, ch.TopicWho.String(), topicTime},
})
}
} else {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_NOTOPIC,
Params: []string{dc.nick, ch.Name, "No topic is set"},
})
@ -3468,7 +3410,7 @@ func sendNames(ctx context.Context, dc *downstreamConn, ch *upstreamChannel) {
members = append(members, s)
})
msgs := xirc.GenerateNamesReply(dc.srv.prefix(), ch.Name, ch.Status, members)
msgs := xirc.GenerateNamesReply(ch.Name, ch.Status, members)
for _, msg := range msgs {
dc.SendMessage(ctx, msg)
}

24
go.mod
View file

@ -12,34 +12,34 @@ require (
github.com/mattn/go-sqlite3 v1.14.22
github.com/msteinert/pam/v2 v2.0.0
github.com/pires/go-proxyproto v0.7.0
github.com/prometheus/client_golang v1.18.0
golang.org/x/crypto v0.21.0
github.com/prometheus/client_golang v1.19.1
golang.org/x/crypto v0.23.0
golang.org/x/time v0.5.0
gopkg.in/irc.v4 v4.0.0
modernc.org/sqlite v1.29.5
nhooyr.io/websocket v1.8.10
modernc.org/sqlite v1.29.10
nhooyr.io/websocket v1.8.11
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.47.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.53.0 // indirect
github.com/prometheus/procfs v0.13.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect
modernc.org/libc v1.41.0 // indirect
modernc.org/libc v1.50.8 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)

59
go.sum
View file

@ -9,8 +9,8 @@ github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKl
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -21,7 +21,7 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
@ -40,12 +40,12 @@ github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwy
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k=
github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE=
github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
@ -61,11 +61,11 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@ -82,14 +82,14 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@ -102,10 +102,10 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/irc.v4 v4.0.0 h1:5jsLkU2Tg+R2nGNqmkGCrciasyi4kNkDXhyZD+C31yY=
gopkg.in/irc.v4 v4.0.0/go.mod h1:BfjDz9MmuWW6OZY7iq4naOhudO8+QQCdO4Ko18jcsRE=
@ -114,20 +114,25 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
modernc.org/ccgo/v4 v4.17.7 h1:+MG+Np7uYtsuPvtoH3KtZ1+pqNiJAOqqqVIxggE1iIo=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8=
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
modernc.org/libc v1.50.8 h1:bL2wUMEguwacIaF7q3Hn8WmjNn/yphgVFIE6xyLdN9c=
modernc.org/libc v1.50.8/go.mod h1:8lr2m1THY5Z3ikGyUc3JhLEQg1oaIBz/AQixw8/eksQ=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE=
modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sqlite v1.29.10 h1:3u93dz83myFnMilBGCOLbr+HjklS6+5rJLx4q86RDAg=
modernc.org/sqlite v1.29.10/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=

3
irc.go
View file

@ -299,7 +299,7 @@ func isNumeric(cmd string) bool {
return true
}
func generateAwayReply(away bool, prefix *irc.Prefix) *irc.Message {
func generateAwayReply(away bool) *irc.Message {
cmd := irc.RPL_NOWAWAY
desc := "You have been marked as being away"
if !away {
@ -307,7 +307,6 @@ func generateAwayReply(away bool, prefix *irc.Prefix) *irc.Message {
desc = "You are no longer marked as being away"
}
return &irc.Message{
Prefix: prefix,
Command: cmd,
Params: []string{"*", desc},
}

View file

@ -279,6 +279,7 @@ func init() {
"user": {
children: serviceCommandSet{
"status": {
usage: "[username]",
desc: "show a list of users and their current status",
handle: handleUserStatus,
admin: true,
@ -900,23 +901,34 @@ func handleServiceSASLReset(ctx *serviceContext, params []string) error {
}
func handleUserStatus(ctx *serviceContext, params []string) error {
if len(params) != 0 {
return fmt.Errorf("expected no argument")
if len(params) > 1 {
return fmt.Errorf("expected 0 or 1 argument")
}
// Limit to a small amount of users to avoid sending
// thousands of messages on large instances.
users := make([]database.User, 0, 50)
var n int
ctx.srv.lock.Lock()
n := len(ctx.srv.users)
for _, user := range ctx.srv.users {
if len(users) == cap(users) {
break
if len(params) == 0 {
ctx.srv.lock.Lock()
n = len(ctx.srv.users)
for _, user := range ctx.srv.users {
if len(users) == cap(users) {
break
}
users = append(users, user.User)
}
users = append(users, user.User)
ctx.srv.lock.Unlock()
} else {
username := params[0]
u := ctx.srv.getUser(username)
if u == nil {
return fmt.Errorf("unknown username %q", username)
}
users = append(users, u.User)
n = 1
}
ctx.srv.lock.Unlock()
for _, user := range users {
var attrs []string

View file

@ -461,7 +461,6 @@ func (uc *upstreamConn) abortPendingCommands() {
switch pendingCmd.msg.Command {
case "LIST":
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_LISTEND,
Params: []string{dc.nick, "Command aborted"},
})
@ -471,26 +470,22 @@ func (uc *upstreamConn) abortPendingCommands() {
mask = pendingCmd.msg.Params[0]
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{dc.nick, mask, "Command aborted"},
})
case "WHOIS":
nick := pendingCmd.msg.Params[len(pendingCmd.msg.Params)-1]
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHOIS,
Params: []string{dc.nick, nick, "Command aborted"},
})
case "AUTHENTICATE":
dc.endSASL(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLABORTED,
Params: []string{dc.nick, "SASL authentication aborted"},
})
case "REGISTER", "VERIFY":
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "FAIL",
Params: []string{pendingCmd.msg.Command, "TEMPORARILY_UNAVAILABLE", pendingCmd.msg.Params[0], "Command aborted"},
})
@ -1017,7 +1012,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
uc.updateMonitor()
uc.forEachDownstream(func(dc *downstreamConn) {
msgs := xirc.GenerateIsupport(dc.srv.prefix(), downstreamIsupport)
msgs := xirc.GenerateIsupport(downstreamIsupport)
for _, msg := range msgs {
dc.SendMessage(ctx, msg)
}
@ -1685,7 +1680,6 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
prefix := irc.ParsePrefix(target)
if dc.monitored.Has(prefix.Name) {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: msg.Command,
Params: []string{dc.nick, target},
})
@ -1703,7 +1697,6 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
for _, target := range targets {
if dc.monitored.Has(target) {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: msg.Command,
Params: []string{dc.nick, limit, target},
})

View file

@ -926,7 +926,6 @@ func (u *user) notifyBouncerNetworkState(netID int64, attrs irc.Tags) {
for _, dc := range u.downstreamConns {
if dc.caps.IsEnabled("soju.im/bouncer-networks-notify") {
dc.SendMessage(context.TODO(), &irc.Message{
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"NETWORK", netIDStr, attrs.String()},
})
@ -1130,7 +1129,6 @@ func (u *user) deleteNetwork(ctx context.Context, id int64) error {
for _, dc := range u.downstreamConns {
if dc.caps.IsEnabled("soju.im/bouncer-networks-notify") {
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"NETWORK", idStr, "*"},
})

View file

@ -82,7 +82,7 @@ func (js *joinSorter) Swap(i, j int) {
js.keys[i], js.keys[j] = js.keys[j], js.keys[i]
}
func GenerateIsupport(prefix *irc.Prefix, tokens []string) []*irc.Message {
func GenerateIsupport(tokens []string) []*irc.Message {
maxTokens := maxMessageParams - 2 // 2 reserved params: nick + text
// TODO: take into account maxMessageLength as well
@ -103,7 +103,6 @@ func GenerateIsupport(prefix *irc.Prefix, tokens []string) []*irc.Message {
}
msgs = append(msgs, &irc.Message{
Prefix: prefix,
Command: irc.RPL_ISUPPORT,
Params: append(append([]string{"*"}, encodedTokens...), "are supported"),
})
@ -114,24 +113,21 @@ func GenerateIsupport(prefix *irc.Prefix, tokens []string) []*irc.Message {
var isupportEncoder = strings.NewReplacer(" ", "\\x20", "\\", "\\x5C")
func GenerateMOTD(prefix *irc.Prefix, motd string) []*irc.Message {
func GenerateMOTD(motd string) []*irc.Message {
var msgs []*irc.Message
msgs = append(msgs, &irc.Message{
Prefix: prefix,
Command: irc.RPL_MOTDSTART,
Params: []string{"*", fmt.Sprintf("- Message of the Day -")},
})
for _, l := range strings.Split(motd, "\n") {
msgs = append(msgs, &irc.Message{
Prefix: prefix,
Command: irc.RPL_MOTD,
Params: []string{"*", l},
})
}
msgs = append(msgs, &irc.Message{
Prefix: prefix,
Command: irc.RPL_ENDOFMOTD,
Params: []string{"*", "End of /MOTD command."},
})
@ -169,9 +165,8 @@ func GenerateMonitor(subcmd string, targets []string) []*irc.Message {
return msgs
}
func GenerateNamesReply(prefix *irc.Prefix, channel string, status ChannelStatus, members []string) []*irc.Message {
func GenerateNamesReply(channel string, status ChannelStatus, members []string) []*irc.Message {
emptyNameReply := irc.Message{
Prefix: prefix,
Command: irc.RPL_NAMREPLY,
Params: []string{"*", string(status), channel, ""},
}
@ -184,7 +179,6 @@ func GenerateNamesReply(prefix *irc.Prefix, channel string, status ChannelStatus
if buf.Len() != 0 && n > maxLength {
// There's not enough space for the next space + nick
msgs = append(msgs, &irc.Message{
Prefix: prefix,
Command: irc.RPL_NAMREPLY,
Params: []string{"*", string(status), channel, buf.String()},
})
@ -199,14 +193,12 @@ func GenerateNamesReply(prefix *irc.Prefix, channel string, status ChannelStatus
if buf.Len() != 0 {
msgs = append(msgs, &irc.Message{
Prefix: prefix,
Command: irc.RPL_NAMREPLY,
Params: []string{"*", string(status), channel, buf.String()},
})
}
msgs = append(msgs, &irc.Message{
Prefix: prefix,
Command: irc.RPL_ENDOFNAMES,
Params: []string{"*", channel, "End of /NAMES list"},
})

View file

@ -93,7 +93,7 @@ func (info *WHOXInfo) set(k byte, v string) {
}
}
func GenerateWHOXReply(prefix *irc.Prefix, fields string, info *WHOXInfo) *irc.Message {
func GenerateWHOXReply(fields string, info *WHOXInfo) *irc.Message {
if fields == "" {
hostname := info.Hostname
if strings.HasPrefix(info.Hostname, ":") {
@ -109,7 +109,6 @@ func GenerateWHOXReply(prefix *irc.Prefix, fields string, info *WHOXInfo) *irc.M
}
return &irc.Message{
Prefix: prefix,
Command: irc.RPL_WHOREPLY,
Params: []string{"*", channel, info.Username, hostname, info.Server, info.Nickname, info.Flags, "0 " + info.Realname},
}
@ -129,7 +128,6 @@ func GenerateWHOXReply(prefix *irc.Prefix, fields string, info *WHOXInfo) *irc.M
}
return &irc.Message{
Prefix: prefix,
Command: RPL_WHOSPCRPL,
Params: append([]string{"*"}, values...),
}