check / check (push) Waiting to run
ADMIN's location and email lines come from service.AdminLines, and KillUser applies the default reason, so both transports send the same reply. The Disconnect test now allows a quarter of one write window, so a Disconnect that writes before returning fails it. QueryUserMode and UserhostReply are tested with a read that fails. The handler tests again cover USERHOST for a nick with no session, MODE +o on your own nick, and the status of a successful KILL. handleMode's comment says what it does. Model: opus-5-5
306 lines
6.4 KiB
Go
306 lines
6.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"sneak.berlin/go/neoirc/pkg/irc"
|
|
)
|
|
|
|
// dispatchOperCommand routes the server operator commands
|
|
// OPER, KILL and WALLOPS to their handlers.
|
|
func (hdlr *Handlers) dispatchOperCommand(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick, command string,
|
|
bodyLines func() []string,
|
|
) {
|
|
switch command {
|
|
case irc.CmdOper:
|
|
hdlr.handleOper(
|
|
writer, request,
|
|
sessionID, clientID, nick, bodyLines,
|
|
)
|
|
case irc.CmdKill:
|
|
hdlr.handleKill(
|
|
writer, request,
|
|
sessionID, clientID, nick, bodyLines,
|
|
)
|
|
case irc.CmdWallops:
|
|
hdlr.handleWallops(
|
|
writer, request,
|
|
sessionID, clientID, nick, bodyLines,
|
|
)
|
|
}
|
|
}
|
|
|
|
// handleUserhost handles USERHOST: user@host for each of
|
|
// up to five nicks given in the body.
|
|
func (hdlr *Handlers) handleUserhost(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick string,
|
|
bodyLines func() []string,
|
|
) {
|
|
lines := bodyLines()
|
|
if len(lines) == 0 {
|
|
hdlr.respondIRCError(
|
|
writer, request, clientID, sessionID,
|
|
irc.ErrNeedMoreParams, nick,
|
|
[]string{irc.CmdUserhost},
|
|
"Not enough parameters",
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
reply, err := hdlr.svc.UserhostReply(
|
|
request.Context(), lines, hdlr.serverName(),
|
|
)
|
|
if hdlr.handleServiceError(
|
|
writer, request, clientID, sessionID, nick, err,
|
|
) {
|
|
return
|
|
}
|
|
|
|
hdlr.enqueueNumeric(
|
|
request.Context(), clientID,
|
|
irc.RplUserHost, nick, nil, reply,
|
|
)
|
|
hdlr.broker.Notify(sessionID)
|
|
hdlr.respondJSON(writer, request,
|
|
map[string]string{statusKey: "ok"},
|
|
http.StatusOK)
|
|
}
|
|
|
|
// handleVersion handles VERSION.
|
|
func (hdlr *Handlers) handleVersion(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick string,
|
|
) {
|
|
hdlr.enqueueNumeric(
|
|
request.Context(), clientID, irc.RplVersion, nick,
|
|
[]string{
|
|
hdlr.svc.ServerVersion() + ".",
|
|
hdlr.serverName(),
|
|
},
|
|
"",
|
|
)
|
|
hdlr.broker.Notify(sessionID)
|
|
hdlr.respondJSON(writer, request,
|
|
map[string]string{statusKey: "ok"},
|
|
http.StatusOK)
|
|
}
|
|
|
|
// handleAdmin handles ADMIN.
|
|
func (hdlr *Handlers) handleAdmin(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick string,
|
|
) {
|
|
ctx := request.Context()
|
|
srvName := hdlr.serverName()
|
|
location1, location2, email := hdlr.svc.AdminLines(srvName)
|
|
|
|
hdlr.enqueueNumeric(
|
|
ctx, clientID, irc.RplAdminMe, nick,
|
|
[]string{srvName}, "Administrative info",
|
|
)
|
|
hdlr.enqueueNumeric(
|
|
ctx, clientID, irc.RplAdminLoc1, nick, nil, location1,
|
|
)
|
|
hdlr.enqueueNumeric(
|
|
ctx, clientID, irc.RplAdminLoc2, nick, nil, location2,
|
|
)
|
|
hdlr.enqueueNumeric(
|
|
ctx, clientID, irc.RplAdminEmail, nick, nil, email,
|
|
)
|
|
hdlr.broker.Notify(sessionID)
|
|
hdlr.respondJSON(writer, request,
|
|
map[string]string{statusKey: "ok"},
|
|
http.StatusOK)
|
|
}
|
|
|
|
// handleInfo handles INFO.
|
|
func (hdlr *Handlers) handleInfo(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick string,
|
|
) {
|
|
ctx := request.Context()
|
|
|
|
for _, line := range hdlr.svc.InfoLines() {
|
|
hdlr.enqueueNumeric(
|
|
ctx, clientID, irc.RplInfo, nick, nil, line,
|
|
)
|
|
}
|
|
|
|
hdlr.enqueueNumeric(
|
|
ctx, clientID, irc.RplEndOfInfo, nick, nil,
|
|
"End of /INFO list",
|
|
)
|
|
hdlr.broker.Notify(sessionID)
|
|
hdlr.respondJSON(writer, request,
|
|
map[string]string{statusKey: "ok"},
|
|
http.StatusOK)
|
|
}
|
|
|
|
// handleTime handles TIME: the server's local time.
|
|
func (hdlr *Handlers) handleTime(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick string,
|
|
) {
|
|
hdlr.enqueueNumeric(
|
|
request.Context(), clientID, irc.RplTime, nick,
|
|
[]string{hdlr.serverName()},
|
|
time.Now().Format(time.RFC1123),
|
|
)
|
|
hdlr.broker.Notify(sessionID)
|
|
hdlr.respondJSON(writer, request,
|
|
map[string]string{statusKey: "ok"},
|
|
http.StatusOK)
|
|
}
|
|
|
|
// handleKill handles KILL: the body is the target nick and
|
|
// an optional reason.
|
|
func (hdlr *Handlers) handleKill(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick string,
|
|
bodyLines func() []string,
|
|
) {
|
|
lines := bodyLines()
|
|
|
|
targetNick := ""
|
|
if len(lines) > 0 {
|
|
targetNick = strings.TrimSpace(lines[0])
|
|
}
|
|
|
|
if targetNick == "" {
|
|
hdlr.respondIRCError(
|
|
writer, request, clientID, sessionID,
|
|
irc.ErrNeedMoreParams, nick,
|
|
[]string{irc.CmdKill},
|
|
"Not enough parameters",
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
reason := ""
|
|
if len(lines) > 1 {
|
|
reason = lines[1]
|
|
}
|
|
|
|
err := hdlr.svc.KillUser(
|
|
request.Context(), sessionID, nick,
|
|
targetNick, reason,
|
|
)
|
|
if hdlr.handleServiceError(
|
|
writer, request, clientID, sessionID, nick, err,
|
|
) {
|
|
return
|
|
}
|
|
|
|
hdlr.respondJSON(writer, request,
|
|
map[string]string{statusKey: "ok"},
|
|
http.StatusOK)
|
|
}
|
|
|
|
// handleWallops handles WALLOPS: the body is the message.
|
|
func (hdlr *Handlers) handleWallops(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick string,
|
|
bodyLines func() []string,
|
|
) {
|
|
lines := bodyLines()
|
|
if len(lines) == 0 {
|
|
hdlr.respondIRCError(
|
|
writer, request, clientID, sessionID,
|
|
irc.ErrNeedMoreParams, nick,
|
|
[]string{irc.CmdWallops},
|
|
"Not enough parameters",
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
err := hdlr.svc.SendWallops(
|
|
request.Context(), sessionID, nick,
|
|
strings.Join(lines, " "),
|
|
)
|
|
if hdlr.handleServiceError(
|
|
writer, request, clientID, sessionID, nick, err,
|
|
) {
|
|
return
|
|
}
|
|
|
|
hdlr.respondJSON(writer, request,
|
|
map[string]string{statusKey: "ok"},
|
|
http.StatusOK)
|
|
}
|
|
|
|
// handleUserMode handles MODE for a nick: a query when the
|
|
// body is empty, otherwise a change by the mode string in
|
|
// the body. Only the caller's own nick, in any letter case,
|
|
// is allowed.
|
|
func (hdlr *Handlers) handleUserMode(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
sessionID, clientID int64,
|
|
nick, target string,
|
|
bodyLines func() []string,
|
|
) {
|
|
if !strings.EqualFold(target, nick) {
|
|
hdlr.respondIRCError(
|
|
writer, request, clientID, sessionID,
|
|
irc.ErrUsersDoNotMatch, nick, nil,
|
|
"Can't change mode for other users",
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
ctx := request.Context()
|
|
|
|
var (
|
|
modes string
|
|
err error
|
|
)
|
|
|
|
lines := bodyLines()
|
|
if len(lines) > 0 {
|
|
modes, err = hdlr.svc.ApplyUserMode(
|
|
ctx, sessionID, lines[0],
|
|
)
|
|
} else {
|
|
modes, err = hdlr.svc.QueryUserMode(ctx, sessionID)
|
|
}
|
|
|
|
if hdlr.handleServiceError(
|
|
writer, request, clientID, sessionID, nick, err,
|
|
) {
|
|
return
|
|
}
|
|
|
|
hdlr.enqueueNumeric(
|
|
ctx, clientID, irc.RplUmodeIs, nick, nil, modes,
|
|
)
|
|
hdlr.broker.Notify(sessionID)
|
|
hdlr.respondJSON(writer, request,
|
|
map[string]string{statusKey: "ok"},
|
|
http.StatusOK)
|
|
}
|