From 18a8b075ea61e47dbf67f7b7d7a987599113fccb Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 19 Mar 2019 03:35:49 -0400 Subject: [PATCH 1/2] fix #425 and #395 --- irc/channel.go | 56 ++++++----- irc/client.go | 6 +- irc/commands.go | 8 +- irc/handlers.go | 252 +++++++++++++----------------------------------- irc/modes.go | 4 +- irc/nickname.go | 11 ++- 6 files changed, 116 insertions(+), 221 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index a1ab386d..89f586ce 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -377,25 +377,25 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp hasPrivs := isSajoin || (founder != "" && founder == details.account) || (persistentMode != 0 && persistentMode != modes.Voice) if !hasPrivs && limit != 0 && chcount >= limit { - rb.Add(nil, client.server.name, ERR_CHANNELISFULL, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l")) + rb.Add(nil, client.server.name, ERR_CHANNELISFULL, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l")) return } if !hasPrivs && chkey != "" && !utils.SecretTokensMatch(chkey, key) { - rb.Add(nil, client.server.name, ERR_BADCHANNELKEY, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "k")) + rb.Add(nil, client.server.name, ERR_BADCHANNELKEY, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "k")) return } isInvited := client.CheckInvited(chcfname) || channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) if !hasPrivs && channel.flags.HasMode(modes.InviteOnly) && !isInvited { - rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i")) + rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i")) return } if !hasPrivs && channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) && !isInvited && !channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) { - rb.Add(nil, client.server.name, ERR_BANNEDFROMCHAN, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b")) + rb.Add(nil, client.server.name, ERR_BANNEDFROMCHAN, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b")) return } @@ -482,7 +482,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer) { chname := channel.Name() if !channel.hasClient(client) { - rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, chname, client.t("You're not on that channel")) + rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), chname, client.t("You're not on that channel")) return } @@ -645,7 +645,7 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I // `sendNoTopic` controls whether RPL_NOTOPIC is sent when the topic is unset func (channel *Channel) SendTopic(client *Client, rb *ResponseBuffer, sendNoTopic bool) { if !channel.hasClient(client) { - rb.Add(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 } @@ -670,12 +670,12 @@ func (channel *Channel) SendTopic(client *Client, rb *ResponseBuffer, sendNoTopi // SetTopic sets the topic of this channel, if the client is allowed to do so. func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffer) { if !(client.HasMode(modes.Operator) || channel.hasClient(client)) { - rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, 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 } if channel.flags.HasMode(modes.OpOnlyTopic) && !channel.ClientIsAtLeast(client, modes.ChannelOperator) { - rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator")) + rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You're not a channel operator")) return } @@ -719,22 +719,30 @@ func (channel *Channel) CanSpeak(client *Client) bool { return true } -func (channel *Channel) SendSplitMessage(command string, minPrefix *modes.Mode, clientOnlyTags map[string]string, client *Client, message utils.SplitMessage, rb *ResponseBuffer) { - var histType history.ItemType +func msgCommandToHistType(server *Server, command string) (history.ItemType, error) { switch command { case "PRIVMSG": - histType = history.Privmsg + return history.Privmsg, nil case "NOTICE": - histType = history.Notice + return history.Notice, nil case "TAGMSG": - histType = history.Tagmsg + return history.Tagmsg, nil default: - channel.server.logger.Error("internal", "unrecognized Channel.SendSplitMessage command", command) + server.logger.Error("internal", "unrecognized messaging command", command) + return history.ItemType(0), errInvalidParams + } +} + +func (channel *Channel) SendSplitMessage(command string, minPrefix *modes.Mode, clientOnlyTags map[string]string, client *Client, message utils.SplitMessage, rb *ResponseBuffer) { + histType, err := msgCommandToHistType(channel.server, command) + if err != nil { return } if !channel.CanSpeak(client) { - rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel")) + if histType != history.Notice { + rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, client.Nick(), channel.Name(), client.t("Cannot send to channel")) + } return } @@ -751,7 +759,7 @@ func (channel *Channel) SendSplitMessage(command string, minPrefix *modes.Mode, } nickMaskString := client.NickMaskString() accountName := client.AccountName() - if command == "TAGMSG" && client.capabilities.Has(caps.MessageTags) { + if histType == history.Tagmsg && client.capabilities.Has(caps.MessageTags) { rb.AddFromClient(message.Msgid, nickMaskString, accountName, tagsToUse, command, channel.name) } else { rb.AddSplitMessageFromClient(nickMaskString, accountName, tagsToUse, command, channel.name, message) @@ -775,11 +783,11 @@ func (channel *Channel) SendSplitMessage(command string, minPrefix *modes.Mode, var tagsToUse map[string]string if member.capabilities.Has(caps.MessageTags) { tagsToUse = clientOnlyTags - } else if command == "TAGMSG" { + } else if histType == history.Tagmsg { continue } - if command == "TAGMSG" { + if histType == history.Tagmsg { member.sendFromClientInternal(false, now, message.Msgid, nickmask, account, tagsToUse, command, channel.name) } else { member.sendSplitMsgFromClientInternal(false, now, nickmask, account, tagsToUse, command, channel.name, message) @@ -861,7 +869,7 @@ func (channel *Channel) applyModeMask(client *Client, mode modes.Mode, op modes. } if !channel.ClientIsAtLeast(client, modes.ChannelOperator) { - rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator")) + rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You're not a channel operator")) return false } @@ -898,15 +906,15 @@ func (channel *Channel) Quit(client *Client) { func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer) { if !(client.HasMode(modes.Operator) || channel.hasClient(client)) { - rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, 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 } if !channel.hasClient(target) { - rb.Add(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) { - rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You don't have enough channel privileges")) + rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You don't have enough channel privileges")) return } @@ -938,12 +946,12 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuffer) { chname := channel.Name() if channel.flags.HasMode(modes.InviteOnly) && !channel.ClientIsAtLeast(inviter, modes.ChannelOperator) { - rb.Add(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, chname, inviter.t("You're not a channel operator")) + rb.Add(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, inviter.Nick(), channel.Name(), inviter.t("You're not a channel operator")) return } if !channel.hasClient(inviter) { - rb.Add(nil, inviter.server.name, ERR_NOTONCHANNEL, chname, inviter.t("You're not on that channel")) + rb.Add(nil, inviter.server.name, ERR_NOTONCHANNEL, inviter.Nick(), channel.Name(), inviter.t("You're not on that channel")) return } diff --git a/irc/client.go b/irc/client.go index 8a53f7d2..0e546477 100644 --- a/irc/client.go +++ b/irc/client.go @@ -325,7 +325,7 @@ func (client *Client) run() { if err == ircmsg.ErrorLineIsEmpty { continue } else if err == ircmsg.ErrorLineTooLong { - client.Send(nil, client.server.name, ERR_INPUTTOOLONG, client.nick, client.t("Input line too long")) + client.Send(nil, client.server.name, ERR_INPUTTOOLONG, client.Nick(), client.t("Input line too long")) continue } else if err != nil { client.Quit(client.t("Received malformed line")) @@ -335,9 +335,9 @@ func (client *Client) run() { cmd, exists := Commands[msg.Command] if !exists { if len(msg.Command) > 0 { - client.Send(nil, client.server.name, ERR_UNKNOWNCOMMAND, client.nick, msg.Command, client.t("Unknown command")) + client.Send(nil, client.server.name, ERR_UNKNOWNCOMMAND, client.Nick(), msg.Command, client.t("Unknown command")) } else { - client.Send(nil, client.server.name, ERR_UNKNOWNCOMMAND, client.nick, "lastcmd", client.t("No command given")) + client.Send(nil, client.server.name, ERR_UNKNOWNCOMMAND, client.Nick(), "lastcmd", client.t("No command given")) } continue } diff --git a/irc/commands.go b/irc/commands.go index 92e1bfb2..0aa6c575 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -23,7 +23,7 @@ type Command struct { // Run runs this command with the given client/message. func (cmd *Command) Run(server *Server, client *Client, msg ircmsg.IrcMessage) bool { if !client.registered && !cmd.usablePreReg { - client.Send(nil, server.name, ERR_NOTREGISTERED, client.nick, client.t("You need to register before you can use that command")) + client.Send(nil, server.name, ERR_NOTREGISTERED, "*", client.t("You need to register before you can use that command")) return false } if cmd.oper && !client.HasMode(modes.Operator) { @@ -184,7 +184,7 @@ func init() { minParams: 1, }, "NOTICE": { - handler: noticeHandler, + handler: messageHandler, minParams: 2, }, "NPC": { @@ -221,7 +221,7 @@ func init() { leaveClientIdle: true, }, "PRIVMSG": { - handler: privmsgHandler, + handler: messageHandler, minParams: 2, }, "RENAME": { @@ -257,7 +257,7 @@ func init() { minParams: 1, }, "TAGMSG": { - handler: tagmsgHandler, + handler: messageHandler, minParams: 1, }, "QUIT": { diff --git a/irc/handlers.go b/irc/handlers.go index 03e0cc22..397fcd66 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -242,7 +242,7 @@ func accVerifyHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb if err == nil { sendSuccessfulRegResponse(client, rb, false) } else { - rb.Add(nil, server.name, code, client.nick, account, client.t(message)) + rb.Add(nil, server.name, code, client.Nick(), account, client.t(message)) } return false @@ -1873,17 +1873,38 @@ func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp } // NOTICE {,} -func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { +// PRIVMSG {,} +// TAGMSG {,} +func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { + histType, err := msgCommandToHistType(server, msg.Command) + if err != nil { + return false + } + + cnick := client.Nick() clientOnlyTags := msg.ClientOnlyTags() + if histType == history.Tagmsg && len(clientOnlyTags) == 0 { + // nothing to do + return false + } + targets := strings.Split(msg.Params[0], ",") - message := msg.Params[1] + var message string + if len(msg.Params) > 1 { + message = msg.Params[1] + } + + // note that error replies are never sent for NOTICE if client.isTor && isRestrictedCTCPMessage(message) { - rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor")) + if histType != history.Notice { + rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor")) + } return false } splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine)) + now := time.Now().UTC() for i, targetString := range targets { // max of four targets per privmsg @@ -1893,52 +1914,66 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re prefixes, targetString := modes.SplitChannelMembershipPrefixes(targetString) lowestPrefix := modes.GetLowestChannelModePrefix(prefixes) - target, cerr := CasefoldChannel(targetString) - if cerr == nil { - channel := server.channels.Get(target) + if len(targetString) == 0 { + continue + } else if targetString[0] == '#' { + channel := server.channels.Get(targetString) if channel == nil { - // errors silently ignored with NOTICE as per RFC + if histType != history.Notice { + rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, cnick, targetString, client.t("No such channel")) + } continue } - if !channel.CanSpeak(client) { - // errors silently ignored with NOTICE as per RFC - continue - } - channel.SendSplitMessage("NOTICE", lowestPrefix, clientOnlyTags, client, splitMsg, rb) + channel.SendSplitMessage(msg.Command, lowestPrefix, clientOnlyTags, client, splitMsg, rb) } else { - target, err := CasefoldName(targetString) - if err != nil { + if service, isService := OragonoServices[strings.ToLower(targetString)]; isService { + // NOTICE and TAGMSG to services are ignored + if histType == history.Privmsg { + servicePrivmsgHandler(service, server, client, message, rb) + } continue } - // NOTICEs sent to services are ignored - if _, isService := OragonoServices[target]; isService { - continue - } - - user := server.clients.Get(target) + user := server.clients.Get(targetString) if user == nil { - // errors silently ignored with NOTICE as per RFC + if histType != history.Notice { + rb.Add(nil, server.name, ERR_NOSUCHNICK, cnick, targetString, "No such nick") + } continue } - if !user.capabilities.Has(caps.MessageTags) { - clientOnlyTags = nil + tnick := user.Nick() + + if histType == history.Tagmsg && !user.capabilities.Has(caps.MessageTags) { + continue // nothing to do } + + nickMaskString := client.NickMaskString() + accountName := client.AccountName() // restrict messages appropriately when +R is set // intentionally make the sending user think the message went through fine allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() allowedTor := !user.isTor || !isRestrictedCTCPMessage(message) if allowedPlusR && allowedTor { - user.SendSplitMsgFromClient(client, clientOnlyTags, "NOTICE", user.nick, splitMsg) + if histType == history.Tagmsg { + user.sendFromClientInternal(false, now, splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick) + } else { + user.SendSplitMsgFromClient(client, clientOnlyTags, msg.Command, tnick, splitMsg) + } } - nickMaskString := client.NickMaskString() - accountName := client.AccountName() if client.capabilities.Has(caps.EchoMessage) { - rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, "NOTICE", user.nick, splitMsg) + if histType == history.Tagmsg && client.capabilities.Has(caps.MessageTags) { + rb.AddFromClient(splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick) + } else { + rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, msg.Command, tnick, splitMsg) + } + } + if histType != history.Notice && user.HasMode(modes.Away) { + //TODO(dan): possibly implement cooldown of away notifications to users + rb.Add(nil, server.name, RPL_AWAY, cnick, tnick, user.AwayMessage()) } user.history.Add(history.Item{ - Type: history.Notice, + Type: histType, Message: splitMsg, Nick: nickMaskString, AccountName: accountName, @@ -1988,7 +2023,7 @@ func npcaHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp // OPER func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { if client.HasMode(modes.Operator) == true { - rb.Add(nil, server.name, ERR_UNKNOWNERROR, "OPER", client.t("You're already opered-up!")) + rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), "OPER", client.t("You're already opered-up!")) return false } @@ -1999,7 +2034,7 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp authorized = (bcrypt.CompareHashAndPassword(oper.Pass, password) == nil) } if !authorized { - rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect")) + rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.Nick(), client.t("Password incorrect")) client.Quit(client.t("Password incorrect")) return true } @@ -2090,90 +2125,6 @@ func isRestrictedCTCPMessage(message string) bool { return strings.HasPrefix(message, "\x01") && !strings.HasPrefix(message, "\x01ACTION") } -// PRIVMSG {,} -func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { - clientOnlyTags := msg.ClientOnlyTags() - targets := strings.Split(msg.Params[0], ",") - message := msg.Params[1] - - if client.isTor && isRestrictedCTCPMessage(message) { - rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor")) - return false - } - - // split privmsg - splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine)) - - cnick := client.Nick() - for i, targetString := range targets { - // max of four targets per privmsg - if i > maxTargets-1 { - break - } - prefixes, targetString := modes.SplitChannelMembershipPrefixes(targetString) - lowestPrefix := modes.GetLowestChannelModePrefix(prefixes) - - // eh, no need to notify them - if len(targetString) < 1 { - continue - } - - target, err := CasefoldChannel(targetString) - if err == nil { - channel := server.channels.Get(target) - if channel == nil { - rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, cnick, targetString, client.t("No such channel")) - continue - } - if !channel.CanSpeak(client) { - rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel")) - continue - } - channel.SendSplitMessage("PRIVMSG", lowestPrefix, clientOnlyTags, client, splitMsg, rb) - } else { - target, err = CasefoldName(targetString) - if service, isService := OragonoServices[target]; isService { - servicePrivmsgHandler(service, server, client, message, rb) - continue - } - user := server.clients.Get(target) - if err != nil || user == nil { - if len(target) > 0 { - client.Send(nil, server.name, ERR_NOSUCHNICK, cnick, target, "No such nick") - } - continue - } - if !user.capabilities.Has(caps.MessageTags) { - clientOnlyTags = nil - } - // restrict messages appropriately when +R is set - // intentionally make the sending user think the message went through fine - allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() - allowedTor := !user.isTor || !isRestrictedCTCPMessage(message) - if allowedPlusR && allowedTor { - user.SendSplitMsgFromClient(client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg) - } - nickMaskString := client.NickMaskString() - accountName := client.AccountName() - if client.capabilities.Has(caps.EchoMessage) { - rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, "PRIVMSG", user.nick, splitMsg) - } - if user.HasMode(modes.Away) { - //TODO(dan): possibly implement cooldown of away notifications to users - rb.Add(nil, server.name, RPL_AWAY, cnick, user.Nick(), user.AwayMessage()) - } - - user.history.Add(history.Item{ - Type: history.Privmsg, - Message: splitMsg, - Nick: nickMaskString, - AccountName: accountName, - }) - } - } - return false -} - // QUIT [] func quitHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { reason := "Quit" @@ -2346,71 +2297,6 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R return false } -// TAGMSG {,} -func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { - clientOnlyTags := msg.ClientOnlyTags() - // no client-only tags, so we can drop it - if clientOnlyTags == nil { - return false - } - - targets := strings.Split(msg.Params[0], ",") - - cnick := client.Nick() - message := utils.MakeSplitMessage("", true) // assign consistent message ID - for i, targetString := range targets { - // max of four targets per privmsg - if i > maxTargets-1 { - break - } - prefixes, targetString := modes.SplitChannelMembershipPrefixes(targetString) - lowestPrefix := modes.GetLowestChannelModePrefix(prefixes) - - // eh, no need to notify them - if len(targetString) < 1 { - continue - } - - target, err := CasefoldChannel(targetString) - if err == nil { - channel := server.channels.Get(target) - if channel == nil { - rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, cnick, targetString, client.t("No such channel")) - continue - } - if !channel.CanSpeak(client) { - rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel")) - continue - } - channel.SendSplitMessage("TAGMSG", lowestPrefix, clientOnlyTags, client, message, rb) - } else { - target, err = CasefoldName(targetString) - user := server.clients.Get(target) - if err != nil || user == nil { - if len(target) > 0 { - client.Send(nil, server.name, ERR_NOSUCHNICK, cnick, target, client.t("No such nick")) - } - continue - } - - // end user can't receive tagmsgs - if !user.capabilities.Has(caps.MessageTags) { - continue - } - unick := user.Nick() - user.SendSplitMsgFromClient(client, clientOnlyTags, "TAGMSG", unick, message) - if client.capabilities.Has(caps.EchoMessage) { - rb.AddSplitMessageFromClient(client.NickMaskString(), client.AccountName(), clientOnlyTags, "TAGMSG", unick, message) - } - if user.HasMode(modes.Away) { - //TODO(dan): possibly implement cooldown of away notifications to users - rb.Add(nil, server.name, RPL_AWAY, cnick, unick, user.AwayMessage()) - } - } - } - return false -} - // TIME func timeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { rb.Add(nil, server.name, RPL_TIME, client.nick, server.name, time.Now().Format(time.RFC1123)) @@ -2502,7 +2388,7 @@ func unKLineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R // USER * 0 func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { if client.registered { - rb.Add(nil, server.name, ERR_ALREADYREGISTRED, client.nick, client.t("You may not reregister")) + rb.Add(nil, server.name, ERR_ALREADYREGISTRED, client.Nick(), client.t("You may not reregister")) return false } @@ -2513,7 +2399,7 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp if client.preregNick == msg.Params[0] { client.SetNames("user", msg.Params[3], false) } else { - rb.Add(nil, server.name, ERR_INVALIDUSERNAME, client.t("Malformed username")) + rb.Add(nil, server.name, ERR_INVALIDUSERNAME, client.Nick(), client.t("Malformed username")) } } @@ -2631,7 +2517,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo if len(msg.Params) > 0 { casefoldedMask, err := Casefold(msg.Params[0]) if err != nil { - rb.Add(nil, server.name, ERR_UNKNOWNERROR, "WHO", client.t("Mask isn't valid")) + rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), "WHO", client.t("Mask isn't valid")) return false } mask = casefoldedMask diff --git a/irc/modes.go b/irc/modes.go index 823bf000..f1f3c293 100644 --- a/irc/modes.go +++ b/irc/modes.go @@ -150,7 +150,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c if !hasPrivs(change) { if !alreadySentPrivError { alreadySentPrivError = true - rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator")) + rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.name, client.t("You're not a channel operator")) } continue } @@ -226,7 +226,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c nick := change.Arg if nick == "" { - rb.Add(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", client.t("Not enough parameters")) + rb.Add(nil, client.server.name, ERR_NEEDMOREPARAMS, client.Nick(), "MODE", client.t("Not enough parameters")) return nil } diff --git a/irc/nickname.go b/irc/nickname.go index 6c43eb32..efb25fbd 100644 --- a/irc/nickname.go +++ b/irc/nickname.go @@ -26,14 +26,15 @@ var ( func performNickChange(server *Server, client *Client, target *Client, newnick string, rb *ResponseBuffer) bool { nickname := strings.TrimSpace(newnick) cfnick, err := CasefoldName(nickname) + currentNick := client.Nick() if len(nickname) < 1 { - rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, client.nick, client.t("No nickname given")) + rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given")) return false } if err != nil || len(nickname) > server.Limits().NickLen || restrictedNicknames[cfnick] { - rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, client.nick, nickname, client.t("Erroneous nickname")) + rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, currentNick, nickname, client.t("Erroneous nickname")) return false } @@ -46,13 +47,13 @@ func performNickChange(server *Server, client *Client, target *Client, newnick s whowas := client.WhoWas() err = client.server.clients.SetNick(target, nickname) if err == errNicknameInUse { - rb.Add(nil, server.name, ERR_NICKNAMEINUSE, client.nick, nickname, client.t("Nickname is already in use")) + rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is already in use")) return false } else if err == errNicknameReserved { - rb.Add(nil, server.name, ERR_NICKNAMEINUSE, client.nick, nickname, client.t("Nickname is reserved by a different account")) + rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is reserved by a different account")) return false } else if err != nil { - rb.Add(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, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error())) return false } From 42dca34c1f392ac284dddaa2bc12497fb3c31fbe Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 19 Mar 2019 05:51:33 -0400 Subject: [PATCH 2/2] ensure each target sees distinct msgids --- irc/handlers.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/irc/handlers.go b/irc/handlers.go index 397fcd66..dde68601 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1903,10 +1903,11 @@ func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R return false } - splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine)) - now := time.Now().UTC() - for i, targetString := range targets { + // each target gets distinct msgids + splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine)) + now := time.Now().UTC() + // max of four targets per privmsg if i > maxTargets-1 { break