3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-23 03:02:48 +01:00
This commit is contained in:
Shivaram Lingamneni 2019-03-19 03:35:49 -04:00
parent 29db70fa7b
commit 18a8b075ea
6 changed files with 116 additions and 221 deletions

View File

@ -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) hasPrivs := isSajoin || (founder != "" && founder == details.account) || (persistentMode != 0 && persistentMode != modes.Voice)
if !hasPrivs && limit != 0 && chcount >= limit { 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 return
} }
if !hasPrivs && chkey != "" && !utils.SecretTokensMatch(chkey, key) { 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 return
} }
isInvited := client.CheckInvited(chcfname) || channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) isInvited := client.CheckInvited(chcfname) || channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded)
if !hasPrivs && channel.flags.HasMode(modes.InviteOnly) && !isInvited { 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 return
} }
if !hasPrivs && channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) && if !hasPrivs && channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) &&
!isInvited && !isInvited &&
!channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) { !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 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) { func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer) {
chname := channel.Name() chname := channel.Name()
if !channel.hasClient(client) { 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 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 // `sendNoTopic` controls whether RPL_NOTOPIC is sent when the topic is unset
func (channel *Channel) SendTopic(client *Client, rb *ResponseBuffer, sendNoTopic bool) { func (channel *Channel) SendTopic(client *Client, rb *ResponseBuffer, sendNoTopic bool) {
if !channel.hasClient(client) { 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 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. // 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) { func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffer) {
if !(client.HasMode(modes.Operator) || channel.hasClient(client)) { 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 return
} }
if channel.flags.HasMode(modes.OpOnlyTopic) && !channel.ClientIsAtLeast(client, modes.ChannelOperator) { 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 return
} }
@ -719,22 +719,30 @@ func (channel *Channel) CanSpeak(client *Client) bool {
return true return true
} }
func (channel *Channel) SendSplitMessage(command string, minPrefix *modes.Mode, clientOnlyTags map[string]string, client *Client, message utils.SplitMessage, rb *ResponseBuffer) { func msgCommandToHistType(server *Server, command string) (history.ItemType, error) {
var histType history.ItemType
switch command { switch command {
case "PRIVMSG": case "PRIVMSG":
histType = history.Privmsg return history.Privmsg, nil
case "NOTICE": case "NOTICE":
histType = history.Notice return history.Notice, nil
case "TAGMSG": case "TAGMSG":
histType = history.Tagmsg return history.Tagmsg, nil
default: 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 return
} }
if !channel.CanSpeak(client) { 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 return
} }
@ -751,7 +759,7 @@ func (channel *Channel) SendSplitMessage(command string, minPrefix *modes.Mode,
} }
nickMaskString := client.NickMaskString() nickMaskString := client.NickMaskString()
accountName := client.AccountName() 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) rb.AddFromClient(message.Msgid, nickMaskString, accountName, tagsToUse, command, channel.name)
} else { } else {
rb.AddSplitMessageFromClient(nickMaskString, accountName, tagsToUse, command, channel.name, message) 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 var tagsToUse map[string]string
if member.capabilities.Has(caps.MessageTags) { if member.capabilities.Has(caps.MessageTags) {
tagsToUse = clientOnlyTags tagsToUse = clientOnlyTags
} else if command == "TAGMSG" { } else if histType == history.Tagmsg {
continue continue
} }
if command == "TAGMSG" { if histType == history.Tagmsg {
member.sendFromClientInternal(false, now, message.Msgid, nickmask, account, tagsToUse, command, channel.name) member.sendFromClientInternal(false, now, message.Msgid, nickmask, account, tagsToUse, command, channel.name)
} else { } else {
member.sendSplitMsgFromClientInternal(false, now, nickmask, account, tagsToUse, command, channel.name, message) 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) { 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 return false
} }
@ -898,15 +906,15 @@ func (channel *Channel) Quit(client *Client) {
func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer) { func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer) {
if !(client.HasMode(modes.Operator) || channel.hasClient(client)) { 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 return
} }
if !channel.hasClient(target) { 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 return
} }
if !channel.ClientHasPrivsOver(client, target) { 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 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) { func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuffer) {
chname := channel.Name() chname := channel.Name()
if channel.flags.HasMode(modes.InviteOnly) && !channel.ClientIsAtLeast(inviter, modes.ChannelOperator) { 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 return
} }
if !channel.hasClient(inviter) { 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 return
} }

