package xmpp import ( "encoding/xml" "encoding/base64" "errors" "log" ) type tokenRouter struct { start xml.StartElement end xml.EndElement buffer []xml.Token level uint16 // XML nesting level enc *encoder } func newTokenRouter() tokenRouter { return tokenRouter{ buffer: make([]xml.Token, 0), level: 0, } } func (r *tokenRouter) route(t xml.Token, c *Conn) error { r.buffer = append(r.buffer, t) switch unwrapped := t.(type) { case xml.StartElement: r.level += 1 if r.level == 1 { r.start = unwrapped r.buffer = r.buffer[:0] // call start handler } case xml.EndElement: if r.level == 0 { log.Println("Ignoring XML end element on nesting level zero") return nil } r.level -= 1 switch r.level { case 0: return errors.New("Stream was closed by server") case 1: err := routeElement(r.buffer, c) if err != nil { return err } r.buffer = r.buffer[:0] } case xml.ProcInst: log.Println("Ignoring xml.ProcInst element") r.buffer = r.buffer[:len(r.buffer)-1] case xml.Directive: log.Println("Ignoring xml.Directive element") r.buffer = r.buffer[:len(r.buffer)-1] case xml.Comment: log.Println("Ignoring xml.Comment element") r.buffer = r.buffer[:len(r.buffer)-1] } return nil } func routeElement(b []xml.Token, c *Conn) error { tab := elementRoutingTable{ {xml.Name{"http://etherx.jabber.org/streams", "features"}, streamFeatures}, } switch unwrapped := b[0].(type) { case xml.StartElement: for _, v := range(tab) { if unwrapped.Name == v.name { err := v.handler(b, c) if err != nil { return err } return nil } } log.Printf("Ignoring XML element '%s' from namespace '%s'", unwrapped.Name.Local, unwrapped.Name.Space) default: log.Println("Ignoring XML element which has no StartElement as first token") } return nil } type elementRoutingTable []struct { name xml.Name handler func(b []xml.Token, c *Conn) error } func streamFeatures(b []xml.Token, c *Conn) error { err := sendSaslAuth(b, c) if err != nil { return err } return nil } func sendSaslAuth(b []xml.Token, c *Conn) error { mechanisms := make([]string, 0) for i, v := range(b) { switch token := v.(type) { case xml.StartElement: expected := xml.Name{"urn:ietf:params:xml:ns:xmpp-sasl", "mechanism"} if token.Name == expected { if i >= (len(b)-1) { continue } switch payload := b[i+1].(type) { case xml.CharData: mechanisms = append(mechanisms, string(payload)) } } } } for _, v := range(mechanisms) { if v == "PLAIN" { start := xml.StartElement{ xml.Name{"urn:ietf:params:xml:ns:xmpp-sasl", "auth"}, []xml.Attr{ xml.Attr{xml.Name{"", "mechanism"}, "PLAIN"}, }, } data := []byte("\x00" + username(c.jid) + "\x00" + c.pwd) dst := make([]byte, base64.StdEncoding.EncodedLen(len(data))) base64.StdEncoding.Encode(dst, data) payload := xml.CharData(dst) end := start.End() c.enc.encodeNow(start) c.enc.encode(payload) c.enc.encodeNow(end) return nil } } return errors.New("No compatible SASL mechanism given") }