hid: port PRIVMSG, NOTICE, NAMES, WHO, WHOIS/WAS, TOPIC, SUMMON, USERS
This commit is contained in:
parent
2dfb4e45d1
commit
b28c20a250
520
hid/main.go
520
hid/main.go
@ -201,7 +201,7 @@ func readConfigFile(name string, output interface{}) error {
|
||||
// --- Rate limiter ------------------------------------------------------------
|
||||
|
||||
type floodDetector struct {
|
||||
interval uint // interval for the limit
|
||||
interval uint // interval for the limit in seconds
|
||||
limit uint // maximum number of events allowed
|
||||
timestamps []int64 // timestamps of last events
|
||||
pos uint // index of the oldest event
|
||||
@ -217,7 +217,7 @@ func newFloodDetector(interval, limit uint) *floodDetector {
|
||||
}
|
||||
|
||||
func (fd *floodDetector) check() bool {
|
||||
now := time.Now().Unix()
|
||||
now := time.Now().UnixNano()
|
||||
fd.timestamps[fd.pos] = now
|
||||
|
||||
fd.pos++
|
||||
@ -226,7 +226,7 @@ func (fd *floodDetector) check() bool {
|
||||
}
|
||||
|
||||
var count uint
|
||||
begin := now - int64(fd.interval)
|
||||
begin := now - int64(time.Second)*int64(fd.interval)
|
||||
for _, ts := range fd.timestamps {
|
||||
if ts >= begin {
|
||||
count++
|
||||
@ -471,12 +471,11 @@ func newWhowasInfo(c *client) *whowasInfo {
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
type ircCommand struct {
|
||||
name string
|
||||
requiresRegistration bool
|
||||
handler func(*message, *client)
|
||||
|
||||
nReceived uint // number of commands received
|
||||
bytesReceived uint // number of bytes received total
|
||||
bytesReceived int // number of bytes received total
|
||||
}
|
||||
|
||||
type preparedEvent struct {
|
||||
@ -563,39 +562,29 @@ func ircChannelDestroyIfEmpty(ch *channel) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// TODO: ircSendToRoommates
|
||||
// Broadcast to all /other/ clients (telnet-friendly, also in accordance to
|
||||
// the plan of extending this to an IRCd).
|
||||
func broadcast(line string, except *client) {
|
||||
for c := range clients {
|
||||
if c != except {
|
||||
c.send(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ircSendToRoommates(c *client, message string) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// --- Clients (continued) -----------------------------------------------------
|
||||
|
||||
func clientModeToString(m uint, mode *[]byte) {
|
||||
func ircAppendClientModes(m uint, mode []byte) []byte {
|
||||
if 0 != m&ircUserModeInvisible {
|
||||
*mode = append(*mode, 'i')
|
||||
mode = append(mode, 'i')
|
||||
}
|
||||
if 0 != m&ircUserModeRxWallops {
|
||||
*mode = append(*mode, 'w')
|
||||
mode = append(mode, 'w')
|
||||
}
|
||||
if 0 != m&ircUserModeRestricted {
|
||||
*mode = append(*mode, 'r')
|
||||
mode = append(mode, 'r')
|
||||
}
|
||||
if 0 != m&ircUserModeOperator {
|
||||
*mode = append(*mode, 'o')
|
||||
mode = append(mode, 'o')
|
||||
}
|
||||
if 0 != m&ircUserModeRxServerNotices {
|
||||
*mode = append(*mode, 's')
|
||||
mode = append(mode, 's')
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
func (c *client) getMode() string {
|
||||
@ -603,8 +592,7 @@ func (c *client) getMode() string {
|
||||
if c.awayMessage != "" {
|
||||
mode = append(mode, 'a')
|
||||
}
|
||||
clientModeToString(c.mode, &mode)
|
||||
return string(mode)
|
||||
return string(ircAppendClientModes(c.mode, mode))
|
||||
}
|
||||
|
||||
func (c *client) send(line string) {
|
||||
@ -1215,15 +1203,15 @@ func ircHandleVERSION(msg *message, c *client) {
|
||||
c.sendISUPPORT()
|
||||
}
|
||||
|
||||
/*
|
||||
func ircChannelMulticast(ch *channel, msg string, except *client) {
|
||||
for c, m := range ch.userModes {
|
||||
for c := range ch.userModes {
|
||||
if c != except {
|
||||
c.send(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func ircModifyMode(mask *uint, mode uint, add bool) bool {
|
||||
orig := *mask
|
||||
if add {
|
||||
@ -1271,21 +1259,484 @@ func ircHandleUserMessage(msg *message, c *client,
|
||||
}
|
||||
|
||||
target, text := msg.params[0], msg.params[1]
|
||||
if client, ok := users[ircToCanon(target)]; ok {
|
||||
// TODO
|
||||
_ = client
|
||||
_ = text
|
||||
} else if ch, ok := channels[ircToCanon(target)]; ok {
|
||||
// TODO
|
||||
_ = ch
|
||||
message := fmt.Sprintf(":%s!%s@%s %s %s :%s",
|
||||
c.nickname, c.username, c.hostname, command, target, text)
|
||||
|
||||
if client := users[ircToCanon(target)]; client != nil {
|
||||
client.send(message)
|
||||
if allowAwayReply && client.awayMessage != "" {
|
||||
c.sendReply(RPL_AWAY, target, client.awayMessage)
|
||||
}
|
||||
|
||||
// Acknowledging a message from the client to itself would be silly.
|
||||
if client != c && (0 != c.capsEnabled&ircCapEchoMessage) {
|
||||
c.send(message)
|
||||
}
|
||||
} else if ch := channels[ircToCanon(target)]; ch != nil {
|
||||
modes, present := ch.userModes[c]
|
||||
|
||||
outsider := !present && 0 != ch.modes&ircChanModeNoOutsideMsgs
|
||||
moderated := 0 != ch.modes&ircChanModeModerated &&
|
||||
0 == modes&(ircChanModeVoice|ircChanModeOperator)
|
||||
banned := c.inMaskList(ch.banList) && !c.inMaskList(ch.exceptionList)
|
||||
|
||||
if outsider || moderated || banned {
|
||||
c.sendReply(ERR_CANNOTSENDTOCHAN, target)
|
||||
return
|
||||
}
|
||||
|
||||
except := c
|
||||
if 0 != c.capsEnabled&ircCapEchoMessage {
|
||||
except = nil
|
||||
}
|
||||
|
||||
ircChannelMulticast(ch, message, except)
|
||||
} else {
|
||||
c.sendReply(ERR_NOSUCHNICK, target)
|
||||
}
|
||||
}
|
||||
|
||||
func ircHandlePRIVMSG(msg *message, c *client) {
|
||||
ircHandleUserMessage(msg, c, "PRIVMSG", true /* allowAwayReply */)
|
||||
// Let's not care too much about success or failure.
|
||||
c.lastActive = time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func ircHandleNOTICE(msg *message, c *client) {
|
||||
ircHandleUserMessage(msg, c, "NOTICE", false /* allowAwayReply */)
|
||||
}
|
||||
|
||||
func ircHandleLIST(msg *message, c *client) {
|
||||
if len(msg.params) > 1 && !isThisMe(msg.params[1]) {
|
||||
c.sendReply(ERR_NOSUCHSERVER, msg.params[1])
|
||||
return
|
||||
}
|
||||
|
||||
// XXX: Maybe we should skip ircUserModeInvisible from user counts.
|
||||
if len(msg.params) == 0 {
|
||||
for _, ch := range channels {
|
||||
if _, present := ch.userModes[c]; present ||
|
||||
0 == ch.modes&(ircChanModePrivate|ircChanModeSecret) {
|
||||
c.sendReply(RPL_LIST, ch.name, len(ch.userModes), ch.topic)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, target := range splitString(msg.params[0], ",", true) {
|
||||
if ch := channels[ircToCanon(target)]; ch != nil &&
|
||||
0 == ch.modes&ircChanModeSecret {
|
||||
c.sendReply(RPL_LIST, ch.name, len(ch.userModes), ch.topic)
|
||||
}
|
||||
}
|
||||
}
|
||||
c.sendReply(RPL_LISTEND)
|
||||
}
|
||||
|
||||
func ircAppendPrefixes(c *client, modes uint, buf []byte) []byte {
|
||||
var all []byte
|
||||
if 0 != modes&ircChanModeOperator {
|
||||
all = append(all, '@')
|
||||
}
|
||||
if 0 != modes&ircChanModeVoice {
|
||||
all = append(all, '+')
|
||||
}
|
||||
|
||||
if len(all) > 0 {
|
||||
if 0 != c.capsEnabled&ircCapMultiPrefix {
|
||||
buf = append(buf, all...)
|
||||
} else {
|
||||
buf = append(buf, all[0])
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func ircMakeRPLNAMREPLYItem(c, target *client, modes uint) string {
|
||||
result := string(ircAppendPrefixes(c, modes, nil)) + target.nickname
|
||||
if 0 != c.capsEnabled&ircCapUserhostInNames {
|
||||
result += fmt.Sprintf("!%s@%s", target.username, target.hostname)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO: Consider using *client instead of string as the map key.
|
||||
func ircSendRPLNAMREPLY(c *client, ch *channel, usedNicks map[string]bool) {
|
||||
kind := '='
|
||||
if 0 != ch.modes&ircChanModeSecret {
|
||||
kind = '@'
|
||||
} else if 0 != ch.modes&ircChanModePrivate {
|
||||
kind = '*'
|
||||
}
|
||||
|
||||
_, present := ch.userModes[c]
|
||||
|
||||
var nicks []string
|
||||
for client, modes := range ch.userModes {
|
||||
if !present && 0 != client.mode&ircUserModeInvisible {
|
||||
continue
|
||||
}
|
||||
if usedNicks != nil {
|
||||
usedNicks[ircToCanon(client.nickname)] = true
|
||||
}
|
||||
nicks = append(nicks, ircMakeRPLNAMREPLYItem(c, client, modes))
|
||||
}
|
||||
c.sendReplyVector(RPL_NAMREPLY, nicks, kind, ch.name, "")
|
||||
}
|
||||
|
||||
func ircSendDisassociatedNames(c *client, usedNicks map[string]bool) {
|
||||
var nicks []string
|
||||
for canonNickname, client := range users {
|
||||
if 0 == client.mode&ircUserModeInvisible && !usedNicks[canonNickname] {
|
||||
nicks = append(nicks, ircMakeRPLNAMREPLYItem(c, client, 0))
|
||||
}
|
||||
}
|
||||
if len(nicks) > 0 {
|
||||
c.sendReplyVector(RPL_NAMREPLY, nicks, '*', "*", "")
|
||||
}
|
||||
}
|
||||
|
||||
func ircHandleNAMES(msg *message, c *client) {
|
||||
if len(msg.params) > 1 && !isThisMe(msg.params[1]) {
|
||||
c.sendReply(ERR_NOSUCHSERVER, msg.params[1])
|
||||
return
|
||||
}
|
||||
|
||||
if len(msg.params) == 0 {
|
||||
usedNicks := make(map[string]bool)
|
||||
|
||||
for _, ch := range channels {
|
||||
if _, present := ch.userModes[c]; present ||
|
||||
0 == ch.modes&(ircChanModePrivate|ircChanModeSecret) {
|
||||
ircSendRPLNAMREPLY(c, ch, usedNicks)
|
||||
}
|
||||
}
|
||||
|
||||
// Also send all visible users we haven't listed yet.
|
||||
ircSendDisassociatedNames(c, usedNicks)
|
||||
c.sendReply(RPL_ENDOFNAMES, "*")
|
||||
} else {
|
||||
for _, target := range splitString(msg.params[0], ",", true) {
|
||||
if ch := channels[ircToCanon(target)]; ch == nil {
|
||||
} else if _, present := ch.userModes[c]; present ||
|
||||
0 == ch.modes&ircChanModeSecret {
|
||||
ircSendRPLNAMREPLY(c, ch, nil)
|
||||
c.sendReply(RPL_ENDOFNAMES, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ircSendRPLWHOREPLY(c *client, ch *channel, target *client) {
|
||||
var chars []byte
|
||||
if target.awayMessage != "" {
|
||||
chars = append(chars, 'G')
|
||||
} else {
|
||||
chars = append(chars, 'H')
|
||||
}
|
||||
|
||||
if 0 != target.mode&ircUserModeOperator {
|
||||
chars = append(chars, '*')
|
||||
}
|
||||
|
||||
channelName := "*"
|
||||
if ch != nil {
|
||||
channelName = ch.name
|
||||
if modes, present := ch.userModes[target]; present {
|
||||
chars = ircAppendPrefixes(c, modes, chars)
|
||||
}
|
||||
}
|
||||
|
||||
c.sendReply(RPL_WHOREPLY, channelName,
|
||||
target.username, target.hostname, serverName,
|
||||
target.nickname, string(chars), 0 /* hop count */, target.realname)
|
||||
}
|
||||
|
||||
func ircMatchSendRPLWHOREPLY(c, target *client, mask string) {
|
||||
isRoommate := false
|
||||
for _, ch := range channels {
|
||||
_, presentClient := ch.userModes[c]
|
||||
_, presentTarget := ch.userModes[target]
|
||||
if presentClient && presentTarget {
|
||||
isRoommate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isRoommate && 0 != target.mode&ircUserModeInvisible {
|
||||
return
|
||||
}
|
||||
|
||||
if !ircFnmatch(mask, target.hostname) &&
|
||||
!ircFnmatch(mask, target.nickname) &&
|
||||
!ircFnmatch(mask, target.realname) &&
|
||||
!ircFnmatch(mask, serverName) {
|
||||
return
|
||||
}
|
||||
|
||||
// Try to find a channel they're on that's visible to us.
|
||||
var userCh *channel
|
||||
for _, ch := range channels {
|
||||
_, presentClient := ch.userModes[c]
|
||||
_, presentTarget := ch.userModes[target]
|
||||
if presentTarget && (presentClient ||
|
||||
0 == ch.modes&(ircChanModePrivate|ircChanModeSecret)) {
|
||||
userCh = ch
|
||||
break
|
||||
}
|
||||
}
|
||||
ircSendRPLWHOREPLY(c, userCh, target)
|
||||
}
|
||||
|
||||
func ircHandleWHO(msg *message, c *client) {
|
||||
onlyOps := len(msg.params) > 1 && msg.params[1] == "o"
|
||||
|
||||
shownMask, usedMask := "*", "*"
|
||||
if len(msg.params) > 0 {
|
||||
shownMask = msg.params[0]
|
||||
if shownMask != "0" {
|
||||
usedMask = shownMask
|
||||
}
|
||||
}
|
||||
|
||||
if ch := channels[ircToCanon(usedMask)]; ch != nil {
|
||||
_, present := ch.userModes[c]
|
||||
if present || 0 == ch.modes&ircChanModeSecret {
|
||||
for client := range ch.userModes {
|
||||
if (present || 0 == client.mode&ircUserModeInvisible) &&
|
||||
(!onlyOps || 0 != client.mode&ircUserModeOperator) {
|
||||
ircSendRPLWHOREPLY(c, ch, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, client := range users {
|
||||
if !onlyOps || 0 != client.mode&ircUserModeOperator {
|
||||
ircMatchSendRPLWHOREPLY(c, client, usedMask)
|
||||
}
|
||||
}
|
||||
}
|
||||
c.sendReply(RPL_ENDOFWHO, shownMask)
|
||||
}
|
||||
|
||||
func ircSendWHOISReply(c, target *client) {
|
||||
nick := target.nickname
|
||||
c.sendReply(RPL_WHOISUSER, nick,
|
||||
target.username, target.hostname, target.realname)
|
||||
c.sendReply(RPL_WHOISSERVER, nick,
|
||||
serverName, "TODO server_info from configuration")
|
||||
if 0 != target.mode&ircUserModeOperator {
|
||||
c.sendReply(RPL_WHOISOPERATOR, nick)
|
||||
}
|
||||
c.sendReply(RPL_WHOISIDLE, nick,
|
||||
(time.Now().UnixNano()-target.lastActive)/int64(time.Second))
|
||||
if target.awayMessage != "" {
|
||||
c.sendReply(RPL_AWAY, nick, target.awayMessage)
|
||||
}
|
||||
|
||||
var chans []string
|
||||
for _, ch := range channels {
|
||||
_, presentClient := ch.userModes[c]
|
||||
modes, presentTarget := ch.userModes[target]
|
||||
if presentTarget && (presentClient ||
|
||||
0 == ch.modes&(ircChanModePrivate|ircChanModeSecret)) {
|
||||
// TODO: Deduplicate, ircAppendPrefixes just also cuts prefixes.
|
||||
var all []byte
|
||||
if 0 != modes&ircChanModeOperator {
|
||||
all = append(all, '@')
|
||||
}
|
||||
if 0 != modes&ircChanModeVoice {
|
||||
all = append(all, '+')
|
||||
}
|
||||
chans = append(chans, string(all)+ch.name)
|
||||
}
|
||||
}
|
||||
c.sendReplyVector(RPL_WHOISCHANNELS, chans, nick, "")
|
||||
c.sendReply(RPL_ENDOFWHOIS, nick)
|
||||
}
|
||||
|
||||
func ircHandleWHOIS(msg *message, c *client) {
|
||||
if len(msg.params) < 1 {
|
||||
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||
return
|
||||
}
|
||||
if len(msg.params) > 1 && !isThisMe(msg.params[0]) {
|
||||
c.sendReply(ERR_NOSUCHSERVER, msg.params[0])
|
||||
return
|
||||
}
|
||||
|
||||
masksStr := msg.params[0]
|
||||
if len(msg.params) > 1 {
|
||||
masksStr = msg.params[1]
|
||||
}
|
||||
|
||||
for _, mask := range splitString(masksStr, ",", true /* ignoreEmpty */) {
|
||||
if strings.IndexAny(mask, "*?") < 0 {
|
||||
if target := users[ircToCanon(mask)]; target == nil {
|
||||
c.sendReply(ERR_NOSUCHNICK, mask)
|
||||
} else {
|
||||
ircSendWHOISReply(c, target)
|
||||
}
|
||||
} else {
|
||||
found := false
|
||||
for _, target := range users {
|
||||
if ircFnmatch(mask, target.nickname) {
|
||||
ircSendWHOISReply(c, target)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
c.sendReply(ERR_NOSUCHNICK, mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ircHandleWHOWAS(msg *message, c *client) {
|
||||
if len(msg.params) < 1 {
|
||||
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||
return
|
||||
}
|
||||
if len(msg.params) > 2 && !isThisMe(msg.params[2]) {
|
||||
c.sendReply(ERR_NOSUCHSERVER, msg.params[2])
|
||||
return
|
||||
}
|
||||
// The "count" parameter is ignored, we only store one entry for a nick.
|
||||
|
||||
for _, nick := range splitString(msg.params[0], ",", true) {
|
||||
if info := whowas[ircToCanon(nick)]; info == nil {
|
||||
c.sendReply(ERR_WASNOSUCHNICK, nick)
|
||||
} else {
|
||||
c.sendReply(RPL_WHOWASUSER, nick,
|
||||
info.username, info.hostname, info.realname)
|
||||
c.sendReply(RPL_WHOISSERVER, nick,
|
||||
serverName, "TODO server_info from configuration")
|
||||
}
|
||||
c.sendReply(RPL_ENDOFWHOWAS, nick)
|
||||
}
|
||||
}
|
||||
|
||||
func ircSendRPLTOPIC(c *client, ch *channel) {
|
||||
if ch.topic == "" {
|
||||
c.sendReply(RPL_NOTOPIC, ch.name)
|
||||
} else {
|
||||
c.sendReply(RPL_TOPIC, ch.name, ch.topic)
|
||||
c.sendReply(RPL_TOPICWHOTIME,
|
||||
ch.name, ch.topicWho, ch.topicTime/int64(time.Second))
|
||||
}
|
||||
}
|
||||
|
||||
func ircHandleTOPIC(msg *message, c *client) {
|
||||
if len(msg.params) < 1 {
|
||||
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||
return
|
||||
}
|
||||
|
||||
target := msg.params[0]
|
||||
ch := channels[ircToCanon(target)]
|
||||
if ch == nil {
|
||||
c.sendReply(ERR_NOSUCHCHANNEL, target)
|
||||
return
|
||||
}
|
||||
|
||||
if len(msg.params) < 2 {
|
||||
ircSendRPLTOPIC(c, ch)
|
||||
return
|
||||
}
|
||||
|
||||
modes, present := ch.userModes[c]
|
||||
if !present {
|
||||
c.sendReply(ERR_NOTONCHANNEL, target)
|
||||
return
|
||||
}
|
||||
|
||||
if 0 != ch.modes&ircChanModeProtectedTopic &&
|
||||
0 == modes&ircChanModeOperator {
|
||||
c.sendReply(ERR_CHANOPRIVSNEEDED, target)
|
||||
return
|
||||
}
|
||||
|
||||
ch.topic = msg.params[1]
|
||||
ch.topicWho = fmt.Sprintf("%s@%s@%s", c.nickname, c.username, c.hostname)
|
||||
ch.topicTime = time.Now().UnixNano()
|
||||
|
||||
message := fmt.Sprintf(":%s!%s@%s TOPIC %s :%s",
|
||||
c.nickname, c.username, c.hostname, target, ch.topic)
|
||||
ircChannelMulticast(ch, message, nil)
|
||||
}
|
||||
|
||||
// TODO: All the various real command handlers.
|
||||
|
||||
func ircHandleX(msg *message, c *client) {
|
||||
if len(msg.params) < 1 {
|
||||
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func ircHandleSUMMON(msg *message, c *client) {
|
||||
c.sendReply(ERR_SUMMONDISABLED)
|
||||
}
|
||||
|
||||
func ircHandleUSERS(msg *message, c *client) {
|
||||
c.sendReply(ERR_USERSDISABLED)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// TODO: Add an index for IRC_ERR_NOSUCHSERVER validation?
|
||||
// TODO: Add a minimal parameter count?
|
||||
// TODO: Add a field for oper-only commands?
|
||||
var ircHandlers = map[string]*ircCommand{
|
||||
"CAP": {false, ircHandleCAP, 0, 0},
|
||||
"PASS": {false, ircHandlePASS, 0, 0},
|
||||
"NICK": {false, ircHandleNICK, 0, 0},
|
||||
"USER": {false, ircHandleUSER, 0, 0},
|
||||
|
||||
"USERHOST": {true, ircHandleUSERHOST, 0, 0},
|
||||
"LUSERS": {true, ircHandleLUSERS, 0, 0},
|
||||
"MOTD": {true, ircHandleMOTD, 0, 0},
|
||||
"PING": {true, ircHandlePING, 0, 0},
|
||||
"PONG": {false, ircHandlePONG, 0, 0},
|
||||
"QUIT": {false, ircHandleQUIT, 0, 0},
|
||||
"TIME": {true, ircHandleTIME, 0, 0},
|
||||
"VERSION": {true, ircHandleVERSION, 0, 0},
|
||||
"USERS": {true, ircHandleUSERS, 0, 0},
|
||||
"SUMMON": {true, ircHandleSUMMON, 0, 0},
|
||||
|
||||
"MODE": {true, ircHandleMODE, 0, 0},
|
||||
"PRIVMSG": {true, ircHandlePRIVMSG, 0, 0},
|
||||
"NOTICE": {true, ircHandleNOTICE, 0, 0},
|
||||
"TOPIC": {true, ircHandleTOPIC, 0, 0},
|
||||
"LIST": {true, ircHandleLIST, 0, 0},
|
||||
"NAMES": {true, ircHandleNAMES, 0, 0},
|
||||
"WHO": {true, ircHandleWHO, 0, 0},
|
||||
"WHOIS": {true, ircHandleWHOIS, 0, 0},
|
||||
"WHOWAS": {true, ircHandleWHOWAS, 0, 0},
|
||||
}
|
||||
|
||||
func ircProcessMessage(c *client, msg *message, raw string) {
|
||||
if c.closing {
|
||||
return
|
||||
}
|
||||
|
||||
c.nReceivedMessages++
|
||||
c.receivedBytes += len(raw) + 2
|
||||
|
||||
if !c.antiflood.check() {
|
||||
c.closeLink("Excess flood")
|
||||
return
|
||||
}
|
||||
|
||||
if cmd, ok := ircHandlers[ircToCanon(msg.command)]; !ok {
|
||||
c.sendReply(ERR_UNKNOWNCOMMAND, msg.command)
|
||||
} else {
|
||||
cmd.nReceived++
|
||||
cmd.bytesReceived += len(raw) + 2
|
||||
|
||||
if cmd.requiresRegistration && !c.registered {
|
||||
c.sendReply(ERR_NOTREGISTERED)
|
||||
} else {
|
||||
cmd.handler(msg, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- ? -----------------------------------------------------------------------
|
||||
@ -1338,7 +1789,8 @@ func (c *client) onRead(data []byte, readErr error) {
|
||||
msg.params = append(msg.params, x[1:])
|
||||
}
|
||||
|
||||
broadcast(line, c)
|
||||
// XXX: And since it accepts LF, we miscalculate receivedBytes within.
|
||||
ircProcessMessage(c, &msg, line)
|
||||
}
|
||||
|
||||
if readErr != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user