~samwhited/xmpp

ec3d2a5b04c538fd4145510470f65afb224870fb — Sam Whited 2 months ago e80ae30
muc: allow re-syncing rooms

Signed-off-by: Sam Whited <sam@samwhited.com>
3 files changed, 86 insertions(+), 49 deletions(-)

M muc/muc.go
M muc/options.go
M muc/room.go
M muc/muc.go => muc/muc.go +2 -49
@@ 13,7 13,6 @@ import (
	"mellium.im/xmlstream"
	"mellium.im/xmpp"
	"mellium.im/xmpp/form"
	"mellium.im/xmpp/internal/attr"
	"mellium.im/xmpp/jid"
	"mellium.im/xmpp/mux"
	"mellium.im/xmpp/stanza"


@@ 198,13 197,6 @@ func (c *Client) Join(ctx context.Context, room jid.JID, s *xmpp.Session, opt ..
// presence.
// Changing the presence type has no effect.
func (c *Client) JoinPresence(ctx context.Context, p stanza.Presence, s *xmpp.Session, opt ...Option) (*Channel, error) {
	if p.Type != "" {
		p.Type = ""
	}
	if p.ID == "" {
		p.ID = attr.RandomID()
	}

	c.managedM.Lock()

	channel := &Channel{


@@ 221,45 213,6 @@ func (c *Client) JoinPresence(ctx context.Context, p stanza.Presence, s *xmpp.Se
	c.managed[p.To.String()] = channel
	c.managedM.Unlock()

	conf := config{}
	for _, o := range opt {
		o(&conf)
	}
	channel.pass = conf.password

	errChan := make(chan error)
	go func(errChan chan<- error) {
		resp, err := s.SendPresenceElement(ctx, conf.TokenReader(), p)
		//err := s.Send(ctx, p.Wrap(conf.TokenReader()))
		if err != nil {
			errChan <- err
			return
		}
		/* #nosec */
		defer resp.Close()
		// Pop the start presence token.
		_, err = resp.Token()
		if err != nil {
			errChan <- err
			return
		}

		stanzaError, err := stanza.UnmarshalError(resp)
		if err != nil {
			errChan <- err
			return
		}
		errChan <- stanzaError
	}(errChan)

	select {
	case err := <-errChan:
		return nil, err
	case roomAddr := <-channel.join:
		channel.addr = roomAddr
	case <-ctx.Done():
		return nil, ctx.Err()
	}

	return channel, nil
	err := channel.JoinPresence(ctx, p, opt...)
	return channel, err
}

M muc/options.go => muc/options.go +15 -0
@@ 72,6 72,7 @@ func (h historyConfig) TokenReader() xml.TokenReader {
type config struct {
	history  historyConfig
	password string
	newNick  string
}

// TokenReader satisfies the xmlstream.Marshaler interface.


@@ 184,3 185,17 @@ func Password(p string) Option {
		c.password = p
	}
}

// Nick overrides the resourcepart of the JID and sets a different nickname in
// the room.
//
// This is mostly useful if you want to change the nickname when re-joining a
// room (when the JID is already known and is not provided to the method) or
// when the room JID is known and you want to let this package handle any errors
// encountered when appending the nickname and reduce boilerplate in your own
// code.
func Nick(n string) Option {
	return func(c *config) {
		c.newNick = n
	}
}

M muc/room.go => muc/room.go +69 -0
@@ 155,3 155,72 @@ func (c *Channel) SetAffiliation(ctx context.Context, a Affiliation, j jid.JID, 
		To:   c.addr.Bare(),
	}, nil)
}

// Join is like the Join function except that it joins or re-synchronizes the
// current room.
// It is useful if somehow the room has become unsyncronized with the server or
// when you want to leave the room and join again later.
func (c *Channel) Join(ctx context.Context, opt ...Option) error {
	return c.JoinPresence(ctx, stanza.Presence{}, opt...)
}

// JoinPresence is like Join except that it gives you more control over the
// presence.
// Changing the presence type or to address has no effect.
func (c *Channel) JoinPresence(ctx context.Context, p stanza.Presence, opt ...Option) error {
	if p.Type != "" {
		p.Type = ""
	}
	if p.ID == "" {
		p.ID = attr.RandomID()
	}
	p.To = c.addr

	conf := config{}
	for _, o := range opt {
		o(&conf)
	}
	c.pass = conf.password
	if conf.newNick != "" {
		newAddr, err := c.addr.WithResource(conf.newNick)
		if err != nil {
			return err
		}
		c.addr = newAddr
	}

	errChan := make(chan error)
	go func(errChan chan<- error) {
		resp, err := c.session.SendPresenceElement(ctx, conf.TokenReader(), p)
		if err != nil {
			errChan <- err
			return
		}
		/* #nosec */
		defer resp.Close()
		// Pop the start presence token.
		_, err = resp.Token()
		if err != nil {
			errChan <- err
			return
		}

		stanzaError, err := stanza.UnmarshalError(resp)
		if err != nil {
			errChan <- err
			return
		}
		errChan <- stanzaError
	}(errChan)

	select {
	case err := <-errChan:
		return err
	case roomAddr := <-c.join:
		c.addr = roomAddr
	case <-ctx.Done():
		return ctx.Err()
	}

	return nil
}