3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-11 06:29:29 +01:00

Merge pull request #1408 from slingamn/services_source.2

fix #1407
This commit is contained in:
Shivaram Lingamneni 2020-11-29 02:31:48 -08:00 committed by GitHub
commit 34fc8aa3c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 427 additions and 414 deletions

View File

@ -335,7 +335,11 @@ server:
# oragono will write files to disk under certain circumstances, e.g., # oragono will write files to disk under certain circumstances, e.g.,
# CPU profiling or data export. by default, these files will be written # CPU profiling or data export. by default, these files will be written
# to the working directory. set this to customize: # to the working directory. set this to customize:
# output-path: "/home/oragono/out" #output-path: "/home/oragono/out"
# the hostname used by "services", e.g., NickServ, defaults to "localhost",
# e.g., `NickServ!NickServ@localhost`. uncomment this to override:
#override-services-hostname: "example.network"
# account options # account options
accounts: accounts:

View File

@ -1037,7 +1037,7 @@ func (channel *Channel) replayHistoryForResume(session *Session, after time.Time
} }
if !complete && !session.resumeDetails.HistoryIncomplete { if !complete && !session.resumeDetails.HistoryIncomplete {
// warn here if we didn't warn already // warn here if we didn't warn already
rb.Add(nil, histServMask, "NOTICE", channel.Name(), session.client.t("Some additional message history may have been lost")) rb.Add(nil, histservService.prefix, "NOTICE", channel.Name(), session.client.t("Some additional message history may have been lost"))
} }
rb.Send(true) rb.Send(true)
} }
@ -1099,7 +1099,7 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
} else { } else {
message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName) message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName)
} }
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
} }
case history.Part: case history.Part:
if eventPlayback { if eventPlayback {
@ -1109,14 +1109,14 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
continue // #474 continue // #474
} }
message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message) message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
} }
case history.Kick: case history.Kick:
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "KICK", chname, item.Params[0], item.Message.Message) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "KICK", chname, item.Params[0], item.Message.Message)
} else { } else {
message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message) message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message)
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
} }
case history.Quit: case history.Quit:
if eventPlayback { if eventPlayback {
@ -1126,21 +1126,21 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
continue // #474 continue // #474
} }
message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message) message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
} }
case history.Nick: case history.Nick:
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "NICK", item.Params[0]) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "NICK", item.Params[0])
} else { } else {
message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0]) message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
} }
case history.Topic: case history.Topic:
if eventPlayback { if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message)
} else { } else {
message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message) message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message)
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
} }
case history.Mode: case history.Mode:
params := make([]string, len(item.Message.Split)+1) params := make([]string, len(item.Message.Split)+1)
@ -1152,7 +1152,7 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "MODE", params...) rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "MODE", params...)
} else { } else {
message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " ")) message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " "))
rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
} }
} }
} }

View File

