3 // This file contains data structures. |
3 // This file contains data structures. |
4 |
4 |
5 import ( |
5 import ( |
6 "bytes" |
6 "bytes" |
7 "encoding/xml" |
7 "encoding/xml" |
8 "flag" |
|
9 "fmt" |
8 "fmt" |
10 // BUG(cjyar): Doesn't use stringprep. Could try the implementation at |
9 "log" |
11 // "code.google.com/p/go-idn/src/stringprep" |
|
12 "reflect" |
10 "reflect" |
13 "regexp" |
|
14 "strings" |
11 "strings" |
15 ) |
12 ) |
|
13 |
|
14 // BUG(cjyar): Doesn't use stringprep. Could try the implementation at |
|
15 // "code.google.com/p/go-idn/src/stringprep" |
16 |
16 |
17 // JID represents an entity that can communicate with other |
17 // JID represents an entity that can communicate with other |
18 // entities. It looks like node@domain/resource. Node and resource are |
18 // entities. It looks like node@domain/resource. Node and resource are |
19 // sometimes optional. |
19 // sometimes optional. |
20 type JID struct { |
20 type JID string |
21 Node string |
|
22 Domain string |
|
23 Resource string |
|
24 } |
|
25 |
|
26 var _ fmt.Stringer = &JID{} |
|
27 var _ flag.Value = &JID{} |
|
28 |
21 |
29 // XMPP's <stream:stream> XML element |
22 // XMPP's <stream:stream> XML element |
30 type stream struct { |
23 type stream struct { |
31 XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"` |
24 XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"` |
32 To string `xml:"to,attr"` |
25 To string `xml:"to,attr"` |
80 } |
73 } |
81 |
74 |
82 // One of the three core XMPP stanza types: iq, message, presence. See |
75 // One of the three core XMPP stanza types: iq, message, presence. See |
83 // RFC3920, section 9. |
76 // RFC3920, section 9. |
84 type Header struct { |
77 type Header struct { |
85 To string `xml:"to,attr,omitempty"` |
78 To JID `xml:"to,attr,omitempty"` |
86 From string `xml:"from,attr,omitempty"` |
79 From JID `xml:"from,attr,omitempty"` |
87 Id string `xml:"id,attr,omitempty"` |
80 Id string `xml:"id,attr,omitempty"` |
88 Type string `xml:"type,attr,omitempty"` |
81 Type string `xml:"type,attr,omitempty"` |
89 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` |
82 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` |
90 Innerxml string `xml:",innerxml"` |
83 Innerxml string `xml:",innerxml"` |
91 Error *Error |
84 Error *Error |
94 |
87 |
95 // message stanza |
88 // message stanza |
96 type Message struct { |
89 type Message struct { |
97 XMLName xml.Name `xml:"jabber:client message"` |
90 XMLName xml.Name `xml:"jabber:client message"` |
98 Header |
91 Header |
99 Subject *Generic `xml:"jabber:client subject"` |
92 Subject []Text `xml:"jabber:client subject"` |
100 Body *Generic `xml:"jabber:client body"` |
93 Body []Text `xml:"jabber:client body"` |
101 Thread *Generic `xml:"jabber:client thread"` |
94 Thread *Data `xml:"jabber:client thread"` |
102 } |
95 } |
103 |
96 |
104 var _ Stanza = &Message{} |
97 var _ Stanza = &Message{} |
105 |
98 |
106 // presence stanza |
99 // presence stanza |
107 type Presence struct { |
100 type Presence struct { |
108 XMLName xml.Name `xml:"presence"` |
101 XMLName xml.Name `xml:"presence"` |
109 Header |
102 Header |
110 Show *Generic `xml:"jabber:client show"` |
103 Show *Data `xml:"jabber:client show"` |
111 Status *Generic `xml:"jabber:client status"` |
104 Status []Text `xml:"jabber:client status"` |
112 Priority *Generic `xml:"jabber:client priority"` |
105 Priority *Data `xml:"jabber:client priority"` |
113 } |
106 } |
114 |
107 |
115 var _ Stanza = &Presence{} |
108 var _ Stanza = &Presence{} |
116 |
109 |
117 // iq stanza |
110 // iq stanza |
135 |
128 |
136 // Used for resource binding as a nested element inside <iq/>. |
129 // Used for resource binding as a nested element inside <iq/>. |
137 type bindIq struct { |
130 type bindIq struct { |
138 XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"` |
131 XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"` |
139 Resource *string `xml:"resource"` |
132 Resource *string `xml:"resource"` |
140 Jid *string `xml:"jid"` |
133 Jid *JID `xml:"jid"` |
|
134 } |
|
135 |
|
136 // Holds human-readable text, with an optional language |
|
137 // specification. Generally multiple instances of these can be found |
|
138 // together, allowing the software to choose which language to present |
|
139 // to the user. |
|
140 type Text struct { |
|
141 XMLName xml.Name |
|
142 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` |
|
143 Chardata string `xml:",chardata"` |
|
144 } |
|
145 |
|
146 // Non-human-readable content of some sort, used by the protocol. |
|
147 type Data struct { |
|
148 XMLName xml.Name |
|
149 Chardata string `xml:",chardata"` |
141 } |
150 } |
142 |
151 |
143 // Holds an XML element not described by the more specific types. |
152 // Holds an XML element not described by the more specific types. |
144 type Generic struct { |
153 type Generic struct { |
145 XMLName xml.Name |
154 XMLName xml.Name |
147 Chardata string `xml:",chardata"` |
156 Chardata string `xml:",chardata"` |
148 } |
157 } |
149 |
158 |
150 var _ fmt.Stringer = &Generic{} |
159 var _ fmt.Stringer = &Generic{} |
151 |
160 |
152 func (jid *JID) String() string { |
161 func (j JID) Node() string { |
153 result := jid.Domain |
162 at := strings.Index(string(j), "@") |
154 if jid.Node != "" { |
163 if at == -1 { |
155 result = jid.Node + "@" + result |
164 return "" |
156 } |
165 } |
157 if jid.Resource != "" { |
166 return string(j[:at]) |
158 result = result + "/" + jid.Resource |
167 } |
159 } |
168 |
160 return result |
169 func (j JID) Domain() string { |
161 } |
170 at := strings.Index(string(j), "@") |
162 |
171 slash := strings.LastIndex(string(j), "/") |
163 // Set implements flag.Value. It returns true if it successfully |
172 if slash == -1 { |
164 // parses the string. |
173 slash = len(j) |
165 func (jid *JID) Set(val string) error { |
174 } |
166 r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$") |
175 return string(j[at+1 : slash]) |
167 parts := r.FindStringSubmatch(val) |
176 } |
168 if parts == nil { |
177 |
169 return fmt.Errorf("%s doesn't match user@domain/resource", val) |
178 func (j JID) Resource() string { |
170 } |
179 slash := strings.LastIndex(string(j), "/") |
171 // jid.Node = stringprep.Nodeprep(parts[2]) |
180 if slash == -1 { |
172 // jid.Domain = stringprep.Nodeprep(parts[3]) |
181 return "" |
173 // jid.Resource = stringprep.Resourceprep(parts[5]) |
182 } |
174 jid.Node = parts[2] |
183 return string(j[slash+1:]) |
175 jid.Domain = parts[3] |
184 } |
176 jid.Resource = parts[5] |
185 |
177 return nil |
186 // Returns the bare JID, which is the JID without the resource part. |
|
187 func (j JID) Bare() JID { |
|
188 node := j.Node() |
|
189 if node == "" { |
|
190 return JID(j.Domain()) |
|
191 } |
|
192 return JID(fmt.Sprintf("%s@%s", node, j.Domain())) |
178 } |
193 } |
179 |
194 |
180 func (s *stream) String() string { |
195 func (s *stream) String() string { |
181 var buf bytes.Buffer |
196 var buf bytes.Buffer |
182 buf.WriteString(`<stream:stream xmlns="`) |
197 buf.WriteString(`<stream:stream xmlns="`) |
258 } |
273 } |
259 |
274 |
260 func (er *Error) Error() string { |
275 func (er *Error) Error() string { |
261 buf, err := xml.Marshal(er) |
276 buf, err := xml.Marshal(er) |
262 if err != nil { |
277 if err != nil { |
263 Warn.Log("double bad error: couldn't marshal error") |
278 log.Println("double bad error: couldn't marshal error") |
264 return "unreadable error" |
279 return "unreadable error" |
265 } |
280 } |
266 return string(buf) |
281 return string(buf) |
267 } |
282 } |
268 |
283 |
269 var bindExt Extension = Extension{} |
284 var bindExt Extension = Extension{} |
270 |
285 |
271 func init() { |
286 func init() { |
272 bindExt.StanzaHandlers = make(map[xml.Name]reflect.Type) |
287 bindExt.StanzaTypes = make(map[xml.Name]reflect.Type) |
273 bName := xml.Name{Space: NsBind, Local: "bind"} |
288 bName := xml.Name{Space: NsBind, Local: "bind"} |
274 bindExt.StanzaHandlers[bName] = reflect.TypeOf(bindIq{}) |
289 bindExt.StanzaTypes[bName] = reflect.TypeOf(bindIq{}) |
275 } |
290 } |