3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-11 06:29:29 +01:00
This commit is contained in:
Shivaram Lingamneni 2020-10-02 08:13:52 -04:00
parent 4a7ca14bdc
commit 8c99dcb2c7
6 changed files with 102 additions and 30 deletions

View File

@ -439,8 +439,12 @@ func (channel *Channel) regenerateMembersCache() {
// Names sends the list of users joined to the channel to the given client. // Names sends the list of users joined to the channel to the given client.
func (channel *Channel) Names(client *Client, rb *ResponseBuffer) { func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
isJoined := channel.hasClient(client) channel.stateMutex.RLock()
clientModes, isJoined := channel.members[client]
channel.stateMutex.RUnlock()
isOper := client.HasMode(modes.Operator) isOper := client.HasMode(modes.Operator)
respectAuditorium := channel.flags.HasMode(modes.Auditorium) && !isOper &&
(!isJoined || clientModes.HighestChannelUserMode() == modes.Mode(0))
isMultiPrefix := rb.session.capabilities.Has(caps.MultiPrefix) isMultiPrefix := rb.session.capabilities.Has(caps.MultiPrefix)
isUserhostInNames := rb.session.capabilities.Has(caps.UserhostInNames) isUserhostInNames := rb.session.capabilities.Has(caps.UserhostInNames)
@ -464,6 +468,9 @@ func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
if !isJoined && target.HasMode(modes.Invisible) && !isOper { if !isJoined && target.HasMode(modes.Invisible) && !isOper {
continue continue
} }
if respectAuditorium && modeSet.HighestChannelUserMode() == modes.Mode(0) {
continue
}
prefix := modeSet.Prefixes(isMultiPrefix) prefix := modeSet.Prefixes(isMultiPrefix)
if buffer.Len()+len(nick)+len(prefix)+1 > maxNamLen { if buffer.Len()+len(nick)+len(prefix)+1 > maxNamLen {
namesLines = append(namesLines, buffer.String()) namesLines = append(namesLines, buffer.String())
@ -748,8 +755,9 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
}() }()
var message utils.SplitMessage var message utils.SplitMessage
respectAuditorium := givenMode == modes.Mode(0) && channel.flags.HasMode(modes.Auditorium)
// no history item for fake persistent joins // no history item for fake persistent joins
if rb != nil { if rb != nil && !respectAuditorium {
message = utils.MakeMessage("") message = utils.MakeMessage("")
histItem := history.Item{ histItem := history.Item{
Type: history.Join, Type: history.Join,
@ -772,6 +780,14 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
isAway, awayMessage := client.Away() isAway, awayMessage := client.Away()
for _, member := range channel.Members() { for _, member := range channel.Members() {
if respectAuditorium {
channel.stateMutex.RLock()
memberModes, ok := channel.members[member]
channel.stateMutex.RUnlock()
if !ok || memberModes.HighestChannelUserMode() == modes.Mode(0) {
continue
}
}
for _, session := range member.Sessions() { for _, session := range member.Sessions() {
if session == rb.session { if session == rb.session {
continue continue
@ -889,8 +905,12 @@ func (channel *Channel) playJoinForSession(session *Session) {
// Part parts the given client from this channel, with the given message. // Part parts the given client from this channel, with the given message.
func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer) { func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer) {
chname := channel.Name() channel.stateMutex.RLock()
if !channel.hasClient(client) { chname := channel.name
clientModes, ok := channel.members[client]
channel.stateMutex.RUnlock()
if !ok {
rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), chname, client.t("You're not on that channel")) rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), chname, client.t("You're not on that channel"))
return return
} }
@ -905,7 +925,17 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
if message != "" { if message != "" {
params = append(params, message) params = append(params, message)
} }
respectAuditorium := channel.flags.HasMode(modes.Auditorium) &&
clientModes.HighestChannelUserMode() == modes.Mode(0)
for _, member := range channel.Members() { for _, member := range channel.Members() {
if respectAuditorium {
channel.stateMutex.RLock()
memberModes, ok := channel.members[member]
channel.stateMutex.RUnlock()
if !ok || memberModes.HighestChannelUserMode() == modes.Mode(0) {
continue
}
}
member.sendFromClientInternal(false, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, nil, "PART", params...) member.sendFromClientInternal(false, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, nil, "PART", params...)
} }
rb.AddFromClient(splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, nil, "PART", params...) rb.AddFromClient(splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, nil, "PART", params...)
@ -915,12 +945,14 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
} }
} }
if !respectAuditorium {
channel.AddHistoryItem(history.Item{ channel.AddHistoryItem(history.Item{
Type: history.Part, Type: history.Part,
Nick: details.nickMask, Nick: details.nickMask,
AccountName: details.accountName, AccountName: details.accountName,
Message: splitMessage, Message: splitMessage,
}, details.account) }, details.account)
}
client.server.logger.Debug("part", fmt.Sprintf("%s left channel %s", details.nick, chname)) client.server.logger.Debug("part", fmt.Sprintf("%s left channel %s", details.nick, chname))
} }
@ -951,6 +983,9 @@ func (channel *Channel) resumeAndAnnounce(session *Session) {
// send join for old clients // send join for old clients
chname := channel.Name() chname := channel.Name()
details := session.client.Details() details := session.client.Details()
// TODO: for now, skip this entirely for auditoriums,
// but really we should send it to voiced clients
if !channel.flags.HasMode(modes.Auditorium) {
for _, member := range channel.Members() { for _, member := range channel.Members() {
for _, session := range member.Sessions() { for _, session := range member.Sessions() {
if session.capabilities.Has(caps.Resume) { if session.capabilities.Has(caps.Resume) {
@ -968,6 +1003,7 @@ func (channel *Channel) resumeAndAnnounce(session *Session) {
} }
} }
} }
}
rb := NewResponseBuffer(session) rb := NewResponseBuffer(session)
// use blocking i/o to synchronize with the later history replay // use blocking i/o to synchronize with the later history replay
@ -1451,6 +1487,30 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
} }
} }
// returns who the client can "see" in the channel, respecting the auditorium mode
func (channel *Channel) auditoriumFriends(client *Client) (friends []*Client) {
channel.stateMutex.RLock()
defer channel.stateMutex.RUnlock()
clientModes := channel.members[client]
if clientModes == nil {
return // non-members have no friends
}
if !channel.flags.HasMode(modes.Auditorium) {
return channel.membersCache // default behavior for members
}
if clientModes.HighestChannelUserMode() != modes.Mode(0) {
return channel.membersCache // +v and up can see everyone in the auditorium
}
// without +v, your friends are those with +v and up
for member, memberModes := range channel.members {
if memberModes.HighestChannelUserMode() != modes.Mode(0) {
friends = append(friends, member)
}
}
return
}
// data for RPL_LIST // data for RPL_LIST
func (channel *Channel) listData() (memberCount int, name, topic string) { func (channel *Channel) listData() (memberCount int, name, topic string) {
channel.stateMutex.RLock() channel.stateMutex.RLock()

View File

@ -934,7 +934,7 @@ func (session *Session) playResume() {
// work out how much time, if any, is not covered by history buffers // work out how much time, if any, is not covered by history buffers
// assume that a persistent buffer covers the whole resume period // assume that a persistent buffer covers the whole resume period
for _, channel := range client.Channels() { for _, channel := range client.Channels() {
for _, member := range channel.Members() { for _, member := range channel.auditoriumFriends(client) {
friends.Add(member) friends.Add(member)
} }
status, _ := channel.historyStatus(config) status, _ := channel.historyStatus(config)
@ -1161,7 +1161,7 @@ func (client *Client) Friends(capabs ...caps.Capability) (result map[*Session]em
addFriendsToSet(result, client, capabs...) addFriendsToSet(result, client, capabs...)
for _, channel := range client.Channels() { for _, channel := range client.Channels() {
for _, member := range channel.Members() { for _, member := range channel.auditoriumFriends(client) {
addFriendsToSet(result, member, capabs...) addFriendsToSet(result, member, capabs...)
} }
} }
@ -1512,10 +1512,10 @@ func (client *Client) destroy(session *Session) {
friends := make(ClientSet) friends := make(ClientSet)
channels = client.Channels() channels = client.Channels()
for _, channel := range channels { for _, channel := range channels {
channel.Quit(client) for _, member := range channel.auditoriumFriends(client) {
for _, member := range channel.Members() {
friends.Add(member) friends.Add(member)
} }
channel.Quit(client)
} }
friends.Remove(client) friends.Remove(client)

View File

@ -3051,7 +3051,13 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
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 || isOper {
for _, member := range channel.Members() { var members []*Client
if isOper {
members = channel.Members()
} else {
members = channel.auditoriumFriends(client)
}
for _, member := range members {
if !member.HasMode(modes.Invisible) || isJoined || isOper { if !member.HasMode(modes.Invisible) || isJoined || isOper {
client.rplWhoReply(channel, member, rb, isWhox, fields, whoType) client.rplWhoReply(channel, member, rb, isWhox, fields, whoType)
} }
@ -3072,6 +3078,9 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
} }
for _, channel := range otherClient.Channels() { for _, channel := range otherClient.Channels() {
if channel.flags.HasMode(modes.Auditorium) {
return false // TODO this should respect +v etc.
}
if _, present := userChannels[channel]; present { if _, present := userChannels[channel]; present {
return true return true
} }

View File

@ -54,6 +54,8 @@ Oragono supports the following channel modes:
+t | Only channel opers can modify the topic. +t | Only channel opers can modify the topic.
+E | Roleplaying commands are enabled in the channel. +E | Roleplaying commands are enabled in the channel.
+C | Clients are blocked from sending CTCP messages in the channel. +C | Clients are blocked from sending CTCP messages in the channel.
+u | Auditorium mode: JOIN, PART, QUIT, NAMES, and WHO are hidden
hidden from unvoiced clients.
= Prefixes = = Prefixes =
@ -74,7 +76,7 @@ Oragono supports the following user modes:
+Z | User is connected via TLS. +Z | User is connected via TLS.
+B | User is a bot. +B | User is a bot.
+E | User can receive roleplaying commands. +E | User can receive roleplaying commands.
+T | User is blocked from sending CTCP messages.` +T | CTCP messages to the user are blocked.`
snomaskHelpText = `== Server Notice Masks == snomaskHelpText = `== Server Notice Masks ==
Oragono supports the following server notice masks for operators: Oragono supports the following server notice masks for operators:

View File

@ -23,7 +23,7 @@ var (
SupportedChannelModes = Modes{ SupportedChannelModes = Modes{
BanMask, ChanRoleplaying, ExceptMask, InviteMask, InviteOnly, Key, BanMask, ChanRoleplaying, ExceptMask, InviteMask, InviteOnly, Key,
Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, RegisteredOnlySpeak, Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, RegisteredOnlySpeak,
Secret, UserLimit, NoCTCP, Secret, UserLimit, NoCTCP, Auditorium,
} }
) )
@ -113,6 +113,7 @@ const (
// Channel Modes // Channel Modes
const ( const (
Auditorium Mode = 'u' // flag
BanMask Mode = 'b' // arg BanMask Mode = 'b' // arg
ChanRoleplaying Mode = 'E' // flag ChanRoleplaying Mode = 'E' // flag
ExceptMask Mode = 'e' // arg ExceptMask Mode = 'e' // arg

@ -1 +1 @@
Subproject commit 0287b837971c27ee55bc4dca95d31afc68d2aeea Subproject commit 616785eae403536954f1b1181f74ef51343e33f7