|
1 // This layer of the XMPP protocol translates between bytes and XMLish |
|
2 // structures. |
|
3 |
|
4 package xmpp |
|
5 |
|
6 import ( |
|
7 "io" |
|
8 "reflect" |
|
9 "encoding/xml" |
|
10 "fmt" |
|
11 "strings" |
|
12 ) |
|
13 |
|
14 func readXml(r io.Reader, ch chan<- interface{}, |
|
15 extStanza map[xml.Name]reflect.Type) { |
|
16 if _, ok := Debug.(*noLog); !ok { |
|
17 pr, pw := io.Pipe() |
|
18 go tee(r, pw, "S: ") |
|
19 r = pr |
|
20 } |
|
21 defer close(ch) |
|
22 |
|
23 // This trick loads our namespaces into the parser. |
|
24 nsstr := fmt.Sprintf(`<a xmlns="%s" xmlns:stream="%s">`, |
|
25 NsClient, NsStream) |
|
26 nsrdr := strings.NewReader(nsstr) |
|
27 p := xml.NewDecoder(io.MultiReader(nsrdr, r)) |
|
28 p.Token() |
|
29 |
|
30 Loop: |
|
31 for { |
|
32 // Sniff the next token on the stream. |
|
33 t, err := p.Token() |
|
34 if t == nil { |
|
35 if err != io.EOF { |
|
36 Warn.Logf("read: %s", err) |
|
37 } |
|
38 break |
|
39 } |
|
40 var se xml.StartElement |
|
41 var ok bool |
|
42 if se, ok = t.(xml.StartElement); !ok { |
|
43 continue |
|
44 } |
|
45 |
|
46 // Allocate the appropriate structure for this token. |
|
47 var obj interface{} |
|
48 switch se.Name.Space + " " + se.Name.Local { |
|
49 case NsStream + " stream": |
|
50 st, err := parseStream(se) |
|
51 if err != nil { |
|
52 Warn.Logf("unmarshal stream: %s", err) |
|
53 break Loop |
|
54 } |
|
55 ch <- st |
|
56 continue |
|
57 case "stream error", NsStream + " error": |
|
58 obj = &streamError{} |
|
59 case NsStream + " features": |
|
60 obj = &Features{} |
|
61 case NsTLS + " proceed", NsTLS + " failure": |
|
62 obj = &starttls{} |
|
63 case NsSASL + " challenge", NsSASL + " failure", |
|
64 NsSASL + " success": |
|
65 obj = &auth{} |
|
66 case NsClient + " iq": |
|
67 obj = &Iq{} |
|
68 case NsClient + " message": |
|
69 obj = &Message{} |
|
70 case NsClient + " presence": |
|
71 obj = &Presence{} |
|
72 default: |
|
73 obj = &Generic{} |
|
74 Info.Logf("Ignoring unrecognized: %s %s", se.Name.Space, |
|
75 se.Name.Local) |
|
76 } |
|
77 |
|
78 // Read the complete XML stanza. |
|
79 err = p.DecodeElement(obj, &se) |
|
80 if err != nil { |
|
81 Warn.Logf("unmarshal: %s", err) |
|
82 break Loop |
|
83 } |
|
84 |
|
85 // If it's a Stanza, we try to unmarshal its innerxml |
|
86 // into objects of the appropriate respective |
|
87 // types. This is specified by our extensions. |
|
88 if st, ok := obj.(Stanza); ok { |
|
89 err = parseExtended(st.GetHeader(), extStanza) |
|
90 if err != nil { |
|
91 Warn.Logf("ext unmarshal: %s", err) |
|
92 break Loop |
|
93 } |
|
94 } |
|
95 |
|
96 // Put it on the channel. |
|
97 ch <- obj |
|
98 } |
|
99 } |
|
100 |
|
101 func parseExtended(st *Header, extStanza map[xml.Name]reflect.Type) error { |
|
102 // Now parse the stanza's innerxml to find the string that we |
|
103 // can unmarshal this nested element from. |
|
104 reader := strings.NewReader(st.Innerxml) |
|
105 p := xml.NewDecoder(reader) |
|
106 for { |
|
107 t, err := p.Token() |
|
108 if err == io.EOF { |
|
109 break |
|
110 } |
|
111 if err != nil { |
|
112 return err |
|
113 } |
|
114 if se, ok := t.(xml.StartElement); ok { |
|
115 if typ, ok := extStanza[se.Name]; ok { |
|
116 nested := reflect.New(typ).Interface() |
|
117 |
|
118 // Unmarshal the nested element and |
|
119 // stuff it back into the stanza. |
|
120 err := p.DecodeElement(nested, &se) |
|
121 if err != nil { |
|
122 return err |
|
123 } |
|
124 st.Nested = append(st.Nested, nested) |
|
125 } |
|
126 } |
|
127 } |
|
128 |
|
129 return nil |
|
130 } |
|
131 |
|
132 func writeXml(w io.Writer, ch <-chan interface{}) { |
|
133 if _, ok := Debug.(*noLog); !ok { |
|
134 pr, pw := io.Pipe() |
|
135 go tee(pr, w, "C: ") |
|
136 w = pw |
|
137 } |
|
138 defer func(w io.Writer) { |
|
139 if c, ok := w.(io.Closer); ok { |
|
140 c.Close() |
|
141 } |
|
142 }(w) |
|
143 |
|
144 enc := xml.NewEncoder(w) |
|
145 |
|
146 for obj := range ch { |
|
147 if st, ok := obj.(*stream); ok { |
|
148 _, err := w.Write([]byte(st.String())) |
|
149 if err != nil { |
|
150 Warn.Logf("write: %s", err) |
|
151 } |
|
152 } else { |
|
153 err := enc.Encode(obj) |
|
154 if err != nil { |
|
155 Warn.Logf("marshal: %s", err) |
|
156 break |
|
157 } |
|
158 } |
|
159 } |
|
160 } |