<commit_before>package client

// TODO implement roster versioning
// TODO implement pre-approval
// TODO handle a roster, that keeps track of presence, the contacts
// who are in it, etc

import (
	"encoding/xml"
	"github.com/davecgh/go-spew/spew"
	"honnef.co/go/xmpp/rfc6120/client"
	"sync"
)

var _ = spew.Dump

type Connection struct {
	*client.Connection
	stanzas            chan client.Stanza
	messageSubscribers messageSubscribers
}

type messageSubscribers struct {
	sync.RWMutex
	chans []chan<- *client.Message
}

func (s *messageSubscribers) send(stanza *client.Message) {
	s.RLock()
	defer s.RUnlock()
	for _, ch := range s.chans {
		select {
		case ch <- stanza:
		default:
		}
	}
}

func (s *messageSubscribers) subscribe(ch chan<- *client.Message) {
	s.Lock()
	defer s.Unlock()
	s.chans = append(s.chans, ch)
}

func (c *Connection) SubscribeMessages(ch chan<- *client.Message) {
	c.messageSubscribers.subscribe(ch)
}

func Wrap(c *client.Connection) *Connection {
	conn := &Connection{
		Connection: c,
		stanzas:    make(chan client.Stanza, 100),
	}
	go conn.read()
	c.SubscribeStanzas(conn.stanzas)
	return conn
}

func (c *Connection) read() {
	for stanza := range c.stanzas {
		// TODO way to subscribe to roster events (roster push, subscription requests, ...)
		switch t := stanza.(type) {
		case *client.IQ:
			if t.Query.Space == "jabber:iq:roster" && t.Type == "set" {
				// TODO check 'from' ("Security Warning:
				// Traditionally, a roster push included no 'from'
				// address")
				c.SendIQReply("", "result", stanza.ID(), nil)
			}
		case *client.Message:
			// TODO track JID etc
			c.messageSubscribers.send(t)
		}
	}
}

type Roster []RosterItem

type RosterItem struct {
	JID  string `xml:"jid,attr"`
	Name string `xml:"name,attr,omitempty"`
	// Groups []string // TODO
	Subscription string `xml:"subscription,attr,omitempty"`
}

type rosterQuery struct {
	XMLName xml.Name    `xml:"jabber:iq:roster query"`
	Item    *RosterItem `xml:"item,omitempty"`
}<commit_msg>don't have message subscribers but just more abstract stanza subscribers<commit_after>package client

// TODO implement roster versioning
// TODO implement pre-approval
// TODO handle a roster, that keeps track of presence, the contacts
// who are in it, etc

import (
	"encoding/xml"
	"github.com/davecgh/go-spew/spew"
	"honnef.co/go/xmpp/rfc6120/client"
	"sync"
)

var _ = spew.Dump

type Connection struct {
	*client.Connection
	stanzas     chan client.Stanza
	subscribers subscribers
}

// TODO this is an exact copy from rfc6120. DRY this.
type subscribers struct {
	sync.RWMutex
	chans []chan<- client.Stanza
}

func (s *subscribers) send(stanza client.Stanza) {
	s.RLock()
	defer s.RUnlock()
	for _, ch := range s.chans {
		select {
		case ch <- stanza:
		default:
		}
	}
}

func (s *subscribers) subscribe(ch chan<- client.Stanza) {
	s.Lock()
	defer s.Unlock()
	s.chans = append(s.chans, ch)
}

func (c *Connection) SubscribeStanzas(ch chan<- client.Stanza) {
	c.subscribers.subscribe(ch)
}

func Wrap(c *client.Connection) *Connection {
	conn := &Connection{
		Connection: c,
		stanzas:    make(chan client.Stanza, 100),
	}
	go conn.read()
	c.SubscribeStanzas(conn.stanzas)
	return conn
}

func (c *Connection) read() {
	for stanza := range c.stanzas {
		// TODO way to subscribe to roster events (roster push, subscription requests, ...)
		switch t := stanza.(type) {
		case *client.IQ:
			if t.Query.Space == "jabber:iq:roster" && t.Type == "set" {
				// TODO check 'from' ("Security Warning:
				// Traditionally, a roster push included no 'from'
				// address")
				c.SendIQReply("", "result", stanza.ID(), nil)
			}
		default:
			// TODO track JID etc
		}
		c.subscribers.send(stanza)
	}
}

type Roster []RosterItem

type RosterItem struct {
	JID  string `xml:"jid,attr"`
	Name string `xml:"name,attr,omitempty"`
	// Groups []string // TODO
	Subscription string `xml:"subscription,attr,omitempty"`
}

type rosterQuery struct {
	XMLName xml.Name    `xml:"jabber:iq:roster query"`
	Item    *RosterItem `xml:"item,omitempty"`
}
{'fraction_non_alphanumeric': 0.1368473150210344, 'fraction_numerical': 0.004454342984409799, 'mean_word_length': 3.6728323699421965, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3592443', 'n_tokens_mistral': 1562, 'n_tokens_neox': 1442, 'n_words': 382}