View File

@ -325,7 +325,7 @@ func (client *Client) run() {
if err == ircmsg.ErrorLineIsEmpty { if err == ircmsg.ErrorLineIsEmpty {
continue continue
} else if err == ircmsg.ErrorLineTooLong { } 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 continue
} else if err != nil { } else if err != nil {
client.Quit(client.t("Received malformed line")) client.Quit(client.t("Received malformed line"))
@ -335,9 +335,9 @@ func (client *Client) run() {
cmd, exists := Commands[msg.Command] cmd, exists := Commands[msg.Command]
if !exists { if !exists {
if len(msg.Command) > 0 { 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 { } 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 continue
} }

View File

@ -23,7 +23,7 @@ type Command struct {
// Run runs this command with the given client/message. // Run runs this command with the given client/message.
func (cmd *Command) Run(server *Server, client *Client, msg ircmsg.IrcMessage) bool { func (cmd *Command) Run(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
if !client.registered && !cmd.usablePreReg { 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 return false
} }
if cmd.oper && !client.HasMode(modes.Operator) { if cmd.oper && !client.HasMode(modes.Operator) {
@ -184,7 +184,7 @@ func init() {
minParams: 1, minParams: 1,
}, },
"NOTICE": { "NOTICE": {
handler: noticeHandler, handler: messageHandler,
minParams: 2, minParams: 2,
}, },
"NPC": { "NPC": {
@ -221,7 +221,7 @@ func init() {
leaveClientIdle: true, leaveClientIdle: true,
}, },
"PRIVMSG": { "PRIVMSG": {
handler: privmsgHandler, handler: messageHandler,
minParams: 2, minParams: 2,
}, },
"RENAME": { "RENAME": {
@ -257,7 +257,7 @@ func init() {
minParams: 1, minParams: 1,
}, },
"TAGMSG": { "TAGMSG": {
handler: tagmsgHandler, handler: messageHandler,
minParams: 1, minParams: 1,
}, },
"QUIT": { "QUIT": {

View File

@ -242,7 +242,7 @@ func accVerifyHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb
if err == nil { if err == nil {
sendSuccessfulRegResponse(client, rb, false) sendSuccessfulRegResponse(client, rb, false)
} else { } 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 return false
@ -1873,17 +1873,38 @@ func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
} }
// NOTICE <target>{,<target>} <message> // NOTICE <target>{,<target>} <message>
func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { // PRIVMSG <target>{,<target>} <message>
// TAGMSG <target>{,<target>}
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() clientOnlyTags := msg.ClientOnlyTags()
if histType == history.Tagmsg && len(clientOnlyTags) == 0 {
// nothing to do
return false
}
targets := strings.Split(msg.Params[0], ",") 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) { 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 return false
} }
splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine)) splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
now := time.Now().UTC()
for i, targetString := range targets { for i, targetString := range targets {
// max of four targets per privmsg // 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) prefixes, targetString := modes.SplitChannelMembershipPrefixes(targetString)
lowestPrefix := modes.GetLowestChannelModePrefix(prefixes) lowestPrefix := modes.GetLowestChannelModePrefix(prefixes)
target, cerr := CasefoldChannel(targetString) if len(targetString) == 0 {
if cerr == nil { continue
channel := server.channels.Get(target) } else if targetString[0] == '#' {
channel := server.channels.Get(targetString)
if channel == nil { 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 continue
} }
if !channel.CanSpeak(client) { channel.SendSplitMessage(msg.Command, lowestPrefix, clientOnlyTags, client, splitMsg, rb)
// errors silently ignored with NOTICE as per RFC
continue
}
channel.SendSplitMessage("NOTICE", lowestPrefix, clientOnlyTags, client, splitMsg, rb)
} else { } else {
target, err := CasefoldName(targetString) if service, isService := OragonoServices[strings.ToLower(targetString)]; isService {
if err != nil { // NOTICE and TAGMSG to services are ignored
if histType == history.Privmsg {
servicePrivmsgHandler(service, server, client, message, rb)
}
continue continue
} }
// NOTICEs sent to services are ignored user := server.clients.Get(targetString)
if _, isService := OragonoServices[target]; isService {
continue
}
user := server.clients.Get(target)
if user == nil { 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 continue
} }
if !user.capabilities.Has(caps.MessageTags) { tnick := user.Nick()
clientOnlyTags = nil
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 // restrict messages appropriately when +R is set
// intentionally make the sending user think the message went through fine // intentionally make the sending user think the message went through fine
allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
allowedTor := !user.isTor || !isRestrictedCTCPMessage(message) allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
if allowedPlusR && allowedTor { 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) { 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{ user.history.Add(history.Item{
Type: history.Notice, Type: histType,
Message: splitMsg, Message: splitMsg,
Nick: nickMaskString, Nick: nickMaskString,
AccountName: accountName, AccountName: accountName,
@ -1988,7 +2023,7 @@ func npcaHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
// OPER <name> <password> // OPER <name> <password>
func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
if client.HasMode(modes.Operator) == true { 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 return false
} }
@ -1999,7 +2034,7 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
authorized = (bcrypt.CompareHashAndPassword(oper.Pass, password) == nil) authorized = (bcrypt.CompareHashAndPassword(oper.Pass, password) == nil)
} }
if !authorized { 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")) client.Quit(client.t("Password incorrect"))
return true return true
} }
@ -2090,90 +2125,6 @@ func isRestrictedCTCPMessage(message string) bool {
return strings.HasPrefix(message, "\x01") && !strings.HasPrefix(message, "\x01ACTION") return strings.HasPrefix(message, "\x01") && !strings.HasPrefix(message, "\x01ACTION")
} }
// PRIVMSG <target>{,<target>} <message>
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 [<reason>] // QUIT [<reason>]
func quitHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { func quitHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
reason := "Quit" reason := "Quit"
@ -2346,71 +2297,6 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
return false return false
} }
// TAGMSG <target>{,<target>}
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 // TIME
func timeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { 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)) 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 <username> * 0 <realname> // USER <username> * 0 <realname>
func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
if client.registered { 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 return false
} }
@ -2513,7 +2399,7 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
if client.preregNick == msg.Params[0] { if client.preregNick == msg.Params[0] {
client.SetNames("user", msg.Params[3], false) client.SetNames("user", msg.Params[3], false)
} else { } 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 { if len(msg.Params) > 0 {
casefoldedMask, err := Casefold(msg.Params[0]) casefoldedMask, err := Casefold(msg.Params[0])
if err != nil { 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 return false
} }
mask = casefoldedMask mask = casefoldedMask

View File

@ -150,7 +150,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
if !hasPrivs(change) { if !hasPrivs(change) {
if !alreadySentPrivError { if !alreadySentPrivError {
alreadySentPrivError = true 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 continue
} }
@ -226,7 +226,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
nick := change.Arg nick := change.Arg
if nick == "" { 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 return nil
} }

View File

@ -26,14 +26,15 @@ var (
func performNickChange(server *Server, client *Client, target *Client, newnick string, rb *ResponseBuffer) 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)
currentNick := client.Nick()
if len(nickname) < 1 { 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 return false
} }
if err != nil || len(nickname) > server.Limits().NickLen || restrictedNicknames[cfnick] { 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 return false
} }
@ -46,13 +47,13 @@ func performNickChange(server *Server, client *Client, target *Client, newnick s
whowas := client.WhoWas() whowas := client.WhoWas()
err = client.server.clients.SetNick(target, nickname) err = client.server.clients.SetNick(target, nickname)
if err == errNicknameInUse { 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 return false
} else if err == errNicknameReserved { } 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 return false
} else if err != nil { } 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 return false
} }