Merge pull request #5 from FluuxIO/parsing

Improve IQ parsing and generation
This commit is contained in:
Mickaël Rémond 2018-01-15 12:54:37 +01:00 committed by GitHub
commit 2e47f1659d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 126 additions and 17 deletions

View file

@ -170,7 +170,8 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) {
return return
} }
switch iq.Payload.(type) { // TODO Check all elements
switch iq.Payload[0].(type) {
case *bindBind: case *bindBind:
result := `<iq id='%s' type='result'> result := `<iq id='%s' type='result'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'> <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/xml"
"fmt" "fmt"
"fluux.io/xmpp" "fluux.io/xmpp"
@ -20,8 +21,15 @@ func main() {
switch p := packet.(type) { switch p := packet.(type) {
case xmpp.IQ: case xmpp.IQ:
switch inner := p.Payload.(type) { switch inner := p.Payload[0].(type) {
case *xmpp.Node: case *xmpp.Node:
fmt.Printf("%q\n", inner)
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) component.processIQ(p.Type, p.Id, p.From, inner)
default: default:
fmt.Println("default") fmt.Println("default")

View file

@ -63,7 +63,7 @@ func processMessage(client *xmpp.Client, p *mpg123.Player, packet *xmpp.Message)
} }
func processIq(client *xmpp.Client, p *mpg123.Player, packet *xmpp.IQ) { func processIq(client *xmpp.Client, p *mpg123.Player, packet *xmpp.IQ) {
switch payload := packet.Payload.(type) { switch payload := packet.Payload[0].(type) {
// We support IOT Control IQ // We support IOT Control IQ
case *iot.ControlSet: case *iot.ControlSet:
var url string var url string
@ -76,7 +76,7 @@ func processIq(client *xmpp.Client, p *mpg123.Player, packet *xmpp.IQ) {
playSCURL(p, url) playSCURL(p, url)
setResponse := new(iot.ControlSetResponse) setResponse := new(iot.ControlSetResponse)
reply := xmpp.IQ{PacketAttrs: xmpp.PacketAttrs{To: packet.From, Type: "result", Id: packet.Id}, Payload: setResponse} reply := xmpp.IQ{PacketAttrs: xmpp.PacketAttrs{To: packet.From, Type: "result", Id: packet.Id}, Payload: []xmpp.IQPayload{setResponse}}
client.Send(reply.XMPPFormat()) client.Send(reply.XMPPFormat())
// TODO add Soundclound artist / title retrieval // TODO add Soundclound artist / title retrieval
sendUserTune(client, "Radiohead", "Spectre") sendUserTune(client, "Radiohead", "Spectre")

79
iq.go
View file

@ -7,18 +7,57 @@ import (
"fluux.io/xmpp/iot" "fluux.io/xmpp/iot"
) )
/*
TODO I would like to be able to write
newIQ(Id, From, To, Type, Lang).AddPayload(IQPayload)
xmpp.IQ{
XMLName: xml.Name{
Space: "",
Local: "",
},
PacketAttrs: xmpp.PacketAttrs{
Id: "",
From: "",
To: "",
Type: "",
Lang: "",
},
Payload: nil,
RawXML: "",
}
*/
// ============================================================================ // ============================================================================
// IQ Packet // IQ Packet
type IQ struct { // Info/Query type IQ struct { // Info/Query
XMLName xml.Name `xml:"iq"` XMLName xml.Name `xml:"iq"`
PacketAttrs PacketAttrs
Payload IQPayload `xml:",omitempty"` Payload []IQPayload `xml:",omitempty"`
RawXML string `xml:",innerxml"` RawXML string `xml:",innerxml"`
// TODO We need to support detecting the IQ namespace / Query packet
// Error clientError // Error clientError
} }
func NewIQ(iqtype, from, to, id, lang string) IQ {
return IQ{
XMLName: xml.Name{Local: "iq"},
PacketAttrs: PacketAttrs{
Id: id,
From: from,
To: to,
Type: iqtype,
Lang: lang,
},
}
}
func (iq *IQ) AddPayload(payload IQPayload) {
iq.Payload = append(iq.Payload, payload)
}
func (IQ) Name() string { func (IQ) Name() string {
return "iq" return "iq"
} }
@ -33,10 +72,6 @@ func (iqDecoder) decode(p *xml.Decoder, se xml.StartElement) (IQ, error) {
return packet, err return packet, err
} }
type IQPayload interface {
IsIQPayload()
}
// UnmarshalXML implements custom parsing for IQs // UnmarshalXML implements custom parsing for IQs
func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
iq.XMLName = start.Name iq.XMLName = start.Name
@ -83,7 +118,7 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if err != nil { if err != nil {
return err return err
} }
iq.Payload = p iq.Payload = []IQPayload{p}
p = nil p = nil
} }
@ -117,12 +152,38 @@ func (iq *IQ) XMPPFormat() string {
} }
// ============================================================================ // ============================================================================
// Genery IQ Node // Generic IQ Payload
type IQPayload interface {
IsIQPayload()
}
type Node struct { type Node struct {
XMLName xml.Name XMLName xml.Name
Content []byte `xml:",innerxml"` Attrs []xml.Attr `xml:"-"`
// Content []byte `xml:",innerxml"`
Nodes []Node `xml:",any"` Nodes []Node `xml:",any"`
} }
func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
// Assign "n.Attrs = start.Attr", without repeating xmlns in attributes
for _, attr := range start.Attr {
// Do not repeat xmlns
if attr.Name.Local != "xmlns" {
n.Attrs = append(n.Attrs, attr)
}
}
type node Node
return d.DecodeElement((*node)(n), &start)
}
func (n *Node) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
start.Attr = n.Attrs
start.Name = n.XMLName
err = e.EncodeToken(start)
e.EncodeElement(n.Nodes, xml.StartElement{Name: n.XMLName})
return e.EncodeToken(xml.EndElement{Name: start.Name})
}
func (*Node) IsIQPayload() {} func (*Node) IsIQPayload() {}

View file

@ -27,3 +27,39 @@ func TestUnmarshalIqs(t *testing.T) {
} }
} }
} }
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",
},
Nodes: []Node{
{XMLName: xml.Name{
Space: "http://jabber.org/protocol/disco#info",
Local: "identity",
},
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 {
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])
}
}

View file

@ -163,7 +163,8 @@ func (s *Session) bind(o Options) {
return return
} }
switch payload := iq.Payload.(type) { // 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 s.BindJid = payload.Jid // our local id (with possibly randomly generated resource
default: default:

View file

@ -11,4 +11,6 @@ for d in $(go list ./... | grep -v vendor); do
fi fi
done done
if [ -f "./codecov.sh" ]; then
./codecov.sh ./codecov.sh
fi