diff --git a/irc/accounts.go b/irc/accounts.go index 032a724f..4487cb7f 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -358,29 +358,37 @@ func unmarshalReservedNicks(nicks string) (result []string) { return strings.Split(nicks, ",") } -func (am *AccountManager) SetNickReserved(client *Client, nick string, reserve bool) error { +func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreserve bool, reserve bool) error { cfnick, err := CasefoldName(nick) - if err != nil { + // garbage nick, or garbage options, or disabled + nrconfig := am.server.AccountConfig().NickReservation + if err != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled { return errAccountNickReservationFailed } - // sanity check so we don't persist bad data - account := client.Account() - if account == "" || cfnick == "" || !am.server.AccountConfig().NickReservation.Enabled { - return errAccountNickReservationFailed - } - - limit := am.server.AccountConfig().NickReservation.AdditionalNickLimit - + // the cache is in sync with the DB while we hold serialCacheUpdateMutex am.serialCacheUpdateMutex.Lock() defer am.serialCacheUpdateMutex.Unlock() - // the cache is in sync with the DB while we hold serialCacheUpdateMutex + // find the affected account, which is usually the client's: + account := client.Account() + if saUnreserve { + // unless this is a sadrop: + account = am.NickToAccount(cfnick) + if account == "" { + // nothing to do + return nil + } + } + if account == "" { + return errAccountNotLoggedIn + } + accountForNick := am.NickToAccount(cfnick) if reserve && accountForNick != "" { return errNicknameReserved - } else if !reserve && accountForNick != account { - return errAccountNickReservationFailed + } else if !reserve && !saUnreserve && accountForNick != account { + return errNicknameReserved } else if !reserve && cfnick == account { return errAccountCantDropPrimaryNick } @@ -405,7 +413,7 @@ func (am *AccountManager) SetNickReserved(client *Client, nick string, reserve b nicks := unmarshalReservedNicks(rawNicks) if reserve { - if len(nicks) >= limit { + if len(nicks) >= nrconfig.AdditionalNickLimit { return errAccountTooManyNicks } nicks = append(nicks, cfnick) diff --git a/irc/nickserv.go b/irc/nickserv.go index d3f61795..bb1cd0d7 100644 --- a/irc/nickserv.go +++ b/irc/nickserv.go @@ -90,7 +90,10 @@ func (server *Server) nickservPrivmsgHandler(client *Client, message string, rb server.nickservGroupHandler(client, rb) } else if command == "drop" { nick, _ := extractParam(params) - server.nickservDropHandler(client, nick, rb) + server.nickservDropHandler(client, nick, false, rb) + } else if command == "sadrop" { + nick, _ := extractParam(params) + server.nickservDropHandler(client, nick, true, rb) } else { rb.Notice(client.t("Command not recognised. To see the available commands, run /NS HELP")) } @@ -294,13 +297,13 @@ func (server *Server) nickservGroupHandler(client *Client, rb *ResponseBuffer) { } nick := client.NickCasefolded() - err := server.accounts.SetNickReserved(client, nick, true) + err := server.accounts.SetNickReserved(client, nick, false, true) if err == nil { rb.Notice(fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick)) } else if err == errAccountTooManyNicks { rb.Notice(client.t("You have too many nicks reserved already (you can remove some with /NS DROP)")) } else if err == errNicknameReserved { - rb.Notice(client.t("That nickname is already reserved")) + rb.Notice(client.t("That nickname is already reserved by someone else")) } else { rb.Notice(client.t("Error reserving nickname")) } @@ -334,20 +337,23 @@ func (server *Server) nickservInfoHandler(client *Client, nick string, rb *Respo } } -func (server *Server) nickservDropHandler(client *Client, nick string, rb *ResponseBuffer) { - account := client.Account() - if account == "" { - rb.Notice(client.t("You're not logged into an account")) - return +func (server *Server) nickservDropHandler(client *Client, nick string, sadrop bool, rb *ResponseBuffer) { + if sadrop { + if !client.HasRoleCapabs("unregister") { + rb.Notice(client.t("Insufficient oper privs")) + return + } } - err := server.accounts.SetNickReserved(client, nick, false) + err := server.accounts.SetNickReserved(client, nick, sadrop, false) if err == nil { rb.Notice(fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick)) + } else if err == errAccountNotLoggedIn { + rb.Notice(fmt.Sprintf(client.t("You're not logged into an account"))) } else if err == errAccountCantDropPrimaryNick { rb.Notice(fmt.Sprintf(client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))) - } else if err == errAccountNickReservationFailed { - rb.Notice(fmt.Sprintf(client.t("You don't own that nick"))) + } else if err == errNicknameReserved { + rb.Notice(fmt.Sprintf(client.t("That nickname is already reserved by someone else"))) } else { rb.Notice(client.t("Error ungrouping nick")) }