Get labeled-reply working just fine

This commit is contained in:
Daniel Oaks 2018-02-06 00:21:08 +10:00
parent 470109461b
commit e0fa97d344
14 changed files with 526 additions and 472 deletions

View File

@ -27,7 +27,7 @@ const (
var (
// EnabledSaslMechanisms contains the SASL mechanisms that exist and that we support.
// This can be moved to some other data structure/place if we need to load/unload mechs later.
EnabledSaslMechanisms = map[string]func(*Server, *Client, string, []byte) bool{
EnabledSaslMechanisms = map[string]func(*Server, *Client, string, []byte, *ResponseBuffer) bool{
"PLAIN": authPlainHandler,
"EXTERNAL": authExternalHandler,
}
@ -128,9 +128,9 @@ func (client *Client) LogoutOfAccount() {
}
// successfulSaslAuth means that a SASL auth attempt completed successfully, and is used to dispatch messages.
func (client *Client) successfulSaslAuth() {
client.Send(nil, client.server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, client.account.Name, fmt.Sprintf("You are now logged in as %s", client.account.Name))
client.Send(nil, client.server.name, RPL_SASLSUCCESS, client.nick, client.t("SASL authentication successful"))
func (client *Client) successfulSaslAuth(rb *ResponseBuffer) {
rb.Add(nil, client.server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, client.account.Name, fmt.Sprintf("You are now logged in as %s", client.account.Name))
rb.Add(nil, client.server.name, RPL_SASLSUCCESS, client.nick, client.t("SASL authentication successful"))
// dispatch account-notify
for friend := range client.Friends(caps.AccountNotify) {

View File

@ -171,7 +171,7 @@ func (channel *Channel) regenerateMembersCache(noLocksNeeded bool) {
}
// Names sends the list of users joined to the channel to the given client.
func (channel *Channel) Names(client *Client) {
func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
currentNicks := channel.nicks(client)
// assemble and send replies
maxNamLen := 480 - len(client.server.name) - len(client.nick)
@ -183,7 +183,7 @@ func (channel *Channel) Names(client *Client) {
}
if len(buffer)+1+len(nick) > maxNamLen {
client.Send(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, buffer)
rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, buffer)
buffer = nick
continue
}
@ -192,8 +192,8 @@ func (channel *Channel) Names(client *Client) {
buffer += nick
}
client.Send(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, buffer)
client.Send(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list"))
rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, buffer)
rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list"))
}
// ClientIsAtLeast returns whether the client has at least the given channel privilege.
@ -349,44 +349,52 @@ func (channel *Channel) IsEmpty() bool {
// Join joins the given client to this channel (if they can be joined).
//TODO(dan): /SAJOIN and maybe a ForceJoin function?
func (channel *Channel) Join(client *Client, key string) {
func (channel *Channel) Join(client *Client, key string, rb *ResponseBuffer) {
if channel.hasClient(client) {
// already joined, no message needs to be sent
return
}
if channel.IsFull() {
client.Send(nil, client.server.name, ERR_CHANNELISFULL, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l"))
rb.Add(nil, client.server.name, ERR_CHANNELISFULL, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l"))
return
}
if !channel.CheckKey(key) {
client.Send(nil, client.server.name, ERR_BADCHANNELKEY, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "k"))
rb.Add(nil, client.server.name, ERR_BADCHANNELKEY, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "k"))
return
}
isInvited := channel.lists[modes.InviteMask].Match(client.nickMaskCasefolded)
if channel.flags[modes.InviteOnly] && !isInvited {
client.Send(nil, client.server.name, ERR_INVITEONLYCHAN, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i"))
rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i"))
return
}
if channel.lists[modes.BanMask].Match(client.nickMaskCasefolded) &&
!isInvited &&
!channel.lists[modes.ExceptMask].Match(client.nickMaskCasefolded) {
client.Send(nil, client.server.name, ERR_BANNEDFROMCHAN, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b"))
rb.Add(nil, client.server.name, ERR_BANNEDFROMCHAN, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b"))
return
}
client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", client.nick, channel.name))
for _, member := range channel.Members() {
if member == client {
if member.capabilities.Has(caps.ExtendedJoin) {
rb.Add(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname)
} else {
rb.Add(nil, client.nickMaskString, "JOIN", channel.name)
}
} else {
if member.capabilities.Has(caps.ExtendedJoin) {
member.Send(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname)
} else {
member.Send(nil, client.nickMaskString, "JOIN", channel.name)
}
}
}
channel.stateMutex.Lock()
channel.members.Add(client)
@ -411,41 +419,49 @@ func (channel *Channel) Join(client *Client, key string) {
}
if client.capabilities.Has(caps.ExtendedJoin) {
client.Send(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname)
rb.Add(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname)
} else {
client.Send(nil, client.nickMaskString, "JOIN", channel.name)
rb.Add(nil, client.nickMaskString, "JOIN", channel.name)
}
// don't send topic when it's an entirely new channel
if !newChannel {
channel.SendTopic(client)
channel.SendTopic(client, rb)
}
channel.Names(client)
channel.Names(client, rb)
if givenMode != nil {
for _, member := range channel.Members() {
if member == client {
rb.Add(nil, client.server.name, "MODE", channel.name, fmt.Sprintf("+%v", *givenMode), client.nick)
} else {
member.Send(nil, client.server.name, "MODE", channel.name, fmt.Sprintf("+%v", *givenMode), client.nick)
}
}
}
}
// Part parts the given client from this channel, with the given message.
func (channel *Channel) Part(client *Client, message string) {
func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer) {
if !channel.hasClient(client) {
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
return
}
for _, member := range channel.Members() {
if member == client {
rb.Add(nil, client.nickMaskString, "PART", channel.name, message)
} else {
member.Send(nil, client.nickMaskString, "PART", channel.name, message)
}
}
channel.Quit(client)
client.server.logger.Debug("part", fmt.Sprintf("%s left channel %s", client.nick, channel.name))
}
// SendTopic sends the channel topic to the given client.
func (channel *Channel) SendTopic(client *Client) {
func (channel *Channel) SendTopic(client *Client, rb *ResponseBuffer) {
if !channel.hasClient(client) {
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, client.nick, channel.name, client.t("You're not on that channel"))
rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.nick, channel.name, client.t("You're not on that channel"))
return
}
@ -457,23 +473,23 @@ func (channel *Channel) SendTopic(client *Client) {
channel.stateMutex.RUnlock()
if topic == "" {
client.Send(nil, client.server.name, RPL_NOTOPIC, client.nick, name, client.t("No topic is set"))
rb.Add(nil, client.server.name, RPL_NOTOPIC, client.nick, name, client.t("No topic is set"))
return
}
client.Send(nil, client.server.name, RPL_TOPIC, client.nick, name, topic)
client.Send(nil, client.server.name, RPL_TOPICTIME, client.nick, name, topicSetBy, strconv.FormatInt(topicSetTime.Unix(), 10))
rb.Add(nil, client.server.name, RPL_TOPIC, client.nick, name, topic)
rb.Add(nil, client.server.name, RPL_TOPICTIME, client.nick, name, topicSetBy, strconv.FormatInt(topicSetTime.Unix(), 10))
}
// SetTopic sets the topic of this channel, if the client is allowed to do so.
func (channel *Channel) SetTopic(client *Client, topic string) {
func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffer) {
if !(client.flags[modes.Operator] || channel.hasClient(client)) {
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
return
}
if channel.HasMode(modes.OpOnlyTopic) && !channel.ClientIsAtLeast(client, modes.ChannelOperator) {
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
return
}
@ -488,8 +504,12 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
channel.stateMutex.Unlock()
for _, member := range channel.Members() {
if member == client {
rb.Add(nil, client.nickMaskString, "TOPIC", channel.name, topic)
} else {
member.Send(nil, client.nickMaskString, "TOPIC", channel.name, topic)
}
}
go channel.server.channelRegistry.StoreChannel(channel, false)
}
@ -513,14 +533,14 @@ func (channel *Channel) CanSpeak(client *Client) bool {
}
// TagMsg sends a tag message to everyone in this channel who can accept them.
func (channel *Channel) TagMsg(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client) {
channel.sendMessage(msgid, "TAGMSG", []caps.Capability{caps.MessageTags}, minPrefix, clientOnlyTags, client, nil)
func (channel *Channel) TagMsg(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, rb *ResponseBuffer) {
channel.sendMessage(msgid, "TAGMSG", []caps.Capability{caps.MessageTags}, minPrefix, clientOnlyTags, client, nil, rb)
}
// sendMessage sends a given message to everyone on this channel.
func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capability, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message *string) {
func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capability, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message *string, rb *ResponseBuffer) {
if !channel.CanSpeak(client) {
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
return
}
@ -554,26 +574,26 @@ func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capab
}
if message == nil {
member.SendFromClient(msgid, client, messageTagsToUse, cmd, channel.name)
rb.AddFromClient(msgid, client, messageTagsToUse, cmd, channel.name)
} else {
member.SendFromClient(msgid, client, messageTagsToUse, cmd, channel.name, *message)
rb.AddFromClient(msgid, client, messageTagsToUse, cmd, channel.name, *message)
}
}
}
// SplitPrivMsg sends a private message to everyone in this channel.
func (channel *Channel) SplitPrivMsg(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message SplitMessage) {
channel.sendSplitMessage(msgid, "PRIVMSG", minPrefix, clientOnlyTags, client, &message)
func (channel *Channel) SplitPrivMsg(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message SplitMessage, rb *ResponseBuffer) {
channel.sendSplitMessage(msgid, "PRIVMSG", minPrefix, clientOnlyTags, client, &message, rb)
}
// SplitNotice sends a private message to everyone in this channel.
func (channel *Channel) SplitNotice(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message SplitMessage) {
channel.sendSplitMessage(msgid, "NOTICE", minPrefix, clientOnlyTags, client, &message)
func (channel *Channel) SplitNotice(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message SplitMessage, rb *ResponseBuffer) {
channel.sendSplitMessage(msgid, "NOTICE", minPrefix, clientOnlyTags, client, &message, rb)
}
func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message *SplitMessage) {
func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message *SplitMessage, rb *ResponseBuffer) {
if !channel.CanSpeak(client) {
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
return
}
@ -595,25 +615,33 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *modes.Mod
tagsToUse = clientOnlyTags
}
if member == client {
if message == nil {
rb.AddFromClient(msgid, client, tagsToUse, cmd, channel.name)
} else {
rb.AddSplitMessageFromClient(msgid, client, tagsToUse, cmd, channel.name, *message)
}
} else {
if message == nil {
member.SendFromClient(msgid, client, tagsToUse, cmd, channel.name)
} else {
member.SendSplitMsgFromClient(msgid, client, tagsToUse, cmd, channel.name, *message)
}
}
}
}
func (channel *Channel) applyModeMemberNoMutex(client *Client, mode modes.Mode, op modes.ModeOp, nick string) *modes.ModeChange {
func (channel *Channel) applyModeMemberNoMutex(client *Client, mode modes.Mode, op modes.ModeOp, nick string, rb *ResponseBuffer) *modes.ModeChange {
if nick == "" {
//TODO(dan): shouldn't this be handled before it reaches this function?
client.Send(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", client.t("Not enough parameters"))
rb.Add(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", client.t("Not enough parameters"))
return nil
}
casefoldedName, err := CasefoldName(nick)
target := channel.server.clients.Get(casefoldedName)
if err != nil || target == nil {
client.Send(nil, client.server.name, ERR_NOSUCHNICK, client.nick, nick, client.t("No such nick"))
rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, nick, client.t("No such nick"))
return nil
}
@ -628,7 +656,7 @@ func (channel *Channel) applyModeMemberNoMutex(client *Client, mode modes.Mode,
channel.stateMutex.Unlock()
if !exists {
client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, client.t("They aren't on that channel"))
rb.Add(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, client.t("They aren't on that channel"))
return nil
} else if already {
return nil
@ -642,7 +670,7 @@ func (channel *Channel) applyModeMemberNoMutex(client *Client, mode modes.Mode,
}
// ShowMaskList shows the given list to the client.
func (channel *Channel) ShowMaskList(client *Client, mode modes.Mode) {
func (channel *Channel) ShowMaskList(client *Client, mode modes.Mode, rb *ResponseBuffer) {
// choose appropriate modes
var rpllist, rplendoflist string
if mode == modes.BanMask {
@ -660,14 +688,14 @@ func (channel *Channel) ShowMaskList(client *Client, mode modes.Mode) {
channel.stateMutex.RLock()
// XXX don't acquire any new locks in this section, besides Socket.Write
for mask := range channel.lists[mode].masks {
client.Send(nil, client.server.name, rpllist, nick, channel.name, mask)
rb.Add(nil, client.server.name, rpllist, nick, channel.name, mask)
}
channel.stateMutex.RUnlock()
client.Send(nil, client.server.name, rplendoflist, nick, channel.name, client.t("End of list"))
rb.Add(nil, client.server.name, rplendoflist, nick, channel.name, client.t("End of list"))
}
func (channel *Channel) applyModeMask(client *Client, mode modes.Mode, op modes.ModeOp, mask string) bool {
func (channel *Channel) applyModeMask(client *Client, mode modes.Mode, op modes.ModeOp, mask string, rb *ResponseBuffer) bool {
list := channel.lists[mode]
if list == nil {
// This should never happen, but better safe than panicky.
@ -675,12 +703,12 @@ func (channel *Channel) applyModeMask(client *Client, mode modes.Mode, op modes.
}
if (op == modes.List) || (mask == "") {
channel.ShowMaskList(client, mode)
channel.ShowMaskList(client, mode, rb)
return false
}
if !channel.ClientIsAtLeast(client, modes.ChannelOperator) {
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
return false
}
@ -705,21 +733,21 @@ func (channel *Channel) Quit(client *Client) {
client.removeChannel(channel)
}
func (channel *Channel) Kick(client *Client, target *Client, comment string) {
func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer) {
if !(client.flags[modes.Operator] || channel.hasClient(client)) {
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
return
}
if !channel.ClientIsAtLeast(client, modes.ChannelOperator) {
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
return
}
if !channel.hasClient(target) {
client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, client.t("They aren't on that channel"))
rb.Add(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, client.t("They aren't on that channel"))
return
}
if !channel.ClientHasPrivsOver(client, target) {
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
return
}
@ -738,14 +766,14 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string) {
}
// Invite invites the given client to the channel, if the inviter can do so.
func (channel *Channel) Invite(invitee *Client, inviter *Client) {
func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuffer) {
if channel.flags[modes.InviteOnly] && !channel.ClientIsAtLeast(inviter, modes.ChannelOperator) {
inviter.Send(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, channel.name, inviter.t("You're not a channel operator"))
rb.Add(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, channel.name, inviter.t("You're not a channel operator"))
return
}
if !channel.hasClient(inviter) {
inviter.Send(nil, inviter.server.name, ERR_NOTONCHANNEL, channel.name, inviter.t("You're not on that channel"))
rb.Add(nil, inviter.server.name, ERR_NOTONCHANNEL, channel.name, inviter.t("You're not on that channel"))
return
}
@ -764,9 +792,9 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
}
//TODO(dan): should inviter.server.name here be inviter.nickMaskString ?
inviter.Send(nil, inviter.server.name, RPL_INVITING, invitee.nick, channel.name)
rb.Add(nil, inviter.server.name, RPL_INVITING, invitee.nick, channel.name)
invitee.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
if invitee.flags[modes.Away] {
inviter.Send(nil, inviter.server.name, RPL_AWAY, invitee.nick, invitee.awayMessage)
rb.Add(nil, inviter.server.name, RPL_AWAY, invitee.nick, invitee.awayMessage)
}
}

View File

@ -45,7 +45,7 @@ func (cm *ChannelManager) Get(name string) *Channel {
}
// Join causes `client` to join the channel named `name`, creating it if necessary.
func (cm *ChannelManager) Join(client *Client, name string, key string) error {
func (cm *ChannelManager) Join(client *Client, name string, key string, rb *ResponseBuffer) error {
server := client.server
casefoldedName, err := CasefoldChannel(name)
if err != nil || len(casefoldedName) > server.Limits().ChannelLen {
@ -74,7 +74,7 @@ func (cm *ChannelManager) Join(client *Client, name string, key string) error {
entry.pendingJoins += 1
cm.Unlock()
entry.channel.Join(client, key)
entry.channel.Join(client, key, rb)
cm.maybeCleanup(entry, true)
@ -107,7 +107,7 @@ func (cm *ChannelManager) maybeCleanup(entry *channelManagerEntry, afterJoin boo
}
// Part parts `client` from the channel named `name`, deleting it if it's empty.
func (cm *ChannelManager) Part(client *Client, name string, message string) error {
func (cm *ChannelManager) Part(client *Client, name string, message string, rb *ResponseBuffer) error {
casefoldedName, err := CasefoldChannel(name)
if err != nil {
return errNoSuchChannel
@ -120,7 +120,7 @@ func (cm *ChannelManager) Part(client *Client, name string, message string) erro
if entry == nil {
return errNoSuchChannel
}
entry.channel.Part(client, message)
entry.channel.Part(client, message, rb)
cm.maybeCleanup(entry, false)
return nil
}

View File

@ -13,17 +13,17 @@ import (
)
// ChanServNotice sends the client a notice from ChanServ.
func (client *Client) ChanServNotice(text string) {
client.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "NOTICE", client.nick, text)
func (rb *ResponseBuffer) ChanServNotice(text string) {
rb.Add(nil, fmt.Sprintf("ChanServ!services@%s", rb.target.server.name), "NOTICE", rb.target.nick, text)
}
// chanservReceiveNotice handles NOTICEs that ChanServ receives.
func (server *Server) chanservNoticeHandler(client *Client, message string) {
func (server *Server) chanservNoticeHandler(client *Client, message string, rb *ResponseBuffer) {
// do nothing
}
// chanservReceiveNotice handles NOTICEs that ChanServ receives.
func (server *Server) chanservPrivmsgHandler(client *Client, message string) {
func (server *Server) chanservPrivmsgHandler(client *Client, message string, rb *ResponseBuffer) {
var params []string
for _, p := range strings.Split(message, " ") {
if len(p) > 0 {
@ -31,7 +31,7 @@ func (server *Server) chanservPrivmsgHandler(client *Client, message string) {
}
}
if len(params) < 1 {
client.ChanServNotice(client.t("You need to run a command"))
rb.ChanServNotice(client.t("You need to run a command"))
//TODO(dan): dump CS help here
return
}
@ -41,57 +41,57 @@ func (server *Server) chanservPrivmsgHandler(client *Client, message string) {
if command == "register" {
if len(params) < 2 {
client.ChanServNotice(client.t("Syntax: REGISTER <channel>"))
rb.ChanServNotice(client.t("Syntax: REGISTER <channel>"))
return
}
server.chanservRegisterHandler(client, params[1])
server.chanservRegisterHandler(client, params[1], rb)
} else {
client.ChanServNotice(client.t("Sorry, I don't know that command"))
rb.ChanServNotice(client.t("Sorry, I don't know that command"))
}
}
// chanservRegisterHandler handles the ChanServ REGISTER subcommand.
func (server *Server) chanservRegisterHandler(client *Client, channelName string) {
func (server *Server) chanservRegisterHandler(client *Client, channelName string, rb *ResponseBuffer) {
if !server.channelRegistrationEnabled {
client.ChanServNotice(client.t("Channel registration is not enabled"))
rb.ChanServNotice(client.t("Channel registration is not enabled"))
return
}
channelKey, err := CasefoldChannel(channelName)
if err != nil {
client.ChanServNotice(client.t("Channel name is not valid"))
rb.ChanServNotice(client.t("Channel name is not valid"))
return
}
channelInfo := server.channels.Get(channelKey)
if channelInfo == nil || !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
client.ChanServNotice(client.t("You must be an oper on the channel to register it"))
rb.ChanServNotice(client.t("You must be an oper on the channel to register it"))
return
}
if client.account == &NoAccount {
client.ChanServNotice(client.t("You must be logged in to register a channel"))
rb.ChanServNotice(client.t("You must be logged in to register a channel"))
return
}
// this provides the synchronization that allows exactly one registration of the channel:
err = channelInfo.SetRegistered(client.AccountName())
if err != nil {
client.ChanServNotice(err.Error())
rb.ChanServNotice(err.Error())
return
}
// registration was successful: make the database reflect it
go server.channelRegistry.StoreChannel(channelInfo, true)
client.ChanServNotice(fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
rb.ChanServNotice(fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
// give them founder privs
change := channelInfo.applyModeMemberNoMutex(client, modes.ChannelFounder, modes.Add, client.NickCasefolded())
change := channelInfo.applyModeMemberNoMutex(client, modes.ChannelFounder, modes.Add, client.NickCasefolded(), rb)
if change != nil {
//TODO(dan): we should change the name of String and make it return a slice here
//TODO(dan): unify this code with code in modes.go

View File

@ -593,12 +593,12 @@ func (client *Client) LoggedIntoAccount() bool {
}
// RplISupport outputs our ISUPPORT lines to the client. This is used on connection and in VERSION responses.
func (client *Client) RplISupport() {
func (client *Client) RplISupport(rb *ResponseBuffer) {
translatedISupport := client.t("are supported by this server")
for _, tokenline := range client.server.ISupport().CachedReply {
// ugly trickery ahead
tokenline = append(tokenline, translatedISupport)
client.Send(nil, client.server.name, RPL_ISUPPORT, append([]string{client.nick}, tokenline...)...)
rb.Add(nil, client.server.name, RPL_ISUPPORT, append([]string{client.nick}, tokenline...)...)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -658,7 +658,7 @@ func GenerateHelpIndices(lm *languages.Manager) error {
}
// sendHelp sends the client help of the given string.
func (client *Client) sendHelp(name string, text string) {
func (client *Client) sendHelp(name string, text string, rb *ResponseBuffer) {
splitName := strings.Split(name, " ")
textLines := strings.Split(text, "\n")
@ -666,14 +666,14 @@ func (client *Client) sendHelp(name string, text string) {
args := splitName
args = append(args, line)
if i == 0 {
client.Send(nil, client.server.name, RPL_HELPSTART, args...)
rb.Add(nil, client.server.name, RPL_HELPSTART, args...)
} else {
client.Send(nil, client.server.name, RPL_HELPTXT, args...)
rb.Add(nil, client.server.name, RPL_HELPTXT, args...)
}
}
args := splitName
args = append(args, client.t("End of /HELPOP"))
client.Send(nil, client.server.name, RPL_ENDOFHELP, args...)
rb.Add(nil, client.server.name, RPL_ENDOFHELP, args...)
}
// GetHelpIndex returns the help index for the given language.

View File

@ -169,7 +169,7 @@ func ParseChannelModeChanges(params ...string) (modes.ModeChanges, map[rune]bool
}
// ApplyChannelModeChanges applies a given set of mode changes.
func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges) modes.ModeChanges {
func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) modes.ModeChanges {
// so we only output one warning for each list type when full
listFullWarned := make(map[modes.Mode]bool)
@ -222,7 +222,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
switch change.Mode {
case modes.BanMask, modes.ExceptMask, modes.InviteMask:
if isListOp(change) {
channel.ShowMaskList(client, change.Mode)
channel.ShowMaskList(client, change.Mode, rb)
continue
}
@ -236,7 +236,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
case modes.Add:
if channel.lists[change.Mode].Length() >= client.server.Limits().ChanListModes {
if !listFullWarned[change.Mode] {
client.Send(nil, client.server.name, ERR_BANLISTFULL, client.Nick(), channel.Name(), change.Mode.String(), client.t("Channel list is full"))
rb.Add(nil, client.server.name, ERR_BANLISTFULL, client.Nick(), channel.Name(), change.Mode.String(), client.t("Channel list is full"))
listFullWarned[change.Mode] = true
}
continue
@ -289,7 +289,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
continue
}
change := channel.applyModeMemberNoMutex(client, change.Mode, change.Op, change.Arg)
change := channel.applyModeMemberNoMutex(client, change.Mode, change.Op, change.Arg, rb)
if change != nil {
applied = append(applied, *change)
}

View File

@ -103,7 +103,7 @@ func (manager *MonitorManager) List(client *Client) (nicks []string) {
}
var (
metadataSubcommands = map[string]func(server *Server, client *Client, msg ircmsg.IrcMessage) bool{
metadataSubcommands = map[string]func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool{
"-": monitorRemoveHandler,
"+": monitorAddHandler,
"c": monitorClearHandler,

View File

@ -20,17 +20,17 @@ var (
}
)
func performNickChange(server *Server, client *Client, target *Client, newnick string) bool {
func performNickChange(server *Server, client *Client, target *Client, newnick string, rb *ResponseBuffer) bool {
nickname := strings.TrimSpace(newnick)
cfnick, err := CasefoldName(nickname)
if len(nickname) < 1 {
client.Send(nil, server.name, ERR_NONICKNAMEGIVEN, client.nick, client.t("No nickname given"))
rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, client.nick, client.t("No nickname given"))
return false
}
if err != nil || len(nickname) > server.Limits().NickLen || restrictedNicknames[cfnick] {
client.Send(nil, server.name, ERR_ERRONEUSNICKNAME, client.nick, nickname, client.t("Erroneous nickname"))
rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, client.nick, nickname, client.t("Erroneous nickname"))
return false
}
@ -43,10 +43,10 @@ func performNickChange(server *Server, client *Client, target *Client, newnick s
origNickMask := target.NickMaskString()
err = client.server.clients.SetNick(target, nickname)
if err == errNicknameInUse {
client.Send(nil, server.name, ERR_NICKNAMEINUSE, client.nick, nickname, client.t("Nickname is already in use"))
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, client.nick, nickname, client.t("Nickname is already in use"))
return false
} else if err != nil {
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.nick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
return false
}

View File

@ -39,18 +39,18 @@ func extractParam(line string) (string, string) {
}
// nickservNoticeHandler handles NOTICEs that NickServ receives.
func (server *Server) nickservNoticeHandler(client *Client, message string) {
func (server *Server) nickservNoticeHandler(client *Client, message string, rb *ResponseBuffer) {
// do nothing
}
// nickservPrivmsgHandler handles PRIVMSGs that NickServ receives.
func (server *Server) nickservPrivmsgHandler(client *Client, message string) {
func (server *Server) nickservPrivmsgHandler(client *Client, message string, rb *ResponseBuffer) {
command, params := extractParam(message)
command = strings.ToLower(command)
if command == "help" {
for _, line := range strings.Split(nickservHelp, "\n") {
client.Notice(line)
rb.Notice(line)
}
} else if command == "register" {
// get params
@ -58,30 +58,30 @@ func (server *Server) nickservPrivmsgHandler(client *Client, message string) {
// fail out if we need to
if username == "" {
client.Notice(client.t("No username supplied"))
rb.Notice(client.t("No username supplied"))
return
}
server.nickservRegisterHandler(client, username, passphrase)
server.nickservRegisterHandler(client, username, passphrase, rb)
} else if command == "identify" {
// get params
username, passphrase := extractParam(params)
server.nickservIdentifyHandler(client, username, passphrase)
server.nickservIdentifyHandler(client, username, passphrase, rb)
} else {
client.Notice(client.t("Command not recognised. To see the available commands, run /NS HELP"))
rb.Notice(client.t("Command not recognised. To see the available commands, run /NS HELP"))
}
}
func (server *Server) nickservRegisterHandler(client *Client, username, passphrase string) {
func (server *Server) nickservRegisterHandler(client *Client, username, passphrase string, rb *ResponseBuffer) {
certfp := client.certfp
if passphrase == "" && certfp == "" {
client.Notice(client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
rb.Notice(client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
return
}
if !server.accountRegistration.Enabled {
client.Notice(client.t("Account registration has been disabled"))
rb.Notice(client.t("Account registration has been disabled"))
return
}
@ -89,7 +89,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
if server.accountRegistration.AllowMultiplePerConnection {
client.LogoutOfAccount()
} else {
client.Notice(client.t("You're already logged into an account"))
rb.Notice(client.t("You're already logged into an account"))
return
}
}
@ -99,7 +99,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
casefoldedAccount, err := CasefoldName(account)
// probably don't need explicit check for "*" here... but let's do it anyway just to make sure
if err != nil || username == "*" {
client.Notice(client.t("Account name is not valid"))
rb.Notice(client.t("Account name is not valid"))
return
}
@ -111,7 +111,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
_, err := tx.Get(accountKey)
if err != buntdb.ErrNotFound {
//TODO(dan): if account verified key doesn't exist account is not verified, calc the maximum time without verification and expire and continue if need be
client.Notice(client.t("Account already exists"))
rb.Notice(client.t("Account already exists"))
return errAccountCreation
}
@ -126,7 +126,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
// account could not be created and relevant numerics have been dispatched, abort
if err != nil {
if err != errAccountCreation {
client.Notice(client.t("Account registration failed"))
rb.Notice(client.t("Account registration failed"))
}
return
}
@ -178,7 +178,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
if err == errCertfpAlreadyExists {
errMsg = "An account already exists for your certificate fingerprint"
}
client.Notice(errMsg)
rb.Notice(errMsg)
removeFailedAccRegisterData(server.store, casefoldedAccount)
return
}
@ -196,23 +196,23 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
server.accounts[casefoldedAccount] = &account
client.account = &account
client.Notice(client.t("Account created"))
client.Send(nil, server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, account.Name, fmt.Sprintf(client.t("You are now logged in as %s"), account.Name))
client.Send(nil, server.name, RPL_SASLSUCCESS, client.nick, client.t("Authentication successful"))
rb.Notice(client.t("Account created"))
rb.Add(nil, server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, account.Name, fmt.Sprintf(client.t("You are now logged in as %s"), account.Name))
rb.Add(nil, server.name, RPL_SASLSUCCESS, client.nick, client.t("Authentication successful"))
server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Account registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), account.Name, client.nickMaskString))
return nil
})
if err != nil {
client.Notice(client.t("Account registration failed"))
rb.Notice(client.t("Account registration failed"))
removeFailedAccRegisterData(server.store, casefoldedAccount)
return
}
}
func (server *Server) nickservIdentifyHandler(client *Client, username, passphrase string) {
func (server *Server) nickservIdentifyHandler(client *Client, username, passphrase string, rb *ResponseBuffer) {
// fail out if we need to
if !server.accountAuthenticationEnabled {
client.Notice(client.t("Login has been disabled"))
rb.Notice(client.t("Login has been disabled"))
return
}
@ -221,7 +221,7 @@ func (server *Server) nickservIdentifyHandler(client *Client, username, passphra
// keep it the same as in the ACC CREATE stage
accountKey, err := CasefoldName(username)
if err != nil {
client.Notice(client.t("Could not login with your username/password"))
rb.Notice(client.t("Could not login with your username/password"))
return
}
@ -259,7 +259,7 @@ func (server *Server) nickservIdentifyHandler(client *Client, username, passphra
})
if err == nil {
client.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
rb.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
return
}
}
@ -306,10 +306,10 @@ func (server *Server) nickservIdentifyHandler(client *Client, username, passphra
})
if err == nil {
client.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
rb.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
return
}
}
client.Notice(client.t("Could not login with your TLS certificate or supplied username/password"))
rb.Notice(client.t("Could not login with your TLS certificate or supplied username/password"))
}

View File

@ -77,7 +77,7 @@ func (rb *ResponseBuffer) AddSplitMessageFromClient(msgid string, from *Client,
func (rb *ResponseBuffer) Send() error {
// fall out if no messages to send
if len(rb.messages) == 0 {
return
return nil
}
// make batch and all if required
@ -126,3 +126,8 @@ func (rb *ResponseBuffer) Send() error {
return nil
}
// Notice sends the client the given notice from the server.
func (rb *ResponseBuffer) Notice(text string) {
rb.Add(nil, rb.target.server.name, "NOTICE", rb.target.nick, text)
}

View File

@ -15,7 +15,7 @@ const (
sceneNickMask = "=Scene=!%s@npc.fakeuser.invalid"
)
func sendRoleplayMessage(server *Server, client *Client, source string, targetString string, isAction bool, message string) {
func sendRoleplayMessage(server *Server, client *Client, source string, targetString string, isAction bool, message string, rb *ResponseBuffer) {
if isAction {
message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
} else {
@ -26,17 +26,17 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
if cerr == nil {
channel := server.channels.Get(target)
if channel == nil {
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, targetString, client.t("No such channel"))
rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, targetString, client.t("No such channel"))
return
}
if !channel.CanSpeak(client) {
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
return
}
if !channel.flags[modes.ChanRoleplaying] {
client.Send(nil, client.server.name, ERR_CANNOTSENDRP, channel.name, client.t("Channel doesn't have roleplaying mode available"))
rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, channel.name, client.t("Channel doesn't have roleplaying mode available"))
return
}
@ -44,28 +44,32 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
if member == client && !client.capabilities.Has(caps.EchoMessage) {
continue
}
if member == client {
rb.Add(nil, source, "PRIVMSG", channel.name, message)
} else {
member.Send(nil, source, "PRIVMSG", channel.name, message)
}
}
} else {
target, err := CasefoldName(targetString)
user := server.clients.Get(target)
if err != nil || user == nil {
client.Send(nil, server.name, ERR_NOSUCHNICK, client.nick, target, client.t("No such nick"))
rb.Add(nil, server.name, ERR_NOSUCHNICK, client.nick, target, client.t("No such nick"))
return
}
if !user.flags[modes.UserRoleplaying] {
client.Send(nil, client.server.name, ERR_CANNOTSENDRP, user.nick, client.t("User doesn't have roleplaying mode enabled"))
rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, user.nick, client.t("User doesn't have roleplaying mode enabled"))
return
}
user.Send(nil, source, "PRIVMSG", user.nick, message)
if client.capabilities.Has(caps.EchoMessage) {
client.Send(nil, source, "PRIVMSG", user.nick, message)
rb.Add(nil, source, "PRIVMSG", user.nick, message)
}
if user.flags[modes.Away] {
//TODO(dan): possibly implement cooldown of away notifications to users
client.Send(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
rb.Add(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
}
}
}

View File

@ -458,8 +458,12 @@ func (server *Server) tryRegister(c *Client) {
c.Send(nil, server.name, RPL_CREATED, c.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
//TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
c.Send(nil, server.name, RPL_MYINFO, c.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
c.RplISupport()
server.MOTD(c)
rb := NewResponseBuffer(c)
c.RplISupport(rb)
server.MOTD(c, rb)
rb.Send()
c.Send(nil, c.nickMaskString, RPL_UMODEIS, c.nick, c.ModeString())
if server.logger.IsLoggingRawIO() {
c.Notice(c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
@ -478,8 +482,10 @@ func (server *Server) tryRegister(c *Client) {
} else {
c.Send(nil, c.nickMaskString, "JOIN", channel.name)
}
channel.SendTopic(c)
channel.Names(c)
// reuse the last rb
channel.SendTopic(c, rb)
channel.Names(c, rb)
rb.Send()
// construct and send fake modestring if necessary
c.stateMutex.RLock()
@ -511,21 +517,21 @@ func (client *Client) t(originalString string) string {
}
// MOTD serves the Message of the Day.
func (server *Server) MOTD(client *Client) {
func (server *Server) MOTD(client *Client, rb *ResponseBuffer) {
server.configurableStateMutex.RLock()
motdLines := server.motdLines
server.configurableStateMutex.RUnlock()
if len(motdLines) < 1 {
client.Send(nil, server.name, ERR_NOMOTD, client.nick, client.t("MOTD File is missing"))
rb.Add(nil, server.name, ERR_NOMOTD, client.nick, client.t("MOTD File is missing"))
return
}
client.Send(nil, server.name, RPL_MOTDSTART, client.nick, fmt.Sprintf(client.t("- %s Message of the day - "), server.name))
rb.Add(nil, server.name, RPL_MOTDSTART, client.nick, fmt.Sprintf(client.t("- %s Message of the day - "), server.name))
for _, line := range motdLines {
client.Send(nil, server.name, RPL_MOTD, client.nick, line)
rb.Add(nil, server.name, RPL_MOTD, client.nick, line)
}
client.Send(nil, server.name, RPL_ENDOFMOTD, client.nick, client.t("End of MOTD command"))
rb.Add(nil, server.name, RPL_ENDOFMOTD, client.nick, client.t("End of MOTD command"))
}
// wordWrap wraps the given text into a series of lines that don't exceed lineWidth characters.
@ -650,7 +656,7 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
// rplWhoReply returns the WHO reply between one user and another channel/user.
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
// :<hopcount> <real name>
func (target *Client) rplWhoReply(channel *Channel, client *Client) {
func (target *Client) rplWhoReply(channel *Channel, client *Client, rb *ResponseBuffer) {
channelName := "*"
flags := ""
@ -667,13 +673,13 @@ func (target *Client) rplWhoReply(channel *Channel, client *Client) {
flags += channel.ClientPrefixes(client, target.capabilities.Has(caps.MultiPrefix))
channelName = channel.name
}
target.Send(nil, target.server.name, RPL_WHOREPLY, target.nick, channelName, client.Username(), client.Hostname(), client.server.name, client.Nick(), flags, strconv.Itoa(client.hops)+" "+client.Realname())
rb.Add(nil, target.server.name, RPL_WHOREPLY, target.nick, channelName, client.Username(), client.Hostname(), client.server.name, client.Nick(), flags, strconv.Itoa(client.hops)+" "+client.Realname())
}
func whoChannel(client *Client, channel *Channel, friends ClientSet) {
func whoChannel(client *Client, channel *Channel, friends ClientSet, rb *ResponseBuffer) {
for _, member := range channel.Members() {
if !client.flags[modes.Invisible] || friends[client] {
client.rplWhoReply(channel, member)
client.rplWhoReply(channel, member, rb)
}
}
}
@ -1140,7 +1146,7 @@ func (matcher *elistMatcher) Matches(channel *Channel) bool {
}
// RplList returns the RPL_LIST numeric for the given channel.
func (target *Client) RplList(channel *Channel) {
func (target *Client) RplList(channel *Channel, rb *ResponseBuffer) {
// get the correct number of channel members
var memberCount int
if target.flags[modes.Operator] || channel.hasClient(target) {
@ -1153,44 +1159,7 @@ func (target *Client) RplList(channel *Channel) {
}
}
target.Send(nil, target.server.name, RPL_LIST, target.nick, channel.name, strconv.Itoa(memberCount), channel.topic)
}
// NAMES [<channel>{,<channel>}]
func namesHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
var channels []string
if len(msg.Params) > 0 {
channels = strings.Split(msg.Params[0], ",")
}
//var target string
//if len(msg.Params) > 1 {
// target = msg.Params[1]
//}
if len(channels) == 0 {
for _, channel := range server.channels.Channels() {
channel.Names(client)
}
return false
}
// limit regular users to only listing one channel
if !client.flags[modes.Operator] {
channels = channels[:1]
}
for _, chname := range channels {
casefoldedChname, err := CasefoldChannel(chname)
channel := server.channels.Get(casefoldedChname)
if err != nil || channel == nil {
if len(chname) > 0 {
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, chname, client.t("No such channel"))
}
continue
}
channel.Names(client)
}
return false
rb.Add(nil, target.server.name, RPL_LIST, target.nick, channel.name, strconv.Itoa(memberCount), channel.topic)
}
// ResumeDetails are the details that we use to resume connections.