1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <string.h>
8 
9 #include <glib.h>
10 
11 #include <wocky/wocky.h>
12 
13 #include "wocky-test-helper.h"
14 
15 #define HEADER \
16 "<?xml version='1.0' encoding='UTF-8'?>                                    " \
17 "<stream:stream xmlns='jabber:client'                                      " \
18 "  xmlns:stream='http://etherx.jabber.org/streams'>                        "
19 
20 #define FOOTER "</stream:stream> "
21 
22 #define BROKEN_HEADER \
23 "<?xml version='1.0' encoding='UTF-8'?>                                    " \
24 "<stream:streamsss xmlns='jabber:client'                                   " \
25 "  xmlns:stream='http://etherx.jabber.org/streams'>                        "
26 
27 #define HEADER_WITH_UNQUALIFIED_LANG \
28 "<?xml version='1.0' encoding='UTF-8'?>                                    " \
29 "<stream:stream xmlns='jabber:client'                                      " \
30 "  xmlns:stream='http://etherx.jabber.org/streams'                         " \
31 "  lang='fi'>                                                              "
32 
33 #define BROKEN_MESSAGE \
34 "  <message to='juliet@example.com' from='romeo@example.net' xml:lang='en' " \
35 "   id=\"0\">                                                              " \
36 "    <body>Art thou not Romeo, and a Montague?</ody>                       " \
37 "  </essage>                                                               "
38 
39 #define MESSAGE_CHUNK0 \
40 "  <message to='juliet@example.com' from='romeo@example.net' xml:lang='en' " \
41 "   id=\"0\">                                                              " \
42 "    <body>Art thou not Romeo,                                             "
43 
44 #define MESSAGE_CHUNK1 \
45 "       and a Montague?</body>                                             " \
46 "  </message>                                                              "
47 
48 #define VCARD_MESSAGE \
49 " <iq id='v1'                                                    " \
50 "     to='stpeter@jabber.org/roundabout'                         " \
51 "     type='result'>                                             " \
52 "   <vCard xmlns='vcard-temp'>                                   " \
53 "     <FN>Peter Saint-Andre</FN>                                 " \
54 "     <N>                                                        " \
55 "      <FAMILY>Saint-Andre</FAMILY>                              " \
56 "      <GIVEN>Peter</GIVEN>                                      " \
57 "      <MIDDLE/>                                                 " \
58 "    </N>                                                        " \
59 "    <NICKNAME>stpeter</NICKNAME>                                " \
60 "    <URL>http://www.xmpp.org/xsf/people/stpeter.shtml</URL>     " \
61 "    <BDAY>1966-08-06</BDAY>                                     " \
62 "    <ORG>                                                       " \
63 "      <ORGNAME>XMPP Standards Foundation</ORGNAME>              " \
64 "      <ORGUNIT/>                                                " \
65 "    </ORG>                                                      " \
66 "    <TITLE>Executive Director</TITLE>                           " \
67 "    <ROLE>Patron Saint</ROLE>                                   " \
68 "    <JABBERID>stpeter@jabber.org</JABBERID>                     " \
69 "  </vCard>                                                      " \
70 "</iq>                                                           "
71 
72 #define VALID_NAMESPACE "http://garden.with.spaces"
73 #define INVALID_NAMESPACE_MESSAGE \
74 "<iq id='badger0' to='plant@collabora.cbg'  type='result'> "\
75 " <branch xmlns='  "VALID_NAMESPACE"                '>     " \
76 "   <leaf colour='green' />                                " \
77 " </branch>                                                " \
78 "</iq>                                                     "
79 
80 #define WHITESPACE_PADDED_BODY "  The Wench is Dead!  "
81 
82 #define MESSAGE_WITH_WHITESPACE_PADDED_BODY \
83 "  <message to='morse@thamesvalley.police.uk' " \
84 "           from='lewis@thamesvalley.police.uk'> " \
85 "    <body>" WHITESPACE_PADDED_BODY "</body>" \
86 "  </message>"
87 
88 
89 #define WHITESPACE_ONLY_BODY "    "
90 
91 #define MESSAGE_WITH_WHITESPACE_ONLY_BODY \
92 "  <message to='morse@thamesvalley.police.uk' " \
93 "           from='lewis@thamesvalley.police.uk'> " \
94 "    <body>" WHITESPACE_ONLY_BODY "</body>" \
95 "  </message>"
96 
97 #define U_FDEF "\xe7\xb7\xaf"     /* a non-character */
98 #define REPLACE "\xef\xbf\xbd"    /* U+FFFD REPLACEMENT CHARACTER */
99 #define MONKEY "\xf0\x9f\x99\x88" /* U+1F648 SEE-NO-EVIL MONKEY */
100 
101 #define NON_CHARACTER_CODEPOINTS U_FDEF MONKEY U_FDEF
102 #define NON_CHARACTER_CODEPOINTS_REPLACEMENT REPLACE MONKEY REPLACE
103 
104 #define MESSAGE_WITH_NON_CHARACTER_CODEPOINTS \
105 "  <message to='morse@thamesvalley.police.uk' " \
106 "           from='lewis@thamesvalley.police.uk'> " \
107 "    <body>" NON_CHARACTER_CODEPOINTS "</body>" \
108 "  </message>"
109 
110 
111 
112 static void
test_stream_no_stanzas(void)113 test_stream_no_stanzas (void)
114 {
115   WockyXmppReader *reader;
116   GError *error = NULL;
117 
118   reader = wocky_xmpp_reader_new ();
119 
120   g_assert (wocky_xmpp_reader_get_state (reader)
121     == WOCKY_XMPP_READER_STATE_INITIAL);
122 
123   wocky_xmpp_reader_push (reader,
124     (guint8 *) HEADER FOOTER, strlen (HEADER FOOTER));
125 
126   g_assert (wocky_xmpp_reader_get_state (reader)
127     == WOCKY_XMPP_READER_STATE_CLOSED);
128   g_assert (wocky_xmpp_reader_peek_stanza (reader) == NULL);
129 
130   g_assert (wocky_xmpp_reader_get_state (reader)
131     == WOCKY_XMPP_READER_STATE_CLOSED);
132   g_assert (wocky_xmpp_reader_pop_stanza (reader) == NULL);
133 
134   g_assert (wocky_xmpp_reader_get_state (reader)
135     == WOCKY_XMPP_READER_STATE_CLOSED);
136 
137   error = wocky_xmpp_reader_get_error (reader);
138 
139   g_assert_no_error (error);
140 
141   g_object_unref (reader);
142 }
143 
144 static void
test_stream_open_error(void)145 test_stream_open_error (void)
146 {
147   WockyXmppReader *reader;
148   GError *error = NULL;
149 
150   reader = wocky_xmpp_reader_new ();
151 
152   g_assert (wocky_xmpp_reader_get_state (reader)
153     == WOCKY_XMPP_READER_STATE_INITIAL);
154 
155   wocky_xmpp_reader_push (reader,
156     (guint8 *) BROKEN_HEADER, strlen (BROKEN_HEADER));
157 
158   g_assert (wocky_xmpp_reader_get_state (reader)
159     == WOCKY_XMPP_READER_STATE_ERROR);
160 
161   error = wocky_xmpp_reader_get_error (reader);
162 
163   g_assert_error (error, WOCKY_XMPP_READER_ERROR,
164       WOCKY_XMPP_READER_ERROR_INVALID_STREAM_START);
165 
166   g_error_free (error);
167 
168   g_object_unref (reader);
169 }
170 
171 static void
test_stream_open_unqualified_lang(void)172 test_stream_open_unqualified_lang (void)
173 {
174   WockyXmppReader *reader = wocky_xmpp_reader_new ();
175 
176   g_assert (wocky_xmpp_reader_get_state (reader)
177     == WOCKY_XMPP_READER_STATE_INITIAL);
178 
179   wocky_xmpp_reader_push (reader,
180     (guint8 *) HEADER_WITH_UNQUALIFIED_LANG,
181     strlen (HEADER_WITH_UNQUALIFIED_LANG));
182 
183   g_assert (wocky_xmpp_reader_get_state (reader)
184     == WOCKY_XMPP_READER_STATE_OPENED);
185 
186   g_object_unref (reader);
187 }
188 
189 static void
test_parse_error(void)190 test_parse_error (void)
191 {
192   WockyXmppReader *reader;
193   GError *error = NULL;
194 
195   reader = wocky_xmpp_reader_new ();
196 
197   g_assert (wocky_xmpp_reader_get_state (reader)
198     == WOCKY_XMPP_READER_STATE_INITIAL);
199 
200   wocky_xmpp_reader_push (reader,
201     (guint8 *) HEADER, strlen (HEADER));
202 
203   g_assert (wocky_xmpp_reader_get_state (reader)
204     == WOCKY_XMPP_READER_STATE_OPENED);
205 
206   wocky_xmpp_reader_push (reader,
207     (guint8 *) BROKEN_MESSAGE, strlen (BROKEN_MESSAGE));
208 
209   g_assert (wocky_xmpp_reader_get_state (reader)
210     == WOCKY_XMPP_READER_STATE_ERROR);
211 
212   g_assert (wocky_xmpp_reader_peek_stanza (reader) == NULL);
213   g_assert (wocky_xmpp_reader_get_state (reader)
214     == WOCKY_XMPP_READER_STATE_ERROR);
215 
216   g_assert (wocky_xmpp_reader_pop_stanza (reader) == NULL);
217   g_assert (wocky_xmpp_reader_get_state (reader)
218     == WOCKY_XMPP_READER_STATE_ERROR);
219 
220   error = wocky_xmpp_reader_get_error (reader);
221 
222   g_assert_error (error, WOCKY_XMPP_READER_ERROR,
223     WOCKY_XMPP_READER_ERROR_PARSE_ERROR);
224 
225   g_error_free (error);
226   g_object_unref (reader);
227 }
228 
229 static void
test_no_stream_parse_message(WockyXmppReader * reader)230 test_no_stream_parse_message (WockyXmppReader *reader)
231 {
232   WockyStanza *stanza;
233 
234   g_assert (wocky_xmpp_reader_get_state (reader)
235     == WOCKY_XMPP_READER_STATE_OPENED);
236 
237   wocky_xmpp_reader_push (reader,
238     (guint8 *) MESSAGE_CHUNK0, strlen (MESSAGE_CHUNK0));
239 
240   g_assert (wocky_xmpp_reader_pop_stanza (reader) == NULL);
241 
242   g_assert (wocky_xmpp_reader_get_state (reader)
243     == WOCKY_XMPP_READER_STATE_OPENED);
244 
245   wocky_xmpp_reader_push (reader,
246     (guint8 *) MESSAGE_CHUNK1, strlen (MESSAGE_CHUNK1));
247 
248   g_assert (wocky_xmpp_reader_get_state (reader)
249     == WOCKY_XMPP_READER_STATE_OPENED);
250 
251   g_assert ((stanza = wocky_xmpp_reader_peek_stanza (reader)) != NULL);
252   g_assert ((stanza = wocky_xmpp_reader_pop_stanza (reader)) != NULL);
253   g_assert (wocky_xmpp_reader_get_state (reader)
254     == WOCKY_XMPP_READER_STATE_CLOSED);
255 
256   g_object_unref (stanza);
257 
258 }
259 
260 static void
test_no_stream_hunks(void)261 test_no_stream_hunks (void)
262 {
263   WockyXmppReader *reader;
264 
265   reader = wocky_xmpp_reader_new_no_stream ();
266   test_no_stream_parse_message (reader);
267 
268   g_object_unref (reader);
269 }
270 
271 static void
test_no_stream_reset(void)272 test_no_stream_reset (void)
273 {
274   WockyXmppReader *reader;
275 
276   reader = wocky_xmpp_reader_new_no_stream ();
277 
278   /* whole message, reset, whole message, reset */
279   test_no_stream_parse_message (reader);
280   wocky_xmpp_reader_reset (reader);
281 
282   test_no_stream_parse_message (reader);
283   wocky_xmpp_reader_reset (reader);
284 
285   /* push half a message and reset the parser*/
286   g_assert (wocky_xmpp_reader_get_state (reader)
287     == WOCKY_XMPP_READER_STATE_OPENED);
288   wocky_xmpp_reader_push (reader,
289     (guint8 *) MESSAGE_CHUNK0, strlen (MESSAGE_CHUNK0));
290   wocky_xmpp_reader_reset (reader);
291 
292   /* And push a whole message through again */
293   test_no_stream_parse_message (reader);
294 
295   g_object_unref (reader);
296 }
297 
298 /* libXML2 doesn't like the vcard-temp namespace test if we can still
299    correctly parse it */
300 static void
test_vcard_namespace(void)301 test_vcard_namespace (void)
302 {
303   WockyXmppReader *reader;
304   WockyStanza *stanza;
305 
306   reader = wocky_xmpp_reader_new_no_stream ();
307 
308   wocky_xmpp_reader_push (reader,
309     (guint8 *) VCARD_MESSAGE, strlen (VCARD_MESSAGE));
310 
311   g_assert ((stanza = wocky_xmpp_reader_pop_stanza (reader)) != NULL);
312   g_assert (wocky_xmpp_reader_get_state (reader)
313     == WOCKY_XMPP_READER_STATE_CLOSED);
314 
315   g_object_unref (stanza);
316   g_object_unref (reader);
317 }
318 
319 static void
test_invalid_namespace(void)320 test_invalid_namespace (void)
321 {
322   WockyXmppReader *reader;
323   WockyStanza *stanza;
324 
325   reader = wocky_xmpp_reader_new_no_stream ();
326 
327   wocky_xmpp_reader_push (reader,
328     (guint8 *) INVALID_NAMESPACE_MESSAGE, strlen (INVALID_NAMESPACE_MESSAGE));
329 
330   g_assert ((stanza = wocky_xmpp_reader_pop_stanza (reader)) != NULL);
331   g_assert (wocky_xmpp_reader_get_state (reader)
332     == WOCKY_XMPP_READER_STATE_CLOSED);
333 
334   g_assert_cmpstr (VALID_NAMESPACE, ==,
335     wocky_node_get_ns (
336       wocky_node_get_child (wocky_stanza_get_top_node (stanza), "branch")));
337 
338   g_object_unref (stanza);
339   g_object_unref (reader);
340 }
341 
342 /* Helper function for the whitespace body tests */
343 static void
test_body_with_alternative(const gchar * xml,const gchar * expected_body_text,const gchar * alt_body_text)344 test_body_with_alternative (
345     const gchar *xml,
346     const gchar *expected_body_text,
347     const gchar *alt_body_text)
348 {
349   WockyXmppReader *reader = wocky_xmpp_reader_new_no_stream ();
350   WockyStanza *stanza;
351   WockyNode *body;
352 
353   wocky_xmpp_reader_push (reader, (guint8 *) xml, strlen (xml));
354 
355   stanza = wocky_xmpp_reader_pop_stanza (reader);
356   g_assert (stanza != NULL);
357 
358   body = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "body");
359   g_assert (body != NULL);
360 
361   g_assert (g_utf8_validate (body->content, -1, NULL));
362 
363   if (alt_body_text == NULL)
364     {
365       g_assert_cmpstr (body->content, ==, expected_body_text);
366     }
367   else
368     {
369       if (wocky_strdiff (body->content, expected_body_text) &&
370           wocky_strdiff (body->content, alt_body_text))
371         {
372           g_error ("Body text «%s» was neither «%s» nor «%s»",
373               body->content, expected_body_text, alt_body_text);
374         }
375     }
376 
377   g_object_unref (stanza);
378   g_object_unref (reader);
379 }
380 
381 static void
test_body(const gchar * xml,const gchar * exp)382 test_body (const gchar *xml,
383     const gchar *exp)
384 {
385   test_body_with_alternative (xml, exp, NULL);
386 }
387 
388 /* Test that whitespace around the text contents of a message isn't ignored */
389 static void
test_whitespace_padding(void)390 test_whitespace_padding (void)
391 {
392   test_body (MESSAGE_WITH_WHITESPACE_PADDED_BODY, WHITESPACE_PADDED_BODY);
393 }
394 
395 /* Test that a message body consisting entirely of whitespace isn't ignored */
396 static void
test_whitespace_only(void)397 test_whitespace_only (void)
398 {
399   test_body (MESSAGE_WITH_WHITESPACE_ONLY_BODY, WHITESPACE_ONLY_BODY);
400 }
401 
402 /* Test that a message body containing non-character codepoints is
403  * handled "appropriately". Older GLib replaces them with U+FFFD,
404  * newer GLib keeps them as-is. */
405 static void
test_non_character_codepoints(void)406 test_non_character_codepoints (void)
407 {
408   test_body_with_alternative (MESSAGE_WITH_NON_CHARACTER_CODEPOINTS,
409     NON_CHARACTER_CODEPOINTS,
410     NON_CHARACTER_CODEPOINTS_REPLACEMENT);
411 }
412 
413 static void
check_namespaces(WockyXmppReader * reader,const gchar * xml,const gchar * expected_namespace)414 check_namespaces (
415     WockyXmppReader *reader,
416     const gchar *xml,
417     const gchar *expected_namespace)
418 {
419   WockyStanza *stanza;
420   WockyNode *top_node, *body_node;
421 
422   wocky_xmpp_reader_push (reader, (const guint8 *) xml, strlen (xml));
423   stanza = wocky_xmpp_reader_pop_stanza (reader);
424   g_assert (stanza != NULL);
425   top_node = wocky_stanza_get_top_node (stanza);
426   g_assert (top_node != NULL);
427   g_assert_cmpstr (expected_namespace, ==, wocky_node_get_ns (top_node));
428   body_node = wocky_node_get_first_child (top_node);
429   g_assert (body_node != NULL);
430   g_assert_cmpstr (expected_namespace, ==, wocky_node_get_ns (body_node));
431 
432   g_object_unref (stanza);
433   wocky_xmpp_reader_reset (reader);
434 }
435 
436 static void
test_no_stream_default_namespace(WockyXmppReader * reader,const gchar * expected_default_namespace)437 test_no_stream_default_namespace (
438     WockyXmppReader *reader,
439     const gchar *expected_default_namespace)
440 {
441   /* Regardless of the reader's default namespace, a root node with an
442    * explicitly-specified namespace should get that namespace.
443    */
444   check_namespaces (reader,
445       "<message xmlns='" WOCKY_XMPP_NS_JABBER_CLIENT "'>"
446       "<body>hai</body></message>",
447       WOCKY_XMPP_NS_JABBER_CLIENT);
448 
449   /* What namespace the nodes here end up in depends on the reader's default.
450    */
451   check_namespaces (reader, "<message><body>hai</body></message>",
452       expected_default_namespace);
453 }
454 
455 static void
test_no_stream_default_default_namespace(void)456 test_no_stream_default_default_namespace (void)
457 {
458   WockyXmppReader *reader = wocky_xmpp_reader_new_no_stream ();
459 
460   /* WockyXmppReader defaults to the empty namespace. */
461   test_no_stream_default_namespace (reader, "");
462 
463   g_object_unref (reader);
464 }
465 
466 static void
test_no_stream_specified_default_namespace(void)467 test_no_stream_specified_default_namespace (void)
468 {
469 #define WEIRD "wocky:weird:namespace"
470   WockyXmppReader *reader = wocky_xmpp_reader_new_no_stream_ns (WEIRD);
471 
472   test_no_stream_default_namespace (reader, WEIRD);
473 
474   g_object_unref (reader);
475 #undef WEIRD
476 }
477 
478 int
main(int argc,char ** argv)479 main (int argc,
480     char **argv)
481 {
482   int result;
483 
484   test_init (argc, argv);
485 
486   g_test_add_func ("/xmpp-reader/stream-no-stanzas", test_stream_no_stanzas);
487   g_test_add_func ("/xmpp-reader/stream-open-error", test_stream_open_error);
488   g_test_add_func ("/xmpp-reader/stream-open-unqualified-lang",
489       test_stream_open_unqualified_lang);
490   g_test_add_func ("/xmpp-reader/parse-error", test_parse_error);
491   g_test_add_func ("/xmpp-reader/no-stream-hunks", test_no_stream_hunks);
492   g_test_add_func ("/xmpp-reader/no-stream-resetting", test_no_stream_reset);
493   g_test_add_func ("/xmpp-reader/vcard-namespace", test_vcard_namespace);
494   g_test_add_func ("/xmpp-reader/invalid-namespace", test_invalid_namespace);
495   g_test_add_func ("/xmpp-reader/whitespace-padding", test_whitespace_padding);
496   g_test_add_func ("/xmpp-reader/whitespace-only", test_whitespace_only);
497   g_test_add_func ("/xmpp-reader/utf-non-character-codepoints",
498     test_non_character_codepoints);
499   g_test_add_func ("/xmpp-reader/no-stream-default-default-namespace",
500       test_no_stream_default_default_namespace);
501   g_test_add_func ("/xmpp-reader/no-stream-specified-default-namespace",
502       test_no_stream_specified_default_namespace);
503 
504   result = g_test_run ();
505   test_deinit ();
506   return result;
507 }
508