diff --git a/irc/channel.go b/irc/channel.go index 09a8a956..99dde88e 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -1324,6 +1324,12 @@ func (channel *Channel) isMuted(client *Client) bool { return muteRe.MatchString(nuh) && !channel.lists[modes.ExceptMask].MatchMute(nuh) } +func (channel *Channel) relayNickMuted(relayNick string) bool { + relayNUH := fmt.Sprintf("%s!*@*", relayNick) + return channel.lists[modes.BanMask].MatchMute(relayNUH) && + !channel.lists[modes.ExceptMask].MatchMute(relayNUH) +} + func msgCommandToHistType(command string) (history.ItemType, error) { switch command { case "PRIVMSG": diff --git a/irc/chanserv.go b/irc/chanserv.go index 3f912ebd..5dfcf44e 100644 --- a/irc/chanserv.go +++ b/irc/chanserv.go @@ -579,7 +579,12 @@ func csTransferHandler(service *ircService, server *Server, client *Client, comm service.Notice(rb, fmt.Sprintf(client.t("Cancelled pending transfer of channel %s"), chname)) } } else { - service.Notice(rb, client.t("Could not transfer channel")) + switch err { + case errChannelNotOwnedByAccount: + service.Notice(rb, client.t("You don't own that channel")) + default: + service.Notice(rb, client.t("Could not transfer channel")) + } } } diff --git a/irc/handlers.go b/irc/handlers.go index 5ad2771c..ac417a6d 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2647,6 +2647,10 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.Message, rb *Res rb.Add(nil, server.name, "FAIL", "RELAYMSG", "INVALID_NICK", fmt.Sprintf(client.t("Relayed nicknames MUST contain a relaymsg separator from this set: %s"), config.Server.Relaymsg.Separators)) return false } + if channel.relayNickMuted(nick) { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "BANNED", fmt.Sprintf(client.t("%s is banned from relaying to the channel"), nick)) + return false + } channel.AddHistoryItem(history.Item{ Type: history.Privmsg, diff --git a/irc/histserv.go b/irc/histserv.go index 4c686737..c7c13257 100644 --- a/irc/histserv.go +++ b/irc/histserv.go @@ -13,6 +13,7 @@ import ( "time" "github.com/oragono/oragono/irc/history" + "github.com/oragono/oragono/irc/modes" "github.com/oragono/oragono/irc/utils" ) @@ -102,21 +103,31 @@ func histservDeleteHandler(service *ircService, server *Server, client *Client, target, msgid = params[0], params[1] } + // operators can delete; if individual delete is allowed, a chanop or + // the message author can delete accountName := "*" - hasPrivs := client.HasRoleCapabs("history") - if !hasPrivs { - accountName = client.AccountName() - if !(server.Config().History.Retention.AllowIndividualDelete && accountName != "*") { - service.Notice(rb, client.t("Insufficient privileges")) - return + isChanop := false + isOper := client.HasRoleCapabs("history") + if !isOper { + if server.Config().History.Retention.AllowIndividualDelete { + channel := server.channels.Get(target) + if channel != nil && channel.ClientIsAtLeast(client, modes.Operator) { + isChanop = true + } else { + accountName = client.AccountName() + } } } + if !isOper && !isChanop && accountName == "*" { + service.Notice(rb, client.t("Insufficient privileges")) + return + } err := server.DeleteMessage(target, msgid, accountName) if err == nil { service.Notice(rb, client.t("Successfully deleted message")) } else { - if hasPrivs { + if isOper { service.Notice(rb, fmt.Sprintf(client.t("Error deleting message: %v"), err)) } else { service.Notice(rb, client.t("Could not delete message")) diff --git a/irc/nickserv.go b/irc/nickserv.go index 9a7d6897..50d497a3 100644 --- a/irc/nickserv.go +++ b/irc/nickserv.go @@ -425,7 +425,7 @@ func displaySetting(service *ircService, settingName string, settings AccountSet case "always-on": stored := settings.AlwaysOn actual := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, stored) - service.Notice(rb, fmt.Sprintf(client.t("Your stored always-on setting is: %s"), persistentStatusToString(stored))) + service.Notice(rb, fmt.Sprintf(client.t("Your stored always-on setting is: %s"), userPersistentStatusToString(stored))) if actual { service.Notice(rb, client.t("Given current server settings, your client is always-on")) } else { @@ -447,7 +447,7 @@ func displaySetting(service *ircService, settingName string, settings AccountSet stored := settings.AutoAway alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn) actual := persistenceEnabled(config.Accounts.Multiclient.AutoAway, settings.AutoAway) - service.Notice(rb, fmt.Sprintf(client.t("Your stored auto-away setting is: %s"), persistentStatusToString(stored))) + service.Notice(rb, fmt.Sprintf(client.t("Your stored auto-away setting is: %s"), userPersistentStatusToString(stored))) if actual && alwaysOn { service.Notice(rb, client.t("Given current server settings, auto-away is enabled for your client")) } else if actual && !alwaysOn { @@ -465,6 +465,15 @@ func displaySetting(service *ircService, settingName string, settings AccountSet } } +func userPersistentStatusToString(status PersistentStatus) string { + // #1544: "mandatory" as a user setting should display as "enabled" + result := persistentStatusToString(status) + if result == "mandatory" { + result = "enabled" + } + return result +} + func nsSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { var account string if command == "saset" { diff --git a/irc/server.go b/irc/server.go index d18787cf..1fece0c1 100644 --- a/irc/server.go +++ b/irc/server.go @@ -153,10 +153,8 @@ func (server *Server) Run() { return case <-server.rehashSignal: - go func() { - server.logger.Info("server", "Rehashing due to SIGHUP") - server.rehash() - }() + server.logger.Info("server", "Rehashing due to SIGHUP") + go server.rehash() } } } @@ -520,14 +518,24 @@ func (client *Client) getWhoisOf(target *Client, hasPrivs bool, rb *ResponseBuff // rehash reloads the config and applies the changes from the config file. func (server *Server) rehash() error { + // #1570; this needs its own panic handling because it can be invoked via SIGHUP + defer func() { + if r := recover(); r != nil { + if server.Config().Debug.recoverFromErrors { + server.logger.Error("internal", + fmt.Sprintf("Panic during rehash: %v\n%s", r, debug.Stack())) + } else { + panic(r) + } + } + }() + server.logger.Info("server", "Attempting rehash") // only let one REHASH go on at a time server.rehashMutex.Lock() defer server.rehashMutex.Unlock() - server.logger.Debug("server", "Got rehash lock") - config, err := LoadConfig(server.configFilename) if err != nil { server.logger.Error("server", "failed to load config file", err.Error())