~samwhited/xmpp

xmpp/stanza/stanza.go -rw-r--r-- 3.0 KiB
70cfc50dSam Whited internal/pubsub: fix publish payload 3 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Copyright 2020 The Mellium Contributors.
// Use of this source code is governed by the BSD 2-clause
// license that can be found in the LICENSE file.

package stanza

import (
	"encoding/xml"

	"mellium.im/xmlstream"
	"mellium.im/xmpp/internal/attr"
	"mellium.im/xmpp/jid"
)

// Namespaces used by this package, provided as a convenience.
const (
	// The namespace for unique and stable stanza and origin IDs.
	NSSid = "urn:xmpp:sid:0"

	// The namespace for delayed delivery.
	NSDelay = "urn:xmpp:delay"
)

const idLen = 32

// ID is a unique and stable stanza ID.
type ID struct {
	XMLName xml.Name `xml:"urn:xmpp:sid:0 stanza-id"`
	ID      string   `xml:"id,attr"`
	By      jid.JID  `xml:"by,attr"`
}

// TokenReader implements xmlstream.Marshaler.
func (id ID) TokenReader() xml.TokenReader {
	return xmlstream.Wrap(nil, xml.StartElement{
		Name: xml.Name{Space: NSSid, Local: "stanza-id"},
		Attr: []xml.Attr{
			{Name: xml.Name{Local: "id"}, Value: id.ID},
			{Name: xml.Name{Local: "by"}, Value: id.By.String()},
		},
	})
}

// WriteXML implements xmlstream.WriterTo.
func (id ID) WriteXML(w xmlstream.TokenWriter) (int, error) {
	return xmlstream.Copy(w, id.TokenReader())
}

// OriginID is a unique and stable stanza ID generated by an originating entity
// that may want to hide its identity.
type OriginID struct {
	XMLName xml.Name `xml:"urn:xmpp:sid:0 origin-id"`
	ID      string   `xml:"id,attr"`
}

// TokenReader implements xmlstream.Marshaler.
func (id OriginID) TokenReader() xml.TokenReader {
	return xmlstream.Wrap(nil, xml.StartElement{
		Name: xml.Name{Space: NSSid, Local: "origin-id"},
		Attr: []xml.Attr{
			{Name: xml.Name{Local: "id"}, Value: id.ID},
		},
	})
}

// WriteXML implements xmlstream.WriterTo.
func (id OriginID) WriteXML(w xmlstream.TokenWriter) (int, error) {
	return xmlstream.Copy(w, id.TokenReader())
}

// Is tests whether name is a valid stanza based on the localname and namespace.
// If stanzaNS is the empty string, Is matches any namespace.
func Is(name xml.Name, stanzaNS string) bool {
	return (name.Local == "iq" || name.Local == "message" || name.Local == "presence") && (stanzaNS == "" || name.Space == stanzaNS)
}

// AddID returns an transformer that adds a random stanza ID to any stanzas that
// does not already have one.
func AddID(by jid.JID, stanzaNS string) xmlstream.Transformer {
	return xmlstream.InsertFunc(func(start xml.StartElement, level uint64, w xmlstream.TokenWriter) error {
		if Is(start.Name, stanzaNS) && level == 1 {
			_, err := ID{
				ID: attr.RandomLen(idLen),
				By: by,
			}.WriteXML(w)
			return err
		}
		return nil
	})
}

// AddOriginID is an xmlstream.Transformer that adds an origin ID to any stanzas
// found in the input stream.
func AddOriginID(r xml.TokenReader, stanzaNS string) xml.TokenReader {
	return xmlstream.InsertFunc(func(start xml.StartElement, level uint64, w xmlstream.TokenWriter) error {
		if Is(start.Name, stanzaNS) && level == 1 {
			_, err := OriginID{
				ID: attr.RandomLen(idLen),
			}.WriteXML(w)
			return err
		}
		return nil
	})(r)
}