Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion irc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ func (client *Client) applyPreregMetadata(session *Session) {
return
}

// TODO this is expensive
// note: this is expensive but it's comparable to destroy(), OK to do once per session
friends := client.FriendsMonitors(caps.Metadata)
for _, s := range client.Sessions() {
if s != session {
Expand Down
29 changes: 29 additions & 0 deletions irc/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,23 @@ func (channel *Channel) GetMetadata(key string) (string, bool) {
return val, ok
}

type MetadataPair struct {
Key string
Value string
}

func (channel *Channel) GetMetadataBulk(keys []string) (result []MetadataPair) {
channel.stateMutex.RLock()
defer channel.stateMutex.RUnlock()

for _, k := range keys {
if val, ok := channel.metadata[k]; ok {
result = append(result, MetadataPair{Key: k, Value: val})
}
}
return result
}

func (channel *Channel) SetMetadata(key string, value string, limit int) (updated bool, err error) {
defer channel.MarkDirty(IncludeAllAttrs)

Expand Down Expand Up @@ -962,6 +979,18 @@ func (client *Client) GetMetadata(key string) (string, bool) {
return val, ok
}

func (client *Client) GetMetadataBulk(keys []string) (result []MetadataPair) {
client.stateMutex.RLock()
defer client.stateMutex.RUnlock()

for _, k := range keys {
if val, ok := client.metadata[k]; ok {
result = append(result, MetadataPair{Key: k, Value: val})
}
}
return result
}

func (client *Client) SetMetadata(key string, value string, limit int) (updated bool, err error) {
var alwaysOn bool
defer func() {
Expand Down
6 changes: 6 additions & 0 deletions irc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3375,6 +3375,12 @@ func metadataSubsHandler(client *Client, subcommand string, params []string, rb
rb.Add(nil, server.name, RPL_METADATASUBOK, params...)
}

if client.registered && len(added) != 0 {
if throttled, _ := client.checkMetadataThrottle(); !throttled {
processMetadataNewSubscriptions(client, rb, added)
}
}

case "unsub":
keys := params[2:]
removed := rb.session.UnsubscribeFrom(keys...)
Expand Down
37 changes: 36 additions & 1 deletion irc/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/ergochat/ergo/irc/caps"
"github.com/ergochat/ergo/irc/modes"
"github.com/ergochat/ergo/irc/utils"
)

const (
Expand All @@ -35,7 +36,6 @@ func notifySubscribers(server *Server, session *Session, targetObj MetadataHaver

switch target := targetObj.(type) {
case *Client:
// TODO this case is expensive and might warrant rate-limiting
friends := target.FriendsMonitors(caps.Metadata)
// broadcast metadata update to other connected sessions
for _, s := range target.Sessions() {
Expand Down Expand Up @@ -126,6 +126,41 @@ func playMetadataVerbBatch(rb *ResponseBuffer, target string, values map[string]
}
}

func processMetadataNewSubscriptions(client *Client, rb *ResponseBuffer, subs []string) {
// "When subscribing to a key, clients SHOULD receive the current value
// of that key for channels/users they are receiving updates for."
// note that this is expensive because we need to compute the friends
visibility := "*"
friendsSeen := make(utils.HashSet[*Client])
for _, channel := range client.Channels() {
chname := channel.Name()
for _, pair := range channel.GetMetadataBulk(subs) {
rb.Add(nil, client.server.name, "METADATA", chname, pair.Key, visibility, pair.Value)
}
// TODO this needs to respect auditorium!
for _, friend := range channel.Members() {
if friendsSeen.Has(friend) {
continue
}
friendsSeen.Add(friend)
for _, pair := range friend.GetMetadataBulk(subs) {
rb.Add(nil, client.server.name, "METADATA", friend.Nick(), pair.Key, visibility, pair.Value)
}
}
}

for _, friendNick := range client.server.monitorManager.List(rb.session) {
friend := client.server.clients.Get(friendNick)
if friend == nil || friendsSeen.Has(friend) {
continue
}
friendsSeen.Add(friend)
for _, pair := range friend.GetMetadataBulk(subs) {
rb.Add(nil, client.server.name, "METADATA", friend.Nick(), pair.Key, visibility, pair.Value)
}
}
}

var validMetadataKeyRegexp = regexp.MustCompile("^[a-z0-9_./-]+$")

func metadataKeyIsEvil(key string) bool {
Expand Down