hid: port PART, KICK, INVITE, JOIN, AWAY, ISON, ADMIN, DIE
This commit is contained in:
parent
3322fe2851
commit
50e7f7dca5
340
xS/main.go
340
xS/main.go
|
@ -556,14 +556,40 @@ func initiateQuit() {
|
||||||
quitting = true
|
quitting = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ircChannelCreate
|
func ircChannelCreate(name string) *channel {
|
||||||
|
ch := &channel{
|
||||||
func ircChannelDestroyIfEmpty(ch *channel) {
|
name: name,
|
||||||
// TODO
|
created: time.Now().UnixNano(),
|
||||||
|
userLimit: -1,
|
||||||
|
}
|
||||||
|
channels[ircToCanon(name)] = ch
|
||||||
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ircChannelDestroyIfEmpty(ch *channel) {
|
||||||
|
if len(ch.userModes) == 0 {
|
||||||
|
delete(channels, ircToCanon(ch.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Improve the name as it takes mode +q into account.
|
||||||
func ircSendToRoommates(c *client, message string) {
|
func ircSendToRoommates(c *client, message string) {
|
||||||
// TODO
|
targets := make(map[*client]bool)
|
||||||
|
for _, ch := range channels {
|
||||||
|
_, present := ch.userModes[c]
|
||||||
|
if !present || 0 != ch.modes&ircChanModeQuiet {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for client := range ch.userModes {
|
||||||
|
targets[client] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for roommate := range targets {
|
||||||
|
if roommate != c {
|
||||||
|
roommate.send(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Clients (continued) -----------------------------------------------------
|
// --- Clients (continued) -----------------------------------------------------
|
||||||
|
@ -1662,13 +1688,248 @@ func ircHandleTOPIC(msg *message, c *client) {
|
||||||
ircChannelMulticast(ch, message, nil)
|
ircChannelMulticast(ch, message, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: All the various real command handlers.
|
func ircTryPart(c *client, target string, reason string) {
|
||||||
|
if reason == "" {
|
||||||
|
reason = c.nickname
|
||||||
|
}
|
||||||
|
|
||||||
func ircHandleX(msg *message, c *client) {
|
ch := channels[ircToCanon(target)]
|
||||||
|
if ch == nil {
|
||||||
|
c.sendReply(ERR_NOSUCHCHANNEL, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, present := ch.userModes[c]; !present {
|
||||||
|
c.sendReply(ERR_NOTONCHANNEL, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf(":%s@%s@%s PART %s :%s",
|
||||||
|
c.nickname, c.username, c.hostname, target, reason)
|
||||||
|
if 0 == ch.modes&ircChanModeQuiet {
|
||||||
|
ircChannelMulticast(ch, message, nil)
|
||||||
|
} else {
|
||||||
|
c.send(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(ch.userModes, c)
|
||||||
|
ircChannelDestroyIfEmpty(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircPartAllChannels(c *client) {
|
||||||
|
for _, ch := range channels {
|
||||||
|
if _, present := ch.userModes[c]; present {
|
||||||
|
ircTryPart(c, ch.name, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircHandlePART(msg *message, c *client) {
|
||||||
if len(msg.params) < 1 {
|
if len(msg.params) < 1 {
|
||||||
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reason := ""
|
||||||
|
if len(msg.params) > 1 {
|
||||||
|
reason = msg.params[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, target := range splitString(msg.params[0], ",", true) {
|
||||||
|
ircTryPart(c, target, reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Undo the rename from channelName to target, also in ircTryPart.
|
||||||
|
func ircTryKick(c *client, target, nick, reason string) {
|
||||||
|
ch := channels[ircToCanon(target)]
|
||||||
|
if ch == nil {
|
||||||
|
c.sendReply(ERR_NOSUCHCHANNEL, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if modes, present := ch.userModes[c]; !present {
|
||||||
|
c.sendReply(ERR_NOTONCHANNEL, target)
|
||||||
|
return
|
||||||
|
} else if 0 == modes&ircChanModeOperator {
|
||||||
|
c.sendReply(ERR_CHANOPRIVSNEEDED, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := users[ircToCanon(nick)]
|
||||||
|
if _, present := ch.userModes[client]; client == nil || !present {
|
||||||
|
c.sendReply(ERR_USERNOTINCHANNEL, nick, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf(":%s@%s@%s KICK %s %s :%s",
|
||||||
|
c.nickname, c.username, c.hostname, target, nick, reason)
|
||||||
|
if 0 == ch.modes&ircChanModeQuiet {
|
||||||
|
ircChannelMulticast(ch, message, nil)
|
||||||
|
} else {
|
||||||
|
c.send(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(ch.userModes, client)
|
||||||
|
ircChannelDestroyIfEmpty(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircHandleKICK(msg *message, c *client) {
|
||||||
|
if len(msg.params) < 2 {
|
||||||
|
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reason := c.nickname
|
||||||
|
if len(msg.params) > 2 {
|
||||||
|
reason = msg.params[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
targetChannels := splitString(msg.params[0], ",", true)
|
||||||
|
targetUsers := splitString(msg.params[1], ",", true)
|
||||||
|
|
||||||
|
if len(channels) == 1 {
|
||||||
|
for i := 0; i < len(targetUsers); i++ {
|
||||||
|
ircTryKick(c, targetChannels[0], targetUsers[i], reason)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < len(channels) && i < len(targetUsers); i++ {
|
||||||
|
ircTryKick(c, targetChannels[i], targetUsers[i], reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircSendInviteNotifications(ch *channel, c, target *client) {
|
||||||
|
for client := range ch.userModes {
|
||||||
|
if client != target && 0 != client.capsEnabled&ircCapInviteNotify {
|
||||||
|
client.sendf(":%s!%s@%s INVITE %s %s",
|
||||||
|
c.nickname, c.username, c.hostname, target.nickname, ch.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircHandleINVITE(msg *message, c *client) {
|
||||||
|
if len(msg.params) < 2 {
|
||||||
|
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
target, channelName := msg.params[0], msg.params[1]
|
||||||
|
client := users[ircToCanon(target)]
|
||||||
|
if client == nil {
|
||||||
|
c.sendReply(ERR_NOSUCHNICK, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch := channels[ircToCanon(channelName)]; ch != nil {
|
||||||
|
invitingModes, invitingPresent := ch.userModes[c]
|
||||||
|
if !invitingPresent {
|
||||||
|
c.sendReply(ERR_NOTONCHANNEL, channelName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, present := ch.userModes[client]; present {
|
||||||
|
c.sendReply(ERR_USERONCHANNEL, target, channelName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if 0 != invitingModes&ircChanModeOperator {
|
||||||
|
client.invites[ircToCanon(channelName)] = true
|
||||||
|
} else if 0 != ch.modes&ircChanModeInviteOnly {
|
||||||
|
c.sendReply(ERR_CHANOPRIVSNEEDED, channelName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's not specified when and how we should send out invite-notify.
|
||||||
|
if 0 != ch.modes&ircChanModeInviteOnly {
|
||||||
|
ircSendInviteNotifications(ch, c, client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.sendf(":%s!%s@%s INVITE %s %s",
|
||||||
|
c.nickname, c.username, c.hostname, client.nickname, channelName)
|
||||||
|
if client.awayMessage != "" {
|
||||||
|
c.sendReply(RPL_AWAY, client.nickname, client.awayMessage)
|
||||||
|
}
|
||||||
|
c.sendReply(RPL_INVITING, client.nickname, channelName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircTryJoin(c *client, channelName, key string) {
|
||||||
|
ch := channels[ircToCanon(channelName)]
|
||||||
|
var userMode uint
|
||||||
|
if ch == nil {
|
||||||
|
if !ircIsValidChannelName(channelName) {
|
||||||
|
c.sendReply(ERR_BADCHANMASK, channelName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch = ircChannelCreate(channelName)
|
||||||
|
userMode = ircChanModeOperator
|
||||||
|
} else if _, present := ch.userModes[c]; present {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, invitedByChanop := c.invites[ircToCanon(channelName)]
|
||||||
|
if 0 != ch.modes&ircChanModeInviteOnly && c.inMaskList(ch.inviteList) &&
|
||||||
|
!invitedByChanop {
|
||||||
|
c.sendReply(ERR_INVITEONLYCHAN, channelName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ch.key != "" && (key == "" || key != ch.key) {
|
||||||
|
c.sendReply(ERR_BADCHANNELKEY, channelName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ch.userLimit != -1 && len(ch.userModes) >= ch.userLimit {
|
||||||
|
c.sendReply(ERR_CHANNELISFULL, channelName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.inMaskList(ch.banList) && !c.inMaskList(ch.exceptionList) &&
|
||||||
|
!invitedByChanop {
|
||||||
|
c.sendReply(ERR_BANNEDFROMCHAN, channelName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy any invitation as there's no other way to get rid of it.
|
||||||
|
delete(c.invites, ircToCanon(channelName))
|
||||||
|
|
||||||
|
ch.userModes[c] = userMode
|
||||||
|
|
||||||
|
message := fmt.Sprintf(":%s!%s@%s JOIN %s",
|
||||||
|
c.nickname, c.username, c.hostname, channelName)
|
||||||
|
if 0 == ch.modes&ircChanModeQuiet {
|
||||||
|
ircChannelMulticast(ch, message, nil)
|
||||||
|
} else {
|
||||||
|
c.send(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
ircSendRPLTOPIC(c, ch)
|
||||||
|
ircSendRPLNAMREPLY(c, ch, nil)
|
||||||
|
c.sendReply(RPL_ENDOFNAMES, ch.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircHandleJOIN(msg *message, c *client) {
|
||||||
|
if len(msg.params) < 1 {
|
||||||
|
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.params[0] == "0" {
|
||||||
|
ircPartAllChannels(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetChannels := splitString(msg.params[0], ",", true)
|
||||||
|
|
||||||
|
var keys []string
|
||||||
|
if len(msg.params) > 1 {
|
||||||
|
keys = splitString(msg.params[1], ",", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, name := range targetChannels {
|
||||||
|
key := ""
|
||||||
|
if i < len(keys) {
|
||||||
|
key = keys[i]
|
||||||
|
}
|
||||||
|
ircTryJoin(c, name, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ircHandleSUMMON(msg *message, c *client) {
|
func ircHandleSUMMON(msg *message, c *client) {
|
||||||
|
@ -1679,11 +1940,63 @@ func ircHandleUSERS(msg *message, c *client) {
|
||||||
c.sendReply(ERR_USERSDISABLED)
|
c.sendReply(ERR_USERSDISABLED)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ircHandleAWAY(msg *message, c *client) {
|
||||||
|
if len(msg.params) < 1 {
|
||||||
|
c.awayMessage = ""
|
||||||
|
c.sendReply(RPL_UNAWAY)
|
||||||
|
} else {
|
||||||
|
c.awayMessage = msg.params[0]
|
||||||
|
c.sendReply(RPL_NOWAWAY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircHandleISON(msg *message, c *client) {
|
||||||
|
if len(msg.params) < 1 {
|
||||||
|
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var on []string
|
||||||
|
for _, nick := range msg.params {
|
||||||
|
if client := users[ircToCanon(nick)]; client != nil {
|
||||||
|
on = append(on, nick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.sendReply(RPL_ISON, strings.Join(on, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircHandleADMIN(msg *message, c *client) {
|
||||||
|
if len(msg.params) > 0 && !isThisMe(msg.params[0]) {
|
||||||
|
c.sendReply(ERR_NOSUCHSERVER, msg.params[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.sendReply(ERR_NOADMININFO, serverName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: All the remaining command handlers.
|
||||||
|
|
||||||
|
func ircHandleX(msg *message, c *client) {
|
||||||
|
if len(msg.params) < 1 {
|
||||||
|
c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ircHandleDIE(msg *message, c *client) {
|
||||||
|
if 0 == c.mode&ircUserModeOperator {
|
||||||
|
c.sendReply(ERR_NOPRIVILEGES)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !quitting {
|
||||||
|
initiateQuit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// TODO: Add an index for IRC_ERR_NOSUCHSERVER validation?
|
// TODO: Add an index for IRC_ERR_NOSUCHSERVER validation?
|
||||||
// TODO: Add a minimal parameter count?
|
// TODO: Add a minimal parameter count?
|
||||||
// TODO: Add a field for oper-only commands?
|
// TODO: Add a field for oper-only commands? Use flags?
|
||||||
var ircHandlers = map[string]*ircCommand{
|
var ircHandlers = map[string]*ircCommand{
|
||||||
"CAP": {false, ircHandleCAP, 0, 0},
|
"CAP": {false, ircHandleCAP, 0, 0},
|
||||||
"PASS": {false, ircHandlePASS, 0, 0},
|
"PASS": {false, ircHandlePASS, 0, 0},
|
||||||
|
@ -1700,16 +2013,25 @@ var ircHandlers = map[string]*ircCommand{
|
||||||
"VERSION": {true, ircHandleVERSION, 0, 0},
|
"VERSION": {true, ircHandleVERSION, 0, 0},
|
||||||
"USERS": {true, ircHandleUSERS, 0, 0},
|
"USERS": {true, ircHandleUSERS, 0, 0},
|
||||||
"SUMMON": {true, ircHandleSUMMON, 0, 0},
|
"SUMMON": {true, ircHandleSUMMON, 0, 0},
|
||||||
|
"AWAY": {true, ircHandleAWAY, 0, 0},
|
||||||
|
"ADMIN": {true, ircHandleADMIN, 0, 0},
|
||||||
|
|
||||||
"MODE": {true, ircHandleMODE, 0, 0},
|
"MODE": {true, ircHandleMODE, 0, 0},
|
||||||
"PRIVMSG": {true, ircHandlePRIVMSG, 0, 0},
|
"PRIVMSG": {true, ircHandlePRIVMSG, 0, 0},
|
||||||
"NOTICE": {true, ircHandleNOTICE, 0, 0},
|
"NOTICE": {true, ircHandleNOTICE, 0, 0},
|
||||||
|
"JOIN": {true, ircHandleJOIN, 0, 0},
|
||||||
|
"PART": {true, ircHandlePART, 0, 0},
|
||||||
|
"KICK": {true, ircHandleKICK, 0, 0},
|
||||||
|
"INVITE": {true, ircHandleINVITE, 0, 0},
|
||||||
"TOPIC": {true, ircHandleTOPIC, 0, 0},
|
"TOPIC": {true, ircHandleTOPIC, 0, 0},
|
||||||
"LIST": {true, ircHandleLIST, 0, 0},
|
"LIST": {true, ircHandleLIST, 0, 0},
|
||||||
"NAMES": {true, ircHandleNAMES, 0, 0},
|
"NAMES": {true, ircHandleNAMES, 0, 0},
|
||||||
"WHO": {true, ircHandleWHO, 0, 0},
|
"WHO": {true, ircHandleWHO, 0, 0},
|
||||||
"WHOIS": {true, ircHandleWHOIS, 0, 0},
|
"WHOIS": {true, ircHandleWHOIS, 0, 0},
|
||||||
"WHOWAS": {true, ircHandleWHOWAS, 0, 0},
|
"WHOWAS": {true, ircHandleWHOWAS, 0, 0},
|
||||||
|
"ISON": {true, ircHandleISON, 0, 0},
|
||||||
|
|
||||||
|
"DIE": {true, ircHandleDIE, 0, 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
func ircProcessMessage(c *client, msg *message, raw string) {
|
func ircProcessMessage(c *client, msg *message, raw string) {
|
||||||
|
@ -1739,7 +2061,7 @@ func ircProcessMessage(c *client, msg *message, raw string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- ? -----------------------------------------------------------------------
|
// --- Network I/O -------------------------------------------------------------
|
||||||
|
|
||||||
// Handle the results from initializing the client's connection.
|
// Handle the results from initializing the client's connection.
|
||||||
func (c *client) onPrepared(host string, isTLS bool) {
|
func (c *client) onPrepared(host string, isTLS bool) {
|
||||||
|
|
Loading…
Reference in New Issue