Compare commits

..

No commits in common. "9bd3bebd198ada9138da36940f4ce73bd03a8c16" and "d5108f3994777f821a28e2c167906e6d0c878413" have entirely different histories.

14 changed files with 178 additions and 116 deletions

View file

@ -1,19 +0,0 @@
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,6 +134,10 @@ 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)
@ -223,9 +227,6 @@ 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)
@ -279,9 +280,6 @@ 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)
@ -383,9 +381,6 @@ 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)

20
cmd/soju/rlimit.go Normal file
View file

@ -0,0 +1,20 @@
//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
}

7
cmd/soju/rlimit_stub.go Normal file
View file

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

View file

@ -483,11 +483,9 @@ character.
*-network* <name>
Select a network. By default, the current network is selected, if any.
*user status* [username]
*user status*
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,12 +530,6 @@ 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)
@ -548,6 +542,7 @@ 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...),
})
@ -557,6 +552,7 @@ 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},
})
@ -701,6 +697,7 @@ 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)},
})
@ -756,7 +753,7 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
dc.away = nil
}
dc.SendMessage(ctx, generateAwayReply(dc.away != nil))
dc.SendMessage(ctx, generateAwayReply(dc.away != nil, dc.srv.prefix()))
default:
dc.logger.Debugf("unhandled message: %v", msg)
return newUnknownCommandError(msg.Command)
@ -908,18 +905,21 @@ 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,6 +952,7 @@ 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"},
}}
@ -966,6 +967,7 @@ 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"},
}}
@ -980,6 +982,7 @@ 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"},
}}
@ -1001,6 +1004,7 @@ 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},
})
@ -1019,6 +1023,7 @@ 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"},
})
@ -1199,11 +1204,13 @@ 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"},
})
@ -1274,6 +1281,7 @@ 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"},
})
@ -1491,28 +1499,33 @@ 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(isupport) {
for _, msg := range xirc.GenerateIsupport(dc.srv.prefix(), 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"},
})
@ -1524,7 +1537,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(motd) {
for _, msg := range xirc.GenerateMOTD(dc.srv.prefix(), motd) {
dc.SendMessage(ctx, msg)
}
} else {
@ -1533,6 +1546,7 @@ 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},
})
@ -1545,6 +1559,7 @@ 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()},
})
@ -1888,6 +1903,7 @@ 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"},
})
@ -1895,6 +1911,7 @@ 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"},
})
@ -2015,6 +2032,7 @@ 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},
})
@ -2060,11 +2078,13 @@ 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},
})
@ -2111,6 +2131,7 @@ 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"},
})
@ -2133,6 +2154,7 @@ 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"},
})
@ -2169,8 +2191,9 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
Account: dc.user.Username,
Realname: dc.realname,
}
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
dc.SendMessage(ctx, xirc.GenerateWHOXReply(dc.srv.prefix(), fields, &info))
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{"*", endOfWhoToken, "End of /WHO list"},
})
@ -2195,8 +2218,9 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
Account: serviceNick,
Realname: serviceRealname,
}
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
dc.SendMessage(ctx, xirc.GenerateWHOXReply(dc.srv.prefix(), fields, &info))
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{"*", endOfWhoToken, "End of /WHO list"},
})
@ -2204,6 +2228,7 @@ 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"},
})
@ -2231,9 +2256,10 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if uc.isChannel(mask) {
info.Channel = mask
}
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
dc.SendMessage(ctx, xirc.GenerateWHOXReply(dc.srv.prefix(), fields, &info))
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.RPL_ENDOFWHO,
Params: []string{"*", endOfWhoToken, "End of /WHO list"},
})
@ -2262,24 +2288,29 @@ 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"},
})
@ -2287,26 +2318,32 @@ 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"},
})
@ -2344,6 +2381,7 @@ 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"},
}}
@ -2480,6 +2518,7 @@ 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"},
})
@ -2506,6 +2545,7 @@ 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"},
})
@ -2515,6 +2555,7 @@ 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"},
})
@ -2543,7 +2584,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
dc.away = nil
}
dc.SendMessage(ctx, generateAwayReply(dc.away != nil))
dc.SendMessage(ctx, generateAwayReply(dc.away != nil, dc.srv.prefix()))
uc := dc.upstream()
if uc != nil {
@ -2595,6 +2636,7 @@ 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"},
})
@ -2606,6 +2648,7 @@ 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},
})
@ -2619,6 +2662,7 @@ 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},
})
@ -2635,11 +2679,13 @@ 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"},
})
@ -2656,6 +2702,7 @@ 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},
})
@ -2785,6 +2832,7 @@ 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)},
})
@ -3016,6 +3064,7 @@ 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()},
})
@ -3050,6 +3099,7 @@ 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)},
})
@ -3093,6 +3143,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"CHANGENETWORK", idStr},
})
@ -3119,6 +3170,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "BOUNCER",
Params: []string{"DELNETWORK", idStr},
})
@ -3222,6 +3274,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "WEBPUSH",
Params: []string{"REGISTER", endpoint},
})
@ -3243,6 +3296,7 @@ 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},
})
@ -3258,6 +3312,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}
dc.SendMessage(ctx, &irc.Message{
Prefix: dc.srv.prefix(),
Command: "WEBPUSH",
Params: []string{"UNREGISTER", endpoint},
})
@ -3385,18 +3440,21 @@ 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"},
})
@ -3410,7 +3468,7 @@ func sendNames(ctx context.Context, dc *downstreamConn, ch *upstreamChannel) {
members = append(members, s)
})
msgs := xirc.GenerateNamesReply(ch.Name, ch.Status, members)
msgs := xirc.GenerateNamesReply(dc.srv.prefix(), 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.19.1
golang.org/x/crypto v0.23.0
github.com/prometheus/client_golang v1.18.0
golang.org/x/crypto v0.21.0
golang.org/x/time v0.5.0
gopkg.in/irc.v4 v4.0.0
modernc.org/sqlite v1.29.10
nhooyr.io/websocket v1.8.11
modernc.org/sqlite v1.29.5
nhooyr.io/websocket v1.8.10
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cespare/xxhash/v2 v2.2.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.1 // indirect
github.com/prometheus/common v0.53.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.47.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.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
google.golang.org/protobuf v1.34.1 // 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
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect
modernc.org/libc v1.50.8 // indirect
modernc.org/libc v1.41.0 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/memory v1.7.2 // 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.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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/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-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
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.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/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/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.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
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/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.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
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.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/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.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
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/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.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/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,25 +114,20 @@ 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.50.8 h1:bL2wUMEguwacIaF7q3Hn8WmjNn/yphgVFIE6xyLdN9c=
modernc.org/libc v1.50.8/go.mod h1:8lr2m1THY5Z3ikGyUc3JhLEQg1oaIBz/AQixw8/eksQ=
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
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.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/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/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.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
nhooyr.io/websocket v1.8.10/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) *irc.Message {
func generateAwayReply(away bool, prefix *irc.Prefix) *irc.Message {
cmd := irc.RPL_NOWAWAY
desc := "You have been marked as being away"
if !away {
@ -307,6 +307,7 @@ func generateAwayReply(away bool) *irc.Message {
desc = "You are no longer marked as being away"
}
return &irc.Message{
Prefix: prefix,
Command: cmd,
Params: []string{"*", desc},
}

View file

@ -279,7 +279,6 @@ func init() {
"user": {
children: serviceCommandSet{
"status": {
usage: "[username]",
desc: "show a list of users and their current status",
handle: handleUserStatus,
admin: true,
@ -901,34 +900,23 @@ func handleServiceSASLReset(ctx *serviceContext, params []string) error {
}
func handleUserStatus(ctx *serviceContext, params []string) error {
if len(params) > 1 {
return fmt.Errorf("expected 0 or 1 argument")
if len(params) != 0 {
return fmt.Errorf("expected no 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
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)
ctx.srv.lock.Lock()
n := len(ctx.srv.users)
for _, user := range ctx.srv.users {
if len(users) == cap(users) {
break
}
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
users = append(users, user.User)
}
ctx.srv.lock.Unlock()
for _, user := range users {
var attrs []string

View file

@ -461,6 +461,7 @@ 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"},
})
@ -470,22 +471,26 @@ 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"},
})
@ -1012,7 +1017,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
uc.updateMonitor()
uc.forEachDownstream(func(dc *downstreamConn) {
msgs := xirc.GenerateIsupport(downstreamIsupport)
msgs := xirc.GenerateIsupport(dc.srv.prefix(), downstreamIsupport)
for _, msg := range msgs {
dc.SendMessage(ctx, msg)
}
@ -1680,6 +1685,7 @@ 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},
})
@ -1697,6 +1703,7 @@ 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,6 +926,7 @@ 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()},
})
@ -1129,6 +1130,7 @@ 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(tokens []string) []*irc.Message {
func GenerateIsupport(prefix *irc.Prefix, tokens []string) []*irc.Message {
maxTokens := maxMessageParams - 2 // 2 reserved params: nick + text
// TODO: take into account maxMessageLength as well
@ -103,6 +103,7 @@ func GenerateIsupport(tokens []string) []*irc.Message {
}
msgs = append(msgs, &irc.Message{
Prefix: prefix,
Command: irc.RPL_ISUPPORT,
Params: append(append([]string{"*"}, encodedTokens...), "are supported"),
})
@ -113,21 +114,24 @@ func GenerateIsupport(tokens []string) []*irc.Message {
var isupportEncoder = strings.NewReplacer(" ", "\\x20", "\\", "\\x5C")
func GenerateMOTD(motd string) []*irc.Message {
func GenerateMOTD(prefix *irc.Prefix, 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."},
})
@ -165,8 +169,9 @@ func GenerateMonitor(subcmd string, targets []string) []*irc.Message {
return msgs
}
func GenerateNamesReply(channel string, status ChannelStatus, members []string) []*irc.Message {
func GenerateNamesReply(prefix *irc.Prefix, channel string, status ChannelStatus, members []string) []*irc.Message {
emptyNameReply := irc.Message{
Prefix: prefix,
Command: irc.RPL_NAMREPLY,
Params: []string{"*", string(status), channel, ""},
}
@ -179,6 +184,7 @@ func GenerateNamesReply(channel string, status ChannelStatus, members []string)
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()},
})
@ -193,12 +199,14 @@ func GenerateNamesReply(channel string, status ChannelStatus, members []string)
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(fields string, info *WHOXInfo) *irc.Message {
func GenerateWHOXReply(prefix *irc.Prefix, fields string, info *WHOXInfo) *irc.Message {
if fields == "" {
hostname := info.Hostname
if strings.HasPrefix(info.Hostname, ":") {
@ -109,6 +109,7 @@ func GenerateWHOXReply(fields string, info *WHOXInfo) *irc.Message {
}
return &irc.Message{
Prefix: prefix,
Command: irc.RPL_WHOREPLY,
Params: []string{"*", channel, info.Username, hostname, info.Server, info.Nickname, info.Flags, "0 " + info.Realname},
}
@ -128,6 +129,7 @@ func GenerateWHOXReply(fields string, info *WHOXInfo) *irc.Message {
}
return &irc.Message{
Prefix: prefix,
Command: RPL_WHOSPCRPL,
Params: append([]string{"*"}, values...),
}