Bug 623810 – Message serialization bug
[platform/upstream/glib.git] / gio / tests / gdbus-serialization.c
1 /* GLib testing framework examples and tests
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include <gio/gio.h>
24
25 #include <string.h>
26 #include <unistd.h>
27 #include <dbus/dbus.h>
28
29 /* ---------------------------------------------------------------------------------------------------- */
30
31 static void
32 hexdump (const guchar *str, gsize len)
33 {
34   const guchar *data = (const guchar *) str;
35   guint n, m;
36
37   for (n = 0; n < len; n += 16)
38     {
39       g_printerr ("%04x: ", n);
40
41       for (m = n; m < n + 16; m++)
42         {
43           if (m > n && (m%4) == 0)
44             g_printerr (" ");
45           if (m < len)
46             g_printerr ("%02x ", data[m]);
47           else
48             g_printerr ("   ");
49         }
50
51       g_printerr ("   ");
52
53       for (m = n; m < len && m < n + 16; m++)
54         g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.');
55
56       g_printerr ("\n");
57     }
58 }
59
60 /* ---------------------------------------------------------------------------------------------------- */
61
62 static gboolean
63 append_gv_to_dbus_iter (DBusMessageIter  *iter,
64                         GVariant         *value,
65                         GError          **error)
66 {
67   const GVariantType *type;
68
69   type = g_variant_get_type (value);
70   if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
71     {
72       dbus_bool_t v = g_variant_get_boolean (value);
73       dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v);
74     }
75   else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
76     {
77       guint8 v = g_variant_get_byte (value);
78       dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v);
79     }
80   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
81     {
82       gint16 v = g_variant_get_int16 (value);
83       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v);
84     }
85   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
86     {
87       guint16 v = g_variant_get_uint16 (value);
88       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v);
89     }
90   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
91     {
92       gint32 v = g_variant_get_int32 (value);
93       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v);
94     }
95   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
96     {
97       guint32 v = g_variant_get_uint32 (value);
98       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v);
99     }
100   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
101     {
102       gint64 v = g_variant_get_int64 (value);
103       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v);
104     }
105   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
106     {
107       guint64 v = g_variant_get_uint64 (value);
108       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v);
109     }
110   else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
111     {
112       gdouble v = g_variant_get_double (value);
113       dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v);
114     }
115   else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
116     {
117       const gchar *v = g_variant_get_string (value, NULL);
118       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v);
119     }
120   else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
121     {
122       const gchar *v = g_variant_get_string (value, NULL);
123       dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v);
124     }
125   else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
126     {
127       const gchar *v = g_variant_get_string (value, NULL);
128       dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v);
129     }
130   else if (g_variant_type_is_variant (type))
131     {
132       DBusMessageIter sub;
133       GVariant *child;
134
135       child = g_variant_get_child_value (value, 0);
136       dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT,
137                                         g_variant_get_type_string (child),
138                                         &sub);
139       if (!append_gv_to_dbus_iter (&sub, child, error))
140         {
141             g_variant_unref (child);
142             goto fail;
143         }
144       dbus_message_iter_close_container (iter, &sub);
145       g_variant_unref (child);
146     }
147   else if (g_variant_type_is_array (type))
148     {
149       DBusMessageIter dbus_iter;
150       const gchar *type_string;
151       GVariantIter gv_iter;
152       GVariant *item;
153
154       type_string = g_variant_get_type_string (value);
155       type_string++; /* skip the 'a' */
156
157       dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
158                                         type_string, &dbus_iter);
159       g_variant_iter_init (&gv_iter, value);
160
161       while ((item = g_variant_iter_next_value (&gv_iter)))
162         {
163           if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
164             {
165               goto fail;
166             }
167         }
168
169       dbus_message_iter_close_container (iter, &dbus_iter);
170     }
171   else if (g_variant_type_is_tuple (type))
172     {
173       DBusMessageIter dbus_iter;
174       GVariantIter gv_iter;
175       GVariant *item;
176
177       dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT,
178                                         NULL, &dbus_iter);
179       g_variant_iter_init (&gv_iter, value);
180
181       while ((item = g_variant_iter_next_value (&gv_iter)))
182         {
183           if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
184             goto fail;
185         }
186
187       dbus_message_iter_close_container (iter, &dbus_iter);
188     }
189   else if (g_variant_type_is_dict_entry (type))
190     {
191       DBusMessageIter dbus_iter;
192       GVariant *key, *val;
193
194       dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY,
195                                         NULL, &dbus_iter);
196       key = g_variant_get_child_value (value, 0);
197       if (!append_gv_to_dbus_iter (&dbus_iter, key, error))
198         {
199           g_variant_unref (key);
200           goto fail;
201         }
202       g_variant_unref (key);
203
204       val = g_variant_get_child_value (value, 1);
205       if (!append_gv_to_dbus_iter (&dbus_iter, val, error))
206         {
207           g_variant_unref (val);
208           goto fail;
209         }
210       g_variant_unref (val);
211
212       dbus_message_iter_close_container (iter, &dbus_iter);
213     }
214   else
215     {
216       g_set_error (error,
217                    G_IO_ERROR,
218                    G_IO_ERROR_INVALID_ARGUMENT,
219                    "Error serializing GVariant with type-string `%s' to a D-Bus message",
220                    g_variant_get_type_string (value));
221       goto fail;
222     }
223
224   return TRUE;
225
226  fail:
227   return FALSE;
228 }
229
230 static gboolean
231 append_gv_to_dbus_message (DBusMessage  *message,
232                            GVariant     *value,
233                            GError      **error)
234 {
235   gboolean ret;
236   guint n;
237
238   ret = FALSE;
239
240   if (value != NULL)
241     {
242       DBusMessageIter iter;
243       GVariantIter gv_iter;
244       GVariant *item;
245
246       dbus_message_iter_init_append (message, &iter);
247
248       g_variant_iter_init (&gv_iter, value);
249       n = 0;
250       while ((item = g_variant_iter_next_value (&gv_iter)))
251         {
252           if (!append_gv_to_dbus_iter (&iter, item, error))
253             {
254               g_prefix_error (error,
255                               "Error encoding in-arg %d: ",
256                               n);
257               goto out;
258             }
259           n++;
260         }
261     }
262
263   ret = TRUE;
264
265  out:
266   return ret;
267 }
268
269 static void
270 print_gv_dbus_message (GVariant *value)
271 {
272   DBusMessage *message;
273   char *blob;
274   int blob_len;
275   GError *error;
276
277   message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
278   dbus_message_set_serial (message, 0x41);
279   dbus_message_set_path (message, "/foo/bar");
280   dbus_message_set_member (message, "Member");
281
282   error = NULL;
283   if (!append_gv_to_dbus_message (message, value, &error))
284     {
285       g_printerr ("Error printing GVariant as DBusMessage: %s", error->message);
286       g_error_free (error);
287       goto out;
288     }
289
290   dbus_message_marshal (message, &blob, &blob_len);
291   g_printerr ("\n");
292   hexdump ((guchar *) blob, blob_len);
293  out:
294   dbus_message_unref (message);
295 }
296
297 /* ---------------------------------------------------------------------------------------------------- */
298
299 static void
300 dbus_1_message_append (GString *s,
301                        guint indent,
302                        DBusMessageIter *iter)
303 {
304   gint arg_type;
305   DBusMessageIter sub;
306
307   g_string_append_printf (s, "%*s", indent, "");
308
309   arg_type = dbus_message_iter_get_arg_type (iter);
310   switch (arg_type)
311     {
312      case DBUS_TYPE_BOOLEAN:
313       {
314         dbus_bool_t value;
315         dbus_message_iter_get_basic (iter, &value);
316         g_string_append_printf (s, "bool: %s\n", value ? "true" : "false");
317         break;
318       }
319
320      case DBUS_TYPE_BYTE:
321       {
322         guchar value;
323         dbus_message_iter_get_basic (iter, &value);
324         g_string_append_printf (s, "byte: 0x%02x\n", (guint) value);
325         break;
326       }
327
328      case DBUS_TYPE_INT16:
329       {
330         gint16 value;
331         dbus_message_iter_get_basic (iter, &value);
332         g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value);
333         break;
334       }
335
336      case DBUS_TYPE_UINT16:
337       {
338         guint16 value;
339         dbus_message_iter_get_basic (iter, &value);
340         g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value);
341         break;
342       }
343
344      case DBUS_TYPE_INT32:
345       {
346         gint32 value;
347         dbus_message_iter_get_basic (iter, &value);
348         g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value);
349         break;
350       }
351
352      case DBUS_TYPE_UINT32:
353       {
354         guint32 value;
355         dbus_message_iter_get_basic (iter, &value);
356         g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value);
357         break;
358       }
359
360      case DBUS_TYPE_INT64:
361       {
362         gint64 value;
363         dbus_message_iter_get_basic (iter, &value);
364         g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value);
365         break;
366       }
367
368      case DBUS_TYPE_UINT64:
369       {
370         guint64 value;
371         dbus_message_iter_get_basic (iter, &value);
372         g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value);
373         break;
374       }
375
376      case DBUS_TYPE_DOUBLE:
377       {
378         gdouble value;
379         dbus_message_iter_get_basic (iter, &value);
380         g_string_append_printf (s, "double: %f\n", value);
381         break;
382       }
383
384      case DBUS_TYPE_STRING:
385       {
386         const gchar *value;
387         dbus_message_iter_get_basic (iter, &value);
388         g_string_append_printf (s, "string: `%s'\n", value);
389         break;
390       }
391
392      case DBUS_TYPE_OBJECT_PATH:
393       {
394         const gchar *value;
395         dbus_message_iter_get_basic (iter, &value);
396         g_string_append_printf (s, "object_path: `%s'\n", value);
397         break;
398       }
399
400      case DBUS_TYPE_SIGNATURE:
401       {
402         const gchar *value;
403         dbus_message_iter_get_basic (iter, &value);
404         g_string_append_printf (s, "signature: `%s'\n", value);
405         break;
406       }
407
408      case DBUS_TYPE_VARIANT:
409        g_string_append_printf (s, "variant:\n");
410        dbus_message_iter_recurse (iter, &sub);
411        while (dbus_message_iter_get_arg_type (&sub))
412          {
413            dbus_1_message_append (s, indent + 2, &sub);
414            dbus_message_iter_next (&sub);
415          }
416        break;
417
418      case DBUS_TYPE_ARRAY:
419        g_string_append_printf (s, "array:\n");
420        dbus_message_iter_recurse (iter, &sub);
421        while (dbus_message_iter_get_arg_type (&sub))
422          {
423            dbus_1_message_append (s, indent + 2, &sub);
424            dbus_message_iter_next (&sub);
425          }
426        break;
427
428      case DBUS_TYPE_STRUCT:
429        g_string_append_printf (s, "struct:\n");
430        dbus_message_iter_recurse (iter, &sub);
431        while (dbus_message_iter_get_arg_type (&sub))
432          {
433            dbus_1_message_append (s, indent + 2, &sub);
434            dbus_message_iter_next (&sub);
435          }
436        break;
437
438      case DBUS_TYPE_DICT_ENTRY:
439        g_string_append_printf (s, "dict_entry:\n");
440        dbus_message_iter_recurse (iter, &sub);
441        while (dbus_message_iter_get_arg_type (&sub))
442          {
443            dbus_1_message_append (s, indent + 2, &sub);
444            dbus_message_iter_next (&sub);
445          }
446        break;
447
448      default:
449        g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type `%c' (%d)",
450                    arg_type,
451                    arg_type);
452        g_assert_not_reached ();
453        break;
454     }
455 }
456
457 static gchar *
458 dbus_1_message_print (DBusMessage *message)
459 {
460   GString *s;
461   guint n;
462   DBusMessageIter iter;
463
464   s = g_string_new (NULL);
465   n = 0;
466   dbus_message_iter_init (message, &iter);
467   while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
468     {
469       g_string_append_printf (s, "value %d: ", n);
470       dbus_1_message_append (s, 2, &iter);
471       dbus_message_iter_next (&iter);
472       n++;
473     }
474
475   return g_string_free (s, FALSE);
476 }
477
478 /* ---------------------------------------------------------------------------------------------------- */
479
480 static gchar *
481 get_body_signature (GVariant *value)
482 {
483   const gchar *s;
484   gsize len;
485   gchar *ret;
486
487   if (value == NULL)
488     {
489       ret = g_strdup ("");
490       goto out;
491     }
492
493   s = g_variant_get_type_string (value);
494   len = strlen (s);
495   g_assert (len >= 2);
496
497   ret = g_strndup (s + 1, len - 2);
498
499  out:
500   return ret;
501 }
502
503 static void
504 check_serialization (GVariant *value,
505                      const gchar *expected_dbus_1_output)
506 {
507   guchar *blob;
508   gsize blob_size;
509   DBusMessage *dbus_1_message;
510   GDBusMessage *message;
511   GDBusMessage *recovered_message;
512   GError *error;
513   DBusError dbus_error;
514   gchar *s;
515   gchar *s1;
516
517   message = g_dbus_message_new ();
518   g_dbus_message_set_body (message, value);
519   g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
520   g_dbus_message_set_serial (message, 0x41);
521   s = get_body_signature (value);
522   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar"));
523   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member"));
524   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s));
525   g_free (s);
526
527   /* First check that the serialization to the D-Bus wire format is correct */
528
529   error = NULL;
530   blob = g_dbus_message_to_blob (message,
531                                  &blob_size,
532                                  G_DBUS_CAPABILITY_FLAGS_NONE,
533                                  &error);
534   g_assert_no_error (error);
535   g_assert (blob != NULL);
536
537   dbus_error_init (&dbus_error);
538   dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error);
539   if (dbus_error_is_set (&dbus_error))
540     {
541       g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n",
542                   dbus_error.name,
543                   dbus_error.message);
544       hexdump (blob, blob_size);
545       dbus_error_free (&dbus_error);
546
547       s = g_variant_print (value, TRUE);
548       g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s);
549       g_free (s);
550
551       g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n");
552       print_gv_dbus_message (value);
553
554       g_assert_not_reached ();
555     }
556
557   s = dbus_1_message_print (dbus_1_message);
558   dbus_message_unref (dbus_1_message);
559
560   g_assert_cmpstr (s, ==, expected_dbus_1_output);
561   g_free (s);
562
563   /* Then serialize back and check that the body is identical */
564
565   error = NULL;
566   recovered_message = g_dbus_message_new_from_blob (blob,
567                                                     blob_size,
568                                                     G_DBUS_CAPABILITY_FLAGS_NONE,
569                                                     &error);
570   g_assert (recovered_message != NULL);
571   g_assert_no_error (error);
572
573   if (value == NULL)
574     {
575       g_assert (g_dbus_message_get_body (recovered_message) == NULL);
576     }
577   else
578     {
579       g_assert (g_dbus_message_get_body (recovered_message) != NULL);
580       if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value))
581         {
582           s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE);
583           s1 = g_variant_print (value, TRUE);
584           g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n",
585                       s,
586                       s1);
587           g_free (s);
588           g_free (s1);
589           g_assert_not_reached ();
590         }
591     }
592   g_object_unref (message);
593   g_object_unref (recovered_message);
594 }
595
596 static void
597 message_serialize_basic (void)
598 {
599   check_serialization (NULL, "");
600
601   check_serialization (g_variant_new ("(sogybnqiuxtd)",
602                                       "this is a string",
603                                       "/this/is/a/path",
604                                       "sad",
605                                       42,
606                                       TRUE,
607                                       -42,
608                                       60000,
609                                       -44,
610                                       100000,
611                                       -G_GINT64_CONSTANT(2)<<34,
612                                       G_GUINT64_CONSTANT(0xffffffffffffffff),
613                                       42.5),
614                        "value 0:   string: `this is a string'\n"
615                        "value 1:   object_path: `/this/is/a/path'\n"
616                        "value 2:   signature: `sad'\n"
617                        "value 3:   byte: 0x2a\n"
618                        "value 4:   bool: true\n"
619                        "value 5:   int16: -42\n"
620                        "value 6:   uint16: 60000\n"
621                        "value 7:   int32: -44\n"
622                        "value 8:   uint32: 100000\n"
623                        "value 9:   int64: -34359738368\n"
624                        "value 10:   uint64: 18446744073709551615\n"
625                        "value 11:   double: 42.500000\n");
626 }
627
628 /* ---------------------------------------------------------------------------------------------------- */
629
630 static void
631 message_serialize_complex (void)
632 {
633   GError *error;
634   GVariant *value;
635
636   error = NULL;
637
638   value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"),
639                            "([1, 2, 3], {'one': 'white', 'two': 'black'})",
640                            NULL, NULL, &error);
641   g_assert_no_error (error);
642   g_assert (value != NULL);
643   check_serialization (value,
644                        "value 0:   array:\n"
645                        "    int32: 1\n"
646                        "    int32: 2\n"
647                        "    int32: 3\n"
648                        "value 1:   array:\n"
649                        "    dict_entry:\n"
650                        "      string: `one'\n"
651                        "      string: `white'\n"
652                        "    dict_entry:\n"
653                        "      string: `two'\n"
654                        "      string: `black'\n");
655
656   value = g_variant_parse (G_VARIANT_TYPE ("(sa{sv}as)"),
657                            "('01234567890123456', {}, ['Something'])",
658                            NULL, NULL, &error);
659   g_assert_no_error (error);
660   g_assert (value != NULL);
661   check_serialization (value,
662                        "value 0:   string: `01234567890123456'\n"
663                        "value 1:   array:\n"
664                        "value 2:   array:\n"
665                        "    string: `Something'\n");
666
667   /* https://bugzilla.gnome.org/show_bug.cgi?id=621838 */
668   check_serialization (g_variant_new_parsed ("(@aay [], {'cwd': <'/home/davidz/Hacking/glib/gio/tests'>})"),
669                        "value 0:   array:\n"
670                        "value 1:   array:\n"
671                        "    dict_entry:\n"
672                        "      string: `cwd'\n"
673                        "      variant:\n"
674                        "        string: `/home/davidz/Hacking/glib/gio/tests'\n");
675 }
676
677
678 /* ---------------------------------------------------------------------------------------------------- */
679
680 int
681 main (int   argc,
682       char *argv[])
683 {
684   g_type_init ();
685   g_test_init (&argc, &argv, NULL);
686
687   g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic);
688   g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex);
689   return g_test_run();
690 }
691