@ -17,7 +17,6 @@ import (
) )
const chanservHelp = `ChanServ lets you register and manage channels.` const chanservHelp = `ChanServ lets you register and manage channels.`
const chanservMask = "ChanServ!ChanServ@localhost"
func chanregEnabled(config *Config) bool { func chanregEnabled(config *Config) bool {
return config.Channels.Registration.Enabled return config.Channels.Registration.Enabled
@ -188,27 +187,22 @@ SET modifies a channel's settings. The following settings are available:`,
} }
) )
// csNotice sends the client a notice from ChanServ func csAmodeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
func csNotice(rb *ResponseBuffer, text string) {
rb.Add(nil, chanservMask, "NOTICE", rb.target.Nick(), text)
}
func csAmodeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channelName := params[0] channelName := params[0]
channel := server.channels.Get(channelName) channel := server.channels.Get(channelName)
if channel == nil { if channel == nil {
csNotice(rb, client.t("Channel does not exist")) service.Notice(rb, client.t("Channel does not exist"))
return return
} else if channel.Founder() == "" { } else if channel.Founder() == "" {
csNotice(rb, client.t("Channel is not registered")) service.Notice(rb, client.t("Channel is not registered"))
return return
} }
modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...) modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...)
var change modes.ModeChange var change modes.ModeChange
if len(modeChanges) > 1 || len(unknown) > 0 { if len(modeChanges) > 1 || len(unknown) > 0 {
csNotice(rb, client.t("Invalid mode change")) service.Notice(rb, client.t("Invalid mode change"))
return return
} else if len(modeChanges) == 1 { } else if len(modeChanges) == 1 {
change = modeChanges[0] change = modeChanges[0]
@ -233,17 +227,17 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
accountIsValid = (change.Arg != "") accountIsValid = (change.Arg != "")
} }
if !accountIsValid { if !accountIsValid {
csNotice(rb, client.t("Account does not exist")) service.Notice(rb, client.t("Account does not exist"))
return return
} }
affectedModes, err := channel.ProcessAccountToUmodeChange(client, change) affectedModes, err := channel.ProcessAccountToUmodeChange(client, change)
if err == errInsufficientPrivs { if err == errInsufficientPrivs {
csNotice(rb, client.t("Insufficient privileges")) service.Notice(rb, client.t("Insufficient privileges"))
return return
} else if err != nil { } else if err != nil {
csNotice(rb, client.t("Internal error")) service.Notice(rb, client.t("Internal error"))
return return
} }
@ -253,13 +247,13 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
sort.Slice(affectedModes, func(i, j int) bool { sort.Slice(affectedModes, func(i, j int) bool {
return umodeGreaterThan(affectedModes[i].Mode, affectedModes[j].Mode) return umodeGreaterThan(affectedModes[i].Mode, affectedModes[j].Mode)
}) })
csNotice(rb, fmt.Sprintf(client.t("Channel %[1]s has %[2]d persistent modes set"), channelName, len(affectedModes))) service.Notice(rb, fmt.Sprintf(client.t("Channel %[1]s has %[2]d persistent modes set"), channelName, len(affectedModes)))
for _, modeChange := range affectedModes { for _, modeChange := range affectedModes {
csNotice(rb, fmt.Sprintf(client.t("Account %[1]s receives mode +%[2]s"), modeChange.Arg, string(modeChange.Mode))) service.Notice(rb, fmt.Sprintf(client.t("Account %[1]s receives mode +%[2]s"), modeChange.Arg, string(modeChange.Mode)))
} }
case modes.Add, modes.Remove: case modes.Add, modes.Remove:
if len(affectedModes) > 0 { if len(affectedModes) > 0 {
csNotice(rb, fmt.Sprintf(client.t("Successfully set persistent mode %[1]s on %[2]s"), strings.Join([]string{string(change.Op), string(change.Mode)}, ""), change.Arg)) service.Notice(rb, fmt.Sprintf(client.t("Successfully set persistent mode %[1]s on %[2]s"), strings.Join([]string{string(change.Op), string(change.Mode)}, ""), change.Arg))
// #729: apply change to current membership // #729: apply change to current membership
for _, member := range channel.Members() { for _, member := range channel.Members() {
if member.Account() == change.Arg { if member.Account() == change.Arg {
@ -270,22 +264,22 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
} }
} }
} else { } else {
csNotice(rb, client.t("No changes were made")) service.Notice(rb, client.t("No changes were made"))
} }
} }
} }
func csOpHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csOpHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channelInfo := server.channels.Get(params[0]) channelInfo := server.channels.Get(params[0])
if channelInfo == nil { if channelInfo == nil {
csNotice(rb, client.t("Channel does not exist")) service.Notice(rb, client.t("Channel does not exist"))
return return
} }
channelName := channelInfo.Name() channelName := channelInfo.Name()
clientAccount := client.Account() clientAccount := client.Account()
if clientAccount == "" || clientAccount != channelInfo.Founder() { if clientAccount == "" || clientAccount != channelInfo.Founder() {
csNotice(rb, client.t("Only the channel founder can do this")) service.Notice(rb, client.t("Only the channel founder can do this"))
return return
} }
@ -293,7 +287,7 @@ func csOpHandler(server *Server, client *Client, command string, params []string
if len(params) > 1 { if len(params) > 1 {
target = server.clients.Get(params[1]) target = server.clients.Get(params[1])
if target == nil { if target == nil {
csNotice(rb, client.t("Could not find given client")) service.Notice(rb, client.t("Could not find given client"))
return return
} }
} else { } else {
@ -315,21 +309,21 @@ func csOpHandler(server *Server, client *Client, command string, params []string
announceCmodeChanges(channelInfo, modes.ModeChanges{change}, server.name, "*", "", rb) announceCmodeChanges(channelInfo, modes.ModeChanges{change}, server.name, "*", "", rb)
} }
csNotice(rb, client.t("Successfully granted operator privileges")) service.Notice(rb, client.t("Successfully granted operator privileges"))
tnick := target.Nick() tnick := target.Nick()
server.logger.Info("services", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.Nick(), tnick, channelName)) server.logger.Info("services", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.Nick(), tnick, channelName))
server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.NickMaskString(), tnick, channelName)) server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.NickMaskString(), tnick, channelName))
} }
func csDeopHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csDeopHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channel := server.channels.Get(params[0]) channel := server.channels.Get(params[0])
if channel == nil { if channel == nil {
csNotice(rb, client.t("Channel does not exist")) service.Notice(rb, client.t("Channel does not exist"))
return return
} }
if !channel.hasClient(client) { if !channel.hasClient(client) {
csNotice(rb, client.t("You're not on that channel")) service.Notice(rb, client.t("You're not on that channel"))
return return
} }
@ -337,7 +331,7 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
if len(params) > 1 { if len(params) > 1 {
target = server.clients.Get(params[1]) target = server.clients.Get(params[1])
if target == nil { if target == nil {
csNotice(rb, client.t("Could not find given client")) service.Notice(rb, client.t("Could not find given client"))
return return
} }
} else { } else {
@ -346,7 +340,7 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
present, cumodes := channel.ClientStatus(target) present, cumodes := channel.ClientStatus(target)
if !present || len(cumodes) == 0 { if !present || len(cumodes) == 0 {
csNotice(rb, client.t("Target has no privileges to remove")) service.Notice(rb, client.t("Target has no privileges to remove"))
return return
} }
@ -370,38 +364,38 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
return return
} }
csNotice(rb, client.t("Successfully removed operator privileges")) service.Notice(rb, client.t("Successfully removed operator privileges"))
} }
func csRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if server.Config().Channels.Registration.OperatorOnly && !client.HasRoleCapabs("chanreg") { if server.Config().Channels.Registration.OperatorOnly && !client.HasRoleCapabs("chanreg") {
csNotice(rb, client.t("Channel registration is restricted to server operators")) service.Notice(rb, client.t("Channel registration is restricted to server operators"))
return return
} }
channelName := params[0] channelName := params[0]
channelInfo := server.channels.Get(channelName) channelInfo := server.channels.Get(channelName)
if channelInfo == nil { if channelInfo == nil {
csNotice(rb, client.t("No such channel")) service.Notice(rb, client.t("No such channel"))
return return
} }
if !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) { if !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
csNotice(rb, client.t("You must be an oper on the channel to register it")) service.Notice(rb, client.t("You must be an oper on the channel to register it"))
return return
} }
account := client.Account() account := client.Account()
if !checkChanLimit(client, rb) { if !checkChanLimit(service, client, rb) {
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 := server.channels.SetRegistered(channelName, account) err := server.channels.SetRegistered(channelName, account)
if err != nil { if err != nil {
csNotice(rb, err.Error()) service.Notice(rb, err.Error())
return return
} }
csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName)) service.Notice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.Nick(), channelName)) server.logger.Info("services", 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))
@ -415,38 +409,38 @@ func csRegisterHandler(server *Server, client *Client, command string, params []
}, },
rb) rb)
if applied { if applied {
announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, "*", "", rb) announceCmodeChanges(channelInfo, modes.ModeChanges{change}, service.prefix, "*", "", rb)
} }
} }
// check whether a client has already registered too many channels // check whether a client has already registered too many channels
func checkChanLimit(client *Client, rb *ResponseBuffer) (ok bool) { func checkChanLimit(service *ircService, client *Client, rb *ResponseBuffer) (ok bool) {
account := client.Account() account := client.Account()
channelsAlreadyRegistered := client.server.accounts.ChannelsForAccount(account) channelsAlreadyRegistered := client.server.accounts.ChannelsForAccount(account)
ok = len(channelsAlreadyRegistered) < client.server.Config().Channels.Registration.MaxChannelsPerAccount || client.HasRoleCapabs("chanreg") ok = len(channelsAlreadyRegistered) < client.server.Config().Channels.Registration.MaxChannelsPerAccount || client.HasRoleCapabs("chanreg")
if !ok { if !ok {
csNotice(rb, client.t("You have already registered the maximum number of channels; try dropping some with /CS UNREGISTER")) service.Notice(rb, client.t("You have already registered the maximum number of channels; try dropping some with /CS UNREGISTER"))
} }
return return
} }
func csPrivsCheck(channel RegisteredChannel, client *Client, rb *ResponseBuffer) (success bool) { func csPrivsCheck(service *ircService, channel RegisteredChannel, client *Client, rb *ResponseBuffer) (success bool) {
founder := channel.Founder founder := channel.Founder
if founder == "" { if founder == "" {
csNotice(rb, client.t("That channel is not registered")) service.Notice(rb, client.t("That channel is not registered"))
return false return false
} }
if client.HasRoleCapabs("chanreg") { if client.HasRoleCapabs("chanreg") {
return true return true
} }
if founder != client.Account() { if founder != client.Account() {
csNotice(rb, client.t("Insufficient privileges")) service.Notice(rb, client.t("Insufficient privileges"))
return false return false
} }
return true return true
} }
func csUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csUnregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channelName := params[0] channelName := params[0]
var verificationCode string var verificationCode string
if len(params) > 1 { if len(params) > 1 {
@ -455,41 +449,41 @@ func csUnregisterHandler(server *Server, client *Client, command string, params
channel := server.channels.Get(channelName) channel := server.channels.Get(channelName)
if channel == nil { if channel == nil {
csNotice(rb, client.t("No such channel")) service.Notice(rb, client.t("No such channel"))
return return
} }
info := channel.ExportRegistration(0) info := channel.ExportRegistration(0)
channelKey := info.NameCasefolded channelKey := info.NameCasefolded
if !csPrivsCheck(info, client, rb) { if !csPrivsCheck(service, info, client, rb) {
return return
} }
expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt) expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt)
if expectedCode != verificationCode { if expectedCode != verificationCode {
csNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b"))) service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b")))
csNotice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/CS UNREGISTER %s %s", channelKey, expectedCode))) service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/CS UNREGISTER %s %s", channelKey, expectedCode)))
return return
} }
server.channels.SetUnregistered(channelKey, info.Founder) server.channels.SetUnregistered(channelKey, info.Founder)
csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey)) service.Notice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
} }
func csClearHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csClearHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channel := server.channels.Get(params[0]) channel := server.channels.Get(params[0])
if channel == nil { if channel == nil {
csNotice(rb, client.t("Channel does not exist")) service.Notice(rb, client.t("Channel does not exist"))
return return
} }
if !csPrivsCheck(channel.ExportRegistration(0), client, rb) { if !csPrivsCheck(service, channel.ExportRegistration(0), client, rb) {
return return
} }
switch strings.ToLower(params[1]) { switch strings.ToLower(params[1]) {
case "access": case "access":
channel.resetAccess() channel.resetAccess()
csNotice(rb, client.t("Successfully reset channel access")) service.Notice(rb, client.t("Successfully reset channel access"))
case "users": case "users":
for _, target := range channel.Members() { for _, target := range channel.Members() {
if target != client { if target != client {
@ -497,20 +491,20 @@ func csClearHandler(server *Server, client *Client, command string, params []str
} }
} }
default: default:
csNotice(rb, client.t("Invalid parameters")) service.Notice(rb, client.t("Invalid parameters"))
} }
} }
func csTransferHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csTransferHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if strings.ToLower(params[0]) == "accept" { if strings.ToLower(params[0]) == "accept" {
processTransferAccept(client, params[1], rb) processTransferAccept(service, client, params[1], rb)
return return
} }
chname := params[0] chname := params[0]
channel := server.channels.Get(chname) channel := server.channels.Get(chname)
if channel == nil { if channel == nil {
csNotice(rb, client.t("Channel does not exist")) service.Notice(rb, client.t("Channel does not exist"))
return return
} }
regInfo := channel.ExportRegistration(0) regInfo := channel.ExportRegistration(0)
@ -519,21 +513,21 @@ func csTransferHandler(server *Server, client *Client, command string, params []
isFounder := account != "" && account == regInfo.Founder isFounder := account != "" && account == regInfo.Founder
hasPrivs := client.HasRoleCapabs("chanreg") hasPrivs := client.HasRoleCapabs("chanreg")
if !(isFounder || hasPrivs) { if !(isFounder || hasPrivs) {
csNotice(rb, client.t("Insufficient privileges")) service.Notice(rb, client.t("Insufficient privileges"))
return return
} }
target := params[1] target := params[1]
targetAccount, err := server.accounts.LoadAccount(params[1]) targetAccount, err := server.accounts.LoadAccount(params[1])
if err != nil { if err != nil {
csNotice(rb, client.t("Account does not exist")) service.Notice(rb, client.t("Account does not exist"))
return return
} }
if targetAccount.NameCasefolded != account { if targetAccount.NameCasefolded != account {
expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt) expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
codeValidated := 2 < len(params) && params[2] == expectedCode codeValidated := 2 < len(params) && params[2] == expectedCode
if !codeValidated { if !codeValidated {
csNotice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b"))) service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b")))
csNotice(rb, fmt.Sprintf(client.t("To confirm your channel transfer, type: /CS TRANSFER %[1]s %[2]s %[3]s"), chname, target, expectedCode)) service.Notice(rb, fmt.Sprintf(client.t("To confirm your channel transfer, type: /CS TRANSFER %[1]s %[2]s %[3]s"), chname, target, expectedCode))
return return
} }
} }
@ -541,19 +535,19 @@ func csTransferHandler(server *Server, client *Client, command string, params []
if err == nil { if err == nil {
switch status { switch status {
case channelTransferComplete: case channelTransferComplete:
csNotice(rb, fmt.Sprintf(client.t("Successfully transferred channel %[1]s to account %[2]s"), chname, target)) service.Notice(rb, fmt.Sprintf(client.t("Successfully transferred channel %[1]s to account %[2]s"), chname, target))
case channelTransferPending: case channelTransferPending:
sendTransferPendingNotice(server, target, chname) sendTransferPendingNotice(service, server, target, chname)
csNotice(rb, fmt.Sprintf(client.t("Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance"), chname, target)) service.Notice(rb, fmt.Sprintf(client.t("Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance"), chname, target))
case channelTransferCancelled: case channelTransferCancelled:
csNotice(rb, fmt.Sprintf(client.t("Cancelled pending transfer of channel %s"), chname)) service.Notice(rb, fmt.Sprintf(client.t("Cancelled pending transfer of channel %s"), chname))
} }
} else { } else {
csNotice(rb, client.t("Could not transfer channel")) service.Notice(rb, client.t("Could not transfer channel"))
} }
} }
func sendTransferPendingNotice(server *Server, account, chname string) { func sendTransferPendingNotice(service *ircService, server *Server, account, chname string) {
clients := server.accounts.AccountToClients(account) clients := server.accounts.AccountToClients(account)
if len(clients) == 0 { if len(clients) == 0 {
return return
@ -565,29 +559,29 @@ func sendTransferPendingNotice(server *Server, account, chname string) {
break // prefer the login where the nick is the account break // prefer the login where the nick is the account
} }
} }
client.Send(nil, chanservMask, "NOTICE", client.Nick(), fmt.Sprintf(client.t("You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s"), chname)) client.Send(nil, service.prefix, "NOTICE", client.Nick(), fmt.Sprintf(client.t("You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s"), chname))
} }
func processTransferAccept(client *Client, chname string, rb *ResponseBuffer) { func processTransferAccept(service *ircService, client *Client, chname string, rb *ResponseBuffer) {
channel := client.server.channels.Get(chname) channel := client.server.channels.Get(chname)
if channel == nil { if channel == nil {
csNotice(rb, client.t("Channel does not exist")) service.Notice(rb, client.t("Channel does not exist"))
return return
} }
if !checkChanLimit(client, rb) { if !checkChanLimit(service, client, rb) {
return return
} }
switch channel.AcceptTransfer(client) { switch channel.AcceptTransfer(client) {
case nil: case nil:
csNotice(rb, fmt.Sprintf(client.t("Successfully accepted ownership of channel %s"), channel.Name())) service.Notice(rb, fmt.Sprintf(client.t("Successfully accepted ownership of channel %s"), channel.Name()))
case errChannelTransferNotOffered: case errChannelTransferNotOffered:
csNotice(rb, fmt.Sprintf(client.t("You weren't offered ownership of channel %s"), channel.Name())) service.Notice(rb, fmt.Sprintf(client.t("You weren't offered ownership of channel %s"), channel.Name()))
default: default:
csNotice(rb, fmt.Sprintf(client.t("Could not accept ownership of channel %s"), channel.Name())) service.Notice(rb, fmt.Sprintf(client.t("Could not accept ownership of channel %s"), channel.Name()))
} }
} }
func csPurgeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csPurgeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
oper := client.Oper() oper := client.Oper()
if oper == nil { if oper == nil {
return // should be impossible because you need oper capabs for this return // should be impossible because you need oper capabs for this
@ -611,29 +605,29 @@ func csPurgeHandler(server *Server, client *Client, command string, params []str
channel.Kick(client, target, "Cleared by ChanServ", rb, true) channel.Kick(client, target, "Cleared by ChanServ", rb, true)
} }
} }
csNotice(rb, fmt.Sprintf(client.t("Successfully purged channel %s from the server"), chname)) service.Notice(rb, fmt.Sprintf(client.t("Successfully purged channel %s from the server"), chname))
case errInvalidChannelName: case errInvalidChannelName:
csNotice(rb, fmt.Sprintf(client.t("Can't purge invalid channel %s"), chname)) service.Notice(rb, fmt.Sprintf(client.t("Can't purge invalid channel %s"), chname))
default: default:
csNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} }
} }
func csUnpurgeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csUnpurgeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
chname := params[0] chname := params[0]
switch server.channels.Unpurge(chname) { switch server.channels.Unpurge(chname) {
case nil: case nil:
csNotice(rb, fmt.Sprintf(client.t("Successfully unpurged channel %s from the server"), chname)) service.Notice(rb, fmt.Sprintf(client.t("Successfully unpurged channel %s from the server"), chname))
case errNoSuchChannel: case errNoSuchChannel:
csNotice(rb, fmt.Sprintf(client.t("Channel %s wasn't previously purged from the server"), chname)) service.Notice(rb, fmt.Sprintf(client.t("Channel %s wasn't previously purged from the server"), chname))
default: default:
csNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} }
} }
func csListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if !client.HasRoleCapabs("chanreg") { if !client.HasRoleCapabs("chanreg") {
csNotice(rb, client.t("Insufficient privileges")) service.Notice(rb, client.t("Insufficient privileges"))
return return
} }
@ -642,27 +636,27 @@ func csListHandler(server *Server, client *Client, command string, params []stri
var err error var err error
searchRegex, err = regexp.Compile(params[0]) searchRegex, err = regexp.Compile(params[0])
if err != nil { if err != nil {
csNotice(rb, client.t("Invalid regex")) service.Notice(rb, client.t("Invalid regex"))
return return
} }
} }
csNotice(rb, ircfmt.Unescape(client.t("*** $bChanServ LIST$b ***"))) service.Notice(rb, ircfmt.Unescape(client.t("*** $bChanServ LIST$b ***")))
channels := server.channelRegistry.AllChannels() channels := server.channelRegistry.AllChannels()
for _, channel := range channels { for _, channel := range channels {
if searchRegex == nil || searchRegex.MatchString(channel) { if searchRegex == nil || searchRegex.MatchString(channel) {
csNotice(rb, fmt.Sprintf(" %s", channel)) service.Notice(rb, fmt.Sprintf(" %s", channel))
} }
} }
csNotice(rb, ircfmt.Unescape(client.t("*** $bEnd of ChanServ LIST$b ***"))) service.Notice(rb, ircfmt.Unescape(client.t("*** $bEnd of ChanServ LIST$b ***")))
} }
func csInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csInfoHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
chname, err := CasefoldChannel(params[0]) chname, err := CasefoldChannel(params[0])
if err != nil { if err != nil {
csNotice(rb, client.t("Invalid channel name")) service.Notice(rb, client.t("Invalid channel name"))
return return
} }
@ -670,16 +664,16 @@ func csInfoHandler(server *Server, client *Client, command string, params []stri
if client.HasRoleCapabs("chanreg") { if client.HasRoleCapabs("chanreg") {
purgeRecord, err := server.channelRegistry.LoadPurgeRecord(chname) purgeRecord, err := server.channelRegistry.LoadPurgeRecord(chname)
if err == nil { if err == nil {
csNotice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname)) service.Notice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname))
csNotice(rb, fmt.Sprintf(client.t("Purged by operator: %s"), purgeRecord.Oper)) service.Notice(rb, fmt.Sprintf(client.t("Purged by operator: %s"), purgeRecord.Oper))
csNotice(rb, fmt.Sprintf(client.t("Purged at: %s"), purgeRecord.PurgedAt.Format(time.RFC1123))) service.Notice(rb, fmt.Sprintf(client.t("Purged at: %s"), purgeRecord.PurgedAt.Format(time.RFC1123)))
if purgeRecord.Reason != "" { if purgeRecord.Reason != "" {
csNotice(rb, fmt.Sprintf(client.t("Purge reason: %s"), purgeRecord.Reason)) service.Notice(rb, fmt.Sprintf(client.t("Purge reason: %s"), purgeRecord.Reason))
} }
} }
} else { } else {
if server.channels.IsPurged(chname) { if server.channels.IsPurged(chname) {
csNotice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname)) service.Notice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname))
} }
} }
@ -690,59 +684,59 @@ func csInfoHandler(server *Server, client *Client, command string, params []stri
} else { } else {
chinfo, err = server.channelRegistry.LoadChannel(chname) chinfo, err = server.channelRegistry.LoadChannel(chname)
if err != nil && !(err == errNoSuchChannel || err == errFeatureDisabled) { if err != nil && !(err == errNoSuchChannel || err == errFeatureDisabled) {
csNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
return return
} }
} }
// channel exists but is unregistered, or doesn't exist: // channel exists but is unregistered, or doesn't exist:
if chinfo.Founder == "" { if chinfo.Founder == "" {
csNotice(rb, fmt.Sprintf(client.t("Channel %s is not registered"), chname)) service.Notice(rb, fmt.Sprintf(client.t("Channel %s is not registered"), chname))
return return
} }
csNotice(rb, fmt.Sprintf(client.t("Channel %s is registered"), chinfo.Name)) service.Notice(rb, fmt.Sprintf(client.t("Channel %s is registered"), chinfo.Name))
csNotice(rb, fmt.Sprintf(client.t("Founder: %s"), chinfo.Founder)) service.Notice(rb, fmt.Sprintf(client.t("Founder: %s"), chinfo.Founder))
csNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), chinfo.RegisteredAt.Format(time.RFC1123))) service.Notice(rb, fmt.Sprintf(client.t("Registered at: %s"), chinfo.RegisteredAt.Format(time.RFC1123)))
} }
func displayChannelSetting(settingName string, settings ChannelSettings, client *Client, rb *ResponseBuffer) { func displayChannelSetting(service *ircService, settingName string, settings ChannelSettings, client *Client, rb *ResponseBuffer) {
config := client.server.Config() config := client.server.Config()
switch strings.ToLower(settingName) { switch strings.ToLower(settingName) {
case "history": case "history":
effectiveValue := historyEnabled(config.History.Persistent.RegisteredChannels, settings.History) effectiveValue := historyEnabled(config.History.Persistent.RegisteredChannels, settings.History)
csNotice(rb, fmt.Sprintf(client.t("The stored channel history setting is: %s"), historyStatusToString(settings.History))) service.Notice(rb, fmt.Sprintf(client.t("The stored channel history setting is: %s"), historyStatusToString(settings.History)))
csNotice(rb, fmt.Sprintf(client.t("Given current server settings, the channel history setting is: %s"), historyStatusToString(effectiveValue))) service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, the channel history setting is: %s"), historyStatusToString(effectiveValue)))
default: default:
csNotice(rb, client.t("Invalid params")) service.Notice(rb, client.t("Invalid params"))
} }
} }
func csGetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csGetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
chname, setting := params[0], params[1] chname, setting := params[0], params[1]
channel := server.channels.Get(chname) channel := server.channels.Get(chname)
if channel == nil { if channel == nil {
csNotice(rb, client.t("No such channel")) service.Notice(rb, client.t("No such channel"))
return return
} }
info := channel.ExportRegistration(IncludeSettings) info := channel.ExportRegistration(IncludeSettings)
if !csPrivsCheck(info, client, rb) { if !csPrivsCheck(service, info, client, rb) {
return return
} }
displayChannelSetting(setting, info.Settings, client, rb) displayChannelSetting(service, setting, info.Settings, client, rb)
} }
func csSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
chname, setting, value := params[0], params[1], params[2] chname, setting, value := params[0], params[1], params[2]
channel := server.channels.Get(chname) channel := server.channels.Get(chname)
if channel == nil { if channel == nil {
csNotice(rb, client.t("No such channel")) service.Notice(rb, client.t("No such channel"))
return return
} }
info := channel.ExportRegistration(IncludeSettings) info := channel.ExportRegistration(IncludeSettings)
settings := info.Settings settings := info.Settings
if !csPrivsCheck(info, client, rb) { if !csPrivsCheck(service, info, client, rb) {
return return
} }
@ -760,12 +754,12 @@ func csSetHandler(server *Server, client *Client, command string, params []strin
switch err { switch err {
case nil: case nil:
csNotice(rb, client.t("Successfully changed the channel settings")) service.Notice(rb, client.t("Successfully changed the channel settings"))
displayChannelSetting(setting, settings, client, rb) displayChannelSetting(service, setting, settings, client, rb)
case errInvalidParams: case errInvalidParams:
csNotice(rb, client.t("Invalid parameters")) service.Notice(rb, client.t("Invalid parameters"))
default: default:
server.logger.Error("internal", "CS SET error:", err.Error()) server.logger.Error("internal", "CS SET error:", err.Error())
csNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} }
} }

View File

@ -526,17 +526,18 @@ type Config struct {
forceTrailing bool forceTrailing bool
SendUnprefixedSasl bool `yaml:"send-unprefixed-sasl"` SendUnprefixedSasl bool `yaml:"send-unprefixed-sasl"`
} }
isupport isupport.List isupport isupport.List
IPLimits connection_limits.LimiterConfig `yaml:"ip-limits"` IPLimits connection_limits.LimiterConfig `yaml:"ip-limits"`
Cloaks cloaks.CloakConfig `yaml:"ip-cloaking"` Cloaks cloaks.CloakConfig `yaml:"ip-cloaking"`
SecureNetDefs []string `yaml:"secure-nets"` SecureNetDefs []string `yaml:"secure-nets"`
secureNets []net.IPNet secureNets []net.IPNet
supportedCaps *caps.Set supportedCaps *caps.Set
capValues caps.Values capValues caps.Values
Casemapping Casemapping Casemapping Casemapping
EnforceUtf8 bool `yaml:"enforce-utf8"` EnforceUtf8 bool `yaml:"enforce-utf8"`
OutputPath string `yaml:"output-path"` OutputPath string `yaml:"output-path"`
IPCheckScript ScriptConfig `yaml:"ip-check-script"` IPCheckScript ScriptConfig `yaml:"ip-check-script"`
OverrideServicesHostname string `yaml:"override-services-hostname"`
} }
Roleplay struct { Roleplay struct {

View File

@ -77,23 +77,23 @@ func registrationErrorToMessage(err error) (message string) {
} }
// helper function to dispatch messages when a client successfully registers // helper function to dispatch messages when a client successfully registers
func sendSuccessfulRegResponse(client *Client, rb *ResponseBuffer, forNS bool) { func sendSuccessfulRegResponse(service *ircService, client *Client, rb *ResponseBuffer) {
details := client.Details() details := client.Details()
if forNS { if service != nil {
nsNotice(rb, client.t("Account created")) service.Notice(rb, client.t("Account created"))
} else { } else {
rb.Add(nil, client.server.name, RPL_REG_SUCCESS, details.nick, details.accountName, client.t("Account created")) rb.Add(nil, client.server.name, RPL_REG_SUCCESS, details.nick, details.accountName, client.t("Account created"))
} }
client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] from IP %s"), details.nickMask, details.accountName, rb.session.IP().String())) client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] from IP %s"), details.nickMask, details.accountName, rb.session.IP().String()))
sendSuccessfulAccountAuth(client, rb, forNS, false) sendSuccessfulAccountAuth(service, client, rb, false)
} }
// sendSuccessfulAccountAuth means that an account auth attempt completed successfully, and is used to dispatch messages. // sendSuccessfulAccountAuth means that an account auth attempt completed successfully, and is used to dispatch messages.
func sendSuccessfulAccountAuth(client *Client, rb *ResponseBuffer, forNS, forSASL bool) { func sendSuccessfulAccountAuth(service *ircService, client *Client, rb *ResponseBuffer, forSASL bool) {
details := client.Details() details := client.Details()
if forNS { if service != nil {
nsNotice(rb, fmt.Sprintf(client.t("You're now logged in as %s"), details.accountName)) service.Notice(rb, fmt.Sprintf(client.t("You're now logged in as %s"), details.accountName))
} else { } else {
//TODO(dan): some servers send this numeric even for NickServ logins iirc? to confirm and maybe do too //TODO(dan): some servers send this numeric even for NickServ logins iirc? to confirm and maybe do too
rb.Add(nil, client.server.name, RPL_LOGGEDIN, details.nick, details.nickMask, details.accountName, fmt.Sprintf(client.t("You are now logged in as %s"), details.accountName)) rb.Add(nil, client.server.name, RPL_LOGGEDIN, details.nick, details.nickMask, details.accountName, fmt.Sprintf(client.t("You are now logged in as %s"), details.accountName))
@ -253,11 +253,11 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
msg := authErrorToMessage(server, err) msg := authErrorToMessage(server, err)
rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg))) rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
return false return false
} else if !fixupNickEqualsAccount(client, rb, server.Config()) { } else if !fixupNickEqualsAccount(client, rb, server.Config(), "") {
return false return false
} }
sendSuccessfulAccountAuth(client, rb, false, true) sendSuccessfulAccountAuth(nil, client, rb, true)
return false return false
} }
@ -311,11 +311,11 @@ func authExternalHandler(server *Server, client *Client, mechanism string, value
msg := authErrorToMessage(server, err) msg := authErrorToMessage(server, err)
rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg))) rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
return false return false
} else if !fixupNickEqualsAccount(client, rb, server.Config()) { } else if !fixupNickEqualsAccount(client, rb, server.Config(), "") {
return false return false
} }
sendSuccessfulAccountAuth(client, rb, false, true) sendSuccessfulAccountAuth(nil, client, rb, true)
return false return false
} }
@ -1525,7 +1525,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
config := server.Config() config := server.Config()
if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" && !client.HasMode(modes.Operator) { if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" && !client.HasMode(modes.Operator) {
remaining := time.Until(client.ctime.Add(config.Channels.ListDelay)) remaining := time.Until(client.ctime.Add(config.Channels.ListDelay))
csNotice(rb, fmt.Sprintf(client.t("This server requires that you wait %v after connecting before you can use /LIST. You have %v left."), config.Channels.ListDelay, remaining)) rb.Notice(fmt.Sprintf(client.t("This server requires that you wait %v after connecting before you can use /LIST. You have %v left."), config.Channels.ListDelay, remaining))
rb.Add(nil, server.name, RPL_LISTEND, client.Nick(), client.t("End of LIST")) rb.Add(nil, server.name, RPL_LISTEND, client.Nick(), client.t("End of LIST"))
return false return false
} }
@ -2355,7 +2355,7 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
} }
err := server.accounts.AuthenticateByPassphrase(client, account, accountPass) err := server.accounts.AuthenticateByPassphrase(client, account, accountPass)
if err == nil { if err == nil {
sendSuccessfulAccountAuth(client, rb, false, true) sendSuccessfulAccountAuth(nil, client, rb, true)
// login-via-pass-command entails that we do not need to check // login-via-pass-command entails that we do not need to check
// an actual server password (either no password or skip-server-password) // an actual server password (either no password or skip-server-password)
rb.session.passStatus = serverPassSuccessful rb.session.passStatus = serverPassSuccessful
@ -2446,13 +2446,13 @@ func registerHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *
err := server.accounts.Verify(client, accountName, "") err := server.accounts.Verify(client, accountName, "")
if err == nil { if err == nil {
if client.registered { if client.registered {
if !fixupNickEqualsAccount(client, rb, config) { if !fixupNickEqualsAccount(client, rb, config, "") {
err = errNickAccountMismatch err = errNickAccountMismatch
} }
} }
if err == nil { if err == nil {
rb.Add(nil, server.name, "REGISTER", "SUCCESS", accountName, client.t("Account successfully registered")) rb.Add(nil, server.name, "REGISTER", "SUCCESS", accountName, client.t("Account successfully registered"))
sendSuccessfulRegResponse(client, rb, true) sendSuccessfulRegResponse(nil, client, rb)
} }
} }
if err != nil { if err != nil {
@ -2494,14 +2494,14 @@ func verifyHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
accountName, verificationCode := msg.Params[0], msg.Params[1] accountName, verificationCode := msg.Params[0], msg.Params[1]
err := server.accounts.Verify(client, accountName, verificationCode) err := server.accounts.Verify(client, accountName, verificationCode)
if err == nil && client.registered { if err == nil && client.registered {
if !fixupNickEqualsAccount(client, rb, config) { if !fixupNickEqualsAccount(client, rb, config, "") {
err = errNickAccountMismatch err = errNickAccountMismatch
} }
} }
switch err { switch err {
case nil: case nil:
rb.Add(nil, server.name, "VERIFY", "SUCCESS", accountName, client.t("Account successfully registered")) rb.Add(nil, server.name, "VERIFY", "SUCCESS", accountName, client.t("Account successfully registered"))
sendSuccessfulRegResponse(client, rb, true) sendSuccessfulRegResponse(nil, client, rb)
case errAccountVerificationInvalidCode: case errAccountVerificationInvalidCode:
rb.Add(nil, server.name, "FAIL", "VERIFY", "INVALID_CODE", client.t("Invalid verification code")) rb.Add(nil, server.name, "FAIL", "VERIFY", "INVALID_CODE", client.t("Invalid verification code"))
default: default:
@ -2873,7 +2873,7 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
username, password = username[:colonIndex], username[colonIndex+1:] username, password = username[:colonIndex], username[colonIndex+1:]
err := server.accounts.AuthenticateByPassphrase(client, username, password) err := server.accounts.AuthenticateByPassphrase(client, username, password)
if err == nil { if err == nil {
sendSuccessfulAccountAuth(client, rb, false, true) sendSuccessfulAccountAuth(nil, client, rb, true)
} else { } else {
// this is wrong, but send something for debugging that will show up in a raw transcript // this is wrong, but send something for debugging that will show up in a raw transcript
rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), client.t("SASL authentication failed")) rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), client.t("SASL authentication failed"))

View File

@ -18,7 +18,6 @@ import (
const ( const (
histservHelp = `HistServ provides commands related to history.` histservHelp = `HistServ provides commands related to history.`
histServMask = "HistServ!HistServ@localhost"
) )
func histservEnabled(config *Config) bool { func histservEnabled(config *Config) bool {
@ -83,24 +82,19 @@ CHATHISTORY.`,
} }
) )
// histNotice sends the client a notice from HistServ func histservForgetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
func histNotice(rb *ResponseBuffer, text string) {
rb.Add(nil, histServMask, "NOTICE", rb.target.Nick(), text)
}
func histservForgetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
accountName := server.accounts.AccountToAccountName(params[0]) accountName := server.accounts.AccountToAccountName(params[0])
if accountName == "" { if accountName == "" {
histNotice(rb, client.t("Could not look up account name, proceeding anyway")) service.Notice(rb, client.t("Could not look up account name, proceeding anyway"))
accountName = params[0] accountName = params[0]
} }
server.ForgetHistory(accountName) server.ForgetHistory(accountName)
histNotice(rb, fmt.Sprintf(client.t("Enqueued account %s for message deletion"), accountName)) service.Notice(rb, fmt.Sprintf(client.t("Enqueued account %s for message deletion"), accountName))
} }
func histservDeleteHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func histservDeleteHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var target, msgid string var target, msgid string
if len(params) == 1 { if len(params) == 1 {
msgid = params[0] msgid = params[0]
@ -113,27 +107,27 @@ func histservDeleteHandler(server *Server, client *Client, command string, param
if !hasPrivs { if !hasPrivs {
accountName = client.AccountName() accountName = client.AccountName()
if !(server.Config().History.Retention.AllowIndividualDelete && accountName != "*") { if !(server.Config().History.Retention.AllowIndividualDelete && accountName != "*") {
histNotice(rb, client.t("Insufficient privileges")) service.Notice(rb, client.t("Insufficient privileges"))
return return
} }
} }
err := server.DeleteMessage(target, msgid, accountName) err := server.DeleteMessage(target, msgid, accountName)
if err == nil { if err == nil {
histNotice(rb, client.t("Successfully deleted message")) service.Notice(rb, client.t("Successfully deleted message"))
} else { } else {
if hasPrivs { if hasPrivs {
histNotice(rb, fmt.Sprintf(client.t("Error deleting message: %v"), err)) service.Notice(rb, fmt.Sprintf(client.t("Error deleting message: %v"), err))
} else { } else {
histNotice(rb, client.t("Could not delete message")) service.Notice(rb, client.t("Could not delete message"))
} }
} }
} }
func histservExportHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func histservExportHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
cfAccount, err := CasefoldName(params[0]) cfAccount, err := CasefoldName(params[0])
if err != nil { if err != nil {
histNotice(rb, client.t("Invalid account name")) service.Notice(rb, client.t("Invalid account name"))
return return
} }
@ -143,15 +137,15 @@ func histservExportHandler(server *Server, client *Client, command string, param
pathname := config.getOutputPath(filename) pathname := config.getOutputPath(filename)
outfile, err := os.Create(pathname) outfile, err := os.Create(pathname)
if err != nil { if err != nil {
histNotice(rb, fmt.Sprintf(client.t("Error opening export file: %v"), err)) service.Notice(rb, fmt.Sprintf(client.t("Error opening export file: %v"), err))
} else { } else {
histNotice(rb, fmt.Sprintf(client.t("Started exporting data for account %[1]s to file %[2]s"), cfAccount, filename)) service.Notice(rb, fmt.Sprintf(client.t("Started exporting data for account %[1]s to file %[2]s"), cfAccount, filename))
} }
go histservExportAndNotify(server, cfAccount, outfile, filename, client.Nick()) go histservExportAndNotify(service, server, cfAccount, outfile, filename, client.Nick())
} }
func histservExportAndNotify(server *Server, cfAccount string, outfile *os.File, filename, alertNick string) { func histservExportAndNotify(service *ircService, server *Server, cfAccount string, outfile *os.File, filename, alertNick string) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
server.logger.Error("history", server.logger.Error("history",
@ -167,19 +161,19 @@ func histservExportAndNotify(server *Server, cfAccount string, outfile *os.File,
client := server.clients.Get(alertNick) client := server.clients.Get(alertNick)
if client != nil && client.HasRoleCapabs("history") { if client != nil && client.HasRoleCapabs("history") {
client.Send(nil, histServMask, "NOTICE", client.Nick(), fmt.Sprintf(client.t("Data export for %[1]s completed and written to %[2]s"), cfAccount, filename)) client.Send(nil, service.prefix, "NOTICE", client.Nick(), fmt.Sprintf(client.t("Data export for %[1]s completed and written to %[2]s"), cfAccount, filename))
} }
} }
func histservPlayHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func histservPlayHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
items, _, err := easySelectHistory(server, client, params) items, _, err := easySelectHistory(server, client, params)
if err != nil { if err != nil {
histNotice(rb, client.t("Could not retrieve history")) service.Notice(rb, client.t("Could not retrieve history"))
return return
} }
playMessage := func(timestamp time.Time, nick, message string) { playMessage := func(timestamp time.Time, nick, message string) {
histNotice(rb, fmt.Sprintf("%s <%s> %s", timestamp.Format("15:04:05"), stripMaskFromNick(nick), message)) service.Notice(rb, fmt.Sprintf("%s <%s> %s", timestamp.Format("15:04:05"), stripMaskFromNick(nick), message))
} }
for _, item := range items { for _, item := range items {
@ -196,7 +190,7 @@ func histservPlayHandler(server *Server, client *Client, command string, params
} }
} }
histNotice(rb, client.t("End of history playback")) service.Notice(rb, client.t("End of history playback"))
} }
// handles parameter parsing and history queries for /HISTORY and /HISTSERV PLAY // handles parameter parsing and history queries for /HISTORY and /HISTSERV PLAY

View File

@ -16,7 +16,6 @@ import (
const ( const (
hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
in place of your client's hostname/IP).` in place of your client's hostname/IP).`
hsNickMask = "HostServ!HostServ@localhost"
) )
var ( var (
@ -95,12 +94,7 @@ display the necessary code.`,
} }
) )
// hsNotice sends the client a notice from HostServ func hsOnOffHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
func hsNotice(rb *ResponseBuffer, text string) {
rb.Add(nil, hsNickMask, "NOTICE", rb.target.Nick(), text)
}
func hsOnOffHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
enable := false enable := false
if command == "on" { if command == "on" {
enable = true enable = true
@ -108,28 +102,28 @@ func hsOnOffHandler(server *Server, client *Client, command string, params []str
_, err := server.accounts.VHostSetEnabled(client, enable) _, err := server.accounts.VHostSetEnabled(client, enable)
if err == errNoVhost { if err == errNoVhost {
hsNotice(rb, client.t(err.Error())) service.Notice(rb, client.t(err.Error()))
} else if err != nil { } else if err != nil {
hsNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} else if enable { } else if enable {
hsNotice(rb, client.t("Successfully enabled your vhost")) service.Notice(rb, client.t("Successfully enabled your vhost"))
} else { } else {
hsNotice(rb, client.t("Successfully disabled your vhost")) service.Notice(rb, client.t("Successfully disabled your vhost"))
} }
} }
func hsStatusHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func hsStatusHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var accountName string var accountName string
if len(params) > 0 { if len(params) > 0 {
if !client.HasRoleCapabs("vhosts") { if !client.HasRoleCapabs("vhosts") {
hsNotice(rb, client.t("Command restricted")) service.Notice(rb, client.t("Command restricted"))
return return
} }
accountName = params[0] accountName = params[0]
} else { } else {
accountName = client.Account() accountName = client.Account()
if accountName == "" { if accountName == "" {
hsNotice(rb, client.t("You're not logged into an account")) service.Notice(rb, client.t("You're not logged into an account"))
return return
} }
} }
@ -139,17 +133,17 @@ func hsStatusHandler(server *Server, client *Client, command string, params []st
if err != errAccountDoesNotExist { if err != errAccountDoesNotExist {
server.logger.Warning("internal", "error loading account info", accountName, err.Error()) server.logger.Warning("internal", "error loading account info", accountName, err.Error())
} }
hsNotice(rb, client.t("No such account")) service.Notice(rb, client.t("No such account"))
return return
} }
if account.VHost.ApprovedVHost != "" { if account.VHost.ApprovedVHost != "" {
hsNotice(rb, fmt.Sprintf(client.t("Account %[1]s has vhost: %[2]s"), accountName, account.VHost.ApprovedVHost)) service.Notice(rb, fmt.Sprintf(client.t("Account %[1]s has vhost: %[2]s"), accountName, account.VHost.ApprovedVHost))
if !account.VHost.Enabled { if !account.VHost.Enabled {
hsNotice(rb, client.t("This vhost is currently disabled, but can be enabled with /HS ON")) service.Notice(rb, client.t("This vhost is currently disabled, but can be enabled with /HS ON"))
} }
} else { } else {
hsNotice(rb, fmt.Sprintf(client.t("Account %s has no vhost"), accountName)) service.Notice(rb, fmt.Sprintf(client.t("Account %s has no vhost"), accountName))
} }
} }
@ -164,14 +158,14 @@ func validateVhost(server *Server, vhost string, oper bool) error {
return nil return nil
} }
func hsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func hsSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
user := params[0] user := params[0]
var vhost string var vhost string
if command == "set" { if command == "set" {
vhost = params[1] vhost = params[1]
if validateVhost(server, vhost, true) != nil { if validateVhost(server, vhost, true) != nil {
hsNotice(rb, client.t("Invalid vhost")) service.Notice(rb, client.t("Invalid vhost"))
return return
} }
} }
@ -179,22 +173,22 @@ func hsSetHandler(server *Server, client *Client, command string, params []strin
_, err := server.accounts.VHostSet(user, vhost) _, err := server.accounts.VHostSet(user, vhost)
if err != nil { if err != nil {
hsNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} else if vhost != "" { } else if vhost != "" {
hsNotice(rb, client.t("Successfully set vhost")) service.Notice(rb, client.t("Successfully set vhost"))
} else { } else {
hsNotice(rb, client.t("Successfully cleared vhost")) service.Notice(rb, client.t("Successfully cleared vhost"))
} }
} }
func hsSetCloakSecretHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func hsSetCloakSecretHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
secret := params[0] secret := params[0]
expectedCode := utils.ConfirmationCode(secret, server.ctime) expectedCode := utils.ConfirmationCode(secret, server.ctime)
if len(params) == 1 || params[1] != expectedCode { if len(params) == 1 || params[1] != expectedCode {
hsNotice(rb, ircfmt.Unescape(client.t("$bWarning: changing the cloak secret will invalidate stored ban/invite/exception lists.$b"))) service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: changing the cloak secret will invalidate stored ban/invite/exception lists.$b")))
hsNotice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/HS SETCLOAKSECRET %s %s", secret, expectedCode))) service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/HS SETCLOAKSECRET %s %s", secret, expectedCode)))
return return
} }
StoreCloakSecret(server.store, secret) StoreCloakSecret(server.store, secret)
hsNotice(rb, client.t("Rotated the cloak secret; you must rehash or restart the server for it to take effect")) service.Notice(rb, client.t("Rotated the cloak secret; you must rehash or restart the server for it to take effect"))
} }

View File

@ -151,7 +151,7 @@ func (server *Server) RandomlyRename(client *Client) {
// so we need to re-NICK automatically on every login event (IDENTIFY, // so we need to re-NICK automatically on every login event (IDENTIFY,
// VERIFY, and a REGISTER that auto-verifies). if we can't get the nick // VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
// then we log them out (they will be able to reattach with SASL) // then we log them out (they will be able to reattach with SASL)
func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config) (success bool) { func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config, source string) (success bool) {
if !config.Accounts.NickReservation.ForceNickEqualsAccount { if !config.Accounts.NickReservation.ForceNickEqualsAccount {
return true return true
} }
@ -161,7 +161,10 @@ func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config)
err := performNickChange(client.server, client, client, rb.session, client.AccountName(), rb) err := performNickChange(client.server, client, client, rb.session, client.AccountName(), rb)
if err != nil && err != errNoop { if err != nil && err != errNoop {
client.server.accounts.Logout(client) client.server.accounts.Logout(client)
nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL")) if source == "" {
source = client.server.name
}
rb.Add(nil, source, "NOTICE", client.t("A client is already using that account; try logging out and logging back in with SASL"))
return false return false
} }
return true return true

View File

@ -36,10 +36,6 @@ func servCmdRequiresBouncerEnabled(config *Config) bool {
return config.Accounts.Multiclient.Enabled return config.Accounts.Multiclient.Enabled
} }
const (
nsPrefix = "NickServ!NickServ@localhost"
)
const nickservHelp = `NickServ lets you register, log in to, and manage an account.` const nickservHelp = `NickServ lets you register, log in to, and manage an account.`
var ( var (
@ -361,14 +357,7 @@ Currently, you can only change the canonical casefolding of an account
} }
) )
// nsNotice sends the client a notice from NickServ func nsGetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
func nsNotice(rb *ResponseBuffer, text string) {
// XXX i can't figure out how to use OragonoServices[servicename].prefix here
// without creating a compile-time initialization loop
rb.Add(nil, nsPrefix, "NOTICE", rb.target.Nick(), text)
}
func nsGetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var account string var account string
if command == "saget" { if command == "saget" {
account = params[0] account = params[0]
@ -379,101 +368,101 @@ func nsGetHandler(server *Server, client *Client, command string, params []strin
accountData, err := server.accounts.LoadAccount(account) accountData, err := server.accounts.LoadAccount(account)
if err == errAccountDoesNotExist { if err == errAccountDoesNotExist {
nsNotice(rb, client.t("No such account")) service.Notice(rb, client.t("No such account"))
return return
} else if err != nil { } else if err != nil {
nsNotice(rb, client.t("Error loading account data")) service.Notice(rb, client.t("Error loading account data"))
return return
} }
displaySetting(params[0], accountData.Settings, client, rb) displaySetting(service, params[0], accountData.Settings, client, rb)
} }
func displaySetting(settingName string, settings AccountSettings, client *Client, rb *ResponseBuffer) { func displaySetting(service *ircService, settingName string, settings AccountSettings, client *Client, rb *ResponseBuffer) {
config := client.server.Config() config := client.server.Config()
switch strings.ToLower(settingName) { switch strings.ToLower(settingName) {
case "enforce": case "enforce":
storedValue := settings.NickEnforcement storedValue := settings.NickEnforcement
serializedStoredValue := nickReservationToString(storedValue) serializedStoredValue := nickReservationToString(storedValue)
nsNotice(rb, fmt.Sprintf(client.t("Your stored nickname enforcement setting is: %s"), serializedStoredValue)) service.Notice(rb, fmt.Sprintf(client.t("Your stored nickname enforcement setting is: %s"), serializedStoredValue))
serializedActualValue := nickReservationToString(configuredEnforcementMethod(config, storedValue)) serializedActualValue := nickReservationToString(configuredEnforcementMethod(config, storedValue))
nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your nickname is enforced with: %s"), serializedActualValue)) service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, your nickname is enforced with: %s"), serializedActualValue))
case "autoreplay-lines": case "autoreplay-lines":
if settings.AutoreplayLines == nil { if settings.AutoreplayLines == nil {
nsNotice(rb, fmt.Sprintf(client.t("You will receive the server default of %d lines of autoreplayed history"), config.History.AutoreplayOnJoin)) service.Notice(rb, fmt.Sprintf(client.t("You will receive the server default of %d lines of autoreplayed history"), config.History.AutoreplayOnJoin))
} else { } else {
nsNotice(rb, fmt.Sprintf(client.t("You will receive %d lines of autoreplayed history"), *settings.AutoreplayLines)) service.Notice(rb, fmt.Sprintf(client.t("You will receive %d lines of autoreplayed history"), *settings.AutoreplayLines))
} }
case "replay-joins": case "replay-joins":
switch settings.ReplayJoins { switch settings.ReplayJoins {
case ReplayJoinsCommandsOnly: case ReplayJoinsCommandsOnly:
nsNotice(rb, client.t("You will see JOINs and PARTs in /HISTORY output, but not in autoreplay")) service.Notice(rb, client.t("You will see JOINs and PARTs in /HISTORY output, but not in autoreplay"))
case ReplayJoinsAlways: case ReplayJoinsAlways:
nsNotice(rb, client.t("You will see JOINs and PARTs in /HISTORY output and in autoreplay")) service.Notice(rb, client.t("You will see JOINs and PARTs in /HISTORY output and in autoreplay"))
case ReplayJoinsNever: case ReplayJoinsNever:
nsNotice(rb, client.t("You will not see JOINs and PARTs in /HISTORY output or in autoreplay")) service.Notice(rb, client.t("You will not see JOINs and PARTs in /HISTORY output or in autoreplay"))
} }
case "multiclient": case "multiclient":
if !config.Accounts.Multiclient.Enabled { if !config.Accounts.Multiclient.Enabled {
nsNotice(rb, client.t("This feature has been disabled by the server administrators")) service.Notice(rb, client.t("This feature has been disabled by the server administrators"))
} else { } else {
switch settings.AllowBouncer { switch settings.AllowBouncer {
case MulticlientAllowedServerDefault: case MulticlientAllowedServerDefault:
if config.Accounts.Multiclient.AllowedByDefault { if config.Accounts.Multiclient.AllowedByDefault {
nsNotice(rb, client.t("Multiclient functionality is currently enabled for your account, but you can opt out")) service.Notice(rb, client.t("Multiclient functionality is currently enabled for your account, but you can opt out"))
} else { } else {
nsNotice(rb, client.t("Multiclient functionality is currently disabled for your account, but you can opt in")) service.Notice(rb, client.t("Multiclient functionality is currently disabled for your account, but you can opt in"))
} }
case MulticlientDisallowedByUser: case MulticlientDisallowedByUser:
nsNotice(rb, client.t("Multiclient functionality is currently disabled for your account")) service.Notice(rb, client.t("Multiclient functionality is currently disabled for your account"))
case MulticlientAllowedByUser: case MulticlientAllowedByUser:
nsNotice(rb, client.t("Multiclient functionality is currently enabled for your account")) service.Notice(rb, client.t("Multiclient functionality is currently enabled for your account"))
} }
} }
case "always-on": case "always-on":
stored := settings.AlwaysOn stored := settings.AlwaysOn
actual := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, stored) actual := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, stored)
nsNotice(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"), persistentStatusToString(stored)))
if actual { if actual {
nsNotice(rb, client.t("Given current server settings, your client is always-on")) service.Notice(rb, client.t("Given current server settings, your client is always-on"))
} else { } else {
nsNotice(rb, client.t("Given current server settings, your client is not always-on")) service.Notice(rb, client.t("Given current server settings, your client is not always-on"))
} }
case "autoreplay-missed": case "autoreplay-missed":
stored := settings.AutoreplayMissed stored := settings.AutoreplayMissed
if stored { if stored {
alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn) alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
if alwaysOn { if alwaysOn {
nsNotice(rb, client.t("Autoreplay of missed messages is enabled")) service.Notice(rb, client.t("Autoreplay of missed messages is enabled"))
} else { } else {
nsNotice(rb, client.t("You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on")) service.Notice(rb, client.t("You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on"))
} }
} else { } else {
nsNotice(rb, client.t("Your account is not configured to receive autoreplayed missed messages")) service.Notice(rb, client.t("Your account is not configured to receive autoreplayed missed messages"))
} }
case "auto-away": case "auto-away":
stored := settings.AutoAway stored := settings.AutoAway
alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn) alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
actual := persistenceEnabled(config.Accounts.Multiclient.AutoAway, settings.AutoAway) actual := persistenceEnabled(config.Accounts.Multiclient.AutoAway, settings.AutoAway)
nsNotice(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"), persistentStatusToString(stored)))
if actual && alwaysOn { if actual && alwaysOn {
nsNotice(rb, client.t("Given current server settings, auto-away is enabled for your client")) service.Notice(rb, client.t("Given current server settings, auto-away is enabled for your client"))
} else if actual && !alwaysOn { } else if actual && !alwaysOn {
nsNotice(rb, client.t("Because your client is not always-on, auto-away is disabled")) service.Notice(rb, client.t("Because your client is not always-on, auto-away is disabled"))
} else if !actual { } else if !actual {
nsNotice(rb, client.t("Given current server settings, auto-away is disabled for your client")) service.Notice(rb, client.t("Given current server settings, auto-away is disabled for your client"))
} }
case "dm-history": case "dm-history":
effectiveValue := historyEnabled(config.History.Persistent.DirectMessages, settings.DMHistory) effectiveValue := historyEnabled(config.History.Persistent.DirectMessages, settings.DMHistory)
nsNotice(rb, fmt.Sprintf(client.t("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory))) service.Notice(rb, fmt.Sprintf(client.t("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory)))
nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue))) service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue)))
default: default:
nsNotice(rb, client.t("No such setting")) service.Notice(rb, client.t("No such setting"))
} }
} }
func nsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var account string var account string
if command == "saset" { if command == "saset" {
account = params[0] account = params[0]
@ -487,7 +476,7 @@ func nsSetHandler(server *Server, client *Client, command string, params []strin
var err error var err error
switch strings.ToLower(params[0]) { switch strings.ToLower(params[0]) {
case "pass", "password": case "pass", "password":
nsNotice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD")) service.Notice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD"))
return return
case "enforce": case "enforce":
var method NickEnforcementMethod var method NickEnforcementMethod
@ -612,19 +601,19 @@ func nsSetHandler(server *Server, client *Client, command string, params []strin
switch err { switch err {
case nil: case nil:
nsNotice(rb, client.t("Successfully changed your account settings")) service.Notice(rb, client.t("Successfully changed your account settings"))
displaySetting(params[0], finalSettings, client, rb) displaySetting(service, params[0], finalSettings, client, rb)
case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed: case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
nsNotice(rb, client.t(err.Error())) service.Notice(rb, client.t(err.Error()))
case errNickAccountMismatch: case errNickAccountMismatch:
nsNotice(rb, fmt.Sprintf(client.t("Your nickname must match your account name %s exactly to modify this setting. Try changing it with /NICK, or logging out and back in with the correct nickname."), client.AccountName())) service.Notice(rb, fmt.Sprintf(client.t("Your nickname must match your account name %s exactly to modify this setting. Try changing it with /NICK, or logging out and back in with the correct nickname."), client.AccountName()))
default: default:
// unknown error // unknown error
nsNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} }
} }
func nsDropHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsDropHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
sadrop := command == "sadrop" sadrop := command == "sadrop"
var nick string var nick string
if len(params) > 0 { if len(params) > 0 {
@ -635,28 +624,28 @@ func nsDropHandler(server *Server, client *Client, command string, params []stri
err := server.accounts.SetNickReserved(client, nick, sadrop, false) err := server.accounts.SetNickReserved(client, nick, sadrop, false)
if err == nil { if err == nil {
nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick)) service.Notice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
} else if err == errAccountNotLoggedIn { } else if err == errAccountNotLoggedIn {
nsNotice(rb, client.t("You're not logged into an account")) service.Notice(rb, client.t("You're not logged into an account"))
} else if err == errAccountCantDropPrimaryNick { } else if err == errAccountCantDropPrimaryNick {
nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)")) service.Notice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
} else { } else {
nsNotice(rb, client.t("Could not ungroup nick")) service.Notice(rb, client.t("Could not ungroup nick"))
} }
} }
func nsGhostHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsGhostHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
nick := params[0] nick := params[0]
ghost := server.clients.Get(nick) ghost := server.clients.Get(nick)
if ghost == nil { if ghost == nil {
nsNotice(rb, client.t("No such nick")) service.Notice(rb, client.t("No such nick"))
return return
} else if ghost == client { } else if ghost == client {
nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)")) service.Notice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
return return
} else if ghost.AlwaysOn() { } else if ghost.AlwaysOn() {
nsNotice(rb, client.t("You can't GHOST an always-on client")) service.Notice(rb, client.t("You can't GHOST an always-on client"))
return return
} }
@ -667,7 +656,7 @@ func nsGhostHandler(server *Server, client *Client, command string, params []str
authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account) authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
} }
if !authorized { if !authorized {
nsNotice(rb, client.t("You don't own that nick")) service.Notice(rb, client.t("You don't own that nick"))
return return
} }
@ -675,31 +664,31 @@ func nsGhostHandler(server *Server, client *Client, command string, params []str
ghost.destroy(nil) ghost.destroy(nil)
} }
func nsGroupHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsGroupHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
nick := client.Nick() nick := client.Nick()
err := server.accounts.SetNickReserved(client, nick, false, true) err := server.accounts.SetNickReserved(client, nick, false, true)
if err == nil { if err == nil {
nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick)) service.Notice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
} else if err == errAccountTooManyNicks { } else if err == errAccountTooManyNicks {
nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)")) service.Notice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
} else if err == errNicknameReserved { } else if err == errNicknameReserved {
nsNotice(rb, client.t("That nickname is already reserved by someone else")) service.Notice(rb, client.t("That nickname is already reserved by someone else"))
} else { } else {
nsNotice(rb, client.t("Error reserving nickname")) service.Notice(rb, client.t("Error reserving nickname"))
} }
} }
func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) { func nsLoginThrottleCheck(service *ircService, client *Client, rb *ResponseBuffer) (success bool) {
throttled, remainingTime := client.checkLoginThrottle() throttled, remainingTime := client.checkLoginThrottle()
if throttled { if throttled {
nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime)) service.Notice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
} }
return !throttled return !throttled
} }
func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsIdentifyHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if client.LoggedIntoAccount() { if client.LoggedIntoAccount() {
nsNotice(rb, client.t("You're already logged into an account")) service.Notice(rb, client.t("You're already logged into an account"))
return return
} }
@ -735,7 +724,7 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
nickFixupFailed := false nickFixupFailed := false
if loginSuccessful { if loginSuccessful {
if !fixupNickEqualsAccount(client, rb, server.Config()) { if !fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
loginSuccessful = false loginSuccessful = false
// fixupNickEqualsAccount sends its own error message, don't send another // fixupNickEqualsAccount sends its own error message, don't send another
nickFixupFailed = true nickFixupFailed = true
@ -743,15 +732,15 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
} }
if loginSuccessful { if loginSuccessful {
sendSuccessfulAccountAuth(client, rb, true, true) sendSuccessfulAccountAuth(service, client, rb, true)
} else if !nickFixupFailed { } else if !nickFixupFailed {
nsNotice(rb, fmt.Sprintf(client.t("Authentication failed: %s"), authErrorToMessage(server, err))) service.Notice(rb, fmt.Sprintf(client.t("Authentication failed: %s"), authErrorToMessage(server, err)))
} }
} }
func nsListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if !client.HasRoleCapabs("accreg") { if !client.HasRoleCapabs("accreg") {
nsNotice(rb, client.t("Insufficient privileges")) service.Notice(rb, client.t("Insufficient privileges"))
return return
} }
@ -760,26 +749,26 @@ func nsListHandler(server *Server, client *Client, command string, params []stri
var err error var err error
searchRegex, err = regexp.Compile(params[0]) searchRegex, err = regexp.Compile(params[0])
if err != nil { if err != nil {
nsNotice(rb, client.t("Invalid regex")) service.Notice(rb, client.t("Invalid regex"))
return return
} }
} }
nsNotice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***"))) service.Notice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***")))
nicks := server.accounts.AllNicks() nicks := server.accounts.AllNicks()
for _, nick := range nicks { for _, nick := range nicks {
if searchRegex == nil || searchRegex.MatchString(nick) { if searchRegex == nil || searchRegex.MatchString(nick) {
nsNotice(rb, fmt.Sprintf(" %s", nick)) service.Notice(rb, fmt.Sprintf(" %s", nick))
} }
} }
nsNotice(rb, ircfmt.Unescape(client.t("*** $bEnd of NickServ LIST$b ***"))) service.Notice(rb, ircfmt.Unescape(client.t("*** $bEnd of NickServ LIST$b ***")))
} }
func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsInfoHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if !server.Config().Accounts.AuthenticationEnabled && !client.HasRoleCapabs("accreg") { if !server.Config().Accounts.AuthenticationEnabled && !client.HasRoleCapabs("accreg") {
nsNotice(rb, client.t("This command has been disabled by the server administrators")) service.Notice(rb, client.t("This command has been disabled by the server administrators"))
return return
} }
@ -789,7 +778,7 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
if server.Config().Accounts.NickReservation.Enabled { if server.Config().Accounts.NickReservation.Enabled {
accountName = server.accounts.NickToAccount(nick) accountName = server.accounts.NickToAccount(nick)
if accountName == "" { if accountName == "" {
nsNotice(rb, client.t("That nickname is not registered")) service.Notice(rb, client.t("That nickname is not registered"))
return return
} }
} else { } else {
@ -798,33 +787,33 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
} else { } else {
accountName = client.Account() accountName = client.Account()
if accountName == "" { if accountName == "" {
nsNotice(rb, client.t("You're not logged into an account")) service.Notice(rb, client.t("You're not logged into an account"))
return return
} }
} }
account, err := server.accounts.LoadAccount(accountName) account, err := server.accounts.LoadAccount(accountName)
if err != nil || !account.Verified { if err != nil || !account.Verified {
nsNotice(rb, client.t("Account does not exist")) service.Notice(rb, client.t("Account does not exist"))
return return
} }
nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name)) service.Notice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
registeredAt := account.RegisteredAt.Format(time.RFC1123) registeredAt := account.RegisteredAt.Format(time.RFC1123)
nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt)) service.Notice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
// TODO nicer formatting for this // TODO nicer formatting for this
for _, nick := range account.AdditionalNicks { for _, nick := range account.AdditionalNicks {
nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick)) service.Notice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
} }
for _, channel := range server.accounts.ChannelsForAccount(accountName) { for _, channel := range server.accounts.ChannelsForAccount(accountName) {
nsNotice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel)) service.Notice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
} }
if account.Suspended != nil { if account.Suspended != nil {
nsNotice(rb, suspensionToString(client, *account.Suspended)) service.Notice(rb, suspensionToString(client, *account.Suspended))
} }
} }
func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
details := client.Details() details := client.Details()
passphrase := params[0] passphrase := params[0]
var email string var email string
@ -835,7 +824,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
certfp := rb.session.certfp certfp := rb.session.certfp
if passphrase == "*" { if passphrase == "*" {
if certfp == "" { if certfp == "" {
nsNotice(rb, client.t("You must be connected with TLS and a client certificate to do this")) service.Notice(rb, client.t("You must be connected with TLS and a client certificate to do this"))
return return
} else { } else {
passphrase = "" passphrase = ""
@ -845,12 +834,12 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
if passphrase != "" { if passphrase != "" {
cfPassphrase, err := Casefold(passphrase) cfPassphrase, err := Casefold(passphrase)
if err == nil && cfPassphrase == details.nickCasefolded { if err == nil && cfPassphrase == details.nickCasefolded {
nsNotice(rb, client.t("Usage: REGISTER <passphrase> [email]")) // #1179 service.Notice(rb, client.t("Usage: REGISTER <passphrase> [email]")) // #1179
return return
} }
} }
if !nsLoginThrottleCheck(client, rb) { if !nsLoginThrottleCheck(service, client, rb) {
return return
} }
@ -859,7 +848,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
if config.Accounts.NickReservation.ForceGuestFormat { if config.Accounts.NickReservation.ForceGuestFormat {
matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account) matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
if matches == nil || len(matches) < 2 { if matches == nil || len(matches) < 2 {
nsNotice(rb, client.t("Erroneous nickname")) service.Notice(rb, client.t("Erroneous nickname"))
return return
} }
account = matches[1] account = matches[1]
@ -867,7 +856,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
callbackNamespace, callbackValue, validationErr := parseCallback(email, config) callbackNamespace, callbackValue, validationErr := parseCallback(email, config)
if validationErr != nil { if validationErr != nil {
nsNotice(rb, client.t("Registration requires a valid e-mail address")) service.Notice(rb, client.t("Registration requires a valid e-mail address"))
return return
} }
@ -875,22 +864,22 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
if err == nil { if err == nil {
if callbackNamespace == "*" { if callbackNamespace == "*" {
err = server.accounts.Verify(client, account, "") err = server.accounts.Verify(client, account, "")
if err == nil && fixupNickEqualsAccount(client, rb, config) { if err == nil && fixupNickEqualsAccount(client, rb, config, service.prefix) {
sendSuccessfulRegResponse(client, rb, true) sendSuccessfulRegResponse(service, client, rb)
} }
} else { } else {
messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s") messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
message := fmt.Sprintf(messageTemplate, callbackValue) message := fmt.Sprintf(messageTemplate, callbackValue)
nsNotice(rb, message) service.Notice(rb, message)
} }
} else { } else {
// details could not be stored and relevant numerics have been dispatched, abort // details could not be stored and relevant numerics have been dispatched, abort
message := registrationErrorToMessage(err) message := registrationErrorToMessage(err)
nsNotice(rb, client.t(message)) service.Notice(rb, client.t(message))
} }
} }
func nsSaregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsSaregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var account, passphrase string var account, passphrase string
account = params[0] account = params[0]
if 1 < len(params) && params[1] != "*" { if 1 < len(params) && params[1] != "*" {
@ -908,14 +897,14 @@ func nsSaregisterHandler(server *Server, client *Client, command string, params
server.logger.Error("services", "unknown error from saregister", err.Error()) server.logger.Error("services", "unknown error from saregister", err.Error())
errMsg = client.t("Could not register") errMsg = client.t("Could not register")
} }
nsNotice(rb, errMsg) service.Notice(rb, errMsg)
} else { } else {
nsNotice(rb, fmt.Sprintf(client.t("Successfully registered account %s"), account)) service.Notice(rb, fmt.Sprintf(client.t("Successfully registered account %s"), account))
server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Operator $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] with SAREGISTER"), client.Oper().Name, account)) server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Operator $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] with SAREGISTER"), client.Oper().Name, account))
} }
} }
func nsUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsUnregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
erase := command == "erase" erase := command == "erase"
username := params[0] username := params[0]
@ -925,7 +914,7 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
} }
if username == "" { if username == "" {
nsNotice(rb, client.t("You must specify an account")) service.Notice(rb, client.t("You must specify an account"))
return return
} }
@ -939,10 +928,10 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
} else { } else {
account, err := server.accounts.LoadAccount(username) account, err := server.accounts.LoadAccount(username)
if err == errAccountDoesNotExist { if err == errAccountDoesNotExist {
nsNotice(rb, client.t("Invalid account name")) service.Notice(rb, client.t("Invalid account name"))
return return
} else if err != nil { } else if err != nil {
nsNotice(rb, client.t("Internal error")) service.Notice(rb, client.t("Internal error"))
return return
} }
accountName = account.Name accountName = account.Name
@ -950,34 +939,34 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
} }
if !(accountName == client.AccountName() || client.HasRoleCapabs("accreg")) { if !(accountName == client.AccountName() || client.HasRoleCapabs("accreg")) {
nsNotice(rb, client.t("Insufficient oper privs")) service.Notice(rb, client.t("Insufficient oper privs"))
return return
} }
expectedCode := utils.ConfirmationCode(accountName, registeredAt) expectedCode := utils.ConfirmationCode(accountName, registeredAt)
if expectedCode != verificationCode { if expectedCode != verificationCode {
if erase { if erase {
nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b"))) service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b")))
} else { } else {
nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b"))) service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
} }
nsNotice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/NS %s %s %s", strings.ToUpper(command), accountName, expectedCode))) service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/NS %s %s %s", strings.ToUpper(command), accountName, expectedCode)))
return return
} }
err := server.accounts.Unregister(accountName, erase) err := server.accounts.Unregister(accountName, erase)
if err == errAccountDoesNotExist { if err == errAccountDoesNotExist {
nsNotice(rb, client.t(err.Error())) service.Notice(rb, client.t(err.Error()))
} else if err != nil { } else if err != nil {
nsNotice(rb, client.t("Error while unregistering account")) service.Notice(rb, client.t("Error while unregistering account"))
} else { } else {
nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), accountName)) service.Notice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), accountName))
server.logger.Info("accounts", "client", client.Nick(), "unregistered account", accountName) server.logger.Info("accounts", "client", client.Nick(), "unregistered account", accountName)
client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] unregistered account $c[grey][$r%s$c[grey]]"), client.NickMaskString(), accountName)) client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] unregistered account $c[grey][$r%s$c[grey]]"), client.NickMaskString(), accountName))
} }
} }
func nsVerifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsVerifyHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
username, code := params[0], params[1] username, code := params[0], params[1]
err := server.accounts.Verify(client, username, code) err := server.accounts.Verify(client, username, code)
@ -992,16 +981,16 @@ func nsVerifyHandler(server *Server, client *Client, command string, params []st
} }
if errorMessage != "" { if errorMessage != "" {
nsNotice(rb, client.t(errorMessage)) service.Notice(rb, client.t(errorMessage))
return return
} }
if fixupNickEqualsAccount(client, rb, server.Config()) { if fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
sendSuccessfulRegResponse(client, rb, true) sendSuccessfulRegResponse(service, client, rb)
} }
} }
func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsPasswdHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var target string var target string
var newPassword string var newPassword string
var errorMessage string var errorMessage string
@ -1025,7 +1014,7 @@ func nsPasswdHandler(server *Server, client *Client, command string, params []st
} else if params[1] != params[2] { } else if params[1] != params[2] {
errorMessage = `Passwords do not match` errorMessage = `Passwords do not match`
} else { } else {
if !nsLoginThrottleCheck(client, rb) { if !nsLoginThrottleCheck(service, client, rb) {
return return
} }
accountData, err := server.accounts.LoadAccount(target) accountData, err := server.accounts.LoadAccount(target)
@ -1048,37 +1037,37 @@ func nsPasswdHandler(server *Server, client *Client, command string, params []st
} }
if errorMessage != "" { if errorMessage != "" {
nsNotice(rb, client.t(errorMessage)) service.Notice(rb, client.t(errorMessage))
return return
} }
err := server.accounts.setPassword(target, newPassword, hasPrivs) err := server.accounts.setPassword(target, newPassword, hasPrivs)
switch err { switch err {
case nil: case nil:
nsNotice(rb, client.t("Password changed")) service.Notice(rb, client.t("Password changed"))
case errEmptyCredentials: case errEmptyCredentials:
nsNotice(rb, client.t("You can't delete your password unless you add a certificate fingerprint")) service.Notice(rb, client.t("You can't delete your password unless you add a certificate fingerprint"))
case errCredsExternallyManaged: case errCredsExternallyManaged:
nsNotice(rb, client.t("Your account credentials are managed externally and cannot be changed here")) service.Notice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
case errCASFailed: case errCASFailed:
nsNotice(rb, client.t("Try again later")) service.Notice(rb, client.t("Try again later"))
default: default:
server.logger.Error("internal", "could not upgrade user password:", err.Error()) server.logger.Error("internal", "could not upgrade user password:", err.Error())
nsNotice(rb, client.t("Password could not be changed due to server error")) service.Notice(rb, client.t("Password could not be changed due to server error"))
} }
} }
func nsEnforceHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsEnforceHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
newParams := []string{"enforce"} newParams := []string{"enforce"}
if len(params) == 0 { if len(params) == 0 {
nsGetHandler(server, client, "get", newParams, rb) nsGetHandler(service, server, client, "get", newParams, rb)
} else { } else {
newParams = append(newParams, params[0]) newParams = append(newParams, params[0])
nsSetHandler(server, client, "set", newParams, rb) nsSetHandler(service, server, client, "set", newParams, rb)
} }
} }
func nsClientsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsClientsHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var verb string var verb string
if command == "sessions" { if command == "sessions" {
@ -1091,56 +1080,56 @@ func nsClientsHandler(server *Server, client *Client, command string, params []s
switch verb { switch verb {
case "list": case "list":
nsClientsListHandler(server, client, params, rb) nsClientsListHandler(service, server, client, params, rb)
case "logout": case "logout":
nsClientsLogoutHandler(server, client, params, rb) nsClientsLogoutHandler(service, server, client, params, rb)
default: default:
nsNotice(rb, client.t("Invalid parameters")) service.Notice(rb, client.t("Invalid parameters"))
} }
} }
func nsClientsListHandler(server *Server, client *Client, params []string, rb *ResponseBuffer) { func nsClientsListHandler(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) {
target := client target := client
hasPrivs := client.HasRoleCapabs("local_ban") hasPrivs := client.HasRoleCapabs("local_ban")
if 0 < len(params) { if 0 < len(params) {
target = server.clients.Get(params[0]) target = server.clients.Get(params[0])
if target == nil { if target == nil {
nsNotice(rb, client.t("No such nick")) service.Notice(rb, client.t("No such nick"))
return return
} }
if target != client && !hasPrivs { if target != client && !hasPrivs {
nsNotice(rb, client.t("Command restricted")) service.Notice(rb, client.t("Command restricted"))
return return
} }
} }
sessionData, currentIndex := target.AllSessionData(rb.session, hasPrivs) sessionData, currentIndex := target.AllSessionData(rb.session, hasPrivs)
nsNotice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached clients(s)"), target.Nick(), len(sessionData))) service.Notice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached clients(s)"), target.Nick(), len(sessionData)))
for i, session := range sessionData { for i, session := range sessionData {
if currentIndex == i { if currentIndex == i {
nsNotice(rb, fmt.Sprintf(client.t("Client %d (currently attached client):"), session.sessionID)) service.Notice(rb, fmt.Sprintf(client.t("Client %d (currently attached client):"), session.sessionID))
} else { } else {
nsNotice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID)) service.Notice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID))
} }
if session.deviceID != "" { if session.deviceID != "" {
nsNotice(rb, fmt.Sprintf(client.t("Device ID: %s"), session.deviceID)) service.Notice(rb, fmt.Sprintf(client.t("Device ID: %s"), session.deviceID))
} }
nsNotice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String())) service.Notice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String()))
nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname)) service.Notice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
if hasPrivs { if hasPrivs {
nsNotice(rb, fmt.Sprintf(client.t("Connection: %s"), session.connInfo)) service.Notice(rb, fmt.Sprintf(client.t("Connection: %s"), session.connInfo))
} }
nsNotice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123))) service.Notice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123)))
nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123))) service.Notice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
if session.certfp != "" { if session.certfp != "" {
nsNotice(rb, fmt.Sprintf(client.t("Certfp: %s"), session.certfp)) service.Notice(rb, fmt.Sprintf(client.t("Certfp: %s"), session.certfp))
} }
} }
} }
func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb *ResponseBuffer) { func nsClientsLogoutHandler(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) {
if len(params) < 1 { if len(params) < 1 {
nsNotice(rb, client.t("Missing client ID to logout (or \"all\")")) service.Notice(rb, client.t("Missing client ID to logout (or \"all\")"))
return return
} }
@ -1149,14 +1138,14 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
// CLIENTS LOGOUT [nickname] [client ID] // CLIENTS LOGOUT [nickname] [client ID]
target = server.clients.Get(params[0]) target = server.clients.Get(params[0])
if target == nil { if target == nil {
nsNotice(rb, client.t("No such nick")) service.Notice(rb, client.t("No such nick"))
return return
} }
// User must have "local_kill" privileges to logout other user sessions. // User must have "local_kill" privileges to logout other user sessions.
if target != client { if target != client {
oper := client.Oper() oper := client.Oper()
if oper == nil || !oper.Class.Capabilities.Has("local_kill") { if oper == nil || !oper.Class.Capabilities.Has("local_kill") {
nsNotice(rb, client.t("Insufficient oper privs")) service.Notice(rb, client.t("Insufficient oper privs"))
return return
} }
} }
@ -1167,7 +1156,7 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
if strings.ToLower(params[0]) != "all" { if strings.ToLower(params[0]) != "all" {
sessionID, err := strconv.ParseInt(params[0], 10, 64) sessionID, err := strconv.ParseInt(params[0], 10, 64)
if err != nil { if err != nil {
nsNotice(rb, client.t("Client ID to logout should be an integer (or \"all\")")) service.Notice(rb, client.t("Client ID to logout should be an integer (or \"all\")"))
return return
} }
// Find the client ID that the user requested to logout. // Find the client ID that the user requested to logout.
@ -1178,7 +1167,7 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
} }
} }
if sessionToDestroy == nil { if sessionToDestroy == nil {
nsNotice(rb, client.t("Specified client ID does not exist")) service.Notice(rb, client.t("Specified client ID does not exist"))
return return
} }
} }
@ -1186,14 +1175,14 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
target.destroy(sessionToDestroy) target.destroy(sessionToDestroy)
if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target { if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target {
if sessionToDestroy != nil { if sessionToDestroy != nil {
nsNotice(rb, client.t("Successfully logged out session")) service.Notice(rb, client.t("Successfully logged out session"))
} else { } else {
nsNotice(rb, client.t("Successfully logged out all sessions")) service.Notice(rb, client.t("Successfully logged out all sessions"))
} }
} }
} }
func nsCertHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsCertHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
verb := strings.ToLower(params[0]) verb := strings.ToLower(params[0])
params = params[1:] params = params[1:]
var target, certfp string var target, certfp string
@ -1211,22 +1200,22 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
} else if len(params) == 0 && verb == "add" && rb.session.certfp != "" { } else if len(params) == 0 && verb == "add" && rb.session.certfp != "" {
certfp = rb.session.certfp // #1059 certfp = rb.session.certfp // #1059
} else { } else {
nsNotice(rb, client.t("Invalid parameters")) service.Notice(rb, client.t("Invalid parameters"))
return return
} }
default: default:
nsNotice(rb, client.t("Invalid parameters")) service.Notice(rb, client.t("Invalid parameters"))
return return
} }
hasPrivs := client.HasRoleCapabs("accreg") hasPrivs := client.HasRoleCapabs("accreg")
if target != "" && !hasPrivs { if target != "" && !hasPrivs {
nsNotice(rb, client.t("Insufficient privileges")) service.Notice(rb, client.t("Insufficient privileges"))
return return
} else if target == "" { } else if target == "" {
target = client.Account() target = client.Account()
if target == "" { if target == "" {
nsNotice(rb, client.t("You're not logged into an account")) service.Notice(rb, client.t("You're not logged into an account"))
return return
} }
} }
@ -1236,16 +1225,16 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
case "list": case "list":
accountData, err := server.accounts.LoadAccount(target) accountData, err := server.accounts.LoadAccount(target)
if err == errAccountDoesNotExist { if err == errAccountDoesNotExist {
nsNotice(rb, client.t("Account does not exist")) service.Notice(rb, client.t("Account does not exist"))
return return
} else if err != nil { } else if err != nil {
nsNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
return return
} }
certfps := accountData.Credentials.Certfps certfps := accountData.Credentials.Certfps
nsNotice(rb, fmt.Sprintf(client.t("There are %[1]d certificate fingerprint(s) authorized for account %[2]s."), len(certfps), accountData.Name)) service.Notice(rb, fmt.Sprintf(client.t("There are %[1]d certificate fingerprint(s) authorized for account %[2]s."), len(certfps), accountData.Name))
for i, certfp := range certfps { for i, certfp := range certfps {
nsNotice(rb, fmt.Sprintf("%d: %s", i+1, certfp)) service.Notice(rb, fmt.Sprintf("%d: %s", i+1, certfp))
} }
return return
case "add": case "add":
@ -1257,54 +1246,54 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
switch err { switch err {
case nil: case nil:
if verb == "add" { if verb == "add" {
nsNotice(rb, client.t("Certificate fingerprint successfully added")) service.Notice(rb, client.t("Certificate fingerprint successfully added"))
} else { } else {
nsNotice(rb, client.t("Certificate fingerprint successfully removed")) service.Notice(rb, client.t("Certificate fingerprint successfully removed"))
} }
case errNoop: case errNoop:
if verb == "add" { if verb == "add" {
nsNotice(rb, client.t("That certificate fingerprint was already authorized")) service.Notice(rb, client.t("That certificate fingerprint was already authorized"))
} else { } else {
nsNotice(rb, client.t("Certificate fingerprint not found")) service.Notice(rb, client.t("Certificate fingerprint not found"))
} }
case errAccountDoesNotExist: case errAccountDoesNotExist:
nsNotice(rb, client.t("Account does not exist")) service.Notice(rb, client.t("Account does not exist"))
case errLimitExceeded: case errLimitExceeded:
nsNotice(rb, client.t("You already have too many certificate fingerprints")) service.Notice(rb, client.t("You already have too many certificate fingerprints"))
case utils.ErrInvalidCertfp: case utils.ErrInvalidCertfp:
nsNotice(rb, client.t("Invalid certificate fingerprint")) service.Notice(rb, client.t("Invalid certificate fingerprint"))
case errCertfpAlreadyExists: case errCertfpAlreadyExists:
nsNotice(rb, client.t("That certificate fingerprint is already associated with another account")) service.Notice(rb, client.t("That certificate fingerprint is already associated with another account"))
case errEmptyCredentials: case errEmptyCredentials:
nsNotice(rb, client.t("You can't remove all your certificate fingerprints unless you add a password")) service.Notice(rb, client.t("You can't remove all your certificate fingerprints unless you add a password"))
case errCredsExternallyManaged: case errCredsExternallyManaged:
nsNotice(rb, client.t("Your account credentials are managed externally and cannot be changed here")) service.Notice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
case errCASFailed: case errCASFailed:
nsNotice(rb, client.t("Try again later")) service.Notice(rb, client.t("Try again later"))
default: default:
server.logger.Error("internal", "could not modify certificates:", err.Error()) server.logger.Error("internal", "could not modify certificates:", err.Error())
nsNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} }
} }
func nsSuspendHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsSuspendHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
subCmd := strings.ToLower(params[0]) subCmd := strings.ToLower(params[0])
params = params[1:] params = params[1:]
switch subCmd { switch subCmd {
case "add": case "add":
nsSuspendAddHandler(server, client, command, params, rb) nsSuspendAddHandler(service, server, client, command, params, rb)
case "del", "delete", "remove": case "del", "delete", "remove":
nsSuspendRemoveHandler(server, client, command, params, rb) nsSuspendRemoveHandler(service, server, client, command, params, rb)
case "list": case "list":
nsSuspendListHandler(server, client, command, params, rb) nsSuspendListHandler(service, server, client, command, params, rb)
default: default:
nsNotice(rb, client.t("Invalid parameters")) service.Notice(rb, client.t("Invalid parameters"))
} }
} }
func nsSuspendAddHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsSuspendAddHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if len(params) == 0 { if len(params) == 0 {
nsNotice(rb, client.t("Invalid parameters")) service.Notice(rb, client.t("Invalid parameters"))
return return
} }
@ -1316,7 +1305,7 @@ func nsSuspendAddHandler(server *Server, client *Client, command string, params
var err error var err error
cDuration, err := custime.ParseDuration(params[1]) cDuration, err := custime.ParseDuration(params[1])
if err != nil { if err != nil {
nsNotice(rb, client.t("Invalid time duration for NS SUSPEND")) service.Notice(rb, client.t("Invalid time duration for NS SUSPEND"))
return return
} }
duration = time.Duration(cDuration) duration = time.Duration(cDuration)
@ -1333,30 +1322,30 @@ func nsSuspendAddHandler(server *Server, client *Client, command string, params
err := server.accounts.Suspend(account, duration, name, reason) err := server.accounts.Suspend(account, duration, name, reason)
switch err { switch err {
case nil: case nil:
nsNotice(rb, fmt.Sprintf(client.t("Successfully suspended account %s"), account)) service.Notice(rb, fmt.Sprintf(client.t("Successfully suspended account %s"), account))
case errAccountDoesNotExist: case errAccountDoesNotExist:
nsNotice(rb, client.t("No such account")) service.Notice(rb, client.t("No such account"))
default: default:
nsNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} }
} }
func nsSuspendRemoveHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsSuspendRemoveHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if len(params) == 0 { if len(params) == 0 {
nsNotice(rb, client.t("Invalid parameters")) service.Notice(rb, client.t("Invalid parameters"))
return return
} }
err := server.accounts.Unsuspend(params[0]) err := server.accounts.Unsuspend(params[0])
switch err { switch err {
case nil: case nil:
nsNotice(rb, fmt.Sprintf(client.t("Successfully un-suspended account %s"), params[0])) service.Notice(rb, fmt.Sprintf(client.t("Successfully un-suspended account %s"), params[0]))
case errAccountDoesNotExist: case errAccountDoesNotExist:
nsNotice(rb, client.t("No such account")) service.Notice(rb, client.t("No such account"))
case errNoop: case errNoop:
nsNotice(rb, client.t("Account was not suspended")) service.Notice(rb, client.t("Account was not suspended"))
default: default:
nsNotice(rb, client.t("An error occurred")) service.Notice(rb, client.t("An error occurred"))
} }
} }
@ -1367,12 +1356,12 @@ func (a ByCreationTime) Len() int { return len(a) }
func (a ByCreationTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByCreationTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByCreationTime) Less(i, j int) bool { return a[i].TimeCreated.After(a[j].TimeCreated) } func (a ByCreationTime) Less(i, j int) bool { return a[i].TimeCreated.After(a[j].TimeCreated) }
func nsSuspendListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsSuspendListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
suspensions := server.accounts.ListSuspended() suspensions := server.accounts.ListSuspended()
sort.Sort(ByCreationTime(suspensions)) sort.Sort(ByCreationTime(suspensions))
nsNotice(rb, fmt.Sprintf(client.t("There are %d active suspensions."), len(suspensions))) service.Notice(rb, fmt.Sprintf(client.t("There are %d active suspensions."), len(suspensions)))
for _, suspension := range suspensions { for _, suspension := range suspensions {
nsNotice(rb, suspensionToString(client, suspension)) service.Notice(rb, suspensionToString(client, suspension))
} }
} }
@ -1389,21 +1378,21 @@ func suspensionToString(client *Client, suspension AccountSuspension) (result st
return fmt.Sprintf(client.t("Account %[1]s suspended at %[2]s. Duration: %[3]s. %[4]s"), suspension.AccountName, ts, duration, reason) return fmt.Sprintf(client.t("Account %[1]s suspended at %[2]s. Duration: %[3]s. %[4]s"), suspension.AccountName, ts, duration, reason)
} }
func nsRenameHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsRenameHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
oldName, newName := params[0], params[1] oldName, newName := params[0], params[1]
err := server.accounts.Rename(oldName, newName) err := server.accounts.Rename(oldName, newName)
if err != nil { if err != nil {
nsNotice(rb, fmt.Sprintf(client.t("Couldn't rename account: %s"), client.t(err.Error()))) service.Notice(rb, fmt.Sprintf(client.t("Couldn't rename account: %s"), client.t(err.Error())))
return return
} }
nsNotice(rb, client.t("Successfully renamed account")) service.Notice(rb, client.t("Successfully renamed account"))
if server.Config().Accounts.NickReservation.ForceNickEqualsAccount { if server.Config().Accounts.NickReservation.ForceNickEqualsAccount {
if curClient := server.clients.Get(oldName); curClient != nil { if curClient := server.clients.Get(oldName); curClient != nil {
renameErr := performNickChange(client.server, client, curClient, nil, newName, rb) renameErr := performNickChange(client.server, client, curClient, nil, newName, rb)
if renameErr != nil && renameErr != errNoop { if renameErr != nil && renameErr != errNoop {
nsNotice(rb, fmt.Sprintf(client.t("Warning: could not rename affected client: %v"), err)) service.Notice(rb, fmt.Sprintf(client.t("Warning: could not rename affected client: %v"), err))
} }
} }
} }

View File

@ -530,6 +530,8 @@ func (server *Server) applyConfig(config *Config) (err error) {
} else if oldConfig.Server.IPCheckScript.MaxConcurrency != config.Server.IPCheckScript.MaxConcurrency || } else if oldConfig.Server.IPCheckScript.MaxConcurrency != config.Server.IPCheckScript.MaxConcurrency ||
oldConfig.Accounts.AuthScript.MaxConcurrency != config.Accounts.AuthScript.MaxConcurrency { oldConfig.Accounts.AuthScript.MaxConcurrency != config.Accounts.AuthScript.MaxConcurrency {
return fmt.Errorf("Cannot change max-concurrency for scripts after launching the server, rehash aborted") return fmt.Errorf("Cannot change max-concurrency for scripts after launching the server, rehash aborted")
} else if oldConfig.Server.OverrideServicesHostname != config.Server.OverrideServicesHostname {
return fmt.Errorf("Cannot change override-services-hostname after launching the server, rehash aborted")
} }
} }
@ -563,6 +565,10 @@ func (server *Server) applyConfig(config *Config) (err error) {
if maxAuthConc != 0 { if maxAuthConc != 0 {
server.semaphores.AuthScript.Initialize(maxAuthConc) server.semaphores.AuthScript.Initialize(maxAuthConc)
} }
if err := overrideServicePrefixes(config.Server.OverrideServicesHostname); err != nil {
return err
}
} }
if oldConfig != nil { if oldConfig != nil {

View File

@ -20,7 +20,7 @@ import (
type ircService struct { type ircService struct {
Name string Name string
ShortName string ShortName string
prefix string prefix string // NUH source of messages from this service
CommandAliases []string CommandAliases []string
Commands map[string]*serviceCommand Commands map[string]*serviceCommand
HelpBanner string HelpBanner string
@ -30,7 +30,7 @@ type ircService struct {
type serviceCommand struct { type serviceCommand struct {
aliasOf string // marks this command as an alias of another aliasOf string // marks this command as an alias of another
capabs []string // oper capabs the given user has to have to access this command capabs []string // oper capabs the given user has to have to access this command
handler func(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) handler func(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer)
help string help string
helpStrings []string helpStrings []string
helpShort string helpShort string
@ -60,36 +60,47 @@ func lookupServiceCommand(commands map[string]*serviceCommand, command string) *
return nil return nil
} }
// all services, by lowercase name var (
var OragonoServices = map[string]*ircService{ nickservService = &ircService{
"nickserv": {
Name: "NickServ", Name: "NickServ",
ShortName: "NS", ShortName: "NS",
CommandAliases: []string{"NICKSERV", "NS"}, CommandAliases: []string{"NICKSERV", "NS"},
Commands: nickservCommands, Commands: nickservCommands,
HelpBanner: nickservHelp, HelpBanner: nickservHelp,
}, }
"chanserv": { chanservService = &ircService{
Name: "ChanServ", Name: "ChanServ",
ShortName: "CS", ShortName: "CS",
CommandAliases: []string{"CHANSERV", "CS"}, CommandAliases: []string{"CHANSERV", "CS"},
Commands: chanservCommands, Commands: chanservCommands,
HelpBanner: chanservHelp, HelpBanner: chanservHelp,
}, }
"hostserv": { hostservService = &ircService{
Name: "HostServ", Name: "HostServ",
ShortName: "HS", ShortName: "HS",
CommandAliases: []string{"HOSTSERV", "HS"}, CommandAliases: []string{"HOSTSERV", "HS"},
Commands: hostservCommands, Commands: hostservCommands,
HelpBanner: hostservHelp, HelpBanner: hostservHelp,
}, }
"histserv": { histservService = &ircService{
Name: "HistServ", Name: "HistServ",
ShortName: "HISTSERV", ShortName: "HISTSERV",
CommandAliases: []string{"HISTSERV"}, CommandAliases: []string{"HISTSERV"},
Commands: histservCommands, Commands: histservCommands,
HelpBanner: histservHelp, HelpBanner: histservHelp,
}, }
)
// all services, by lowercase name
var OragonoServices = map[string]*ircService{
"nickserv": nickservService,
"chanserv": chanservService,
"hostserv": hostservService,
"histserv": histservService,
}
func (service *ircService) Notice(rb *ResponseBuffer, text string) {
rb.Add(nil, service.prefix, "NOTICE", rb.target.Nick(), text)
} }
// all service commands at the protocol level, by uppercase command name // all service commands at the protocol level, by uppercase command name
@ -212,7 +223,7 @@ func serviceRunCommand(service *ircService, server *Server, client *Client, cmd
if commandName == "help" { if commandName == "help" {
serviceHelpHandler(service, server, client, params, rb) serviceHelpHandler(service, server, client, params, rb)
} else { } else {
cmd.handler(server, client, commandName, params, rb) cmd.handler(service, server, client, commandName, params, rb)
} }
} }
@ -305,6 +316,19 @@ func makeServiceHelpTextGenerator(cmd string, banner string) func(*Client) strin
} }
} }
func overrideServicePrefixes(hostname string) error {
if hostname == "" {
return nil
}
if !utils.IsHostname(hostname) {
return fmt.Errorf("`%s` is an invalid services hostname", hostname)
}
for _, serv := range OragonoServices {
serv.prefix = fmt.Sprintf("%s!%s@%s", serv.Name, serv.Name, hostname)
}
return nil
}
func initializeServices() { func initializeServices() {
// this modifies the global Commands map, // this modifies the global Commands map,
// so it must be called from irc/commands.go's init() // so it must be called from irc/commands.go's init()

View File

@ -307,7 +307,11 @@ server:
# oragono will write files to disk under certain circumstances, e.g., # oragono will write files to disk under certain circumstances, e.g.,
# CPU profiling or data export. by default, these files will be written # CPU profiling or data export. by default, these files will be written
# to the working directory. set this to customize: # to the working directory. set this to customize:
# output-path: "/home/oragono/out" #output-path: "/home/oragono/out"
# the hostname used by "services", e.g., NickServ, defaults to "localhost",
# e.g., `NickServ!NickServ@localhost`. uncomment this to override:
#override-services-hostname: "example.network"
# account options # account options
accounts: accounts: