3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-22 11:59:40 +01:00
Transition most "is an operator" checks to require a specific operator
capability
This commit is contained in:
Shivaram Lingamneni 2021-02-06 22:45:34 -05:00
parent 4c08bc9c49
commit 42316bc04f
11 changed files with 59 additions and 65 deletions

View File

@ -593,6 +593,7 @@ oper-classes:
- "vhosts" - "vhosts"
- "sajoin" - "sajoin"
- "samode" - "samode"
- "snomasks"
# server admin: has full control of the ircd, including nickname and # server admin: has full control of the ircd, including nickname and
# channel registrations # channel registrations

View File

@ -439,7 +439,7 @@ func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
channel.stateMutex.RLock() channel.stateMutex.RLock()
clientData, isJoined := channel.members[client] clientData, isJoined := channel.members[client]
channel.stateMutex.RUnlock() channel.stateMutex.RUnlock()
isOper := client.HasMode(modes.Operator) isOper := client.HasRoleCapabs("sajoin")
respectAuditorium := channel.flags.HasMode(modes.Auditorium) && !isOper && respectAuditorium := channel.flags.HasMode(modes.Auditorium) && !isOper &&
(!isJoined || clientData.modes.HighestChannelUserMode() == modes.Mode(0)) (!isJoined || clientData.modes.HighestChannelUserMode() == modes.Mode(0))
isMultiPrefix := rb.session.capabilities.Has(caps.MultiPrefix) isMultiPrefix := rb.session.capabilities.Has(caps.MultiPrefix)
@ -607,7 +607,7 @@ func (channel *Channel) hasClient(client *Client) bool {
// <mode> <mode params> // <mode> <mode params>
func (channel *Channel) modeStrings(client *Client) (result []string) { func (channel *Channel) modeStrings(client *Client) (result []string) {
hasPrivs := client.HasMode(modes.Operator) hasPrivs := client.HasRoleCapabs("sajoin")
channel.stateMutex.RLock() channel.stateMutex.RLock()
defer channel.stateMutex.RUnlock() defer channel.stateMutex.RUnlock()
@ -1245,12 +1245,12 @@ func (channel *Channel) SendTopic(client *Client, rb *ResponseBuffer, sendNoTopi
// SetTopic sets the topic of this channel, if the client is allowed to do so. // SetTopic sets the topic of this channel, if the client is allowed to do so.
func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffer) { func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffer) {
if !(client.HasMode(modes.Operator) || channel.hasClient(client)) { if !channel.hasClient(client) {
rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), channel.Name(), client.t("You're not on that channel")) rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), channel.Name(), client.t("You're not on that channel"))
return return
} }
if channel.flags.HasMode(modes.OpOnlyTopic) && !channel.ClientIsAtLeast(client, modes.Halfop) { if channel.flags.HasMode(modes.OpOnlyTopic) && !(channel.ClientIsAtLeast(client, modes.Halfop) || client.HasRoleCapabs("samode")) {
rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You're not a channel operator")) rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You're not a channel operator"))
return return
} }
@ -1487,10 +1487,6 @@ func (channel *Channel) Quit(client *Client) {
func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer, hasPrivs bool) { func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer, hasPrivs bool) {
if !hasPrivs { if !hasPrivs {
if !(client.HasMode(modes.Operator) || channel.hasClient(client)) {
rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), channel.Name(), client.t("You're not on that channel"))
return
}
if !channel.ClientHasPrivsOver(client, target) { if !channel.ClientHasPrivsOver(client, target) {
rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You don't have enough channel privileges")) rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You don't have enough channel privileges"))
return return

View File

@ -860,7 +860,7 @@ func csHowToBanHandler(service *ircService, server *Server, client *Client, comm
return return
} }
if !(channel.ClientIsAtLeast(client, modes.Operator) || client.HasRoleCapabs("samode")) { if !(channel.ClientIsAtLeast(client, modes.ChannelOperator) || client.HasRoleCapabs("samode")) {
service.Notice(rb, client.t("Insufficient privileges")) service.Notice(rb, client.t("Insufficient privileges"))
return return
} }

View File

@ -1512,7 +1512,7 @@ func (client *Client) destroy(session *Session) {
// decrement stats if we have no more sessions, even if the client will not be destroyed // decrement stats if we have no more sessions, even if the client will not be destroyed
if shouldDecrement { if shouldDecrement {
invisible := client.HasMode(modes.Invisible) invisible := client.HasMode(modes.Invisible)
operator := client.HasMode(modes.LocalOperator) || client.HasMode(modes.Operator) operator := client.HasMode(modes.Operator)
client.server.stats.Remove(registered, invisible, operator) client.server.stats.Remove(registered, invisible, operator)
} }

View File

@ -222,7 +222,7 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
} }
if numSessions == 1 { if numSessions == 1 {
invisible := currentClient.HasMode(modes.Invisible) invisible := currentClient.HasMode(modes.Invisible)
operator := currentClient.HasMode(modes.Operator) || currentClient.HasMode(modes.LocalOperator) operator := currentClient.HasMode(modes.Operator)
client.server.stats.AddRegistered(invisible, operator) client.server.stats.AddRegistered(invisible, operator)
} }
session.autoreplayMissedSince = lastSeen session.autoreplayMissedSince = lastSeen

View File

@ -7,13 +7,11 @@ package irc
import ( import (
"github.com/goshuirc/irc-go/ircmsg" "github.com/goshuirc/irc-go/ircmsg"
"github.com/oragono/oragono/irc/modes"
) )
// Command represents a command accepted from a client. // Command represents a command accepted from a client.
type Command struct { type Command struct {
handler func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool handler func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool
oper bool
usablePreReg bool usablePreReg bool
allowedInBatch bool // allowed in client-to-server batches allowedInBatch bool // allowed in client-to-server batches
minParams int minParams int
@ -32,10 +30,6 @@ func (cmd *Command) Run(server *Server, client *Client, session *Session, msg ir
rb.Add(nil, server.name, ERR_NOTREGISTERED, "*", client.t("You need to register before you can use that command")) rb.Add(nil, server.name, ERR_NOTREGISTERED, "*", client.t("You need to register before you can use that command"))
return false return false
} }
if cmd.oper && !client.HasMode(modes.Operator) {
rb.Add(nil, server.name, ERR_NOPRIVILEGES, client.Nick(), client.t("Permission Denied - You're not an IRC operator"))
return false
}
if len(cmd.capabs) > 0 && !client.HasRoleCapabs(cmd.capabs...) { if len(cmd.capabs) > 0 && !client.HasRoleCapabs(cmd.capabs...) {
rb.Add(nil, server.name, ERR_NOPRIVILEGES, client.Nick(), client.t("Permission Denied")) rb.Add(nil, server.name, ERR_NOPRIVILEGES, client.Nick(), client.t("Permission Denied"))
return false return false
@ -115,7 +109,7 @@ func init() {
"DEBUG": { "DEBUG": {
handler: debugHandler, handler: debugHandler,
minParams: 1, minParams: 1,
oper: true, capabs: []string{"rehash"},
}, },
"DEFCON": { "DEFCON": {
handler: defconHandler, handler: defconHandler,
@ -124,12 +118,11 @@ func init() {
"DEOPER": { "DEOPER": {
handler: deoperHandler, handler: deoperHandler,
minParams: 0, minParams: 0,
oper: true,
}, },
"DLINE": { "DLINE": {
handler: dlineHandler, handler: dlineHandler,
minParams: 1, minParams: 1,
oper: true, capabs: []string{"ban"},
}, },
"EXTJWT": { "EXTJWT": {
handler: extjwtHandler, handler: extjwtHandler,
@ -169,13 +162,12 @@ func init() {
"KILL": { "KILL": {
handler: killHandler, handler: killHandler,
minParams: 1, minParams: 1,
oper: true,
capabs: []string{"kill"}, capabs: []string{"kill"},
}, },
"KLINE": { "KLINE": {
handler: klineHandler, handler: klineHandler,
minParams: 1, minParams: 1,
oper: true, capabs: []string{"ban"},
}, },
"LANGUAGE": { "LANGUAGE": {
handler: languageHandler, handler: languageHandler,
@ -278,7 +270,7 @@ func init() {
"SANICK": { "SANICK": {
handler: sanickHandler, handler: sanickHandler,
minParams: 2, minParams: 2,
oper: true, capabs: []string{"samode"},
}, },
"SAMODE": { "SAMODE": {
handler: modeHandler, handler: modeHandler,
@ -308,7 +300,6 @@ func init() {
"REHASH": { "REHASH": {
handler: rehashHandler, handler: rehashHandler,
minParams: 0, minParams: 0,
oper: true,
capabs: []string{"rehash"}, capabs: []string{"rehash"},
}, },
"TIME": { "TIME": {
@ -327,7 +318,7 @@ func init() {
"UNDLINE": { "UNDLINE": {
handler: unDLineHandler, handler: unDLineHandler,
minParams: 1, minParams: 1,
oper: true, capabs: []string{"ban"},
}, },
"UNINVITE": { "UNINVITE": {
handler: inviteHandler, handler: inviteHandler,
@ -336,7 +327,7 @@ func init() {
"UNKLINE": { "UNKLINE": {
handler: unKLineHandler, handler: unKLineHandler,
minParams: 1, minParams: 1,
oper: true, capabs: []string{"ban"},
}, },
"USER": { "USER": {
handler: userHandler, handler: userHandler,

View File

@ -757,10 +757,6 @@ func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
rb.Notice(fmt.Sprintf("CPU profiling stopped")) rb.Notice(fmt.Sprintf("CPU profiling stopped"))
case "CRASHSERVER": case "CRASHSERVER":
if !client.HasRoleCapabs("rehash") {
rb.Notice(client.t("You must have rehash permissions in order to execute DEBUG CRASHSERVER"))
return false
}
code := utils.ConfirmationCode(server.name, server.ctime) code := utils.ConfirmationCode(server.name, server.ctime)
if len(msg.Params) == 1 || msg.Params[1] != code { if len(msg.Params) == 1 || msg.Params[1] != code {
rb.Notice(fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/DEBUG CRASHSERVER %s", code))) rb.Notice(fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/DEBUG CRASHSERVER %s", code)))
@ -1293,6 +1289,7 @@ func sajoinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
// KICK <channel>{,<channel>} <user>{,<user>} [<comment>] // KICK <channel>{,<channel>} <user>{,<user>} [<comment>]
func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
hasPrivs := client.HasRoleCapabs("samode")
channels := strings.Split(msg.Params[0], ",") channels := strings.Split(msg.Params[0], ",")
users := strings.Split(msg.Params[1], ",") users := strings.Split(msg.Params[1], ",")
if (len(channels) != len(users)) && (len(users) != 1) { if (len(channels) != len(users)) && (len(users) != 1) {
@ -1336,7 +1333,7 @@ func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
if comment == "" { if comment == "" {
comment = kick.nick comment = kick.nick
} }
channel.Kick(client, target, comment, rb, false) channel.Kick(client, target, comment, rb, hasPrivs)
} }
return false return false
} }
@ -1618,7 +1615,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
rb.Add(nil, client.server.name, RPL_LIST, nick, name, strconv.Itoa(members), topic) rb.Add(nil, client.server.name, RPL_LIST, nick, name, strconv.Itoa(members), topic)
} }
clientIsOp := client.HasMode(modes.Operator) clientIsOp := client.HasRoleCapabs("sajoin")
if len(channels) == 0 { if len(channels) == 0 {
for _, channel := range server.channels.Channels() { for _, channel := range server.channels.Channels() {
if !clientIsOp && channel.flags.HasMode(modes.Secret) { if !clientIsOp && channel.flags.HasMode(modes.Secret) {
@ -1775,7 +1772,7 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
rb.Add(nil, cDetails.nickMask, "MODE", args...) rb.Add(nil, cDetails.nickMask, "MODE", args...)
} else if hasPrivs { } else if hasPrivs {
rb.Add(nil, server.name, RPL_UMODEIS, targetNick, target.ModeString()) rb.Add(nil, server.name, RPL_UMODEIS, targetNick, target.ModeString())
if target.HasMode(modes.LocalOperator) || target.HasMode(modes.Operator) { if target.HasMode(modes.Operator) {
masks := server.snomasks.String(target) masks := server.snomasks.String(target)
if 0 < len(masks) { if 0 < len(masks) {
rb.Add(nil, server.name, RPL_SNOMASKIS, targetNick, masks, client.t("Server notice masks")) rb.Add(nil, server.name, RPL_SNOMASKIS, targetNick, masks, client.t("Server notice masks"))
@ -1959,7 +1956,7 @@ func namesHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
success := false success := false
channel := server.channels.Get(chname) channel := server.channels.Get(chname)
if channel != nil { if channel != nil {
if !channel.flags.HasMode(modes.Secret) || channel.hasClient(client) || client.HasMode(modes.Operator) { if !channel.flags.HasMode(modes.Secret) || channel.hasClient(client) || client.HasRoleCapabs("sajoin") {
channel.Names(client, rb) channel.Names(client, rb)
success = true success = true
} }
@ -2338,6 +2335,10 @@ func applyOper(client *Client, oper *Oper, rb *ResponseBuffer) {
// DEOPER // DEOPER
func deoperHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { func deoperHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
if client.Oper() == nil {
rb.Notice(client.t("Insufficient oper privs"))
return false
}
// pretend they sent /MODE $nick -o // pretend they sent /MODE $nick -o
fakeModeMsg := ircmsg.MakeMessage(nil, "", "MODE", client.Nick(), "-o") fakeModeMsg := ircmsg.MakeMessage(nil, "", "MODE", client.Nick(), "-o")
return umodeHandler(server, client, fakeModeMsg, rb) return umodeHandler(server, client, fakeModeMsg, rb)
@ -2944,7 +2945,7 @@ func operStatusVisible(client, target *Client, hasPrivs bool) bool {
// USERHOST <nickname>{ <nickname>} // USERHOST <nickname>{ <nickname>}
func userhostHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { func userhostHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
hasPrivs := client.HasMode(modes.Operator) // TODO(#1176) figure out the right capab for this hasPrivs := client.HasMode(modes.Operator)
returnedClients := make(ClientSet) returnedClients := make(ClientSet)
var tl utils.TokenLineBuilder var tl utils.TokenLineBuilder
@ -3083,7 +3084,7 @@ func (fields whoxFields) Has(field rune) bool {
// <channel> <user> <host> <server> <nick> <H|G>[*][~|&|@|%|+][B] :<hopcount> <real name> // <channel> <user> <host> <server> <nick> <H|G>[*][~|&|@|%|+][B] :<hopcount> <real name>
// whox format: // whox format:
// <type> <channel> <user> <ip> <host> <server> <nick> <H|G>[*][~|&|@|%|+][B] <hops> <idle> <account> <rank> :<real name> // <type> <channel> <user> <ip> <host> <server> <nick> <H|G>[*][~|&|@|%|+][B] <hops> <idle> <account> <rank> :<real name>
func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer, hasPrivs, includeRFlag, isWhox bool, fields whoxFields, whoType string) { func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer, canSeeIPs, canSeeOpers, includeRFlag, isWhox bool, fields whoxFields, whoType string) {
params := []string{client.Nick()} params := []string{client.Nick()}
details := target.Details() details := target.Details()
@ -3103,7 +3104,7 @@ func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *Response
} }
if fields.Has('i') { if fields.Has('i') {
fIP := "255.255.255.255" fIP := "255.255.255.255"
if hasPrivs || client == target { if canSeeIPs || client == target {
// you can only see a target's IP if they're you or you're an oper // you can only see a target's IP if they're you or you're an oper
fIP = target.IPString() fIP = target.IPString()
} }
@ -3126,7 +3127,7 @@ func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *Response
flags.WriteRune('H') // Here flags.WriteRune('H') // Here
} }
if target.HasMode(modes.Operator) && operStatusVisible(client, target, hasPrivs) { if target.HasMode(modes.Operator) && operStatusVisible(client, target, canSeeOpers) {
flags.WriteRune('*') flags.WriteRune('*')
} }
@ -3229,23 +3230,23 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
// operatorOnly = true // operatorOnly = true
//} //}
isOper := client.HasMode(modes.Operator) oper := client.Oper()
hasPrivs := oper.HasRoleCapab("sajoin")
canSeeIPs := oper.HasRoleCapab("ban")
if mask[0] == '#' { if mask[0] == '#' {
// TODO implement wildcard matching
//TODO(dan): ^ only for opers
channel := server.channels.Get(mask) channel := server.channels.Get(mask)
if channel != nil { if channel != nil {
isJoined := channel.hasClient(client) isJoined := channel.hasClient(client)
if !channel.flags.HasMode(modes.Secret) || isJoined || isOper { if !channel.flags.HasMode(modes.Secret) || isJoined || hasPrivs {
var members []*Client var members []*Client
if isOper { if hasPrivs {
members = channel.Members() members = channel.Members()
} else { } else {
members = channel.auditoriumFriends(client) members = channel.auditoriumFriends(client)
} }
for _, member := range members { for _, member := range members {
if !member.HasMode(modes.Invisible) || isJoined || isOper { if !member.HasMode(modes.Invisible) || isJoined || hasPrivs {
client.rplWhoReply(channel, member, rb, isOper, includeRFlag, isWhox, fields, whoType) client.rplWhoReply(channel, member, rb, canSeeIPs, oper != nil, includeRFlag, isWhox, fields, whoType)
} }
} }
} }
@ -3275,8 +3276,8 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
} }
for mclient := range server.clients.FindAll(mask) { for mclient := range server.clients.FindAll(mask) {
if isOper || !mclient.HasMode(modes.Invisible) || isFriend(mclient) { if hasPrivs || !mclient.HasMode(modes.Invisible) || isFriend(mclient) {
client.rplWhoReply(nil, mclient, rb, isOper, includeRFlag, isWhox, fields, whoType) client.rplWhoReply(nil, mclient, rb, canSeeIPs, oper != nil, includeRFlag, isWhox, fields, whoType)
} }
} }
} }
@ -3319,7 +3320,7 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
return true return true
} }
hasPrivs := client.HasMode(modes.Operator) // TODO(#1176) figure out the right capab for this hasPrivs := client.HasRoleCapabs("samode")
if hasPrivs { if hasPrivs {
for _, mask := range strings.Split(masksString, ",") { for _, mask := range strings.Split(masksString, ",") {
matches := server.clients.FindAll(mask) matches := server.clients.FindAll(mask)

View File

@ -37,14 +37,14 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool,
if change.Mode != modes.ServerNotice { if change.Mode != modes.ServerNotice {
switch change.Op { switch change.Op {
case modes.Add: case modes.Add:
if (change.Mode == modes.Operator || change.Mode == modes.LocalOperator) && !(force && oper != nil) { if (change.Mode == modes.Operator) && !(force && oper != nil) {
continue continue
} }
if client.SetMode(change.Mode, true) { if client.SetMode(change.Mode, true) {
if change.Mode == modes.Invisible { if change.Mode == modes.Invisible {
client.server.stats.ChangeInvisible(1) client.server.stats.ChangeInvisible(1)
} else if change.Mode == modes.Operator || change.Mode == modes.LocalOperator { } else if change.Mode == modes.Operator {
client.server.stats.ChangeOperators(1) client.server.stats.ChangeOperators(1)
} }
applied = append(applied, change) applied = append(applied, change)
@ -55,7 +55,7 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool,
if client.SetMode(change.Mode, false) { if client.SetMode(change.Mode, false) {
if change.Mode == modes.Invisible { if change.Mode == modes.Invisible {
client.server.stats.ChangeInvisible(-1) client.server.stats.ChangeInvisible(-1)
} else if change.Mode == modes.Operator || change.Mode == modes.LocalOperator { } else if change.Mode == modes.Operator {
removedSnomasks = client.server.snomasks.String(client) removedSnomasks = client.server.snomasks.String(client)
client.server.stats.ChangeOperators(-1) client.server.stats.ChangeOperators(-1)
applyOper(client, nil, nil) applyOper(client, nil, nil)
@ -91,8 +91,12 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool,
change.Arg = newArg change.Arg = newArg
} }
if change.Op == modes.Add { if change.Op == modes.Add {
oper := client.Oper()
// #1176: require special operator privileges to subscribe to snomasks
if oper.HasRoleCapab("snomasks") || oper.HasRoleCapab("ban") {
client.server.snomasks.AddMasks(client, masks...) client.server.snomasks.AddMasks(client, masks...)
applied = append(applied, change) applied = append(applied, change)
}
} else if change.Op == modes.Remove { } else if change.Op == modes.Remove {
client.server.snomasks.RemoveMasks(client, masks...) client.server.snomasks.RemoveMasks(client, masks...)
applied = append(applied, change) applied = append(applied, change)

View File

@ -101,7 +101,6 @@ func (modes Modes) String() string {
const ( const (
Bot Mode = 'B' Bot Mode = 'B'
Invisible Mode = 'i' Invisible Mode = 'i'
LocalOperator Mode = 'O'
Operator Mode = 'o' Operator Mode = 'o'
Restricted Mode = 'r' Restricted Mode = 'r'
RegisteredOnly Mode = 'R' RegisteredOnly Mode = 'R'

View File

@ -459,39 +459,40 @@ func (server *Server) MOTD(client *Client, rb *ResponseBuffer) {
rb.Add(nil, server.name, RPL_ENDOFMOTD, client.nick, client.t("End of MOTD command")) rb.Add(nil, server.name, RPL_ENDOFMOTD, client.nick, client.t("End of MOTD command"))
} }
// WhoisChannelsNames returns the common channel names between two users. func (client *Client) whoisChannelsNames(target *Client, multiPrefix bool, hasPrivs bool) []string {
func (client *Client) WhoisChannelsNames(target *Client, multiPrefix bool) []string {
var chstrs []string var chstrs []string
targetInvis := target.HasMode(modes.Invisible)
for _, channel := range target.Channels() { for _, channel := range target.Channels() {
// channel is secret and the target can't see it if !hasPrivs && (targetInvis || channel.flags.HasMode(modes.Secret)) && !channel.hasClient(client) {
if !client.HasMode(modes.Operator) { // client can't see *this* channel membership
if (target.HasMode(modes.Invisible) || channel.flags.HasMode(modes.Secret)) && !channel.hasClient(client) {
continue continue
} }
}
chstrs = append(chstrs, channel.ClientPrefixes(target, multiPrefix)+channel.name) chstrs = append(chstrs, channel.ClientPrefixes(target, multiPrefix)+channel.name)
} }
return chstrs return chstrs
} }
func (client *Client) getWhoisOf(target *Client, hasPrivs bool, rb *ResponseBuffer) { func (client *Client) getWhoisOf(target *Client, hasPrivs bool, rb *ResponseBuffer) {
oper := client.Oper()
cnick := client.Nick() cnick := client.Nick()
targetInfo := target.Details() targetInfo := target.Details()
rb.Add(nil, client.server.name, RPL_WHOISUSER, cnick, targetInfo.nick, targetInfo.username, targetInfo.hostname, "*", targetInfo.realname) rb.Add(nil, client.server.name, RPL_WHOISUSER, cnick, targetInfo.nick, targetInfo.username, targetInfo.hostname, "*", targetInfo.realname)
tnick := targetInfo.nick tnick := targetInfo.nick
whoischannels := client.WhoisChannelsNames(target, rb.session.capabilities.Has(caps.MultiPrefix)) whoischannels := client.whoisChannelsNames(target, rb.session.capabilities.Has(caps.MultiPrefix), oper.HasRoleCapab("sajoin"))
if whoischannels != nil { if whoischannels != nil {
rb.Add(nil, client.server.name, RPL_WHOISCHANNELS, cnick, tnick, strings.Join(whoischannels, " ")) rb.Add(nil, client.server.name, RPL_WHOISCHANNELS, cnick, tnick, strings.Join(whoischannels, " "))
} }
if target.HasMode(modes.Operator) && operStatusVisible(client, target, hasPrivs) { if target.HasMode(modes.Operator) && operStatusVisible(client, target, oper != nil) {
tOper := target.Oper() tOper := target.Oper()
if tOper != nil { if tOper != nil {
rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine) rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine)
} }
} }
if client == target || hasPrivs { if client == target || oper.HasRoleCapab("ban") {
rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, cnick, tnick, fmt.Sprintf("%s@%s", targetInfo.username, target.RawHostname()), target.IPString(), client.t("Actual user@host, Actual IP")) rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, cnick, tnick, fmt.Sprintf("%s@%s", targetInfo.username, target.RawHostname()), target.IPString(), client.t("Actual user@host, Actual IP"))
}
if client == target || oper.HasRoleCapab("samode") {
rb.Add(nil, client.server.name, RPL_WHOISMODES, cnick, tnick, fmt.Sprintf(client.t("is using modes +%s"), target.modes.String())) rb.Add(nil, client.server.name, RPL_WHOISMODES, cnick, tnick, fmt.Sprintf(client.t("is using modes +%s"), target.modes.String()))
} }
if target.HasMode(modes.TLS) { if target.HasMode(modes.TLS) {
@ -504,7 +505,7 @@ func (client *Client) getWhoisOf(target *Client, hasPrivs bool, rb *ResponseBuff
rb.Add(nil, client.server.name, RPL_WHOISBOT, cnick, tnick, fmt.Sprintf(ircfmt.Unescape(client.t("is a $bBot$b on %s")), client.server.Config().Network.Name)) rb.Add(nil, client.server.name, RPL_WHOISBOT, cnick, tnick, fmt.Sprintf(ircfmt.Unescape(client.t("is a $bBot$b on %s")), client.server.Config().Network.Name))
} }
if client == target || hasPrivs { if client == target || oper.HasRoleCapab("ban") {
for _, session := range target.Sessions() { for _, session := range target.Sessions() {
if session.certfp != "" { if session.certfp != "" {
rb.Add(nil, client.server.name, RPL_WHOISCERTFP, cnick, tnick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), session.certfp)) rb.Add(nil, client.server.name, RPL_WHOISCERTFP, cnick, tnick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), session.certfp))

View File

@ -565,6 +565,7 @@ oper-classes:
- "vhosts" - "vhosts"
- "sajoin" - "sajoin"
- "samode" - "samode"
- "snomasks"
# server admin: has full control of the ircd, including nickname and # server admin: has full control of the ircd, including nickname and
# channel registrations # channel registrations