diff --git a/auth.go b/auth.go
index 846dd33..3b4b754 100644
--- a/auth.go
+++ b/auth.go
@@ -32,37 +32,72 @@ func authPlain(socket io.ReadWriter, decoder *xml.Decoder, user string, password
fmt.Fprintf(socket, "%s", nsSASL, enc)
// Next message should be either success or failure.
- name, val, err := next(decoder)
+ val, err := next(decoder)
if err != nil {
return err
}
switch v := val.(type) {
- case *saslSuccess:
- case *saslFailure:
+ case SASLSuccess:
+ case SASLFailure:
// v.Any is type of sub-element in failure, which gives a description of what failed.
return errors.New("auth failure: " + v.Any.Local)
default:
- return errors.New("expected success or failure, got " + name.Local + " in " + name.Space)
+ return errors.New("expected SASL success or failure, got " + v.Name())
}
return err
}
-// XMPP Packet Parsing
type saslMechanisms struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
Mechanism []string `xml:"mechanism"`
}
-type saslSuccess struct {
+// ============================================================================
+// SASLSuccess
+
+type SASLSuccess struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl success"`
}
-type saslFailure struct {
+func (SASLSuccess) Name() string {
+ return "sasl:success"
+}
+
+type saslSuccessDecoder struct{}
+
+var saslSuccess saslSuccessDecoder
+
+func (saslSuccessDecoder) decode(p *xml.Decoder, se xml.StartElement) (SASLSuccess, error) {
+ var packet SASLSuccess
+ err := p.DecodeElement(&packet, &se)
+ return packet, err
+}
+
+// ============================================================================
+// SASLFailure
+
+type SASLFailure struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
Any xml.Name // error reason is a subelement
}
+func (SASLFailure) Name() string {
+ return "sasl:failure"
+}
+
+type saslFailureDecoder struct{}
+
+var saslFailure saslFailureDecoder
+
+func (saslFailureDecoder) decode(p *xml.Decoder, se xml.StartElement) (SASLFailure, error) {
+ var packet SASLFailure
+ err := p.DecodeElement(&packet, &se)
+ return packet, err
+}
+
+// ============================================================================
+
type auth struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl auth"`
Mechanism string `xml:"mecanism,attr"`
diff --git a/client.go b/client.go
index 689720e..defa642 100644
--- a/client.go
+++ b/client.go
@@ -108,7 +108,7 @@ func (c *Client) Connect() (*Session, error) {
func (c *Client) recv(receiver chan<- interface{}) (err error) {
for {
- _, val, err := next(c.Session.decoder)
+ val, err := next(c.Session.decoder)
if err != nil {
return err
}
@@ -128,7 +128,7 @@ func (c *Client) Recv() <-chan interface{} {
// Send sends message text.
func (c *Client) Send(packet string) error {
- fmt.Fprintf(c.Session.socketProxy, packet)
+ fmt.Fprintf(c.Session.socketProxy, packet) // TODO handle errors
return nil
}
diff --git a/client_test.go b/client_test.go
index a6746d2..2746041 100644
--- a/client_test.go
+++ b/client_test.go
@@ -163,7 +163,7 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) {
return
}
- iq := &ClientIQ{}
+ iq := &IQ{}
// Decode element into pointer storage
if err = decoder.DecodeElement(&iq, &se); err != nil {
t.Errorf("cannot decode bind iq: %s", err)
diff --git a/cmd/xmpp_component/xmpp_component.go b/cmd/xmpp_component/xmpp_component.go
new file mode 100644
index 0000000..7b794c2
--- /dev/null
+++ b/cmd/xmpp_component/xmpp_component.go
@@ -0,0 +1,32 @@
+package main
+
+import (
+ "fmt"
+
+ "fluux.io/xmpp"
+)
+
+func main() {
+ component := xmpp.Component{Host: "mqtt.localhost", Secret: "mypass"}
+ component.Connect("localhost:8888")
+
+ for {
+ packet, err := component.ReadPacket()
+ if err != nil {
+ fmt.Println("read error", err)
+ return
+ }
+
+ switch p := packet.(type) {
+ case xmpp.IQ:
+ switch inner := p.Payload.(type) {
+ case *xmpp.Node:
+ fmt.Println("Node:", inner.XMLName.Space, inner.XMLName.Local)
+ default:
+ fmt.Println("default")
+ }
+ default:
+ fmt.Println("Packet unhandled packet:", packet)
+ }
+ }
+}
diff --git a/cmd/xmpp_echo/xmpp_echo.go b/cmd/xmpp_echo/xmpp_echo.go
index 69067be..d092508 100644
--- a/cmd/xmpp_echo/xmpp_echo.go
+++ b/cmd/xmpp_echo/xmpp_echo.go
@@ -31,9 +31,9 @@ func main() {
// Iterator to receive packets coming from our XMPP connection
for packet := range client.Recv() {
switch packet := packet.(type) {
- case *xmpp.ClientMessage:
+ case *xmpp.Message:
fmt.Fprintf(os.Stdout, "Body = %s - from = %s\n", packet.Body, packet.From)
- reply := xmpp.ClientMessage{Packet: xmpp.Packet{To: packet.From}, Body: packet.Body}
+ reply := xmpp.Message{PacketAttrs: xmpp.PacketAttrs{To: packet.From}, Body: packet.Body}
client.Send(reply.XMPPFormat())
default:
fmt.Fprintf(os.Stdout, "Ignoring packet: %T\n", packet)
diff --git a/cmd/xmpp_jukebox/xmpp_jukebox.go b/cmd/xmpp_jukebox/xmpp_jukebox.go
index 0591b4b..a67d1fe 100644
--- a/cmd/xmpp_jukebox/xmpp_jukebox.go
+++ b/cmd/xmpp_jukebox/xmpp_jukebox.go
@@ -40,11 +40,11 @@ func main() {
for packet := range client.Recv() {
switch packet := packet.(type) {
- case *xmpp.ClientMessage:
+ case *xmpp.Message:
processMessage(client, p, packet)
- case *xmpp.ClientIQ:
+ case *xmpp.IQ:
processIq(client, p, packet)
- case *xmpp.ClientPresence:
+ case *xmpp.Presence:
// Do nothing with received presence
default:
fmt.Fprintf(os.Stdout, "Ignoring packet: %T\n", packet)
@@ -52,7 +52,7 @@ func main() {
}
}
-func processMessage(client *xmpp.Client, p *mpg123.Player, packet *xmpp.ClientMessage) {
+func processMessage(client *xmpp.Client, p *mpg123.Player, packet *xmpp.Message) {
command := strings.Trim(packet.Body, " ")
if command == "stop" {
p.Stop()
@@ -62,7 +62,7 @@ func processMessage(client *xmpp.Client, p *mpg123.Player, packet *xmpp.ClientMe
}
}
-func processIq(client *xmpp.Client, p *mpg123.Player, packet *xmpp.ClientIQ) {
+func processIq(client *xmpp.Client, p *mpg123.Player, packet *xmpp.IQ) {
switch payload := packet.Payload.(type) {
// We support IOT Control IQ
case *iot.ControlSet:
@@ -76,7 +76,7 @@ func processIq(client *xmpp.Client, p *mpg123.Player, packet *xmpp.ClientIQ) {
playSCURL(p, url)
setResponse := new(iot.ControlSetResponse)
- reply := xmpp.ClientIQ{Packet: xmpp.Packet{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: setResponse}
client.Send(reply.XMPPFormat())
// TODO add Soundclound artist / title retrieval
sendUserTune(client, "Radiohead", "Spectre")
diff --git a/component.go b/component.go
new file mode 100644
index 0000000..6474660
--- /dev/null
+++ b/component.go
@@ -0,0 +1,116 @@
+package xmpp
+
+import (
+ "crypto/sha1"
+ "encoding/hex"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "time"
+)
+
+const componentStreamOpen = ""
+
+// Component implements an XMPP extension allowing to extend XMPP server
+// using external components. Component specifications are defined
+// in XEP-0114, XEP-0355 and XEP-0356.
+type Component struct {
+ Host string
+ Secret string
+
+ // TCP level connection
+ conn net.Conn
+
+ // read / write
+ socketProxy io.ReadWriter // TODO
+ decoder *xml.Decoder
+}
+
+// handshake generates an authentication token based on StreamID and shared secret.
+func (c *Component) handshake(streamId string) string {
+ // 1. Concatenate the Stream ID received from the server with the shared secret.
+ concatStr := streamId + c.Secret
+
+ // 2. Hash the concatenated string according to the SHA1 algorithm, i.e., SHA1( concat (sid, password)).
+ h := sha1.New()
+ h.Write([]byte(concatStr))
+ hash := h.Sum(nil)
+
+ // 3. Ensure that the hash output is in hexadecimal format, not binary or base64.
+ // 4. Convert the hash output to all lowercase characters.
+ encodedStr := hex.EncodeToString(hash)
+
+ return encodedStr
+}
+
+// TODO Helper to prepare connection string
+func (c *Component) Connect(connStr string) error {
+ var conn net.Conn
+ var err error
+ if conn, err = net.DialTimeout("tcp", connStr, time.Duration(5)*time.Second); err != nil {
+ return err
+ }
+ c.conn = conn
+
+ // 1. Send stream open tag
+ if _, err := fmt.Fprintf(conn, componentStreamOpen, c.Host, NSComponent, NSStream); err != nil {
+ return errors.New("cannot send stream open " + err.Error())
+ }
+ c.decoder = xml.NewDecoder(conn)
+
+ // 2. Initialize xml decoder and extract streamID from reply
+ streamId, err := initDecoder(c.decoder)
+ if err != nil {
+ return errors.New("cannot init decoder " + err.Error())
+ }
+
+ // 3. Authentication
+ if _, err := fmt.Fprintf(conn, "%s", c.handshake(streamId)); err != nil {
+ return errors.New("cannot send handshake " + err.Error())
+ }
+
+ // 4. Check server response for authentication
+ val, err := next(c.decoder)
+ if err != nil {
+ return err
+ }
+
+ switch v := val.(type) {
+ case *StreamError:
+ return errors.New("handshake failed " + v.Error.Local)
+ case *Handshake:
+ return nil
+ default:
+ return errors.New("unexpected packet, got " + v.Name())
+ }
+ panic("unreachable")
+}
+
+// ReadPacket reads next incoming XMPP packet
+// TODO use defined interface Packet
+func (c *Component) ReadPacket() (Packet, error) {
+ return next(c.decoder)
+}
+
+// ============================================================================
+// Handshake Packet
+
+type Handshake struct {
+ XMLName xml.Name `xml:"jabber:component:accept handshake"`
+}
+
+func (Handshake) Name() string {
+ return "component:handshake"
+}
+
+type handshakeDecoder struct{}
+
+var handshake handshakeDecoder
+
+func (handshakeDecoder) decode(p *xml.Decoder, se xml.StartElement) (Handshake, error) {
+ var packet Handshake
+ err := p.DecodeElement(&packet, &se)
+ return packet, err
+}
diff --git a/component_test.go b/component_test.go
new file mode 100644
index 0000000..76e3849
--- /dev/null
+++ b/component_test.go
@@ -0,0 +1,18 @@
+package xmpp
+
+import "testing"
+
+func TestHandshake(t *testing.T) {
+ c := Component{
+ Host: "test.localhost",
+ Secret: "mypass",
+ }
+
+ streamID := "1263952298440005243"
+ expected := "c77e2ef0109fbbc5161e83b51629cd1353495332"
+
+ result := c.handshake(streamID)
+ if result != expected {
+ t.Errorf("incorrect handshake calculation '%s' != '%s'", result, expected)
+ }
+}
diff --git a/iq.go b/iq.go
index f737e82..63667f1 100644
--- a/iq.go
+++ b/iq.go
@@ -7,22 +7,38 @@ import (
"fluux.io/xmpp/iot"
)
-// info/query
-type ClientIQ struct {
- XMLName xml.Name `xml:"jabber:client iq"`
- Packet
+// ============================================================================
+// IQ Packet
+
+type IQ struct { // Info/Query
+ XMLName xml.Name `xml:"iq"`
+ PacketAttrs
Payload IQPayload `xml:",omitempty"`
RawXML string `xml:",innerxml"`
// TODO We need to support detecting the IQ namespace / Query packet
// Error clientError
}
+func (IQ) Name() string {
+ return "iq"
+}
+
+type iqDecoder struct{}
+
+var iq iqDecoder
+
+func (iqDecoder) decode(p *xml.Decoder, se xml.StartElement) (IQ, error) {
+ var packet IQ
+ err := p.DecodeElement(&packet, &se)
+ return packet, err
+}
+
type IQPayload interface {
IsIQPayload()
}
// UnmarshalXML implements custom parsing for IQs
-func (iq *ClientIQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
iq.XMLName = start.Name
// Extract IQ attributes
for _, attr := range start.Attr {
@@ -59,7 +75,8 @@ func (iq *ClientIQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
p = new(bindBind)
case "urn:xmpp:iot:control set":
p = new(iot.ControlSet)
- // TODO: Add a default Type that passes RawXML
+ default:
+ p = new(Node)
}
if p != nil {
err = d.DecodeElement(p, &tt)
@@ -80,7 +97,7 @@ func (iq *ClientIQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
// XMPPFormat returns the string representation of the XMPP packet.
// TODO: Should I simply rely on xml.Marshal ?
-func (iq *ClientIQ) XMPPFormat() string {
+func (iq *IQ) XMPPFormat() string {
if iq.Payload != nil {
var payload []byte
var err error
@@ -98,3 +115,14 @@ func (iq *ClientIQ) XMPPFormat() string {
iq.To, iq.Type, iq.Id,
iq.RawXML)
}
+
+// ============================================================================
+// Genery IQ Node
+
+type Node struct {
+ XMLName xml.Name
+ Content []byte `xml:",innerxml"`
+ Nodes []Node `xml:",any"`
+}
+
+func (*Node) IsIQPayload() {}
diff --git a/iq_test.go b/iq_test.go
index b8859c0..9042ebc 100644
--- a/iq_test.go
+++ b/iq_test.go
@@ -10,14 +10,14 @@ func TestUnmarshalIqs(t *testing.T) {
//var cs1 = new(iot.ControlSet)
var tests = []struct {
iqString string
- parsedIQ ClientIQ
+ parsedIQ IQ
}{
- {"", ClientIQ{XMLName: xml.Name{Space: "", Local: "iq"}, Packet: Packet{To: "test@localhost", Type: "set", Id: "1"}}},
- //{"", ClientIQ{XMLName: xml.Name{Space: "jabber:client", Local: "iq"}, Packet: Packet{To: "test@localhost", From: "server", Type: "set", Id: "2"}, Payload: cs1}},
+ {"", IQ{XMLName: xml.Name{Space: "", Local: "iq"}, PacketAttrs: PacketAttrs{To: "test@localhost", Type: "set", Id: "1"}}},
+ //{"", IQ{XMLName: xml.Name{Space: "jabber:client", Local: "iq"}, PacketAttrs: PacketAttrs{To: "test@localhost", From: "server", Type: "set", Id: "2"}, Payload: cs1}},
}
for _, test := range tests {
- var parsedIQ = new(ClientIQ)
+ var parsedIQ = new(IQ)
err := xml.Unmarshal([]byte(test.iqString), parsedIQ)
if err != nil {
t.Errorf("Unmarshal(%s) returned error", test.iqString)
diff --git a/message.go b/message.go
index dbcd0ad..be3d758 100644
--- a/message.go
+++ b/message.go
@@ -5,20 +5,36 @@ import (
"fmt"
)
-// XMPP Packet Parsing
-type ClientMessage struct {
- XMLName xml.Name `xml:"jabber:client message"`
- Packet
+// ============================================================================
+// Message Packet
+
+type Message struct {
+ XMLName xml.Name `xml:"message"`
+ PacketAttrs
Subject string `xml:"subject,omitempty"`
Body string `xml:"body,omitempty"`
Thread string `xml:"thread,omitempty"`
}
-// TODO: Func new message to create an empty message structure without the XML tag matching elements
+func (Message) Name() string {
+ return "message"
+}
-func (message *ClientMessage) XMPPFormat() string {
+type messageDecoder struct{}
+
+var message messageDecoder
+
+func (messageDecoder) decode(p *xml.Decoder, se xml.StartElement) (Message, error) {
+ var packet Message
+ err := p.DecodeElement(&packet, &se)
+ return packet, err
+}
+
+func (msg *Message) XMPPFormat() string {
return fmt.Sprintf(""+
"%s",
- message.To,
- xmlEscape(message.Body))
+ msg.To,
+ xmlEscape(msg.Body))
}
+
+// TODO: Func new message to create an empty message structure without the XML tag matching elements
diff --git a/ns.go b/ns.go
index 9b3bdac..9cd162c 100644
--- a/ns.go
+++ b/ns.go
@@ -1,10 +1,11 @@
package xmpp // import "fluux.io/xmpp"
const (
- NSStream = "http://etherx.jabber.org/streams"
- nsTLS = "urn:ietf:params:xml:ns:xmpp-tls"
- nsSASL = "urn:ietf:params:xml:ns:xmpp-sasl"
- nsBind = "urn:ietf:params:xml:ns:xmpp-bind"
- nsSession = "urn:ietf:params:xml:ns:xmpp-session"
- NSClient = "jabber:client"
+ NSStream = "http://etherx.jabber.org/streams"
+ nsTLS = "urn:ietf:params:xml:ns:xmpp-tls"
+ nsSASL = "urn:ietf:params:xml:ns:xmpp-sasl"
+ nsBind = "urn:ietf:params:xml:ns:xmpp-bind"
+ nsSession = "urn:ietf:params:xml:ns:xmpp-session"
+ NSClient = "jabber:client"
+ NSComponent = "jabber:component:accept"
)
diff --git a/packet.go b/packet.go
index a896440..ca42d6c 100644
--- a/packet.go
+++ b/packet.go
@@ -1,7 +1,11 @@
package xmpp // import "fluux.io/xmpp"
-// Packet represents the root default structure for an XMPP packet.
-type Packet struct {
+type Packet interface {
+ Name() string
+}
+
+// PacketAttrs represents the common structure for base XMPP packets.
+type PacketAttrs struct {
Id string `xml:"id,attr,omitempty"`
From string `xml:"from,attr,omitempty"`
To string `xml:"to,attr,omitempty"`
diff --git a/parser.go b/parser.go
index 39165cb..aec306c 100644
--- a/parser.go
+++ b/parser.go
@@ -8,11 +8,12 @@ import (
)
// Reads and checks the opening XMPP stream element.
-// It returns a stream structure containing:
+// TODO It returns a stream structure containing:
// - Host: You can check the host against the host you were expecting to connect to
// - Id: the Stream ID is a temporary shared secret used for some hash calculation. It is also used by ProcessOne
// reattach features (allowing to resume an existing stream at the point the connection was interrupted, without
// getting through the authentication process.
+// TODO We should handle stream error from XEP-0114 ( or )
func initDecoder(p *xml.Decoder) (sessionID string, err error) {
for {
var t xml.Token
@@ -59,38 +60,76 @@ func nextStart(p *xml.Decoder) (xml.StartElement, error) {
panic("unreachable")
}
-// Scan XML token stream for next element and save into val.
-// If val == nil, allocate new element based on proto map.
-// Either way, return val.
-func next(p *xml.Decoder) (xml.Name, interface{}, error) {
- // Read start element to find out what type we want.
+// next scans XML token stream for next element and then assign a structure to decode
+// that elements.
+// TODO Use an interface to return packets interface xmppDecoder
+func next(p *xml.Decoder) (Packet, error) {
+ // Read start element to find out how we want to parse the XMPP packet
se, err := nextStart(p)
if err != nil {
- return xml.Name{}, nil, err
+ return nil, err
}
- // Put it in an interface and allocate one.
- var nv interface{}
- switch se.Name.Space + " " + se.Name.Local {
- // TODO: general case = Parse IQ / presence / message => split SASL case
- case nsSASL + " success":
- nv = &saslSuccess{}
- case nsSASL + " failure":
- nv = &saslFailure{}
- case NSClient + " message":
- nv = &ClientMessage{}
- case NSClient + " presence":
- nv = &ClientPresence{}
- case NSClient + " iq":
- nv = &ClientIQ{}
+ // TODO: general case = Parse IQ / presence / message => split SASL Stream and component cases
+ switch se.Name.Space {
+ case NSStream:
+ return decodeStream(p, se)
+ case nsSASL:
+ return decodeSASL(p, se)
+ case NSClient:
+ return decodeClient(p, se)
+ case NSComponent:
+ return decodeComponent(p, se)
default:
- return xml.Name{}, nil, errors.New("unexpected XMPP message " +
+ return nil, errors.New("unknown namespace " +
+ se.Name.Space + " <" + se.Name.Local + "/>")
+ }
+}
+
+func decodeStream(p *xml.Decoder, se xml.StartElement) (Packet, error) {
+ switch se.Name.Local {
+ case "error":
+ return streamError.decode(p, se)
+ default:
+ return nil, errors.New("unexpected XMPP packet " +
+ se.Name.Space + " <" + se.Name.Local + "/>")
+ }
+}
+
+func decodeSASL(p *xml.Decoder, se xml.StartElement) (Packet, error) {
+ switch se.Name.Local {
+ case "success":
+ return saslSuccess.decode(p, se)
+ case "failure":
+ return saslFailure.decode(p, se)
+ default:
+ return nil, errors.New("unexpected XMPP packet " +
+ se.Name.Space + " <" + se.Name.Local + "/>")
+ }
+}
+
+func decodeClient(p *xml.Decoder, se xml.StartElement) (Packet, error) {
+ switch se.Name.Local {
+ case "message":
+ return message.decode(p, se)
+ case "presence":
+ return presence.decode(p, se)
+ case "iq":
+ return iq.decode(p, se)
+ default:
+ return nil, errors.New("unexpected XMPP packet " +
+ se.Name.Space + " <" + se.Name.Local + "/>")
+ }
+}
+
+func decodeComponent(p *xml.Decoder, se xml.StartElement) (Packet, error) {
+ switch se.Name.Local {
+ case "handshake":
+ return handshake.decode(p, se)
+ case "iq":
+ return iq.decode(p, se)
+ default:
+ return nil, errors.New("unexpected XMPP packet " +
se.Name.Space + " <" + se.Name.Local + "/>")
}
-
- // Decode element into pointer storage
- if err = p.DecodeElement(nv, &se); err != nil {
- return xml.Name{}, nil, err
- }
- return se.Name, nv, err
}
diff --git a/pep/user_tune.go b/pep/user_tune.go
index 846dda0..70b4b47 100644
--- a/pep/user_tune.go
+++ b/pep/user_tune.go
@@ -7,9 +7,9 @@ import (
)
type iq struct {
- XMLName xml.Name `xml:"jabber:client iq"`
- C pubSub // c for "contains"
- xmpp.Packet // Rename h for "header" ?
+ XMLName xml.Name `xml:"jabber:client iq"`
+ C pubSub // c for "contains"
+ xmpp.PacketAttrs // Rename h for "header" ?
}
type pubSub struct {
@@ -68,7 +68,7 @@ type Tune struct {
*/
func (t *Tune) XMPPFormat() (s string) {
- packet, _ := xml.Marshal(iq{Packet: xmpp.Packet{Id: "tunes", Type: "set"}, C: pubSub{Publish: publish{Node: "http://jabber.org/protocol/tune", Item: item{Tune: *t}}}})
+ packet, _ := xml.Marshal(iq{PacketAttrs: xmpp.PacketAttrs{Id: "tunes", Type: "set"}, C: pubSub{Publish: publish{Node: "http://jabber.org/protocol/tune", Item: item{Tune: *t}}}})
return string(packet)
}
diff --git a/presence.go b/presence.go
index 21cfd77..6d9eaed 100644
--- a/presence.go
+++ b/presence.go
@@ -2,12 +2,28 @@ package xmpp // import "fluux.io/xmpp"
import "encoding/xml"
-// XMPP Packet Parsing
-type ClientPresence struct {
- XMLName xml.Name `xml:"jabber:client presence"`
- Packet
+// ============================================================================
+// Presence Packet
+
+type Presence struct {
+ XMLName xml.Name `xml:"presence"`
+ PacketAttrs
Show string `xml:"show,attr,omitempty"` // away, chat, dnd, xa
Status string `xml:"status,attr,omitempty"`
Priority string `xml:"priority,attr,omitempty"`
//Error *clientError
}
+
+func (Presence) Name() string {
+ return "presence"
+}
+
+type presenceDecoder struct{}
+
+var presence presenceDecoder
+
+func (presenceDecoder) decode(p *xml.Decoder, se xml.StartElement) (Presence, error) {
+ var packet Presence
+ err := p.DecodeElement(&packet, &se)
+ return packet, err
+}
diff --git a/session.go b/session.go
index 6012dad..dc47ffe 100644
--- a/session.go
+++ b/session.go
@@ -157,7 +157,7 @@ func (s *Session) bind(o Options) {
fmt.Fprintf(s.socketProxy, "", s.PacketId(), nsBind)
}
- var iq ClientIQ
+ var iq IQ
if s.err = s.decoder.Decode(&iq); s.err != nil {
s.err = errors.New("error decoding iq bind result: " + s.err.Error())
return
@@ -180,7 +180,7 @@ func (s *Session) rfc3921Session(o Options) {
return
}
- var iq ClientIQ
+ var iq IQ
if s.Features.Session.optional.Local != "" {
fmt.Fprintf(s.socketProxy, "", s.PacketId(), nsSession)
if s.err = s.decoder.Decode(&iq); s.err != nil {
diff --git a/stream.go b/stream.go
index 9138c2f..3c6afd6 100644
--- a/stream.go
+++ b/stream.go
@@ -1,8 +1,12 @@
package xmpp // import "fluux.io/xmpp"
-import "encoding/xml"
+import (
+ "encoding/xml"
+)
+
+// ============================================================================
+// StreamFeatures Packet
-// XMPP Packet Parsing
type streamFeatures struct {
XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"`
StartTLS tlsStartTLS
@@ -13,6 +17,31 @@ type streamFeatures struct {
Any []xml.Name `xml:",any"`
}
+// ============================================================================
+// StreamError Packet
+
+type StreamError struct {
+ XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
+ Error xml.Name `xml:",any"`
+}
+
+func (StreamError) Name() string {
+ return "stream:error"
+}
+
+type streamErrorDecoder struct{}
+
+var streamError streamErrorDecoder
+
+func (streamErrorDecoder) decode(p *xml.Decoder, se xml.StartElement) (StreamError, error) {
+ var packet StreamError
+ err := p.DecodeElement(&packet, &se)
+ return packet, err
+}
+
+// ============================================================================
+// Caps subElement
+
type Caps struct {
XMLName xml.Name `xml:"http://jabber.org/protocol/caps c"`
Hash string `xml:"hash,attr"`