114 lines
3.3 KiB
Go
114 lines
3.3 KiB
Go
// 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"
|
|
|
|
// The namespace used by stanza errors.
|
|
NSError = "urn:ietf:params:xml:ns:xmpp-stanzas"
|
|
|
|
// The namespace used by stanzas in the client-to-server protocol.
|
|
NSClient = "jabber:client"
|
|
|
|
// The namespace used by stanzas in the server-to-server protocol.
|
|
NSServer = "jabber:server"
|
|
)
|
|
|
|
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)
|
|
}
|