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 "log" |
9 "log" |
11 "reflect" |
10 "reflect" |
12 "regexp" |
|
13 "strings" |
11 "strings" |
14 ) |
12 ) |
15 |
13 |
16 // BUG(cjyar): Doesn't use stringprep. Could try the implementation at |
14 // BUG(cjyar): Doesn't use stringprep. Could try the implementation at |
17 // "code.google.com/p/go-idn/src/stringprep" |
15 // "code.google.com/p/go-idn/src/stringprep" |
18 |
16 |
19 // JID represents an entity that can communicate with other |
17 // JID represents an entity that can communicate with other |
20 // entities. It looks like node@domain/resource. Node and resource are |
18 // entities. It looks like node@domain/resource. Node and resource are |
21 // sometimes optional. |
19 // sometimes optional. |
22 type JID struct { |
20 type JID string |
23 Node string |
|
24 Domain string |
|
25 Resource string |
|
26 } |
|
27 |
|
28 var _ fmt.Stringer = &JID{} |
|
29 var _ flag.Value = &JID{} |
|
30 |
21 |
31 // XMPP's <stream:stream> XML element |
22 // XMPP's <stream:stream> XML element |
32 type stream struct { |
23 type stream struct { |
33 XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"` |
24 XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"` |
34 To string `xml:"to,attr"` |
25 To string `xml:"to,attr"` |
96 |
87 |
97 // message stanza |
88 // message stanza |
98 type Message struct { |
89 type Message struct { |
99 XMLName xml.Name `xml:"jabber:client message"` |
90 XMLName xml.Name `xml:"jabber:client message"` |
100 Header |
91 Header |
101 Subject []Text `xml:"jabber:client subject"` |
92 Subject []Text `xml:"jabber:client subject"` |
102 Body []Text `xml:"jabber:client body"` |
93 Body []Text `xml:"jabber:client body"` |
103 Thread *Data `xml:"jabber:client thread"` |
94 Thread *Data `xml:"jabber:client thread"` |
104 } |
95 } |
105 |
96 |
106 var _ Stanza = &Message{} |
97 var _ Stanza = &Message{} |
107 |
98 |
108 // presence stanza |
99 // presence stanza |
109 type Presence struct { |
100 type Presence struct { |
110 XMLName xml.Name `xml:"presence"` |
101 XMLName xml.Name `xml:"presence"` |
111 Header |
102 Header |
112 Show *Data `xml:"jabber:client show"` |
103 Show *Data `xml:"jabber:client show"` |
113 Status []Text `xml:"jabber:client status"` |
104 Status []Text `xml:"jabber:client status"` |
114 Priority *Data `xml:"jabber:client priority"` |
105 Priority *Data `xml:"jabber:client priority"` |
115 } |
106 } |
116 |
107 |
117 var _ Stanza = &Presence{} |
108 var _ Stanza = &Presence{} |
118 |
109 |
119 // iq stanza |
110 // iq stanza |
145 // Holds human-readable text, with an optional language |
136 // Holds human-readable text, with an optional language |
146 // specification. Generally multiple instances of these can be found |
137 // specification. Generally multiple instances of these can be found |
147 // together, allowing the software to choose which language to present |
138 // together, allowing the software to choose which language to present |
148 // to the user. |
139 // to the user. |
149 type Text struct { |
140 type Text struct { |
150 XMLName xml.Name |
141 XMLName xml.Name |
151 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` |
142 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` |
152 Chardata string `xml:",chardata"` |
143 Chardata string `xml:",chardata"` |
153 } |
144 } |
154 |
145 |
155 // Non-human-readable content of some sort, used by the protocol. |
146 // Non-human-readable content of some sort, used by the protocol. |
156 type Data struct { |
147 type Data struct { |
157 XMLName xml.Name |
148 XMLName xml.Name |
158 Chardata string `xml:",chardata"` |
149 Chardata string `xml:",chardata"` |
159 } |
150 } |
160 |
151 |
161 // Holds an XML element not described by the more specific types. |
152 // Holds an XML element not described by the more specific types. |
162 type Generic struct { |
153 type Generic struct { |
165 Chardata string `xml:",chardata"` |
156 Chardata string `xml:",chardata"` |
166 } |
157 } |
167 |
158 |
168 var _ fmt.Stringer = &Generic{} |
159 var _ fmt.Stringer = &Generic{} |
169 |
160 |
170 func (jid *JID) String() string { |
161 func (j JID) Node() string { |
171 result := jid.Domain |
162 at := strings.Index(string(j), "@") |
172 if jid.Node != "" { |
163 if at == -1 { |
173 result = jid.Node + "@" + result |
164 return "" |
174 } |
165 } |
175 if jid.Resource != "" { |
166 return string(j[:at]) |
176 result = result + "/" + jid.Resource |
167 } |
177 } |
168 |
178 return result |
169 func (j JID) Domain() string { |
179 } |
170 at := strings.Index(string(j), "@") |
180 |
171 slash := strings.LastIndex(string(j), "/") |
181 // Set implements flag.Value. |
172 if slash == -1 { |
182 func (jid *JID) Set(val string) error { |
173 slash = len(j) |
183 r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$") |
174 } |
184 parts := r.FindStringSubmatch(val) |
175 return string(j[at+1 : slash]) |
185 if parts == nil { |
176 } |
186 return fmt.Errorf("%s doesn't match user@domain/resource", val) |
177 |
187 } |
178 func (j JID) Resource() string { |
188 // jid.Node = stringprep.Nodeprep(parts[2]) |
179 slash := strings.LastIndex(string(j), "/") |
189 // jid.Domain = stringprep.Nodeprep(parts[3]) |
180 if slash == -1 { |
190 // jid.Resource = stringprep.Resourceprep(parts[5]) |
181 return "" |
191 jid.Node = parts[2] |
182 } |
192 jid.Domain = parts[3] |
183 return string(j[slash+1:]) |
193 jid.Resource = parts[5] |
|
194 return nil |
|
195 } |
184 } |
196 |
185 |
197 func (s *stream) String() string { |
186 func (s *stream) String() string { |
198 var buf bytes.Buffer |
187 var buf bytes.Buffer |
199 buf.WriteString(`<stream:stream xmlns="`) |
188 buf.WriteString(`<stream:stream xmlns="`) |