diff -r 0ff033eed887 -r 62166e57800e xmpp/layer2.go --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xmpp/layer2.go Sun Sep 15 12:00:17 2013 -0600 @@ -0,0 +1,160 @@ +// This layer of the XMPP protocol translates between bytes and XMLish +// structures. + +package xmpp + +import ( + "io" + "reflect" + "encoding/xml" + "fmt" + "strings" +) + +func readXml(r io.Reader, ch chan<- interface{}, + extStanza map[xml.Name]reflect.Type) { + if _, ok := Debug.(*noLog); !ok { + pr, pw := io.Pipe() + go tee(r, pw, "S: ") + r = pr + } + defer close(ch) + + // This trick loads our namespaces into the parser. + nsstr := fmt.Sprintf(``, + NsClient, NsStream) + nsrdr := strings.NewReader(nsstr) + p := xml.NewDecoder(io.MultiReader(nsrdr, r)) + p.Token() + +Loop: + for { + // Sniff the next token on the stream. + t, err := p.Token() + if t == nil { + if err != io.EOF { + Warn.Logf("read: %s", err) + } + break + } + var se xml.StartElement + var ok bool + if se, ok = t.(xml.StartElement); !ok { + continue + } + + // Allocate the appropriate structure for this token. + var obj interface{} + switch se.Name.Space + " " + se.Name.Local { + case NsStream + " stream": + st, err := parseStream(se) + if err != nil { + Warn.Logf("unmarshal stream: %s", err) + break Loop + } + ch <- st + continue + case "stream error", NsStream + " error": + obj = &streamError{} + case NsStream + " features": + obj = &Features{} + case NsTLS + " proceed", NsTLS + " failure": + obj = &starttls{} + case NsSASL + " challenge", NsSASL + " failure", + NsSASL + " success": + obj = &auth{} + case NsClient + " iq": + obj = &Iq{} + case NsClient + " message": + obj = &Message{} + case NsClient + " presence": + obj = &Presence{} + default: + obj = &Generic{} + Info.Logf("Ignoring unrecognized: %s %s", se.Name.Space, + se.Name.Local) + } + + // Read the complete XML stanza. + err = p.DecodeElement(obj, &se) + if err != nil { + Warn.Logf("unmarshal: %s", err) + break Loop + } + + // If it's a Stanza, we try to unmarshal its innerxml + // into objects of the appropriate respective + // types. This is specified by our extensions. + if st, ok := obj.(Stanza); ok { + err = parseExtended(st.GetHeader(), extStanza) + if err != nil { + Warn.Logf("ext unmarshal: %s", err) + break Loop + } + } + + // Put it on the channel. + ch <- obj + } +} + +func parseExtended(st *Header, extStanza map[xml.Name]reflect.Type) error { + // Now parse the stanza's innerxml to find the string that we + // can unmarshal this nested element from. + reader := strings.NewReader(st.Innerxml) + p := xml.NewDecoder(reader) + for { + t, err := p.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + if se, ok := t.(xml.StartElement); ok { + if typ, ok := extStanza[se.Name]; ok { + nested := reflect.New(typ).Interface() + + // Unmarshal the nested element and + // stuff it back into the stanza. + err := p.DecodeElement(nested, &se) + if err != nil { + return err + } + st.Nested = append(st.Nested, nested) + } + } + } + + return nil +} + +func writeXml(w io.Writer, ch <-chan interface{}) { + if _, ok := Debug.(*noLog); !ok { + pr, pw := io.Pipe() + go tee(pr, w, "C: ") + w = pw + } + defer func(w io.Writer) { + if c, ok := w.(io.Closer); ok { + c.Close() + } + }(w) + + enc := xml.NewEncoder(w) + + for obj := range ch { + if st, ok := obj.(*stream); ok { + _, err := w.Write([]byte(st.String())) + if err != nil { + Warn.Logf("write: %s", err) + } + } else { + err := enc.Encode(obj) + if err != nil { + Warn.Logf("marshal: %s", err) + break + } + } + } +}