Compare commits
No commits in common. "9bd3bebd198ada9138da36940f4ce73bd03a8c16" and "d5108f3994777f821a28e2c167906e6d0c878413" have entirely different histories.
9bd3bebd19
...
d5108f3994
|
@ -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"]
|
|
@ -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
20
cmd/soju/rlimit.go
Normal 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
7
cmd/soju/rlimit_stub.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris
|
||||
|
||||
package main
|
||||
|
||||
func bumpOpenedFileLimit() error {
|
||||
return nil
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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
24
go.mod
|
@ -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
59
go.sum
|
@ -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
3
irc.go
|
@ -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},
|
||||
}
|
||||
|
|
30
service.go
30
service.go
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
})
|
||||
|
|
2
user.go
2
user.go
|
@ -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, "*"},
|
||||
})
|
||||
|
|
|
@ -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"},
|
||||
})
|
||||
|
|
|
@ -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...),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue