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

@ -335,7 +335,11 @@ server:
# oragono will write files to disk under certain circumstances, e.g.,
# CPU profiling or data export. by default, these files will be written
# 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
accounts:

@ -1037,7 +1037,7 @@ func (channel *Channel) replayHistoryForResume(session *Session, after time.Time
}
if !complete && !session.resumeDetails.HistoryIncomplete {
// 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)
}
@ -1099,7 +1099,7 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
} else {
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:
if eventPlayback {
@ -1109,14 +1109,14 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
continue // #474
}
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:
if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "KICK", chname, item.Params[0], item.Message.Message)
} else {
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:
if eventPlayback {
@ -1126,21 +1126,21 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
continue // #474
}
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:
if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "NICK", item.Params[0])
} else {
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:
if eventPlayback {
rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message)
} else {
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:
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...)
} else {
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)
}
}
}

@ -17,7 +17,6 @@ import (
)
const chanservHelp = `ChanServ lets you register and manage channels.`
const chanservMask = "ChanServ!ChanServ@localhost"
func chanregEnabled(config *Config) bool {
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 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) {
func csAmodeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channelName := params[0]
channel := server.channels.Get(channelName)
if channel == nil {
csNotice(rb, client.t("Channel does not exist"))
service.Notice(rb, client.t("Channel does not exist"))
return
} else if channel.Founder() == "" {
csNotice(rb, client.t("Channel is not registered"))
service.Notice(rb, client.t("Channel is not registered"))
return
}
modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...)
var change modes.ModeChange
if len(modeChanges) > 1 || len(unknown) > 0 {
csNotice(rb, client.t("Invalid mode change"))
service.Notice(rb, client.t("Invalid mode change"))
return
} else if len(modeChanges) == 1 {
change = modeChanges[0]
@ -233,17 +227,17 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
accountIsValid = (change.Arg != "")
}
if !accountIsValid {
csNotice(rb, client.t("Account does not exist"))
service.Notice(rb, client.t("Account does not exist"))
return
}
affectedModes, err := channel.ProcessAccountToUmodeChange(client, change)
if err == errInsufficientPrivs {
csNotice(rb, client.t("Insufficient privileges"))
service.Notice(rb, client.t("Insufficient privileges"))
return
} else if err != nil {
csNotice(rb, client.t("Internal error"))
service.Notice(rb, client.t("Internal error"))
return
}
@ -253,13 +247,13 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
sort.Slice(affectedModes, func(i, j int) bool {
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 {
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:
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
for _, member := range channel.Members() {
if member.Account() == change.Arg {
@ -270,22 +264,22 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
}
}
} 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])
if channelInfo == nil {
csNotice(rb, client.t("Channel does not exist"))
service.Notice(rb, client.t("Channel does not exist"))
return
}
channelName := channelInfo.Name()
clientAccount := client.Account()
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
}
@ -293,7 +287,7 @@ func csOpHandler(server *Server, client *Client, command string, params []string
if len(params) > 1 {
target = server.clients.Get(params[1])
if target == nil {
csNotice(rb, client.t("Could not find given client"))
service.Notice(rb, client.t("Could not find given client"))
return
}
} else {
@ -315,21 +309,21 @@ func csOpHandler(server *Server, client *Client, command string, params []string
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()
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))
}
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])
if channel == nil {
csNotice(rb, client.t("Channel does not exist"))
service.Notice(rb, client.t("Channel does not exist"))
return
}
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
}
@ -337,7 +331,7 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
if len(params) > 1 {
target = server.clients.Get(params[1])
if target == nil {
csNotice(rb, client.t("Could not find given client"))
service.Notice(rb, client.t("Could not find given client"))
return
}
} else {
@ -346,7 +340,7 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
present, cumodes := channel.ClientStatus(target)
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
}
@ -370,38 +364,38 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
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") {
csNotice(rb, client.t("Channel registration is restricted to server operators"))
service.Notice(rb, client.t("Channel registration is restricted to server operators"))
return
}
channelName := params[0]
channelInfo := server.channels.Get(channelName)
if channelInfo == nil {
csNotice(rb, client.t("No such channel"))
service.Notice(rb, client.t("No such channel"))
return
}
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
}
account := client.Account()
if !checkChanLimit(client, rb) {
if !checkChanLimit(service, client, rb) {
return
}
// this provides the synchronization that allows exactly one registration of the channel:
err := server.channels.SetRegistered(channelName, account)
if err != nil {
csNotice(rb, err.Error())
service.Notice(rb, err.Error())
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.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)
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
func checkChanLimit(client *Client, rb *ResponseBuffer) (ok bool) {
func checkChanLimit(service *ircService, client *Client, rb *ResponseBuffer) (ok bool) {
account := client.Account()
channelsAlreadyRegistered := client.server.accounts.ChannelsForAccount(account)
ok = len(channelsAlreadyRegistered) < client.server.Config().Channels.Registration.MaxChannelsPerAccount || client.HasRoleCapabs("chanreg")
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
}
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
if founder == "" {
csNotice(rb, client.t("That channel is not registered"))
service.Notice(rb, client.t("That channel is not registered"))
return false
}
if client.HasRoleCapabs("chanreg") {
return true
}
if founder != client.Account() {
csNotice(rb, client.t("Insufficient privileges"))
service.Notice(rb, client.t("Insufficient privileges"))
return false
}
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]
var verificationCode string
if len(params) > 1 {
@ -455,41 +449,41 @@ func csUnregisterHandler(server *Server, client *Client, command string, params
channel := server.channels.Get(channelName)
if channel == nil {
csNotice(rb, client.t("No such channel"))
service.Notice(rb, client.t("No such channel"))
return
}
info := channel.ExportRegistration(0)
channelKey := info.NameCasefolded
if !csPrivsCheck(info, client, rb) {
if !csPrivsCheck(service, info, client, rb) {
return
}
expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt)
if expectedCode != verificationCode {
csNotice(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, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b")))
service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/CS UNREGISTER %s %s", channelKey, expectedCode)))
return
}
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])
if channel == nil {
csNotice(rb, client.t("Channel does not exist"))
service.Notice(rb, client.t("Channel does not exist"))
return
}
if !csPrivsCheck(channel.ExportRegistration(0), client, rb) {
if !csPrivsCheck(service, channel.ExportRegistration(0), client, rb) {
return
}
switch strings.ToLower(params[1]) {
case "access":
channel.resetAccess()
csNotice(rb, client.t("Successfully reset channel access"))
service.Notice(rb, client.t("Successfully reset channel access"))
case "users":
for _, target := range channel.Members() {
if target != client {
@ -497,20 +491,20 @@ func csClearHandler(server *Server, client *Client, command string, params []str
}
}
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" {
processTransferAccept(client, params[1], rb)
processTransferAccept(service, client, params[1], rb)
return
}
chname := params[0]
channel := server.channels.Get(chname)
if channel == nil {
csNotice(rb, client.t("Channel does not exist"))
service.Notice(rb, client.t("Channel does not exist"))
return
}
regInfo := channel.ExportRegistration(0)
@ -519,21 +513,21 @@ func csTransferHandler(server *Server, client *Client, command string, params []
isFounder := account != "" && account == regInfo.Founder
hasPrivs := client.HasRoleCapabs("chanreg")
if !(isFounder || hasPrivs) {
csNotice(rb, client.t("Insufficient privileges"))
service.Notice(rb, client.t("Insufficient privileges"))
return
}
target := params[1]
targetAccount, err := server.accounts.LoadAccount(params[1])
if err != nil {
csNotice(rb, client.t("Account does not exist"))
service.Notice(rb, client.t("Account does not exist"))
return
}
if targetAccount.NameCasefolded != account {
expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
codeValidated := 2 < len(params) && params[2] == expectedCode
if !codeValidated {
csNotice(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, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b")))
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
}
}
@ -541,19 +535,19 @@ func csTransferHandler(server *Server, client *Client, command string, params []
if err == nil {
switch status {
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:
sendTransferPendingNotice(server, target, chname)
csNotice(rb, fmt.Sprintf(client.t("Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance"), chname, target))
sendTransferPendingNotice(service, server, target, chname)
service.Notice(rb, fmt.Sprintf(client.t("Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance"), chname, target))
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 {
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)
if len(clients) == 0 {
return
@ -565,29 +559,29 @@ func sendTransferPendingNotice(server *Server, account, chname string) {
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)
if channel == nil {
csNotice(rb, client.t("Channel does not exist"))
service.Notice(rb, client.t("Channel does not exist"))
return
}
if !checkChanLimit(client, rb) {
if !checkChanLimit(service, client, rb) {
return
}
switch channel.AcceptTransfer(client) {
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:
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:
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()
if oper == nil {
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)
}
}
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:
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:
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]
switch server.channels.Unpurge(chname) {
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:
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:
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") {
csNotice(rb, client.t("Insufficient privileges"))
service.Notice(rb, client.t("Insufficient privileges"))
return
}
@ -642,27 +636,27 @@ func csListHandler(server *Server, client *Client, command string, params []stri
var err error
searchRegex, err = regexp.Compile(params[0])
if err != nil {
csNotice(rb, client.t("Invalid regex"))
service.Notice(rb, client.t("Invalid regex"))
return
}
}
csNotice(rb, ircfmt.Unescape(client.t("*** $bChanServ LIST$b ***")))
service.Notice(rb, ircfmt.Unescape(client.t("*** $bChanServ LIST$b ***")))
channels := server.channelRegistry.AllChannels()
for _, channel := range channels {
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])
if err != nil {
csNotice(rb, client.t("Invalid channel name"))
service.Notice(rb, client.t("Invalid channel name"))
return
}
@ -670,16 +664,16 @@ func csInfoHandler(server *Server, client *Client, command string, params []stri
if client.HasRoleCapabs("chanreg") {
purgeRecord, err := server.channelRegistry.LoadPurgeRecord(chname)
if err == nil {
csNotice(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))
csNotice(rb, fmt.Sprintf(client.t("Purged at: %s"), purgeRecord.PurgedAt.Format(time.RFC1123)))
service.Notice(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("Purged by operator: %s"), purgeRecord.Oper))
service.Notice(rb, fmt.Sprintf(client.t("Purged at: %s"), purgeRecord.PurgedAt.Format(time.RFC1123)))
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 {
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 {
chinfo, err = server.channelRegistry.LoadChannel(chname)
if err != nil && !(err == errNoSuchChannel || err == errFeatureDisabled) {
csNotice(rb, client.t("An error occurred"))
service.Notice(rb, client.t("An error occurred"))
return
}
}
// channel exists but is unregistered, or doesn't exist:
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
}
csNotice(rb, fmt.Sprintf(client.t("Channel %s is registered"), chinfo.Name))
csNotice(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("Channel %s is registered"), chinfo.Name))
service.Notice(rb, fmt.Sprintf(client.t("Founder: %s"), chinfo.Founder))
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()
switch strings.ToLower(settingName) {
case "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)))
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("The stored channel history setting is: %s"), historyStatusToString(settings.History)))
service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, the channel history setting is: %s"), historyStatusToString(effectiveValue)))
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]
channel := server.channels.Get(chname)
if channel == nil {
csNotice(rb, client.t("No such channel"))
service.Notice(rb, client.t("No such channel"))
return
}
info := channel.ExportRegistration(IncludeSettings)
if !csPrivsCheck(info, client, rb) {
if !csPrivsCheck(service, info, client, rb) {
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]
channel := server.channels.Get(chname)
if channel == nil {
csNotice(rb, client.t("No such channel"))
service.Notice(rb, client.t("No such channel"))
return
}
info := channel.ExportRegistration(IncludeSettings)
settings := info.Settings
if !csPrivsCheck(info, client, rb) {
if !csPrivsCheck(service, info, client, rb) {
return
}
@ -760,12 +754,12 @@ func csSetHandler(server *Server, client *Client, command string, params []strin
switch err {
case nil:
csNotice(rb, client.t("Successfully changed the channel settings"))
displayChannelSetting(setting, settings, client, rb)
service.Notice(rb, client.t("Successfully changed the channel settings"))
displayChannelSetting(service, setting, settings, client, rb)
case errInvalidParams:
csNotice(rb, client.t("Invalid parameters"))
service.Notice(rb, client.t("Invalid parameters"))
default:
server.logger.Error("internal", "CS SET error:", err.Error())
csNotice(rb, client.t("An error occurred"))
service.Notice(rb, client.t("An error occurred"))
}
}

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

@ -77,23 +77,23 @@ func registrationErrorToMessage(err error) (message string) {
}
// 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()
if forNS {
nsNotice(rb, client.t("Account created"))
if service != nil {
service.Notice(rb, client.t("Account created"))
} else {
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()))
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.
func sendSuccessfulAccountAuth(client *Client, rb *ResponseBuffer, forNS, forSASL bool) {
func sendSuccessfulAccountAuth(service *ircService, client *Client, rb *ResponseBuffer, forSASL bool) {
details := client.Details()
if forNS {
nsNotice(rb, fmt.Sprintf(client.t("You're now logged in as %s"), details.accountName))
if service != nil {
service.Notice(rb, fmt.Sprintf(client.t("You're now logged in as %s"), details.accountName))
} else {
//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))
@ -253,11 +253,11 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
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)))
return false
} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
} else if !fixupNickEqualsAccount(client, rb, server.Config(), "") {
return false
}
sendSuccessfulAccountAuth(client, rb, false, true)
sendSuccessfulAccountAuth(nil, client, rb, true)
return false
}
@ -311,11 +311,11 @@ func authExternalHandler(server *Server, client *Client, mechanism string, value
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)))
return false
} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
} else if !fixupNickEqualsAccount(client, rb, server.Config(), "") {
return false
}
sendSuccessfulAccountAuth(client, rb, false, true)
sendSuccessfulAccountAuth(nil, client, rb, true)
return false
}
@ -1525,7 +1525,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
config := server.Config()
if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" && !client.HasMode(modes.Operator) {
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"))
return false
}
@ -2355,7 +2355,7 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
}
err := server.accounts.AuthenticateByPassphrase(client, account, accountPass)
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
// an actual server password (either no password or skip-server-password)
rb.session.passStatus = serverPassSuccessful
@ -2446,13 +2446,13 @@ func registerHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *
err := server.accounts.Verify(client, accountName, "")
if err == nil {
if client.registered {
if !fixupNickEqualsAccount(client, rb, config) {
if !fixupNickEqualsAccount(client, rb, config, "") {
err = errNickAccountMismatch
}
}
if err == nil {
rb.Add(nil, server.name, "REGISTER", "SUCCESS", accountName, client.t("Account successfully registered"))
sendSuccessfulRegResponse(client, rb, true)
sendSuccessfulRegResponse(nil, client, rb)
}
}
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]
err := server.accounts.Verify(client, accountName, verificationCode)
if err == nil && client.registered {
if !fixupNickEqualsAccount(client, rb, config) {
if !fixupNickEqualsAccount(client, rb, config, "") {
err = errNickAccountMismatch
}
}
switch err {
case nil:
rb.Add(nil, server.name, "VERIFY", "SUCCESS", accountName, client.t("Account successfully registered"))
sendSuccessfulRegResponse(client, rb, true)
sendSuccessfulRegResponse(nil, client, rb)
case errAccountVerificationInvalidCode:
rb.Add(nil, server.name, "FAIL", "VERIFY", "INVALID_CODE", client.t("Invalid verification code"))
default:
@ -2873,7 +2873,7 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
username, password = username[:colonIndex], username[colonIndex+1:]
err := server.accounts.AuthenticateByPassphrase(client, username, password)
if err == nil {
sendSuccessfulAccountAuth(client, rb, false, true)
sendSuccessfulAccountAuth(nil, client, rb, true)
} else {
// 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"))

@ -18,7 +18,6 @@ import (
const (
histservHelp = `HistServ provides commands related to history.`
histServMask = "HistServ!HistServ@localhost"
)
func histservEnabled(config *Config) bool {
@ -83,24 +82,19 @@ CHATHISTORY.`,
}
)
// histNotice sends the client a notice from HistServ
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) {
func histservForgetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
accountName := server.accounts.AccountToAccountName(params[0])
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]
}
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
if len(params) == 1 {
msgid = params[0]
@ -113,27 +107,27 @@ func histservDeleteHandler(server *Server, client *Client, command string, param
if !hasPrivs {
accountName = client.AccountName()
if !(server.Config().History.Retention.AllowIndividualDelete && accountName != "*") {
histNotice(rb, client.t("Insufficient privileges"))
service.Notice(rb, client.t("Insufficient privileges"))
return
}
}
err := server.DeleteMessage(target, msgid, accountName)
if err == nil {
histNotice(rb, client.t("Successfully deleted message"))
service.Notice(rb, client.t("Successfully deleted message"))
} else {
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 {
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])
if err != nil {
histNotice(rb, client.t("Invalid account name"))
service.Notice(rb, client.t("Invalid account name"))
return
}
@ -143,15 +137,15 @@ func histservExportHandler(server *Server, client *Client, command string, param
pathname := config.getOutputPath(filename)
outfile, err := os.Create(pathname)
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 {
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() {
if r := recover(); r != nil {
server.logger.Error("history",
@ -167,19 +161,19 @@ func histservExportAndNotify(server *Server, cfAccount string, outfile *os.File,
client := server.clients.Get(alertNick)
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)
if err != nil {
histNotice(rb, client.t("Could not retrieve history"))
service.Notice(rb, client.t("Could not retrieve history"))
return
}
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 {
@ -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

@ -16,7 +16,6 @@ import (
const (
hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
in place of your client's hostname/IP).`
hsNickMask = "HostServ!HostServ@localhost"
)
var (
@ -95,12 +94,7 @@ display the necessary code.`,
}
)
// hsNotice sends the client a notice from HostServ
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) {
func hsOnOffHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
enable := false
if command == "on" {
enable = true
@ -108,28 +102,28 @@ func hsOnOffHandler(server *Server, client *Client, command string, params []str
_, err := server.accounts.VHostSetEnabled(client, enable)
if err == errNoVhost {
hsNotice(rb, client.t(err.Error()))
service.Notice(rb, client.t(err.Error()))
} else if err != nil {
hsNotice(rb, client.t("An error occurred"))
service.Notice(rb, client.t("An error occurred"))
} else if enable {
hsNotice(rb, client.t("Successfully enabled your vhost"))
service.Notice(rb, client.t("Successfully enabled your vhost"))
} 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
if len(params) > 0 {
if !client.HasRoleCapabs("vhosts") {
hsNotice(rb, client.t("Command restricted"))
service.Notice(rb, client.t("Command restricted"))
return
}
accountName = params[0]
} else {
accountName = client.Account()
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
}
}
@ -139,17 +133,17 @@ func hsStatusHandler(server *Server, client *Client, command string, params []st
if err != errAccountDoesNotExist {
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
}
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 {
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 {
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
}
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]
var vhost string
if command == "set" {
vhost = params[1]
if validateVhost(server, vhost, true) != nil {
hsNotice(rb, client.t("Invalid vhost"))
service.Notice(rb, client.t("Invalid vhost"))
return
}
}
@ -179,22 +173,22 @@ func hsSetHandler(server *Server, client *Client, command string, params []strin
_, err := server.accounts.VHostSet(user, vhost)
if err != nil {
hsNotice(rb, client.t("An error occurred"))
service.Notice(rb, client.t("An error occurred"))
} else if vhost != "" {
hsNotice(rb, client.t("Successfully set vhost"))
service.Notice(rb, client.t("Successfully set vhost"))
} 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]
expectedCode := utils.ConfirmationCode(secret, server.ctime)
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")))
hsNotice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/HS SETCLOAKSECRET %s %s", secret, expectedCode)))
service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: changing the cloak secret will invalidate stored ban/invite/exception lists.$b")))
service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/HS SETCLOAKSECRET %s %s", secret, expectedCode)))
return
}
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"))
}

@ -151,7 +151,7 @@ func (server *Server) RandomlyRename(client *Client) {
// 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
// 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 {
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)
if err != nil && err != errNoop {
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 true

@ -36,10 +36,6 @@ func servCmdRequiresBouncerEnabled(config *Config) bool {
return config.Accounts.Multiclient.Enabled
}
const (
nsPrefix = "NickServ!NickServ@localhost"
)
const nickservHelp = `NickServ lets you register, log in to, and manage an account.`
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 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) {
func nsGetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var account string
if command == "saget" {
account = params[0]
@ -379,101 +368,101 @@ func nsGetHandler(server *Server, client *Client, command string, params []strin
accountData, err := server.accounts.LoadAccount(account)
if err == errAccountDoesNotExist {
nsNotice(rb, client.t("No such account"))
service.Notice(rb, client.t("No such account"))
return
} else if err != nil {
nsNotice(rb, client.t("Error loading account data"))
service.Notice(rb, client.t("Error loading account data"))
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()
switch strings.ToLower(settingName) {
case "enforce":
storedValue := settings.NickEnforcement
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))
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":
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 {
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":
switch settings.ReplayJoins {
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:
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:
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":
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 {
switch settings.AllowBouncer {
case MulticlientAllowedServerDefault:
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 {
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:
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:
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":
stored := settings.AlwaysOn
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 {
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 {
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":
stored := settings.AutoreplayMissed
if stored {
alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.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 {
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 {
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":
stored := settings.AutoAway
alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
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 {
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 {
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 {
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":
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)))
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("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory)))
service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue)))
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
if command == "saset" {
account = params[0]
@ -487,7 +476,7 @@ func nsSetHandler(server *Server, client *Client, command string, params []strin
var err error
switch strings.ToLower(params[0]) {
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
case "enforce":
var method NickEnforcementMethod
@ -612,19 +601,19 @@ func nsSetHandler(server *Server, client *Client, command string, params []strin
switch err {
case nil:
nsNotice(rb, client.t("Successfully changed your account settings"))
displaySetting(params[0], finalSettings, client, rb)
service.Notice(rb, client.t("Successfully changed your account settings"))
displaySetting(service, params[0], finalSettings, client, rb)
case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
nsNotice(rb, client.t(err.Error()))
service.Notice(rb, client.t(err.Error()))
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:
// 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"
var nick string
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)
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 {
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 {
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 {
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]
ghost := server.clients.Get(nick)
if ghost == nil {
nsNotice(rb, client.t("No such nick"))
service.Notice(rb, client.t("No such nick"))
return
} 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
} 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
}
@ -667,7 +656,7 @@ func nsGhostHandler(server *Server, client *Client, command string, params []str
authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
}
if !authorized {
nsNotice(rb, client.t("You don't own that nick"))
service.Notice(rb, client.t("You don't own that nick"))
return
}
@ -675,31 +664,31 @@ func nsGhostHandler(server *Server, client *Client, command string, params []str
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()
err := server.accounts.SetNickReserved(client, nick, false, true)
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 {
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 {
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 {
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()
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
}
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() {
nsNotice(rb, client.t("You're already logged into an account"))
service.Notice(rb, client.t("You're already logged into an account"))
return
}
@ -735,7 +724,7 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
nickFixupFailed := false
if loginSuccessful {
if !fixupNickEqualsAccount(client, rb, server.Config()) {
if !fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
loginSuccessful = false
// fixupNickEqualsAccount sends its own error message, don't send another
nickFixupFailed = true
@ -743,15 +732,15 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
}
if loginSuccessful {
sendSuccessfulAccountAuth(client, rb, true, true)
sendSuccessfulAccountAuth(service, client, rb, true)
} 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") {
nsNotice(rb, client.t("Insufficient privileges"))
service.Notice(rb, client.t("Insufficient privileges"))
return
}
@ -760,26 +749,26 @@ func nsListHandler(server *Server, client *Client, command string, params []stri
var err error
searchRegex, err = regexp.Compile(params[0])
if err != nil {
nsNotice(rb, client.t("Invalid regex"))
service.Notice(rb, client.t("Invalid regex"))
return
}
}
nsNotice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***")))
service.Notice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***")))
nicks := server.accounts.AllNicks()
for _, nick := range nicks {
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") {
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
}
@ -789,7 +778,7 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
if server.Config().Accounts.NickReservation.Enabled {
accountName = server.accounts.NickToAccount(nick)
if accountName == "" {
nsNotice(rb, client.t("That nickname is not registered"))
service.Notice(rb, client.t("That nickname is not registered"))
return
}
} else {
@ -798,33 +787,33 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
} else {
accountName = client.Account()
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
}
}
account, err := server.accounts.LoadAccount(accountName)
if err != nil || !account.Verified {
nsNotice(rb, client.t("Account does not exist"))
service.Notice(rb, client.t("Account does not exist"))
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)
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
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) {
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 {
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()
passphrase := params[0]
var email string
@ -835,7 +824,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
certfp := rb.session.certfp
if passphrase == "*" {
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
} else {
passphrase = ""
@ -845,12 +834,12 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
if passphrase != "" {
cfPassphrase, err := Casefold(passphrase)
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
}
}
if !nsLoginThrottleCheck(client, rb) {
if !nsLoginThrottleCheck(service, client, rb) {
return
}
@ -859,7 +848,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
if config.Accounts.NickReservation.ForceGuestFormat {
matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
if matches == nil || len(matches) < 2 {
nsNotice(rb, client.t("Erroneous nickname"))
service.Notice(rb, client.t("Erroneous nickname"))
return
}
account = matches[1]
@ -867,7 +856,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
callbackNamespace, callbackValue, validationErr := parseCallback(email, config)
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
}
@ -875,22 +864,22 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
if err == nil {
if callbackNamespace == "*" {
err = server.accounts.Verify(client, account, "")
if err == nil && fixupNickEqualsAccount(client, rb, config) {
sendSuccessfulRegResponse(client, rb, true)
if err == nil && fixupNickEqualsAccount(client, rb, config, service.prefix) {
sendSuccessfulRegResponse(service, client, rb)
}
} else {
messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
message := fmt.Sprintf(messageTemplate, callbackValue)
nsNotice(rb, message)
service.Notice(rb, message)
}
} else {
// details could not be stored and relevant numerics have been dispatched, abort
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
account = params[0]
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())
errMsg = client.t("Could not register")
}
nsNotice(rb, errMsg)
service.Notice(rb, errMsg)
} 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))
}
}
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"
username := params[0]
@ -925,7 +914,7 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
}
if username == "" {
nsNotice(rb, client.t("You must specify an account"))
service.Notice(rb, client.t("You must specify an account"))
return
}
@ -939,10 +928,10 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
} else {
account, err := server.accounts.LoadAccount(username)
if err == errAccountDoesNotExist {
nsNotice(rb, client.t("Invalid account name"))
service.Notice(rb, client.t("Invalid account name"))
return
} else if err != nil {
nsNotice(rb, client.t("Internal error"))
service.Notice(rb, client.t("Internal error"))
return
}
accountName = account.Name
@ -950,34 +939,34 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
}
if !(accountName == client.AccountName() || client.HasRoleCapabs("accreg")) {
nsNotice(rb, client.t("Insufficient oper privs"))
service.Notice(rb, client.t("Insufficient oper privs"))
return
}
expectedCode := utils.ConfirmationCode(accountName, registeredAt)
if expectedCode != verificationCode {
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 {
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
}
err := server.accounts.Unregister(accountName, erase)
if err == errAccountDoesNotExist {
nsNotice(rb, client.t(err.Error()))
service.Notice(rb, client.t(err.Error()))
} else if err != nil {
nsNotice(rb, client.t("Error while unregistering account"))
service.Notice(rb, client.t("Error while unregistering account"))
} 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)
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]
err := server.accounts.Verify(client, username, code)
@ -992,16 +981,16 @@ func nsVerifyHandler(server *Server, client *Client, command string, params []st
}
if errorMessage != "" {
nsNotice(rb, client.t(errorMessage))
service.Notice(rb, client.t(errorMessage))
return
}
if fixupNickEqualsAccount(client, rb, server.Config()) {
sendSuccessfulRegResponse(client, rb, true)
if fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
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 newPassword string
var errorMessage string
@ -1025,7 +1014,7 @@ func nsPasswdHandler(server *Server, client *Client, command string, params []st
} else if params[1] != params[2] {
errorMessage = `Passwords do not match`
} else {
if !nsLoginThrottleCheck(client, rb) {
if !nsLoginThrottleCheck(service, client, rb) {
return
}
accountData, err := server.accounts.LoadAccount(target)
@ -1048,37 +1037,37 @@ func nsPasswdHandler(server *Server, client *Client, command string, params []st
}
if errorMessage != "" {
nsNotice(rb, client.t(errorMessage))
service.Notice(rb, client.t(errorMessage))
return
}
err := server.accounts.setPassword(target, newPassword, hasPrivs)
switch err {
case nil:
nsNotice(rb, client.t("Password changed"))
service.Notice(rb, client.t("Password changed"))
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:
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:
nsNotice(rb, client.t("Try again later"))
service.Notice(rb, client.t("Try again later"))
default:
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"}
if len(params) == 0 {
nsGetHandler(server, client, "get", newParams, rb)
nsGetHandler(service, server, client, "get", newParams, rb)
} else {
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
if command == "sessions" {
@ -1091,56 +1080,56 @@ func nsClientsHandler(server *Server, client *Client, command string, params []s
switch verb {
case "list":
nsClientsListHandler(server, client, params, rb)
nsClientsListHandler(service, server, client, params, rb)
case "logout":
nsClientsLogoutHandler(server, client, params, rb)
nsClientsLogoutHandler(service, server, client, params, rb)
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
hasPrivs := client.HasRoleCapabs("local_ban")
if 0 < len(params) {
target = server.clients.Get(params[0])
if target == nil {
nsNotice(rb, client.t("No such nick"))
service.Notice(rb, client.t("No such nick"))
return
}
if target != client && !hasPrivs {
nsNotice(rb, client.t("Command restricted"))
service.Notice(rb, client.t("Command restricted"))
return
}
}
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 {
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 {
nsNotice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID))
service.Notice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID))
}
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()))
nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
service.Notice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String()))
service.Notice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
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)))
nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
service.Notice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123)))
service.Notice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
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 {
nsNotice(rb, client.t("Missing client ID to logout (or \"all\")"))
service.Notice(rb, client.t("Missing client ID to logout (or \"all\")"))
return
}
@ -1149,14 +1138,14 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
// CLIENTS LOGOUT [nickname] [client ID]
target = server.clients.Get(params[0])
if target == nil {
nsNotice(rb, client.t("No such nick"))
service.Notice(rb, client.t("No such nick"))
return
}
// User must have "local_kill" privileges to logout other user sessions.
if target != client {
oper := client.Oper()
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
}
}
@ -1167,7 +1156,7 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
if strings.ToLower(params[0]) != "all" {
sessionID, err := strconv.ParseInt(params[0], 10, 64)
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
}
// 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 {
nsNotice(rb, client.t("Specified client ID does not exist"))
service.Notice(rb, client.t("Specified client ID does not exist"))
return
}
}
@ -1186,14 +1175,14 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
target.destroy(sessionToDestroy)
if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target {
if sessionToDestroy != nil {
nsNotice(rb, client.t("Successfully logged out session"))
service.Notice(rb, client.t("Successfully logged out session"))
} 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])
params = params[1:]
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 != "" {
certfp = rb.session.certfp // #1059
} else {
nsNotice(rb, client.t("Invalid parameters"))
service.Notice(rb, client.t("Invalid parameters"))
return
}
default:
nsNotice(rb, client.t("Invalid parameters"))
service.Notice(rb, client.t("Invalid parameters"))
return
}
hasPrivs := client.HasRoleCapabs("accreg")
if target != "" && !hasPrivs {
nsNotice(rb, client.t("Insufficient privileges"))
service.Notice(rb, client.t("Insufficient privileges"))
return
} else if target == "" {
target = client.Account()
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
}
}
@ -1236,16 +1225,16 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
case "list":
accountData, err := server.accounts.LoadAccount(target)
if err == errAccountDoesNotExist {
nsNotice(rb, client.t("Account does not exist"))
service.Notice(rb, client.t("Account does not exist"))
return
} else if err != nil {
nsNotice(rb, client.t("An error occurred"))
service.Notice(rb, client.t("An error occurred"))
return
}
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 {
nsNotice(rb, fmt.Sprintf("%d: %s", i+1, certfp))
service.Notice(rb, fmt.Sprintf("%d: %s", i+1, certfp))
}
return
case "add":
@ -1257,54 +1246,54 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
switch err {
case nil:
if verb == "add" {
nsNotice(rb, client.t("Certificate fingerprint successfully added"))
service.Notice(rb, client.t("Certificate fingerprint successfully added"))
} else {
nsNotice(rb, client.t("Certificate fingerprint successfully removed"))
service.Notice(rb, client.t("Certificate fingerprint successfully removed"))
}
case errNoop:
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 {
nsNotice(rb, client.t("Certificate fingerprint not found"))
service.Notice(rb, client.t("Certificate fingerprint not found"))
}
case errAccountDoesNotExist:
nsNotice(rb, client.t("Account does not exist"))
service.Notice(rb, client.t("Account does not exist"))
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:
nsNotice(rb, client.t("Invalid certificate fingerprint"))
service.Notice(rb, client.t("Invalid certificate fingerprint"))
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:
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:
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:
nsNotice(rb, client.t("Try again later"))
service.Notice(rb, client.t("Try again later"))
default:
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])
params = params[1:]
switch subCmd {
case "add":
nsSuspendAddHandler(server, client, command, params, rb)
nsSuspendAddHandler(service, server, client, command, params, rb)
case "del", "delete", "remove":
nsSuspendRemoveHandler(server, client, command, params, rb)
nsSuspendRemoveHandler(service, server, client, command, params, rb)
case "list":
nsSuspendListHandler(server, client, command, params, rb)
nsSuspendListHandler(service, server, client, command, params, rb)
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 {
nsNotice(rb, client.t("Invalid parameters"))
service.Notice(rb, client.t("Invalid parameters"))
return
}
@ -1316,7 +1305,7 @@ func nsSuspendAddHandler(server *Server, client *Client, command string, params
var err error
cDuration, err := custime.ParseDuration(params[1])
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
}
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)
switch err {
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:
nsNotice(rb, client.t("No such account"))
service.Notice(rb, client.t("No such account"))
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 {
nsNotice(rb, client.t("Invalid parameters"))
service.Notice(rb, client.t("Invalid parameters"))
return
}
err := server.accounts.Unsuspend(params[0])
switch err {
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:
nsNotice(rb, client.t("No such account"))
service.Notice(rb, client.t("No such account"))
case errNoop:
nsNotice(rb, client.t("Account was not suspended"))
service.Notice(rb, client.t("Account was not suspended"))
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) 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()
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 {
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)
}
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]
err := server.accounts.Rename(oldName, newName)
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
}
nsNotice(rb, client.t("Successfully renamed account"))
service.Notice(rb, client.t("Successfully renamed account"))
if server.Config().Accounts.NickReservation.ForceNickEqualsAccount {
if curClient := server.clients.Get(oldName); curClient != nil {
renameErr := performNickChange(client.server, client, curClient, nil, newName, rb)
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))
}
}
}

@ -530,6 +530,8 @@ func (server *Server) applyConfig(config *Config) (err error) {
} else if oldConfig.Server.IPCheckScript.MaxConcurrency != config.Server.IPCheckScript.MaxConcurrency ||
oldConfig.Accounts.AuthScript.MaxConcurrency != config.Accounts.AuthScript.MaxConcurrency {
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 {
server.semaphores.AuthScript.Initialize(maxAuthConc)
}
if err := overrideServicePrefixes(config.Server.OverrideServicesHostname); err != nil {
return err
}
}
if oldConfig != nil {

@ -20,7 +20,7 @@ import (
type ircService struct {
Name string
ShortName string
prefix string
prefix string // NUH source of messages from this service
CommandAliases []string
Commands map[string]*serviceCommand
HelpBanner string
@ -30,7 +30,7 @@ type ircService struct {
type serviceCommand struct {
aliasOf string // marks this command as an alias of another
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
helpStrings []string
helpShort string
@ -60,36 +60,47 @@ func lookupServiceCommand(commands map[string]*serviceCommand, command string) *
return nil
}
// all services, by lowercase name
var OragonoServices = map[string]*ircService{
"nickserv": {
var (
nickservService = &ircService{
Name: "NickServ",
ShortName: "NS",
CommandAliases: []string{"NICKSERV", "NS"},
Commands: nickservCommands,
HelpBanner: nickservHelp,
},
"chanserv": {
}
chanservService = &ircService{
Name: "ChanServ",
ShortName: "CS",
CommandAliases: []string{"CHANSERV", "CS"},
Commands: chanservCommands,
HelpBanner: chanservHelp,
},
"hostserv": {
}
hostservService = &ircService{
Name: "HostServ",
ShortName: "HS",
CommandAliases: []string{"HOSTSERV", "HS"},
Commands: hostservCommands,
HelpBanner: hostservHelp,
},
"histserv": {
}
histservService = &ircService{
Name: "HistServ",
ShortName: "HISTSERV",
CommandAliases: []string{"HISTSERV"},
Commands: histservCommands,
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
@ -212,7 +223,7 @@ func serviceRunCommand(service *ircService, server *Server, client *Client, cmd
if commandName == "help" {
serviceHelpHandler(service, server, client, params, rb)
} 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() {
// this modifies the global Commands map,
// so it must be called from irc/commands.go's init()

@ -307,7 +307,11 @@ server:
# oragono will write files to disk under certain circumstances, e.g.,
# CPU profiling or data export. by default, these files will be written
# 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
accounts: