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) }