GDBus: Add support for D-Bus type 'h' (ie. G_VARIANT_TYPE_HANDLE)
[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 #ifdef DBUS_TYPE_UNIX_FD
409     case DBUS_TYPE_UNIX_FD:
410       {
411         /* unfortunately there's currently no way to get just the
412          * protocol value, since dbus_message_iter_get_basic() wants
413          * to be 'helpful' and dup the fd for the user...
414          */
415         g_string_append (s, "unix-fd: (not extracted)\n");
416         break;
417       }
418 #endif
419
420      case DBUS_TYPE_VARIANT:
421        g_string_append_printf (s, "variant:\n");
422        dbus_message_iter_recurse (iter, &sub);
423        while (dbus_message_iter_get_arg_type (&sub))
424          {
425            dbus_1_message_append (s, indent + 2, &sub);
426            dbus_message_iter_next (&sub);
427          }
428        break;
429
430      case DBUS_TYPE_ARRAY:
431        g_string_append_printf (s, "array:\n");
432        dbus_message_iter_recurse (iter, &sub);
433        while (dbus_message_iter_get_arg_type (&sub))
434          {
435            dbus_1_message_append (s, indent + 2, &sub);
436            dbus_message_iter_next (&sub);
437          }
438        break;
439
440      case DBUS_TYPE_STRUCT:
441        g_string_append_printf (s, "struct:\n");
442        dbus_message_iter_recurse (iter, &sub);
443        while (dbus_message_iter_get_arg_type (&sub))
444          {
445            dbus_1_message_append (s, indent + 2, &sub);
446            dbus_message_iter_next (&sub);
447          }
448        break;
449
450      case DBUS_TYPE_DICT_ENTRY:
451        g_string_append_printf (s, "dict_entry:\n");
452        dbus_message_iter_recurse (iter, &sub);
453        while (dbus_message_iter_get_arg_type (&sub))
454          {
455            dbus_1_message_append (s, indent + 2, &sub);
456            dbus_message_iter_next (&sub);
457          }
458        break;
459
460      default:
461        g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type `%c' (%d)",
462                    arg_type,
463                    arg_type);
464        g_assert_not_reached ();
465        break;
466     }
467 }
468
469 static gchar *
470 dbus_1_message_print (DBusMessage *message)
471 {
472   GString *s;
473   guint n;
474   DBusMessageIter iter;
475
476   s = g_string_new (NULL);
477   n = 0;
478   dbus_message_iter_init (message, &iter);
479   while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
480     {
481       g_string_append_printf (s, "value %d: ", n);
482       dbus_1_message_append (s, 2, &iter);
483       dbus_message_iter_next (&iter);
484       n++;
485     }
486
487   return g_string_free (s, FALSE);
488 }
489
490 /* ---------------------------------------------------------------------------------------------------- */
491
492 static gchar *
493 get_body_signature (GVariant *value)
494 {
495   const gchar *s;
496   gsize len;
497   gchar *ret;
498
499   if (value == NULL)
500     {
501       ret = g_strdup ("");
502       goto out;
503     }
504
505   s = g_variant_get_type_string (value);
506   len = strlen (s);
507   g_assert (len >= 2);
508
509   ret = g_strndup (s + 1, len - 2);
510
511  out:
512   return ret;
513 }
514
515 static void
516 check_serialization (GVariant *value,
517                      const gchar *expected_dbus_1_output)
518 {
519   guchar *blob;
520   gsize blob_size;
521   DBusMessage *dbus_1_message;
522   GDBusMessage *message;
523   GDBusMessage *recovered_message;
524   GError *error;
525   DBusError dbus_error;
526   gchar *s;
527   gchar *s1;
528
529   message = g_dbus_message_new ();
530   g_dbus_message_set_body (message, value);
531   g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
532   g_dbus_message_set_serial (message, 0x41);
533   s = get_body_signature (value);
534   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar"));
535   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member"));
536   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s));
537   g_free (s);
538
539   /* First check that the serialization to the D-Bus wire format is correct */
540
541   error = NULL;
542   blob = g_dbus_message_to_blob (message,
543                                  &blob_size,
544                                  G_DBUS_CAPABILITY_FLAGS_NONE,
545                                  &error);
546   g_assert_no_error (error);
547   g_assert (blob != NULL);
548
549   dbus_error_init (&dbus_error);
550   dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error);
551   if (dbus_error_is_set (&dbus_error))
552     {
553       g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n",
554                   dbus_error.name,
555                   dbus_error.message);
556       hexdump (blob, blob_size);
557       dbus_error_free (&dbus_error);
558
559       s = g_variant_print (value, TRUE);
560       g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s);
561       g_free (s);
562
563       g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n");
564       print_gv_dbus_message (value);
565
566       g_assert_not_reached ();
567     }
568
569   s = dbus_1_message_print (dbus_1_message);
570   dbus_message_unref (dbus_1_message);
571
572   g_assert_cmpstr (s, ==, expected_dbus_1_output);
573   g_free (s);
574
575   /* Then serialize back and check that the body is identical */
576
577   error = NULL;
578   recovered_message = g_dbus_message_new_from_blob (blob,
579                                                     blob_size,
580                                                     G_DBUS_CAPABILITY_FLAGS_NONE,
581                                                     &error);
582   g_assert (recovered_message != NULL);
583   g_assert_no_error (error);
584
585   if (value == NULL)
586     {
587       g_assert (g_dbus_message_get_body (recovered_message) == NULL);
588     }
589   else
590     {
591       g_assert (g_dbus_message_get_body (recovered_message) != NULL);
592       if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value))
593         {
594           s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE);
595           s1 = g_variant_print (value, TRUE);
596           g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n",
597                       s,
598                       s1);
599           g_free (s);
600           g_free (s1);
601           g_assert_not_reached ();
602         }
603     }
604   g_object_unref (message);
605   g_object_unref (recovered_message);
606 }
607
608 static void
609 message_serialize_basic (void)
610 {
611   check_serialization (NULL, "");
612
613   check_serialization (g_variant_new ("(sogybnqiuxtd)",
614                                       "this is a string",
615                                       "/this/is/a/path",
616                                       "sad",
617                                       42,
618                                       TRUE,
619                                       -42,
620                                       60000,
621                                       -44,
622                                       100000,
623                                       -G_GINT64_CONSTANT(2)<<34,
624                                       G_GUINT64_CONSTANT(0xffffffffffffffff),
625                                       42.5),
626                        "value 0:   string: `this is a string'\n"
627                        "value 1:   object_path: `/this/is/a/path'\n"
628                        "value 2:   signature: `sad'\n"
629                        "value 3:   byte: 0x2a\n"
630                        "value 4:   bool: true\n"
631                        "value 5:   int16: -42\n"
632                        "value 6:   uint16: 60000\n"
633                        "value 7:   int32: -44\n"
634                        "value 8:   uint32: 100000\n"
635                        "value 9:   int64: -34359738368\n"
636                        "value 10:   uint64: 18446744073709551615\n"
637                        "value 11:   double: 42.500000\n");
638 }
639
640 /* ---------------------------------------------------------------------------------------------------- */
641
642 static void
643 message_serialize_complex (void)
644 {
645   GError *error;
646   GVariant *value;
647
648   error = NULL;
649
650   value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"),
651                            "([1, 2, 3], {'one': 'white', 'two': 'black'})",
652                            NULL, NULL, &error);
653   g_assert_no_error (error);
654   g_assert (value != NULL);
655   check_serialization (value,
656                        "value 0:   array:\n"
657                        "    int32: 1\n"
658                        "    int32: 2\n"
659                        "    int32: 3\n"
660                        "value 1:   array:\n"
661                        "    dict_entry:\n"
662                        "      string: `one'\n"
663                        "      string: `white'\n"
664                        "    dict_entry:\n"
665                        "      string: `two'\n"
666                        "      string: `black'\n");
667
668   value = g_variant_parse (G_VARIANT_TYPE ("(sa{sv}as)"),
669                            "('01234567890123456', {}, ['Something'])",
670                            NULL, NULL, &error);
671   g_assert_no_error (error);
672   g_assert (value != NULL);
673   check_serialization (value,
674                        "value 0:   string: `01234567890123456'\n"
675                        "value 1:   array:\n"
676                        "value 2:   array:\n"
677                        "    string: `Something'\n");
678
679   /* https://bugzilla.gnome.org/show_bug.cgi?id=621838 */
680   check_serialization (g_variant_new_parsed ("(@aay [], {'cwd': <'/home/davidz/Hacking/glib/gio/tests'>})"),
681                        "value 0:   array:\n"
682                        "value 1:   array:\n"
683                        "    dict_entry:\n"
684                        "      string: `cwd'\n"
685                        "      variant:\n"
686                        "        string: `/home/davidz/Hacking/glib/gio/tests'\n");
687
688 #ifdef DBUS_TYPE_UNIX_FD
689   value = g_variant_parse (G_VARIANT_TYPE ("(hah)"),
690                            "(42, [43, 44])",
691                            NULL, NULL, &error);
692   g_assert_no_error (error);
693   g_assert (value != NULL);
694   /* about (not extracted), see comment in DBUS_TYPE_UNIX_FD case in
695    * dbus_1_message_append() above.
696    */
697   check_serialization (value,
698                        "value 0:   unix-fd: (not extracted)\n"
699                        "value 1:   array:\n"
700                        "    unix-fd: (not extracted)\n"
701                        "    unix-fd: (not extracted)\n");
702 #endif
703 }
704
705
706 /* ---------------------------------------------------------------------------------------------------- */
707
708 int
709 main (int   argc,
710       char *argv[])
711 {
712   g_type_init ();
713   g_test_init (&argc, &argv, NULL);
714
715   g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic);
716   g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex);
717   return g_test_run();
718 }
719