~samwhited/xmpp

68530b00abfaf0635dc617ee2c735251e2060a96 — Sam Whited 4 months ago e8dab77
xmpp: return start token for iter IQ payloads

Previously the start token was discarded when calling IterIQ or
IterIQElement. However, we may need attributes from it, making IterIQ
impossible to use for some cases where we still might want an easy way
to iterate. Returning the start element fixes this and avoids a lot of
boilerplate re-implementing iter IQ for every place we might want to do
this.

Signed-off-by: Sam Whited <sam@samwhited.com>
4 files changed, 17 insertions(+), 12 deletions(-)

M CHANGELOG.md
M blocklist/blocking.go
M disco/items.go
M session_iq.go
M CHANGELOG.md => CHANGELOG.md +2 -0
@@ 10,6 10,8 @@ All notable changes to this project will be documented in this file.
- styling: decoding tokens now uses an iterator pattern
- xmpp: the `WebSocket` option on `StreamConfig` has been removed in favor of
  `websocket.Negotiator`
- xmpp: the `IterIQ` and `IterIQElement` methods on `Session` now return the
  start element token associated with the IQ payload


### Security

M blocklist/blocking.go => blocklist/blocking.go +1 -1
@@ 98,7 98,7 @@ func FetchIQ(ctx context.Context, iq stanza.IQ, s *xmpp.Session) *Iter {
	if iq.Type != stanza.GetIQ {
		iq.Type = stanza.GetIQ
	}
	iter, err := s.IterIQ(ctx, iq.Wrap(xmlstream.Wrap(nil, xml.StartElement{
	iter, _, err := s.IterIQ(ctx, iq.Wrap(xmlstream.Wrap(nil, xml.StartElement{
		Name: xml.Name{Space: NS, Local: "blocklist"},
	})))
	if err != nil {

M disco/items.go => disco/items.go +1 -1
@@ 167,7 167,7 @@ func FetchItemsIQ(ctx context.Context, node string, iq stanza.IQ, s *xmpp.Sessio
	query := ItemsQuery{
		Node: node,
	}
	iter, err := s.IterIQ(ctx, iq.Wrap(query.TokenReader()))
	iter, _, err := s.IterIQ(ctx, iq.Wrap(query.TokenReader()))
	if err != nil {
		return &ItemIter{err: err}
	}

M session_iq.go => session_iq.go +13 -10
@@ 130,7 130,7 @@ func (s *Session) UnmarshalIQElement(ctx context.Context, payload xml.TokenReade
// For more information see SendIQ.
//
// IterIQ is safe for concurrent use by multiple goroutines.
func (s *Session) IterIQ(ctx context.Context, iq xml.TokenReader) (*xmlstream.Iter, error) {
func (s *Session) IterIQ(ctx context.Context, iq xml.TokenReader) (*xmlstream.Iter, *xml.StartElement, error) {
	return iterIQ(ctx, iq, s)
}



@@ 138,14 138,14 @@ func (s *Session) IterIQ(ctx context.Context, iq xml.TokenReader) (*xmlstream.It
// For more information see SendIQ.
//
// IterIQElement is safe for concurrent use by multiple goroutines.
func (s *Session) IterIQElement(ctx context.Context, payload xml.TokenReader, iq stanza.IQ) (*xmlstream.Iter, error) {
func (s *Session) IterIQElement(ctx context.Context, payload xml.TokenReader, iq stanza.IQ) (*xmlstream.Iter, *xml.StartElement, error) {
	return iterIQ(ctx, iq.Wrap(payload), s)
}

func iterIQ(ctx context.Context, iq xml.TokenReader, s *Session) (_ *xmlstream.Iter, e error) {
func iterIQ(ctx context.Context, iq xml.TokenReader, s *Session) (_ *xmlstream.Iter, _ *xml.StartElement, e error) {
	resp, err := s.SendIQ(ctx, iq)
	if err != nil {
		return nil, err
		return nil, nil, err
	}
	defer func() {
		if e != nil {


@@ 156,24 156,27 @@ func iterIQ(ctx context.Context, iq xml.TokenReader, s *Session) (_ *xmlstream.I

	tok, err := resp.Token()
	if err != nil {
		return nil, err
		return nil, nil, err
	}

	start, ok := tok.(xml.StartElement)
	if !ok {
		return nil, fmt.Errorf("stanza: expected IQ start token, got %T %[1]v", tok)
		return nil, nil, fmt.Errorf("stanza: expected IQ start token, got %T %[1]v", tok)
	}
	_, err = stanza.UnmarshalIQError(resp, start)
	if err != nil {
		return nil, err
		return nil, nil, err
	}

	// Pop the payload start token, we want to iterate over its children.
	_, err = resp.Token()
	tok, err = resp.Token()
	start, _ = tok.(xml.StartElement)

	// Discard early EOF so that the iterator doesn't end up returning it.
	if err != nil && err != io.EOF {
		return nil, err
		return nil, nil, err
	}
	return xmlstream.NewIter(resp), nil
	return xmlstream.NewIter(resp), &start, nil
}

func unmarshalIQ(ctx context.Context, iq xml.TokenReader, v interface{}, s *Session) (e error) {