~samwhited/xmpp

70dcb72120efc65ec0e893f9e186e13cfe5cb8ff — Sam Whited 8 months ago ed13d6c s2s_package
xmpp: always set "from" on s2s stanzas

Per RFC6120 § 4.7.1

Signed-off-by: Sam Whited <sam@samwhited.com>
2 files changed, 66 insertions(+), 13 deletions(-)

M session.go
M session_test.go
M session.go => session.go +37 -12
@@ 185,7 185,11 @@ func NegotiateSession(ctx context.Context, location, origin jid.JID, rw io.ReadW
	}

	s.in.d = intstream.Reader(s.in.d)
	s.out.e = &stanzaEncoder{TokenWriteFlusher: s.out.e}
	se := &stanzaEncoder{TokenWriteFlusher: s.out.e}
	if s.state&S2S == S2S {
		se.from = s.LocalAddr()
	}
	s.out.e = se

	return s, nil
}


@@ 946,27 950,48 @@ func (s *Session) closeInputStream() {
type stanzaEncoder struct {
	xmlstream.TokenWriteFlusher
	depth int
	from  jid.JID
}

func (se *stanzaEncoder) EncodeToken(t xml.Token) error {
tokswitch:
	switch tok := t.(type) {
	case xml.StartElement:
		se.depth++
		// RFC6120 §8.1.3
		// For <message/> and <presence/> stanzas, it is RECOMMENDED for the
		// originating entity to include an 'id' attribute; for <iq/> stanzas, it is
		// REQUIRED.
		// Add required attributes if missing:
		if se.depth == 1 && isStanzaEmptySpace(tok.Name) {
			var foundID, foundFrom bool
			for _, attr := range tok.Attr {
				if attr.Name.Local == "id" {
					break tokswitch
				switch attr.Name.Local {
				case "id":
					// RFC6120 § 8.1.3
					// For <message/> and <presence/> stanzas, it is RECOMMENDED for the
					// originating entity to include an 'id' attribute; for <iq/> stanzas,
					// it is REQUIRED.
					foundID = true
				case "from":
					// RFC6120 § 4.7.1
					// the 'to' and 'from' attributes are OPTIONAL on stanzas sent over
					// XML streams qualified by the 'jabber:client' namespace, whereas
					// they are REQUIRED on stanzas sent over XML streams qualified by the
					// 'jabber: server' namespace
					foundFrom = true
				}
				if foundID && foundFrom {
					break
				}
			}
			tok.Attr = append(tok.Attr, xml.Attr{
				Name:  xml.Name{Local: "id"},
				Value: attr.RandomID(),
			})
			if f := se.from.String(); f != "" && !foundFrom {
				tok.Attr = append(tok.Attr, xml.Attr{
					Name:  xml.Name{Local: "from"},
					Value: se.from.String(),
				})
			}
			if !foundID {
				tok.Attr = append(tok.Attr, xml.Attr{
					Name:  xml.Name{Local: "id"},
					Value: attr.RandomID(),
				})
			}
			t = tok
		}
	case xml.EndElement:

M session_test.go => session_test.go +29 -1
@@ 172,6 172,7 @@ var serveTests = [...]struct {
	in           string
	err          error
	errStringCmp bool
	state        xmpp.SessionState
}{
	0: {
		in:  `<test></test>`,


@@ 324,6 325,33 @@ var serveTests = [...]struct {
		in:  `<a>test</a><b></b>`,
		out: `<a xmlns="jabber:client">test</a></stream:stream>`,
	},
	15: {
		// S2S stanzas always have "from" set if not already set.
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
			_, err := xmlstream.Copy(rw, stanza.IQ{
				ID:   "1234",
				Type: stanza.ResultIQ,
			}.Wrap(nil))
			return err
		}),
		in:    `<iq type="get" id="1234"><unknownpayload xmlns="unknown"/></iq>`,
		out:   `<iq type="result" id="1234" from="test@example.net"></iq></stream:stream>`,
		state: xmpp.S2S,
	},
	16: {
		// S2S stanzas always have "from" set, unless it was already set.
		handler: xmpp.HandlerFunc(func(rw xmlstream.TokenReadEncoder, start *xml.StartElement) error {
			_, err := xmlstream.Copy(rw, stanza.IQ{
				ID:   "1234",
				From: jid.MustParse("from@example.net"),
				Type: stanza.ResultIQ,
			}.Wrap(nil))
			return err
		}),
		in:    `<iq type="get" id="1234"><unknownpayload xmlns="unknown"/></iq>`,
		out:   `<iq type="result" from="from@example.net" id="1234"></iq></stream:stream>`,
		state: xmpp.S2S,
	},
}

func TestServe(t *testing.T) {


@@ 331,7 359,7 @@ func TestServe(t *testing.T) {
		t.Run(strconv.Itoa(i), func(t *testing.T) {
			out := &bytes.Buffer{}
			in := strings.NewReader(tc.in)
			s := xmpptest.NewSession(0, struct {
			s := xmpptest.NewSession(tc.state, struct {
				io.Reader
				io.Writer
			}{