forked from jshiffer/go-xmpp
97 lines
2.4 KiB
Go
97 lines
2.4 KiB
Go
|
package xmpp
|
||
|
|
||
|
import (
|
||
|
"encoding/xml"
|
||
|
"errors"
|
||
|
"io"
|
||
|
"log"
|
||
|
)
|
||
|
|
||
|
// Reads and checks the opening XMPP stream element.
|
||
|
// 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.
|
||
|
func initDecoder(p *xml.Decoder) (sessionID string, err error) {
|
||
|
for {
|
||
|
var t xml.Token
|
||
|
t, err = p.Token()
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
switch elem := t.(type) {
|
||
|
case xml.StartElement:
|
||
|
if elem.Name.Space != nsStream || elem.Name.Local != "stream" {
|
||
|
err = errors.New("xmpp: expected <stream> but got <" + elem.Name.Local + "> in " + elem.Name.Space)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Parse Stream attributes
|
||
|
for _, attrs := range elem.Attr {
|
||
|
switch attrs.Name.Local {
|
||
|
case "id":
|
||
|
sessionID = attrs.Value
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
|
||
|
// Scan XML token stream to find next StartElement.
|
||
|
func nextStart(p *xml.Decoder) (xml.StartElement, error) {
|
||
|
for {
|
||
|
t, err := p.Token()
|
||
|
if err == io.EOF {
|
||
|
return xml.StartElement{}, nil
|
||
|
}
|
||
|
if err != nil {
|
||
|
log.Fatal("token:", err)
|
||
|
}
|
||
|
switch t := t.(type) {
|
||
|
case xml.StartElement:
|
||
|
return t, nil
|
||
|
}
|
||
|
}
|
||
|
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.
|
||
|
se, err := nextStart(p)
|
||
|
if err != nil {
|
||
|
return xml.Name{}, 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{}
|
||
|
default:
|
||
|
return xml.Name{}, nil, errors.New("unexpected XMPP message " +
|
||
|
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
|
||
|
}
|