1package xmpp
2
3import (
4	"encoding/xml"
5	"fmt"
6	"io"
7	"time"
8
9	"github.com/coyim/coyim/xmpp/data"
10	. "gopkg.in/check.v1"
11)
12
13type DiscoveryXMPPSuite struct{}
14
15var _ = Suite(&DiscoveryXMPPSuite{})
16
17func (s *DiscoveryXMPPSuite) Test_SendDiscoveryInfoRequest(c *C) {
18	mockOut := &mockConnIOReaderWriter{}
19	conn := conn{
20		out: mockOut,
21		jid: "juliet@example.com/chamber",
22	}
23	conn.inflights = make(map[data.Cookie]inflight)
24
25	reply, cookie, err := conn.sendDiscoveryInfo("example.com")
26	c.Assert(err, IsNil)
27	c.Assert(string(mockOut.write), Matches, "<iq xmlns='jabber:client' to='example.com' from='juliet@example.com/chamber' type='get' id='.+'><query xmlns=\"http://jabber.org/protocol/disco#info\"></query></iq>")
28	c.Assert(reply, NotNil)
29	c.Assert(cookie, NotNil)
30}
31
32func (s *DiscoveryXMPPSuite) Test_ReceiveDiscoveryResult(c *C) {
33	// See: XEP-0030, Section: 3.1 Basic Protocol, Example: 2
34	fromServer := `
35<iq xmlns='jabber:client' type='result'
36    from='plays.shakespeare.lit'
37    to='romeo@montague.net/orchard'
38    id='100000'>
39  <query xmlns='http://jabber.org/protocol/disco#info'>
40    <identity
41        category='conference'
42        type='text'
43        name='Play-Specific Chatrooms'/>
44    <identity
45        category='directory'
46        type='chatroom'
47        name='Play-Specific Chatrooms'/>
48    <feature var='http://jabber.org/protocol/disco#info'/>
49    <feature var='http://jabber.org/protocol/disco#items'/>
50  </query>
51</iq>
52`
53	reply := make(chan data.Stanza, 1)
54	mockIn := &mockConnIOReaderWriter{read: []byte(fromServer)}
55	conn := conn{
56		in: xml.NewDecoder(mockIn),
57		inflights: map[data.Cookie]inflight{
58			0x100000: inflight{
59				to:        "plays.shakespeare.lit",
60				replyChan: reply,
61			},
62		},
63	}
64
65	_, err := conn.Next()
66	c.Assert(err, Equals, io.EOF)
67
68	iq, ok := (<-reply).Value.(*data.ClientIQ)
69	c.Assert(ok, Equals, true)
70
71	discoveryReply, err := parseDiscoveryInfoReply(iq)
72	c.Assert(err, IsNil)
73
74	c.Assert(discoveryReply, DeepEquals, &data.DiscoveryInfoQuery{
75		XMLName: xml.Name{Space: "http://jabber.org/protocol/disco#info", Local: "query"},
76		Identities: []data.DiscoveryIdentity{
77			data.DiscoveryIdentity{
78				XMLName:  xml.Name{Space: "http://jabber.org/protocol/disco#info", Local: "identity"},
79				Category: "conference",
80				Type:     "text",
81				Name:     "Play-Specific Chatrooms"},
82			data.DiscoveryIdentity{
83				XMLName:  xml.Name{Space: "http://jabber.org/protocol/disco#info", Local: "identity"},
84				Category: "directory",
85				Type:     "chatroom",
86				Name:     "Play-Specific Chatrooms"},
87		},
88		Features: []data.DiscoveryFeature{
89			data.DiscoveryFeature{
90				XMLName: xml.Name{Space: "http://jabber.org/protocol/disco#info", Local: "feature"},
91				Var:     "http://jabber.org/protocol/disco#info",
92			},
93			data.DiscoveryFeature{
94				XMLName: xml.Name{Space: "http://jabber.org/protocol/disco#info", Local: "feature"},
95				Var:     "http://jabber.org/protocol/disco#items",
96			},
97		},
98	})
99
100	_, ok = conn.inflights[0x100000]
101	c.Assert(ok, Equals, false)
102}
103
104func (s *DiscoveryXMPPSuite) Test_HasSupportTo(c *C) {
105	// See: XEP-0030, Section: 3.1 Basic Protocol, Example: 2
106	fromServer := `
107<iq xmlns='jabber:client' type='result'
108    from='plays.shakespeare.lit'
109    to='romeo@montague.net/orchard'
110    id='%s'>
111  <query xmlns='http://jabber.org/protocol/disco#info'>
112    <feature var='jabber:iq:privacy'/>
113    <feature var='http://jabber.org/protocol/disco#items'/>
114  </query>
115</iq>
116`
117	mockOut := &mockConnIOReaderWriter{}
118
119	conn := conn{
120		in:        xml.NewDecoder(nil),
121		out:       mockOut,
122		inflights: make(map[data.Cookie]inflight),
123
124		jid: "romeo@montague.net/orchard",
125	}
126
127	done := make(chan bool, 1)
128	go func() {
129		ok := conn.HasSupportTo("plays.shakespeare.lit", "jabber:iq:privacy")
130		c.Assert(ok, Equals, true)
131		done <- true
132	}()
133
134	<-time.After(1 * time.Millisecond)
135
136	var iq data.ClientIQ
137	c.Assert(string(mockOut.Written()), Matches, "<iq xmlns='jabber:client' to='plays.shakespeare.lit' from='romeo@montague.net/orchard' type='get' id='.+'><query xmlns=\"http://jabber.org/protocol/disco#info\"></query></iq>")
138
139	err := xml.Unmarshal(mockOut.Written(), &iq)
140	c.Assert(err, IsNil)
141
142	mockIn := &mockConnIOReaderWriter{read: []byte(fmt.Sprintf(fromServer, iq.ID))}
143	conn.in = xml.NewDecoder(mockIn)
144
145	_, err = conn.Next()
146	c.Assert(err, Equals, io.EOF)
147	<-done
148}
149
150func (s *DiscoveryXMPPSuite) Test_VerificationString_failsIfThereAreDuplicateIdentities(c *C) {
151	reply := &data.DiscoveryInfoQuery{
152		Identities: []data.DiscoveryIdentity{
153			data.DiscoveryIdentity{
154				Lang:     "en",
155				Category: "stuff",
156				Type:     "thing",
157				Name:     "something",
158			},
159			data.DiscoveryIdentity{
160				Lang:     "en",
161				Category: "stuff",
162				Type:     "thing",
163				Name:     "something",
164			},
165		},
166	}
167
168	_, err := VerificationString(reply)
169	c.Assert(err.Error(), Equals, "duplicate discovery identity")
170}
171
172func (s *DiscoveryXMPPSuite) Test_VerificationString_failsIfThereAreDuplicateFeatures(c *C) {
173	reply := &data.DiscoveryInfoQuery{
174		Features: []data.DiscoveryFeature{
175			data.DiscoveryFeature{
176				Var: "foo",
177			},
178			data.DiscoveryFeature{
179				Var: "foo",
180			},
181		},
182	}
183
184	_, err := VerificationString(reply)
185	c.Assert(err.Error(), Equals, "duplicate discovery feature")
186}
187
188func (s *DiscoveryXMPPSuite) Test_VerificationString_failsIfThereAreDuplicateFormTypes(c *C) {
189	reply := &data.DiscoveryInfoQuery{
190		Forms: []data.Form{
191			data.Form{
192				Type: "foo",
193				Fields: []data.FormFieldX{
194					data.FormFieldX{
195						Var:    "FORM_TYPE",
196						Type:   "Foo",
197						Values: []string{"foo"},
198					},
199				},
200			},
201			data.Form{
202				Type: "foo",
203				Fields: []data.FormFieldX{
204					data.FormFieldX{
205						Var:    "FORM_TYPE",
206						Type:   "Foo",
207						Values: []string{"foo"},
208					},
209				},
210			},
211		},
212	}
213
214	_, err := VerificationString(reply)
215	c.Assert(err.Error(), Equals, "multiple forms of the same type")
216}
217
218func (s *DiscoveryXMPPSuite) Test_VerificationString_failsIfThereAreNoValues(c *C) {
219	reply := &data.DiscoveryInfoQuery{
220		Forms: []data.Form{
221			data.Form{
222				Type: "foo",
223			},
224			data.Form{
225				Type: "foo",
226				Fields: []data.FormFieldX{
227					data.FormFieldX{
228						Var:  "FORM_TYPE2",
229						Type: "Foo",
230					},
231				},
232			},
233			data.Form{
234				Type: "foo",
235				Fields: []data.FormFieldX{
236					data.FormFieldX{
237						Var:  "FORM_TYPE",
238						Type: "Foo",
239					},
240				},
241			},
242			data.Form{
243				Type: "foo",
244				Fields: []data.FormFieldX{
245					data.FormFieldX{
246						Var:  "FORM_TYPE",
247						Type: "Foo",
248					},
249				},
250			},
251		},
252	}
253
254	_, err := VerificationString(reply)
255	c.Assert(err.Error(), Equals, "form does not have a single FORM_TYPE value")
256}
257
258func (s *DiscoveryXMPPSuite) Test_DiscoveryReply_returnsSupportedValues(c *C) {
259	rep := DiscoveryReply("foo@bar.com")
260	c.Assert(rep, DeepEquals,
261		data.DiscoveryInfoQuery{
262			XMLName: xml.Name{Space: "", Local: ""},
263			Node:    "",
264			Identities: []data.DiscoveryIdentity{
265				data.DiscoveryIdentity{
266					XMLName:  xml.Name{Space: "", Local: ""},
267					Lang:     "",
268					Category: "client",
269					Type:     "pc",
270					Name:     "foo@bar.com"}},
271			Features: []data.DiscoveryFeature{
272				{Var: "http://jabber.org/protocol/disco#info"},
273				{Var: "urn:xmpp:bob"},
274				{Var: "urn:xmpp:ping"},
275				{Var: "http://jabber.org/protocol/caps"},
276				{Var: "jabber:iq:version"},
277				{Var: "vcard-temp"},
278				{Var: "jabber:x:data"},
279				{Var: "http://jabber.org/protocol/si"},
280				{Var: "http://jabber.org/protocol/si/profile/file-transfer"},
281				{Var: "http://jabber.org/protocol/si/profile/directory-transfer"},
282				//				{Var: "http://jabber.org/protocol/si/profile/encrypted-data-transfer"},
283				{Var: "http://jabber.org/protocol/bytestreams"},
284			},
285			Forms: []data.Form(nil)})
286}
287