Merge pull request #6 "Error parsing / generation"
This commit is contained in:
commit
adb14260f0
4
auth.go
4
auth.go
|
@ -104,13 +104,13 @@ type auth struct {
|
|||
Value string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
type bindBind struct {
|
||||
type BindBind struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
|
||||
Resource string `xml:"resource,omitempty"`
|
||||
Jid string `xml:"jid,omitempty"`
|
||||
}
|
||||
|
||||
func (*bindBind) IsIQPayload() {
|
||||
func (*BindBind) IsIQPayload() {
|
||||
}
|
||||
|
||||
// Session is obsolete in RFC 6121.
|
||||
|
|
|
@ -127,6 +127,7 @@ func (c *Client) Recv() <-chan interface{} {
|
|||
}
|
||||
|
||||
// Send sends message text.
|
||||
// TODO Move to Go XML Marshaller
|
||||
func (c *Client) Send(packet string) error {
|
||||
fmt.Fprintf(c.Session.socketProxy, packet) // TODO handle errors
|
||||
return nil
|
||||
|
|
|
@ -172,7 +172,7 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) {
|
|||
|
||||
// TODO Check all elements
|
||||
switch iq.Payload[0].(type) {
|
||||
case *bindBind:
|
||||
case *BindBind:
|
||||
result := `<iq id='%s' type='result'>
|
||||
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
|
||||
<jid>%s</jid>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
|
||||
"fluux.io/xmpp"
|
||||
|
@ -22,28 +21,28 @@ func main() {
|
|||
switch p := packet.(type) {
|
||||
case xmpp.IQ:
|
||||
switch inner := p.Payload[0].(type) {
|
||||
case *xmpp.Node:
|
||||
fmt.Printf("%q\n", inner)
|
||||
case *xmpp.DiscoInfo:
|
||||
fmt.Println("Disco Info")
|
||||
if p.Type == "get" {
|
||||
DiscoResult(component, p.From, p.To, p.Id)
|
||||
}
|
||||
|
||||
data, err := xml.Marshal(inner)
|
||||
if err != nil {
|
||||
fmt.Println("cannot marshall payload")
|
||||
}
|
||||
fmt.Println("data=", string(data))
|
||||
component.processIQ(p.Type, p.Id, p.From, inner)
|
||||
default:
|
||||
fmt.Println("default")
|
||||
fmt.Println("ignoring iq packet", inner)
|
||||
xerror := xmpp.Err{
|
||||
Code: 501,
|
||||
Reason: "feature-not-implemented",
|
||||
Type: "cancel",
|
||||
}
|
||||
reply := p.MakeError(xerror)
|
||||
component.xmpp.Send(&reply)
|
||||
}
|
||||
default:
|
||||
fmt.Println("Packet unhandled packet:", packet)
|
||||
fmt.Println("ignoring packet:", packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
NSDiscoInfo = "http://jabber.org/protocol/disco#info"
|
||||
)
|
||||
|
||||
type MyComponent struct {
|
||||
Name string
|
||||
// Typical categories and types: https://xmpp.org/registrar/disco-categories.html
|
||||
|
@ -53,34 +52,19 @@ type MyComponent struct {
|
|||
xmpp *xmpp.Component
|
||||
}
|
||||
|
||||
func (c MyComponent) processIQ(iqType, id, from string, inner *xmpp.Node) {
|
||||
fmt.Println("Node:", inner.XMLName.Space, inner.XMLName.Local)
|
||||
switch inner.XMLName.Space + " " + iqType {
|
||||
case NSDiscoInfo + " get":
|
||||
fmt.Println("Send Disco Info")
|
||||
result := fmt.Sprintf(`<iq type='result'
|
||||
from='%s'
|
||||
to='%s'
|
||||
id='%s'>
|
||||
<query xmlns='http://jabber.org/protocol/disco#info'>
|
||||
<identity
|
||||
category='%s'
|
||||
type='%s'
|
||||
name='%s'/>
|
||||
<feature var='http://jabber.org/protocol/disco#info'/>
|
||||
<feature var='http://jabber.org/protocol/disco#items'/>
|
||||
</query>
|
||||
</iq>`, c.xmpp.Host, from, id, c.Category, c.Type, c.Name)
|
||||
c.xmpp.Send(result)
|
||||
default:
|
||||
iqErr := fmt.Sprintf(`<iq type='error'
|
||||
from='%s'
|
||||
to='%s'
|
||||
id='%s'>
|
||||
<error type="cancel" code="501">
|
||||
<feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
|
||||
</error>
|
||||
</iq>`, c.xmpp.Host, from, id)
|
||||
c.xmpp.Send(iqErr)
|
||||
func DiscoResult(c MyComponent, from, to, id string) {
|
||||
iq := xmpp.NewIQ("result", to, from, id, "en")
|
||||
payload := xmpp.DiscoInfo{
|
||||
Identity: xmpp.Identity{
|
||||
Name: c.Name,
|
||||
Category: c.Category,
|
||||
Type: c.Type,
|
||||
},
|
||||
Features: []xmpp.Feature{
|
||||
{Var: "http://jabber.org/protocol/disco#info"},
|
||||
{Var: "http://jabber.org/protocol/disco#item"},
|
||||
},
|
||||
}
|
||||
iq.AddPayload(&payload)
|
||||
c.xmpp.Send(iq)
|
||||
}
|
||||
|
|
|
@ -94,8 +94,13 @@ func (c *Component) ReadPacket() (Packet, error) {
|
|||
return next(c.decoder)
|
||||
}
|
||||
|
||||
func (c *Component) Send(packet string) error {
|
||||
if _, err := fmt.Fprintf(c.conn, packet); err != nil {
|
||||
func (c *Component) Send(packet Packet) error {
|
||||
data, err := xml.Marshal(packet)
|
||||
if err != nil {
|
||||
return errors.New("cannot marshal packet " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprintf(c.conn, string(data)); err != nil {
|
||||
return errors.New("cannot send packet " + err.Error())
|
||||
}
|
||||
return nil
|
||||
|
|
245
iq.go
245
iq.go
|
@ -4,32 +4,156 @@ import (
|
|||
"encoding/xml"
|
||||
"fmt"
|
||||
|
||||
"reflect"
|
||||
|
||||
"strconv"
|
||||
|
||||
"fluux.io/xmpp/iot"
|
||||
)
|
||||
|
||||
/*
|
||||
TODO I would like to be able to write
|
||||
|
||||
newIQ(Id, From, To, Type, Lang).AddPayload(IQPayload)
|
||||
NewIQ(Id, From, To, Type, Lang).AddPayload(IQPayload)
|
||||
Payload would be:
|
||||
|
||||
xmpp.IQ{
|
||||
XMLName: xml.Name{
|
||||
Space: "",
|
||||
Local: "",
|
||||
payload := Node{
|
||||
Ns: "http://jabber.org/protocol/disco#info",
|
||||
Tag: "identity",
|
||||
Attrs: map[string]string{
|
||||
"category":"gateway",
|
||||
"type": "skype",
|
||||
"name": "Test Gateway",
|
||||
},
|
||||
PacketAttrs: xmpp.PacketAttrs{
|
||||
Id: "",
|
||||
From: "",
|
||||
To: "",
|
||||
Type: "",
|
||||
Lang: "",
|
||||
},
|
||||
Payload: nil,
|
||||
RawXML: "",
|
||||
Nodes: []Node{},
|
||||
}
|
||||
|
||||
AddPayload(Ns, Tag, Attrs)
|
||||
|
||||
ex:
|
||||
|
||||
NewIQ("get", "test@localhost", "admin@localhost", "en")
|
||||
.AddPayload("http://jabber.org/protocol/disco#info",
|
||||
"identity",
|
||||
map[string]string{
|
||||
"category":"gateway",
|
||||
"type": "skype",
|
||||
"name": "Test Gateway",
|
||||
})
|
||||
|
||||
NewNode(Ns, Tag, Attrs)
|
||||
NewNodeWithChildren(Ns, Tag, Attrs, Nodes)
|
||||
|
||||
Attr {
|
||||
K string
|
||||
V string
|
||||
}
|
||||
|
||||
xmpp.Elt.DiscoInfo("identity", "gateway", "skype", "Test Gateway")
|
||||
xmppElt.DiscoInfo.identity("
|
||||
import xmpp/node/discoinfo
|
||||
|
||||
discoinfo.Identity("gateway", "skype", "Test Gateway")
|
||||
|
||||
|
||||
[]Attr{{"category", "gateway"}
|
||||
|
||||
TODO support ability to put Raw payload
|
||||
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// XMPP Errors
|
||||
|
||||
type Err struct {
|
||||
XMLName xml.Name `xml:"error"`
|
||||
Code int `xml:"code,attr,omitempty"`
|
||||
Type string `xml:"type,attr,omitempty"`
|
||||
Reason string
|
||||
Text string `xml:"urn:ietf:params:xml:ns:xmpp-stanzas text,omitempty"`
|
||||
}
|
||||
|
||||
func (*Err) IsIQPayload() {}
|
||||
|
||||
// UnmarshalXML implements custom parsing for IQs
|
||||
func (x *Err) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
x.XMLName = start.Name
|
||||
|
||||
// Extract attributes
|
||||
for _, attr := range start.Attr {
|
||||
if attr.Name.Local == "type" {
|
||||
x.Type = attr.Value
|
||||
}
|
||||
if attr.Name.Local == "code" {
|
||||
if code, err := strconv.Atoi(attr.Value); err == nil {
|
||||
x.Code = code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch tt := t.(type) {
|
||||
|
||||
case xml.StartElement:
|
||||
elt := new(Node)
|
||||
|
||||
err = d.DecodeElement(elt, &tt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
textName := xml.Name{Space: "urn:ietf:params:xml:ns:xmpp-stanzas", Local: "text"}
|
||||
if elt.XMLName == textName {
|
||||
x.Text = string(elt.Content)
|
||||
} else if elt.XMLName.Space == "urn:ietf:params:xml:ns:xmpp-stanzas" {
|
||||
x.Reason = elt.XMLName.Local
|
||||
}
|
||||
|
||||
case xml.EndElement:
|
||||
if tt == start.End() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (x Err) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
|
||||
code := xml.Attr{
|
||||
Name: xml.Name{Local: "code"},
|
||||
Value: strconv.Itoa(x.Code),
|
||||
}
|
||||
typ := xml.Attr{
|
||||
Name: xml.Name{Local: "type"},
|
||||
Value: x.Type,
|
||||
}
|
||||
start.Name = xml.Name{Local: "error"}
|
||||
start.Attr = append(start.Attr, code, typ)
|
||||
err = e.EncodeToken(start)
|
||||
|
||||
// Subtags
|
||||
// Reason
|
||||
if x.Reason != "" {
|
||||
reason := xml.Name{Space: "urn:ietf:params:xml:ns:xmpp-stanzas", Local: x.Reason}
|
||||
e.EncodeToken(xml.StartElement{Name: reason})
|
||||
e.EncodeToken(xml.EndElement{Name: reason})
|
||||
}
|
||||
|
||||
// Text
|
||||
if x.Text != "" {
|
||||
text := xml.Name{Space: "urn:ietf:params:xml:ns:xmpp-stanzas", Local: "text"}
|
||||
e.EncodeToken(xml.StartElement{Name: text})
|
||||
e.EncodeToken(xml.CharData(x.Text))
|
||||
e.EncodeToken(xml.EndElement{Name: text})
|
||||
}
|
||||
|
||||
return e.EncodeToken(xml.EndElement{Name: start.Name})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// IQ Packet
|
||||
|
||||
|
@ -38,7 +162,7 @@ type IQ struct { // Info/Query
|
|||
PacketAttrs
|
||||
Payload []IQPayload `xml:",omitempty"`
|
||||
RawXML string `xml:",innerxml"`
|
||||
// Error clientError
|
||||
Error Err `xml:"error,omitempty"`
|
||||
}
|
||||
|
||||
func NewIQ(iqtype, from, to, id, lang string) IQ {
|
||||
|
@ -58,6 +182,18 @@ func (iq *IQ) AddPayload(payload IQPayload) {
|
|||
iq.Payload = append(iq.Payload, payload)
|
||||
}
|
||||
|
||||
func (iq IQ) MakeError(xerror Err) IQ {
|
||||
from := iq.From
|
||||
to := iq.To
|
||||
|
||||
iq.Type = "error"
|
||||
iq.From = to
|
||||
iq.To = from
|
||||
iq.Error = xerror
|
||||
|
||||
return iq
|
||||
}
|
||||
|
||||
func (IQ) Name() string {
|
||||
return "iq"
|
||||
}
|
||||
|
@ -75,6 +211,7 @@ func (iqDecoder) decode(p *xml.Decoder, se xml.StartElement) (IQ, error) {
|
|||
// UnmarshalXML implements custom parsing for IQs
|
||||
func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
iq.XMLName = start.Name
|
||||
|
||||
// Extract IQ attributes
|
||||
for _, attr := range start.Attr {
|
||||
if attr.Name.Local == "id" {
|
||||
|
@ -95,34 +232,38 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|||
}
|
||||
|
||||
// decode inner elements
|
||||
level := 0
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var p IQPayload
|
||||
switch tt := t.(type) {
|
||||
|
||||
case xml.StartElement:
|
||||
switch tt.Name.Space + " " + tt.Name.Local {
|
||||
case "urn:ietf:params:xml:ns:xmpp-bind bind":
|
||||
p = new(bindBind)
|
||||
case "urn:xmpp:iot:control set":
|
||||
p = new(iot.ControlSet)
|
||||
default:
|
||||
p = new(Node)
|
||||
level++
|
||||
if level <= 1 {
|
||||
var elt interface{}
|
||||
payloadType := tt.Name.Space + " " + tt.Name.Local
|
||||
if payloadType := typeRegistry[payloadType]; payloadType != nil {
|
||||
val := reflect.New(payloadType)
|
||||
elt = val.Interface()
|
||||
} else {
|
||||
elt = new(Node)
|
||||
}
|
||||
if p != nil {
|
||||
err = d.DecodeElement(p, &tt)
|
||||
|
||||
if iqPl, ok := elt.(IQPayload); ok {
|
||||
err = d.DecodeElement(elt, &tt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iq.Payload = []IQPayload{p}
|
||||
p = nil
|
||||
iq.Payload = append(iq.Payload, iqPl)
|
||||
}
|
||||
}
|
||||
|
||||
case xml.EndElement:
|
||||
level--
|
||||
if tt == start.End() {
|
||||
return nil
|
||||
}
|
||||
|
@ -161,14 +302,19 @@ type IQPayload interface {
|
|||
type Node struct {
|
||||
XMLName xml.Name
|
||||
Attrs []xml.Attr `xml:"-"`
|
||||
// Content []byte `xml:",innerxml"`
|
||||
Content string `xml:",innerxml"`
|
||||
Nodes []Node `xml:",any"`
|
||||
}
|
||||
|
||||
type Attr struct {
|
||||
K string
|
||||
V string
|
||||
}
|
||||
|
||||
func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
// Assign "n.Attrs = start.Attr", without repeating xmlns in attributes
|
||||
// Assign "n.Attrs = start.Attr", without repeating xmlns in attributes:
|
||||
for _, attr := range start.Attr {
|
||||
// Do not repeat xmlns
|
||||
// Do not repeat xmlns, it is already in XMLName
|
||||
if attr.Name.Local != "xmlns" {
|
||||
n.Attrs = append(n.Attrs, attr)
|
||||
}
|
||||
|
@ -177,7 +323,7 @@ func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|||
return d.DecodeElement((*node)(n), &start)
|
||||
}
|
||||
|
||||
func (n *Node) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
|
||||
func (n Node) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
|
||||
start.Attr = n.Attrs
|
||||
start.Name = n.XMLName
|
||||
|
||||
|
@ -187,3 +333,40 @@ func (n *Node) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
|
|||
}
|
||||
|
||||
func (*Node) IsIQPayload() {}
|
||||
|
||||
// ============================================================================
|
||||
// Disco
|
||||
|
||||
const (
|
||||
NSDiscoInfo = "http://jabber.org/protocol/disco#info"
|
||||
)
|
||||
|
||||
type DiscoInfo struct {
|
||||
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#info query"`
|
||||
Identity Identity `xml:"identity"`
|
||||
Features []Feature `xml:"feature"`
|
||||
}
|
||||
|
||||
func (*DiscoInfo) IsIQPayload() {}
|
||||
|
||||
type Identity struct {
|
||||
XMLName xml.Name `xml:"identity,omitempty"`
|
||||
Name string `xml:"name,attr,omitempty"`
|
||||
Category string `xml:"category,attr,omitempty"`
|
||||
Type string `xml:"type,attr,omitempty"`
|
||||
}
|
||||
|
||||
type Feature struct {
|
||||
XMLName xml.Name `xml:"feature"`
|
||||
Var string `xml:"var,attr"`
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
var typeRegistry = make(map[string]reflect.Type)
|
||||
|
||||
func init() {
|
||||
typeRegistry["http://jabber.org/protocol/disco#info query"] = reflect.TypeOf(DiscoInfo{})
|
||||
typeRegistry["urn:ietf:params:xml:ns:xmpp-bind bind"] = reflect.TypeOf(BindBind{})
|
||||
typeRegistry["urn:xmpp:iot:control set"] = reflect.TypeOf(iot.ControlSet{})
|
||||
}
|
||||
|
|
93
iq_test.go
93
iq_test.go
|
@ -2,8 +2,9 @@ package xmpp // import "fluux.io/xmpp"
|
|||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestUnmarshalIqs(t *testing.T) {
|
||||
|
@ -17,49 +18,91 @@ func TestUnmarshalIqs(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
var parsedIQ = new(IQ)
|
||||
err := xml.Unmarshal([]byte(test.iqString), parsedIQ)
|
||||
parsedIQ := IQ{}
|
||||
err := xml.Unmarshal([]byte(test.iqString), &parsedIQ)
|
||||
if err != nil {
|
||||
t.Errorf("Unmarshal(%s) returned error", test.iqString)
|
||||
}
|
||||
if !reflect.DeepEqual(parsedIQ, &test.parsedIQ) {
|
||||
t.Errorf("Unmarshal(%s) expecting result %+v = %+v", test.iqString, parsedIQ, &test.parsedIQ)
|
||||
|
||||
if !xmlEqual(parsedIQ, test.parsedIQ) {
|
||||
t.Errorf("non matching items\n%s", cmp.Diff(parsedIQ, test.parsedIQ))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateIq(t *testing.T) {
|
||||
iq := NewIQ("get", "admin@localhost", "test@localhost", "1", "en")
|
||||
payload := Node{
|
||||
XMLName: xml.Name{
|
||||
Space: "http://jabber.org/protocol/disco#info",
|
||||
Local: "query",
|
||||
iq := NewIQ("result", "admin@localhost", "test@localhost", "1", "en")
|
||||
payload := DiscoInfo{
|
||||
Identity: Identity{
|
||||
Name: "Test Gateway",
|
||||
Category: "gateway",
|
||||
Type: "mqtt",
|
||||
},
|
||||
Nodes: []Node{
|
||||
{XMLName: xml.Name{
|
||||
Space: "http://jabber.org/protocol/disco#info",
|
||||
Local: "identity",
|
||||
Features: []Feature{
|
||||
{Var: "http://jabber.org/protocol/disco#info"},
|
||||
{Var: "http://jabber.org/protocol/disco#item"},
|
||||
},
|
||||
Attrs: []xml.Attr{
|
||||
{Name: xml.Name{Local: "category"}, Value: "gateway"},
|
||||
{Name: xml.Name{Local: "type"}, Value: "skype"},
|
||||
{Name: xml.Name{Local: "name"}, Value: "Test Gateway"},
|
||||
},
|
||||
Nodes: nil,
|
||||
}},
|
||||
}
|
||||
iq.AddPayload(&payload)
|
||||
|
||||
data, err := xml.Marshal(iq)
|
||||
if err != nil {
|
||||
t.Errorf("cannot marshal xml structure")
|
||||
}
|
||||
|
||||
var parsedIQ = new(IQ)
|
||||
if err = xml.Unmarshal(data, parsedIQ); err != nil {
|
||||
parsedIQ := IQ{}
|
||||
if err = xml.Unmarshal(data, &parsedIQ); err != nil {
|
||||
t.Errorf("Unmarshal(%s) returned error", data)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(parsedIQ.Payload[0], iq.Payload[0]) {
|
||||
t.Errorf("expecting result %+v = %+v", parsedIQ.Payload[0], iq.Payload[0])
|
||||
if !xmlEqual(parsedIQ.Payload, iq.Payload) {
|
||||
t.Errorf("non matching items\n%s", cmp.Diff(parsedIQ.Payload, iq.Payload))
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorTag(t *testing.T) {
|
||||
xError := Err{
|
||||
XMLName: xml.Name{Local: "error"},
|
||||
Code: 503,
|
||||
Type: "cancel",
|
||||
Reason: "service-unavailable",
|
||||
Text: "User session not found",
|
||||
}
|
||||
|
||||
data, err := xml.Marshal(xError)
|
||||
if err != nil {
|
||||
t.Errorf("cannot marshal xml structure: %s", err)
|
||||
}
|
||||
|
||||
parsedError := Err{}
|
||||
if err = xml.Unmarshal(data, &parsedError); err != nil {
|
||||
t.Errorf("Unmarshal(%s) returned error", data)
|
||||
}
|
||||
|
||||
if !xmlEqual(parsedError, xError) {
|
||||
t.Errorf("non matching items\n%s", cmp.Diff(parsedError, xError))
|
||||
}
|
||||
}
|
||||
|
||||
// Compare iq structure but ignore empty namespace as they are set properly on
|
||||
// marshal / unmarshal. There is no need to manage them on the manually
|
||||
// crafted structure.
|
||||
func xmlEqual(x, y interface{}) bool {
|
||||
alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true })
|
||||
opts := cmp.Options{
|
||||
cmp.FilterValues(func(x, y interface{}) bool {
|
||||
xx, xok := x.(xml.Name)
|
||||
yy, yok := y.(xml.Name)
|
||||
if xok && yok {
|
||||
zero := xml.Name{}
|
||||
if xx == zero || yy == zero {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, alwaysEqual),
|
||||
}
|
||||
|
||||
return cmp.Equal(x, y, opts)
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ func (s *Session) bind(o Options) {
|
|||
|
||||
// TODO Check all elements
|
||||
switch payload := iq.Payload[0].(type) {
|
||||
case *bindBind:
|
||||
case *BindBind:
|
||||
s.BindJid = payload.Jid // our local id (with possibly randomly generated resource
|
||||
default:
|
||||
s.err = errors.New("iq bind result missing")
|
||||
|
|
Loading…
Reference in a new issue