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 ( var (
// EnabledSaslMechanisms contains the SASL mechanisms that exist and that we support. // 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. // 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, "PLAIN": authPlainHandler,
"EXTERNAL": authExternalHandler, "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. // successfulSaslAuth means that a SASL auth attempt completed successfully, and is used to dispatch messages.
func (client *Client) successfulSaslAuth() { func (client *Client) successfulSaslAuth(rb *ResponseBuffer) {
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)) 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))
client.Send(nil, client.server.name, RPL_SASLSUCCESS, client.nick, client.t("SASL authentication successful")) rb.Add(nil, client.server.name, RPL_SASLSUCCESS, client.nick, client.t("SASL authentication successful"))
// dispatch account-notify // dispatch account-notify
for friend := range client.Friends(caps.AccountNotify) { 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. // 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) currentNicks := channel.nicks(client)
// assemble and send replies // assemble and send replies
maxNamLen := 480 - len(client.server.name) - len(client.nick) 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 { 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 buffer = nick
continue continue
} }
@ -192,8 +192,8 @@ func (channel *Channel) Names(client *Client) {
buffer += nick buffer += nick
} }
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)
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_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list"))
} }
// ClientIsAtLeast returns whether the client has at least the given channel privilege. // ClientIsAtLeast returns whether the client has at least the given channel privilege.
@ -349,42 +349,50 @@ func (channel *Channel) IsEmpty() bool {
// Join joins the given client to this channel (if they can be joined). // Join joins the given client to this channel (if they can be joined).
//TODO(dan): /SAJOIN and maybe a ForceJoin function? //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) { if channel.hasClient(client) {
// already joined, no message needs to be sent // already joined, no message needs to be sent
return return
} }
if channel.IsFull() { 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 return
} }
if !channel.CheckKey(key) { 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 return
} }
isInvited := channel.lists[modes.InviteMask].Match(client.nickMaskCasefolded) isInvited := channel.lists[modes.InviteMask].Match(client.nickMaskCasefolded)
if channel.flags[modes.InviteOnly] && !isInvited { 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 return
} }
if channel.lists[modes.BanMask].Match(client.nickMaskCasefolded) && if channel.lists[modes.BanMask].Match(client.nickMaskCasefolded) &&
!isInvited && !isInvited &&
!channel.lists[modes.ExceptMask].Match(client.nickMaskCasefolded) { !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 return
} }
client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", client.nick, channel.name)) client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", client.nick, channel.name))
for _, member := range channel.Members() { for _, member := range channel.Members() {
if member.capabilities.Has(caps.ExtendedJoin) { if member == client {
member.Send(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname) 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 { } else {
member.Send(nil, client.nickMaskString, "JOIN", channel.name) 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)
}
} }
} }
@ -411,31 +419,39 @@ func (channel *Channel) Join(client *Client, key string) {
} }
if client.capabilities.Has(caps.ExtendedJoin) { 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 { } 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 // don't send topic when it's an entirely new channel
if !newChannel { if !newChannel {
channel.SendTopic(client) channel.SendTopic(client, rb)
} }
channel.Names(client) channel.Names(client, rb)
if givenMode != nil { if givenMode != nil {
for _, member := range channel.Members() { for _, member := range channel.Members() {
member.Send(nil, client.server.name, "MODE", channel.name, fmt.Sprintf("+%v", *givenMode), client.nick) 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. // 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) { 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 return
} }
for _, member := range channel.Members() { for _, member := range channel.Members() {
member.Send(nil, client.nickMaskString, "PART", channel.name, message) 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) channel.Quit(client)
@ -443,9 +459,9 @@ func (channel *Channel) Part(client *Client, message string) {
} }
// SendTopic sends the channel topic to the given client. // 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) { 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 return
} }
@ -457,23 +473,23 @@ func (channel *Channel) SendTopic(client *Client) {
channel.stateMutex.RUnlock() channel.stateMutex.RUnlock()
if topic == "" { 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 return
} }
client.Send(nil, client.server.name, RPL_TOPIC, client.nick, name, topic) rb.Add(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_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. // 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)) { 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 return
} }
if channel.HasMode(modes.OpOnlyTopic) && !channel.ClientIsAtLeast(client, modes.ChannelOperator) { 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 return
} }
@ -488,7 +504,11 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
channel.stateMutex.Unlock() channel.stateMutex.Unlock()
for _, member := range channel.Members() { for _, member := range channel.Members() {
member.Send(nil, client.nickMaskString, "TOPIC", channel.name, topic) 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) 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. // 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) { 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) channel.sendMessage(msgid, "TAGMSG", []caps.Capability{caps.MessageTags}, minPrefix, clientOnlyTags, client, nil, rb)
} }
// sendMessage sends a given message to everyone on this channel. // 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) { 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 return
} }
@ -554,26 +574,26 @@ func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capab
} }
if message == nil { if message == nil {
member.SendFromClient(msgid, client, messageTagsToUse, cmd, channel.name) rb.AddFromClient(msgid, client, messageTagsToUse, cmd, channel.name)
} else { } 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. // 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) { 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) channel.sendSplitMessage(msgid, "PRIVMSG", minPrefix, clientOnlyTags, client, &message, rb)
} }
// SplitNotice sends a private message to everyone in this channel. // 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) { 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) 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) { 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 return
} }
@ -595,25 +615,33 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *modes.Mod
tagsToUse = clientOnlyTags tagsToUse = clientOnlyTags
} }
if message == nil { if member == client {
member.SendFromClient(msgid, client, tagsToUse, cmd, channel.name) if message == nil {
rb.AddFromClient(msgid, client, tagsToUse, cmd, channel.name)
} else {
rb.AddSplitMessageFromClient(msgid, client, tagsToUse, cmd, channel.name, *message)
}
} else { } else {
member.SendSplitMsgFromClient(msgid, client, tagsToUse, cmd, channel.name, *message) 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 == "" { if nick == "" {
//TODO(dan): shouldn't this be handled before it reaches this function? //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 return nil
} }
casefoldedName, err := CasefoldName(nick) casefoldedName, err := CasefoldName(nick)
target := channel.server.clients.Get(casefoldedName) target := channel.server.clients.Get(casefoldedName)
if err != nil || target == nil { 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 return nil
} }
@ -628,7 +656,7 @@ func (channel *Channel) applyModeMemberNoMutex(client *Client, mode modes.Mode,
channel.stateMutex.Unlock() channel.stateMutex.Unlock()
if !exists { 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 return nil
} else if already { } else if already {
return nil return nil
@ -642,7 +670,7 @@ func (channel *Channel) applyModeMemberNoMutex(client *Client, mode modes.Mode,
} }
// ShowMaskList shows the given list to the client. // 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 // choose appropriate modes
var rpllist, rplendoflist string var rpllist, rplendoflist string
if mode == modes.BanMask { if mode == modes.BanMask {
@ -660,14 +688,14 @@ func (channel *Channel) ShowMaskList(client *Client, mode modes.Mode) {
channel.stateMutex.RLock() channel.stateMutex.RLock()
// XXX don't acquire any new locks in this section, besides Socket.Write // XXX don't acquire any new locks in this section, besides Socket.Write
for mask := range channel.lists[mode].masks { 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() 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] list := channel.lists[mode]
if list == nil { if list == nil {
// This should never happen, but better safe than panicky. // 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 == "") { if (op == modes.List) || (mask == "") {
channel.ShowMaskList(client, mode) channel.ShowMaskList(client, mode, rb)
return false return false
} }
if !channel.ClientIsAtLeast(client, modes.ChannelOperator) { 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 return false
} }
@ -705,21 +733,21 @@ func (channel *Channel) Quit(client *Client) {
client.removeChannel(channel) 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)) { 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 return
} }
if !channel.ClientIsAtLeast(client, modes.ChannelOperator) { 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 return
} }
if !channel.hasClient(target) { 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 return
} }
if !channel.ClientHasPrivsOver(client, target) { 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 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. // 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) { 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 return
} }
if !channel.hasClient(inviter) { 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 return
} }
@ -764,9 +792,9 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
} }
//TODO(dan): should inviter.server.name here be inviter.nickMaskString ? //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) invitee.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
if invitee.flags[modes.Away] { 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. // 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 server := client.server
casefoldedName, err := CasefoldChannel(name) casefoldedName, err := CasefoldChannel(name)
if err != nil || len(casefoldedName) > server.Limits().ChannelLen { 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 entry.pendingJoins += 1
cm.Unlock() cm.Unlock()
entry.channel.Join(client, key) entry.channel.Join(client, key, rb)
cm.maybeCleanup(entry, true) 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. // 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) casefoldedName, err := CasefoldChannel(name)
if err != nil { if err != nil {
return errNoSuchChannel return errNoSuchChannel
@ -120,7 +120,7 @@ func (cm *ChannelManager) Part(client *Client, name string, message string) erro
if entry == nil { if entry == nil {
return errNoSuchChannel return errNoSuchChannel
} }
entry.channel.Part(client, message) entry.channel.Part(client, message, rb)
cm.maybeCleanup(entry, false) cm.maybeCleanup(entry, false)
return nil return nil
} }

View File

@ -13,17 +13,17 @@ import (
) )
// ChanServNotice sends the client a notice from ChanServ. // ChanServNotice sends the client a notice from ChanServ.
func (client *Client) ChanServNotice(text string) { func (rb *ResponseBuffer) ChanServNotice(text string) {
client.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "NOTICE", client.nick, text) rb.Add(nil, fmt.Sprintf("ChanServ!services@%s", rb.target.server.name), "NOTICE", rb.target.nick, text)
} }
// chanservReceiveNotice handles NOTICEs that ChanServ receives. // 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 // do nothing
} }
// chanservReceiveNotice handles NOTICEs that ChanServ receives. // 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 var params []string
for _, p := range strings.Split(message, " ") { for _, p := range strings.Split(message, " ") {
if len(p) > 0 { if len(p) > 0 {
@ -31,7 +31,7 @@ func (server *Server) chanservPrivmsgHandler(client *Client, message string) {
} }
} }
if len(params) < 1 { 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 //TODO(dan): dump CS help here
return return
} }
@ -41,57 +41,57 @@ func (server *Server) chanservPrivmsgHandler(client *Client, message string) {
if command == "register" { if command == "register" {
if len(params) < 2 { if len(params) < 2 {
client.ChanServNotice(client.t("Syntax: REGISTER <channel>")) rb.ChanServNotice(client.t("Syntax: REGISTER <channel>"))
return return
} }
server.chanservRegisterHandler(client, params[1]) server.chanservRegisterHandler(client, params[1], rb)
} else { } 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. // 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 { if !server.channelRegistrationEnabled {
client.ChanServNotice(client.t("Channel registration is not enabled")) rb.ChanServNotice(client.t("Channel registration is not enabled"))
return return
} }
channelKey, err := CasefoldChannel(channelName) channelKey, err := CasefoldChannel(channelName)
if err != nil { if err != nil {
client.ChanServNotice(client.t("Channel name is not valid")) rb.ChanServNotice(client.t("Channel name is not valid"))
return return
} }
channelInfo := server.channels.Get(channelKey) channelInfo := server.channels.Get(channelKey)
if channelInfo == nil || !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) { 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 return
} }
if client.account == &NoAccount { 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 return
} }
// this provides the synchronization that allows exactly one registration of the channel: // this provides the synchronization that allows exactly one registration of the channel:
err = channelInfo.SetRegistered(client.AccountName()) err = channelInfo.SetRegistered(client.AccountName())
if err != nil { if err != nil {
client.ChanServNotice(err.Error()) rb.ChanServNotice(err.Error())
return return
} }
// registration was successful: make the database reflect it // registration was successful: make the database reflect it
go server.channelRegistry.StoreChannel(channelInfo, true) 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.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)) 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 // 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 { if change != nil {
//TODO(dan): we should change the name of String and make it return a slice here //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 //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. // 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") translatedISupport := client.t("are supported by this server")
for _, tokenline := range client.server.ISupport().CachedReply { for _, tokenline := range client.server.ISupport().CachedReply {
// ugly trickery ahead // ugly trickery ahead
tokenline = append(tokenline, translatedISupport) 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. // 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, " ") splitName := strings.Split(name, " ")
textLines := strings.Split(text, "\n") textLines := strings.Split(text, "\n")
@ -666,14 +666,14 @@ func (client *Client) sendHelp(name string, text string) {
args := splitName args := splitName
args = append(args, line) args = append(args, line)
if i == 0 { if i == 0 {
client.Send(nil, client.server.name, RPL_HELPSTART, args...) rb.Add(nil, client.server.name, RPL_HELPSTART, args...)
} else { } else {
client.Send(nil, client.server.name, RPL_HELPTXT, args...) rb.Add(nil, client.server.name, RPL_HELPTXT, args...)
} }
} }
args := splitName args := splitName
args = append(args, client.t("End of /HELPOP")) 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. // 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. // 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 // so we only output one warning for each list type when full
listFullWarned := make(map[modes.Mode]bool) listFullWarned := make(map[modes.Mode]bool)
@ -222,7 +222,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
switch change.Mode { switch change.Mode {
case modes.BanMask, modes.ExceptMask, modes.InviteMask: case modes.BanMask, modes.ExceptMask, modes.InviteMask:
if isListOp(change) { if isListOp(change) {
channel.ShowMaskList(client, change.Mode) channel.ShowMaskList(client, change.Mode, rb)
continue continue
} }
@ -236,7 +236,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
case modes.Add: case modes.Add:
if channel.lists[change.Mode].Length() >= client.server.Limits().ChanListModes { if channel.lists[change.Mode].Length() >= client.server.Limits().ChanListModes {
if !listFullWarned[change.Mode] { 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 listFullWarned[change.Mode] = true
} }
continue continue
@ -289,7 +289,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
continue 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 { if change != nil {
applied = append(applied, *change) applied = append(applied, *change)
} }

View File

@ -103,7 +103,7 @@ func (manager *MonitorManager) List(client *Client) (nicks []string) {
} }
var ( 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, "-": monitorRemoveHandler,
"+": monitorAddHandler, "+": monitorAddHandler,
"c": monitorClearHandler, "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) nickname := strings.TrimSpace(newnick)
cfnick, err := CasefoldName(nickname) cfnick, err := CasefoldName(nickname)
if len(nickname) < 1 { 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 return false
} }
if err != nil || len(nickname) > server.Limits().NickLen || restrictedNicknames[cfnick] { 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 return false
} }
@ -43,10 +43,10 @@ func performNickChange(server *Server, client *Client, target *Client, newnick s
origNickMask := target.NickMaskString() origNickMask := target.NickMaskString()
err = client.server.clients.SetNick(target, nickname) err = client.server.clients.SetNick(target, nickname)
if err == errNicknameInUse { 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 return false
} else if err != nil { } 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 return false
} }

View File

@ -39,18 +39,18 @@ func extractParam(line string) (string, string) {
} }
// nickservNoticeHandler handles NOTICEs that NickServ receives. // 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 // do nothing
} }
// nickservPrivmsgHandler handles PRIVMSGs that NickServ receives. // 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, params := extractParam(message)
command = strings.ToLower(command) command = strings.ToLower(command)
if command == "help" { if command == "help" {
for _, line := range strings.Split(nickservHelp, "\n") { for _, line := range strings.Split(nickservHelp, "\n") {
client.Notice(line) rb.Notice(line)
} }
} else if command == "register" { } else if command == "register" {
// get params // get params
@ -58,30 +58,30 @@ func (server *Server) nickservPrivmsgHandler(client *Client, message string) {
// fail out if we need to // fail out if we need to
if username == "" { if username == "" {
client.Notice(client.t("No username supplied")) rb.Notice(client.t("No username supplied"))
return return
} }
server.nickservRegisterHandler(client, username, passphrase) server.nickservRegisterHandler(client, username, passphrase, rb)
} else if command == "identify" { } else if command == "identify" {
// get params // get params
username, passphrase := extractParam(params) username, passphrase := extractParam(params)
server.nickservIdentifyHandler(client, username, passphrase) server.nickservIdentifyHandler(client, username, passphrase, rb)
} else { } 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 certfp := client.certfp
if passphrase == "" && 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 return
} }
if !server.accountRegistration.Enabled { if !server.accountRegistration.Enabled {
client.Notice(client.t("Account registration has been disabled")) rb.Notice(client.t("Account registration has been disabled"))
return return
} }
@ -89,7 +89,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
if server.accountRegistration.AllowMultiplePerConnection { if server.accountRegistration.AllowMultiplePerConnection {
client.LogoutOfAccount() client.LogoutOfAccount()
} else { } else {
client.Notice(client.t("You're already logged into an account")) rb.Notice(client.t("You're already logged into an account"))
return return
} }
} }
@ -99,7 +99,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
casefoldedAccount, err := CasefoldName(account) casefoldedAccount, err := CasefoldName(account)
// probably don't need explicit check for "*" here... but let's do it anyway just to make sure // probably don't need explicit check for "*" here... but let's do it anyway just to make sure
if err != nil || username == "*" { if err != nil || username == "*" {
client.Notice(client.t("Account name is not valid")) rb.Notice(client.t("Account name is not valid"))
return return
} }
@ -111,7 +111,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
_, err := tx.Get(accountKey) _, err := tx.Get(accountKey)
if err != buntdb.ErrNotFound { 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 //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 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 // account could not be created and relevant numerics have been dispatched, abort
if err != nil { if err != nil {
if err != errAccountCreation { if err != errAccountCreation {
client.Notice(client.t("Account registration failed")) rb.Notice(client.t("Account registration failed"))
} }
return return
} }
@ -178,7 +178,7 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
if err == errCertfpAlreadyExists { if err == errCertfpAlreadyExists {
errMsg = "An account already exists for your certificate fingerprint" errMsg = "An account already exists for your certificate fingerprint"
} }
client.Notice(errMsg) rb.Notice(errMsg)
removeFailedAccRegisterData(server.store, casefoldedAccount) removeFailedAccRegisterData(server.store, casefoldedAccount)
return return
} }
@ -196,23 +196,23 @@ func (server *Server) nickservRegisterHandler(client *Client, username, passphra
server.accounts[casefoldedAccount] = &account server.accounts[casefoldedAccount] = &account
client.account = &account client.account = &account
client.Notice(client.t("Account created")) rb.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)) 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))
client.Send(nil, server.name, RPL_SASLSUCCESS, client.nick, client.t("Authentication successful")) 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)) 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 return nil
}) })
if err != nil { if err != nil {
client.Notice(client.t("Account registration failed")) rb.Notice(client.t("Account registration failed"))
removeFailedAccRegisterData(server.store, casefoldedAccount) removeFailedAccRegisterData(server.store, casefoldedAccount)
return 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 // fail out if we need to
if !server.accountAuthenticationEnabled { if !server.accountAuthenticationEnabled {
client.Notice(client.t("Login has been disabled")) rb.Notice(client.t("Login has been disabled"))
return return
} }
@ -221,7 +221,7 @@ func (server *Server) nickservIdentifyHandler(client *Client, username, passphra
// keep it the same as in the ACC CREATE stage // keep it the same as in the ACC CREATE stage
accountKey, err := CasefoldName(username) accountKey, err := CasefoldName(username)
if err != nil { 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 return
} }
@ -259,7 +259,7 @@ func (server *Server) nickservIdentifyHandler(client *Client, username, passphra
}) })
if err == nil { 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 return
} }
} }
@ -306,10 +306,10 @@ func (server *Server) nickservIdentifyHandler(client *Client, username, passphra
}) })
if err == nil { 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 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 { func (rb *ResponseBuffer) Send() error {
// fall out if no messages to send // fall out if no messages to send
if len(rb.messages) == 0 { if len(rb.messages) == 0 {
return return nil
} }
// make batch and all if required // make batch and all if required
@ -126,3 +126,8 @@ func (rb *ResponseBuffer) Send() error {
return nil 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" 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 { if isAction {
message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick) message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
} else { } else {
@ -26,17 +26,17 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
if cerr == nil { if cerr == nil {
channel := server.channels.Get(target) channel := server.channels.Get(target)
if channel == nil { 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 return
} }
if !channel.CanSpeak(client) { 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 return
} }
if !channel.flags[modes.ChanRoleplaying] { 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 return
} }
@ -44,28 +44,32 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
if member == client && !client.capabilities.Has(caps.EchoMessage) { if member == client && !client.capabilities.Has(caps.EchoMessage) {
continue continue
} }
member.Send(nil, source, "PRIVMSG", channel.name, message) if member == client {
rb.Add(nil, source, "PRIVMSG", channel.name, message)
} else {
member.Send(nil, source, "PRIVMSG", channel.name, message)
}
} }
} else { } else {
target, err := CasefoldName(targetString) target, err := CasefoldName(targetString)
user := server.clients.Get(target) user := server.clients.Get(target)
if err != nil || user == nil { 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 return
} }
if !user.flags[modes.UserRoleplaying] { 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 return
} }
user.Send(nil, source, "PRIVMSG", user.nick, message) user.Send(nil, source, "PRIVMSG", user.nick, message)
if client.capabilities.Has(caps.EchoMessage) { 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] { if user.flags[modes.Away] {
//TODO(dan): possibly implement cooldown of away notifications to users //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))) 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 //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.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()) c.Send(nil, c.nickMaskString, RPL_UMODEIS, c.nick, c.ModeString())
if server.logger.IsLoggingRawIO() { 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.")) 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 { } else {
c.Send(nil, c.nickMaskString, "JOIN", channel.name) c.Send(nil, c.nickMaskString, "JOIN", channel.name)
} }
channel.SendTopic(c) // reuse the last rb
channel.Names(c) channel.SendTopic(c, rb)
channel.Names(c, rb)
rb.Send()
// construct and send fake modestring if necessary // construct and send fake modestring if necessary
c.stateMutex.RLock() c.stateMutex.RLock()
@ -511,21 +517,21 @@ func (client *Client) t(originalString string) string {
} }
// MOTD serves the Message of the Day. // MOTD serves the Message of the Day.
func (server *Server) MOTD(client *Client) { func (server *Server) MOTD(client *Client, rb *ResponseBuffer) {
server.configurableStateMutex.RLock() server.configurableStateMutex.RLock()
motdLines := server.motdLines motdLines := server.motdLines
server.configurableStateMutex.RUnlock() server.configurableStateMutex.RUnlock()
if len(motdLines) < 1 { 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 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 { 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. // 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. // rplWhoReply returns the WHO reply between one user and another channel/user.
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ] // <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
// :<hopcount> <real name> // :<hopcount> <real name>
func (target *Client) rplWhoReply(channel *Channel, client *Client) { func (target *Client) rplWhoReply(channel *Channel, client *Client, rb *ResponseBuffer) {
channelName := "*" channelName := "*"
flags := "" flags := ""
@ -667,13 +673,13 @@ func (target *Client) rplWhoReply(channel *Channel, client *Client) {
flags += channel.ClientPrefixes(client, target.capabilities.Has(caps.MultiPrefix)) flags += channel.ClientPrefixes(client, target.capabilities.Has(caps.MultiPrefix))
channelName = channel.name 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() { for _, member := range channel.Members() {
if !client.flags[modes.Invisible] || friends[client] { 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. // 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 // get the correct number of channel members
var memberCount int var memberCount int
if target.flags[modes.Operator] || channel.hasClient(target) { 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) rb.Add(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
} }
// ResumeDetails are the details that we use to resume connections. // ResumeDetails are the details that we use to resume connections.