Merge remote-tracking branch 'gvdb/master'
[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 <locale.h>
24 #include <gio/gio.h>
25
26 #include <string.h>
27 #include <unistd.h>
28 #include <dbus/dbus.h>
29
30 /* ---------------------------------------------------------------------------------------------------- */
31
32 static void
33 hexdump (const guchar *str, gsize len)
34 {
35   const guchar *data = (const guchar *) str;
36   guint n, m;
37
38   for (n = 0; n < len; n += 16)
39     {
40       g_printerr ("%04x: ", n);
41
42       for (m = n; m < n + 16; m++)
43         {
44           if (m > n && (m%4) == 0)
45             g_printerr (" ");
46           if (m < len)
47             g_printerr ("%02x ", data[m]);
48           else
49             g_printerr ("   ");
50         }
51
52       g_printerr ("   ");
53
54       for (m = n; m < len && m < n + 16; m++)
55         g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.');
56
57       g_printerr ("\n");
58     }
59 }
60
61 /* ---------------------------------------------------------------------------------------------------- */
62
63 static gboolean
64 append_gv_to_dbus_iter (DBusMessageIter  *iter,
65                         GVariant         *value,
66                         GError          **error)
67 {
68   const GVariantType *type;
69
70   type = g_variant_get_type (value);
71   if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
72     {
73       dbus_bool_t v = g_variant_get_boolean (value);
74       dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v);
75     }
76   else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
77     {
78       guint8 v = g_variant_get_byte (value);
79       dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v);
80     }
81   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
82     {
83       gint16 v = g_variant_get_int16 (value);
84       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v);
85     }
86   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
87     {
88       guint16 v = g_variant_get_uint16 (value);
89       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v);
90     }
91   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
92     {
93       gint32 v = g_variant_get_int32 (value);
94       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v);
95     }
96   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
97     {
98       guint32 v = g_variant_get_uint32 (value);
99       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v);
100     }
101   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
102     {
103       gint64 v = g_variant_get_int64 (value);
104       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v);
105     }
106   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
107     {
108       guint64 v = g_variant_get_uint64 (value);
109       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v);
110     }
111   else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
112     {
113       gdouble v = g_variant_get_double (value);
114       dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v);
115     }
116   else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
117     {
118       const gchar *v = g_variant_get_string (value, NULL);
119       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v);
120     }
121   else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
122     {
123       const gchar *v = g_variant_get_string (value, NULL);
124       dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v);
125     }
126   else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
127     {
128       const gchar *v = g_variant_get_string (value, NULL);
129       dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v);
130     }
131   else if (g_variant_type_is_variant (type))
132     {
133       DBusMessageIter sub;
134       GVariant *child;
135
136       child = g_variant_get_child_value (value, 0);
137       dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT,
138                                         g_variant_get_type_string (child),
139                                         &sub);
140       if (!append_gv_to_dbus_iter (&sub, child, error))
141         {
142             g_variant_unref (child);
143             goto fail;
144         }
145       dbus_message_iter_close_container (iter, &sub);
146       g_variant_unref (child);
147     }
148   else if (g_variant_type_is_array (type))
149     {
150       DBusMessageIter dbus_iter;
151       const gchar *type_string;
152       GVariantIter gv_iter;
153       GVariant *item;
154
155       type_string = g_variant_get_type_string (value);
156       type_string++; /* skip the 'a' */
157
158       dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
159                                         type_string, &dbus_iter);
160       g_variant_iter_init (&gv_iter, value);
161
162       while ((item = g_variant_iter_next_value (&gv_iter)))
163         {
164           if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
165             {
166               goto fail;
167             }
168         }
169
170       dbus_message_iter_close_container (iter, &dbus_iter);
171     }
172   else if (g_variant_type_is_tuple (type))
173     {
174       DBusMessageIter dbus_iter;
175       GVariantIter gv_iter;
176       GVariant *item;
177
178       dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT,
179                                         NULL, &dbus_iter);
180       g_variant_iter_init (&gv_iter, value);
181
182       while ((item = g_variant_iter_next_value (&gv_iter)))
183         {
184           if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
185             goto fail;
186         }
187
188       dbus_message_iter_close_container (iter, &dbus_iter);
189     }
190   else if (g_variant_type_is_dict_entry (type))
191     {
192       DBusMessageIter dbus_iter;
193       GVariant *key, *val;
194
195       dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY,
196                                         NULL, &dbus_iter);
197       key = g_variant_get_child_value (value, 0);
198       if (!append_gv_to_dbus_iter (&dbus_iter, key, error))
199         {
200           g_variant_unref (key);
201           goto fail;
202         }
203       g_variant_unref (key);
204
205       val = g_variant_get_child_value (value, 1);
206       if (!append_gv_to_dbus_iter (&dbus_iter, val, error))
207         {
208           g_variant_unref (val);
209           goto fail;
210         }
211       g_variant_unref (val);
212
213       dbus_message_iter_close_container (iter, &dbus_iter);
214     }
215   else
216     {
217       g_set_error (error,
218                    G_IO_ERROR,
219                    G_IO_ERROR_INVALID_ARGUMENT,
220                    "Error serializing GVariant with type-string `%s' to a D-Bus message",
221                    g_variant_get_type_string (value));
222       goto fail;
223     }
224
225   return TRUE;
226
227  fail:
228   return FALSE;
229 }
230
231 static gboolean
232 append_gv_to_dbus_message (DBusMessage  *message,
233                            GVariant     *value,
234                            GError      **error)
235 {
236   gboolean ret;
237   guint n;
238
239   ret = FALSE;
240
241   if (value != NULL)
242     {
243       DBusMessageIter iter;
244       GVariantIter gv_iter;
245       GVariant *item;
246
247       dbus_message_iter_init_append (message, &iter);
248
249       g_variant_iter_init (&gv_iter, value);
250       n = 0;
251       while ((item = g_variant_iter_next_value (&gv_iter)))
252         {
253           if (!append_gv_to_dbus_iter (&iter, item, error))
254             {
255               g_prefix_error (error,
256                               "Error encoding in-arg %d: ",
257                               n);
258               goto out;
259             }
260           n++;
261         }
262     }
263
264   ret = TRUE;
265
266  out:
267   return ret;
268 }
269
270 static void
271 print_gv_dbus_message (GVariant *value)
272 {
273   DBusMessage *message;
274   char *blob;
275   int blob_len;
276   GError *error;
277
278   message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
279   dbus_message_set_serial (message, 0x41);
280   dbus_message_set_path (message, "/foo/bar");
281   dbus_message_set_member (message, "Member");
282
283   error = NULL;
284   if (!append_gv_to_dbus_message (message, value, &error))
285     {
286       g_printerr ("Error printing GVariant as DBusMessage: %s", error->message);
287       g_error_free (error);
288       goto out;
289     }
290
291   dbus_message_marshal (message, &blob, &blob_len);
292   g_printerr ("\n");
293   hexdump ((guchar *) blob, blob_len);
294  out:
295   dbus_message_unref (message);
296 }
297
298 /* ---------------------------------------------------------------------------------------------------- */
299
300 static void
301 dbus_1_message_append (GString *s,
302                        guint indent,
303                        DBusMessageIter *iter)
304 {
305   gint arg_type;
306   DBusMessageIter sub;
307
308   g_string_append_printf (s, "%*s", indent, "");
309
310   arg_type = dbus_message_iter_get_arg_type (iter);
311   switch (arg_type)
312     {
313      case DBUS_TYPE_BOOLEAN:
314       {
315         dbus_bool_t value;
316         dbus_message_iter_get_basic (iter, &value);
317         g_string_append_printf (s, "bool: %s\n", value ? "true" : "false");
318         break;
319       }
320
321      case DBUS_TYPE_BYTE:
322       {
323         guchar value;
324         dbus_message_iter_get_basic (iter, &value);
325         g_string_append_printf (s, "byte: 0x%02x\n", (guint) value);
326         break;
327       }
328
329      case DBUS_TYPE_INT16:
330       {
331         gint16 value;
332         dbus_message_iter_get_basic (iter, &value);
333         g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value);
334         break;
335       }
336
337      case DBUS_TYPE_UINT16:
338       {
339         guint16 value;
340         dbus_message_iter_get_basic (iter, &value);
341         g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value);
342         break;
343       }
344
345      case DBUS_TYPE_INT32:
346       {
347         gint32 value;
348         dbus_message_iter_get_basic (iter, &value);
349         g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value);
350         break;
351       }
352
353      case DBUS_TYPE_UINT32:
354       {
355         guint32 value;
356         dbus_message_iter_get_basic (iter, &value);
357         g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value);
358         break;
359       }
360
361      case DBUS_TYPE_INT64:
362       {
363         gint64 value;
364         dbus_message_iter_get_basic (iter, &value);
365         g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value);
366         break;
367       }
368
369      case DBUS_TYPE_UINT64:
370       {
371         guint64 value;
372         dbus_message_iter_get_basic (iter, &value);
373         g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value);
374         break;
375       }
376
377      case DBUS_TYPE_DOUBLE:
378       {
379         gdouble value;
380         dbus_message_iter_get_basic (iter, &value);
381         g_string_append_printf (s, "double: %f\n", value);
382         break;
383       }
384
385      case DBUS_TYPE_STRING:
386       {
387         const gchar *value;
388         dbus_message_iter_get_basic (iter, &value);
389         g_string_append_printf (s, "string: `%s'\n", value);
390         break;
391       }
392
393      case DBUS_TYPE_OBJECT_PATH:
394       {
395         const gchar *value;
396         dbus_message_iter_get_basic (iter, &value);
397         g_string_append_printf (s, "object_path: `%s'\n", value);
398         break;
399       }
400
401      case DBUS_TYPE_SIGNATURE:
402       {
403         const gchar *value;
404         dbus_message_iter_get_basic (iter, &value);
405         g_string_append_printf (s, "signature: `%s'\n", value);
406         break;
407       }
408
409 #ifdef DBUS_TYPE_UNIX_FD
410     case DBUS_TYPE_UNIX_FD:
411       {
412         /* unfortunately there's currently no way to get just the
413          * protocol value, since dbus_message_iter_get_basic() wants
414          * to be 'helpful' and dup the fd for the user...
415          */
416         g_string_append (s, "unix-fd: (not extracted)\n");
417         break;
418       }
419 #endif
420
421      case DBUS_TYPE_VARIANT:
422        g_string_append_printf (s, "variant:\n");
423        dbus_message_iter_recurse (iter, &sub);
424        while (dbus_message_iter_get_arg_type (&sub))
425          {
426            dbus_1_message_append (s, indent + 2, &sub);
427            dbus_message_iter_next (&sub);
428          }
429        break;
430
431      case DBUS_TYPE_ARRAY:
432        g_string_append_printf (s, "array:\n");
433        dbus_message_iter_recurse (iter, &sub);
434        while (dbus_message_iter_get_arg_type (&sub))
435          {
436            dbus_1_message_append (s, indent + 2, &sub);
437            dbus_message_iter_next (&sub);
438          }
439        break;
440
441      case DBUS_TYPE_STRUCT:
442        g_string_append_printf (s, "struct:\n");
443        dbus_message_iter_recurse (iter, &sub);
444        while (dbus_message_iter_get_arg_type (&sub))
445          {
446            dbus_1_message_append (s, indent + 2, &sub);
447            dbus_message_iter_next (&sub);
448          }
449        break;
450
451      case DBUS_TYPE_DICT_ENTRY:
452        g_string_append_printf (s, "dict_entry:\n");
453        dbus_message_iter_recurse (iter, &sub);
454        while (dbus_message_iter_get_arg_type (&sub))
455          {
456            dbus_1_message_append (s, indent + 2, &sub);
457            dbus_message_iter_next (&sub);
458          }
459        break;
460
461      default:
462        g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type `%c' (%d)",
463                    arg_type,
464                    arg_type);
465        g_assert_not_reached ();
466        break;
467     }
468 }
469
470 static gchar *
471 dbus_1_message_print (DBusMessage *message)
472 {
473   GString *s;
474   guint n;
475   DBusMessageIter iter;
476
477   s = g_string_new (NULL);
478   n = 0;
479   dbus_message_iter_init (message, &iter);
480   while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
481     {
482       g_string_append_printf (s, "value %d: ", n);
483       dbus_1_message_append (s, 2, &iter);
484       dbus_message_iter_next (&iter);
485       n++;
486     }
487
488   return g_string_free (s, FALSE);
489 }
490
491 /* ---------------------------------------------------------------------------------------------------- */
492
493 static gchar *
494 get_body_signature (GVariant *value)
495 {
496   const gchar *s;
497   gsize len;
498   gchar *ret;
499
500   if (value == NULL)
501     {
502       ret = g_strdup ("");
503       goto out;
504     }
505
506   s = g_variant_get_type_string (value);
507   len = strlen (s);
508   g_assert (len >= 2);
509
510   ret = g_strndup (s + 1, len - 2);
511
512  out:
513   return ret;
514 }
515
516 static void
517 check_serialization (GVariant *value,
518                      const gchar *expected_dbus_1_output)
519 {
520   guchar *blob;
521   gsize blob_size;
522   DBusMessage *dbus_1_message;
523   GDBusMessage *message;
524   GDBusMessage *recovered_message;
525   GError *error;
526   DBusError dbus_error;
527   gchar *s;
528   gchar *s1;
529   guint n;
530
531   message = g_dbus_message_new ();
532   g_dbus_message_set_body (message, value);
533   g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
534   g_dbus_message_set_serial (message, 0x41);
535   s = get_body_signature (value);
536   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar"));
537   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member"));
538   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s));
539   g_free (s);
540
541   /* First check that the serialization to the D-Bus wire format is correct - do this for both byte orders */
542   for (n = 0; n < 2; n++)
543     {
544       GDBusMessageByteOrder byte_order;
545       switch (n)
546         {
547         case 0:
548           byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
549           break;
550         case 1:
551           byte_order = G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN;
552           break;
553         case 2:
554           g_assert_not_reached ();
555           break;
556         }
557       g_dbus_message_set_byte_order (message, byte_order);
558
559       error = NULL;
560       blob = g_dbus_message_to_blob (message,
561                                      &blob_size,
562                                      G_DBUS_CAPABILITY_FLAGS_NONE,
563                                      &error);
564       g_assert_no_error (error);
565       g_assert (blob != NULL);
566
567       switch (byte_order)
568         {
569         case G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN:
570           g_assert_cmpint (blob[0], ==, 'B');
571           break;
572         case G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN:
573           g_assert_cmpint (blob[0], ==, 'l');
574           break;
575         }
576
577       dbus_error_init (&dbus_error);
578       dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error);
579       if (dbus_error_is_set (&dbus_error))
580         {
581           g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n",
582                       dbus_error.name,
583                       dbus_error.message);
584           hexdump (blob, blob_size);
585           dbus_error_free (&dbus_error);
586
587           s = g_variant_print (value, TRUE);
588           g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s);
589           g_free (s);
590
591           g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n");
592           print_gv_dbus_message (value);
593
594           g_assert_not_reached ();
595         }
596
597       s = dbus_1_message_print (dbus_1_message);
598       dbus_message_unref (dbus_1_message);
599
600       g_assert_cmpstr (s, ==, expected_dbus_1_output);
601       g_free (s);
602
603       /* Then serialize back and check that the body is identical */
604
605       error = NULL;
606       recovered_message = g_dbus_message_new_from_blob (blob,
607                                                         blob_size,
608                                                         G_DBUS_CAPABILITY_FLAGS_NONE,
609                                                         &error);
610       g_assert (recovered_message != NULL);
611       g_assert_no_error (error);
612
613       if (value == NULL)
614         {
615           g_assert (g_dbus_message_get_body (recovered_message) == NULL);
616         }
617       else
618         {
619           g_assert (g_dbus_message_get_body (recovered_message) != NULL);
620           if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value))
621             {
622               s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE);
623               s1 = g_variant_print (value, TRUE);
624               g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n",
625                           s,
626                           s1);
627               g_free (s);
628               g_free (s1);
629               g_assert_not_reached ();
630             }
631         }
632       g_object_unref (recovered_message);
633     }
634
635   g_object_unref (message);
636 }
637
638 static void
639 message_serialize_basic (void)
640 {
641   check_serialization (NULL, "");
642
643   check_serialization (g_variant_new ("(sogybnqiuxtd)",
644                                       "this is a string",
645                                       "/this/is/a/path",
646                                       "sad",
647                                       42,
648                                       TRUE,
649                                       -42,
650                                       60000,
651                                       -44,
652                                       100000,
653                                       -G_GINT64_CONSTANT(2)<<34,
654                                       G_GUINT64_CONSTANT(0xffffffffffffffff),
655                                       42.5),
656                        "value 0:   string: `this is a string'\n"
657                        "value 1:   object_path: `/this/is/a/path'\n"
658                        "value 2:   signature: `sad'\n"
659                        "value 3:   byte: 0x2a\n"
660                        "value 4:   bool: true\n"
661                        "value 5:   int16: -42\n"
662                        "value 6:   uint16: 60000\n"
663                        "value 7:   int32: -44\n"
664                        "value 8:   uint32: 100000\n"
665                        "value 9:   int64: -34359738368\n"
666                        "value 10:   uint64: 18446744073709551615\n"
667                        "value 11:   double: 42.500000\n");
668 }
669
670 /* ---------------------------------------------------------------------------------------------------- */
671
672 static void
673 message_serialize_complex (void)
674 {
675   GError *error;
676   GVariant *value;
677
678   error = NULL;
679
680   value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"),
681                            "([1, 2, 3], {'one': 'white', 'two': 'black'})",
682                            NULL, NULL, &error);
683   g_assert_no_error (error);
684   g_assert (value != NULL);
685   check_serialization (value,
686                        "value 0:   array:\n"
687                        "    int32: 1\n"
688                        "    int32: 2\n"
689                        "    int32: 3\n"
690                        "value 1:   array:\n"
691                        "    dict_entry:\n"
692                        "      string: `one'\n"
693                        "      string: `white'\n"
694                        "    dict_entry:\n"
695                        "      string: `two'\n"
696                        "      string: `black'\n");
697
698   value = g_variant_parse (G_VARIANT_TYPE ("(sa{sv}as)"),
699                            "('01234567890123456', {}, ['Something'])",
700                            NULL, NULL, &error);
701   g_assert_no_error (error);
702   g_assert (value != NULL);
703   check_serialization (value,
704                        "value 0:   string: `01234567890123456'\n"
705                        "value 1:   array:\n"
706                        "value 2:   array:\n"
707                        "    string: `Something'\n");
708
709   /* https://bugzilla.gnome.org/show_bug.cgi?id=621838 */
710   check_serialization (g_variant_new_parsed ("(@aay [], {'cwd': <'/home/davidz/Hacking/glib/gio/tests'>})"),
711                        "value 0:   array:\n"
712                        "value 1:   array:\n"
713                        "    dict_entry:\n"
714                        "      string: `cwd'\n"
715                        "      variant:\n"
716                        "        string: `/home/davidz/Hacking/glib/gio/tests'\n");
717
718 #ifdef DBUS_TYPE_UNIX_FD
719   value = g_variant_parse (G_VARIANT_TYPE ("(hah)"),
720                            "(42, [43, 44])",
721                            NULL, NULL, &error);
722   g_assert_no_error (error);
723   g_assert (value != NULL);
724   /* about (not extracted), see comment in DBUS_TYPE_UNIX_FD case in
725    * dbus_1_message_append() above.
726    */
727   check_serialization (value,
728                        "value 0:   unix-fd: (not extracted)\n"
729                        "value 1:   array:\n"
730                        "    unix-fd: (not extracted)\n"
731                        "    unix-fd: (not extracted)\n");
732 #endif
733 }
734
735
736 /* ---------------------------------------------------------------------------------------------------- */
737
738 static void
739 replace (char       *blob,
740          gsize       len,
741          const char *before,
742          const char *after)
743 {
744   gsize i;
745   gsize slen = strlen (before) + 1;
746
747   g_assert_cmpuint (strlen (before), ==, strlen (after));
748   g_assert_cmpuint (len, >=, slen);
749
750   for (i = 0; i < (len - slen + 1); i++)
751     {
752       if (memcmp (blob + i, before, slen) == 0)
753         memcpy (blob + i, after, slen);
754     }
755 }
756
757 static void
758 message_serialize_invalid (void)
759 {
760   guint n;
761
762   /* Other things we could check (note that GDBus _does_ check for all
763    * these things - we just don't have test-suit coverage for it)
764    *
765    *  - array exceeding 64 MiB (2^26 bytes) - unfortunately libdbus-1 checks
766    *    this, e.g.
767    *
768    *      process 19620: arguments to dbus_message_iter_append_fixed_array() were incorrect,
769    *      assertion "n_elements <= DBUS_MAXIMUM_ARRAY_LENGTH / _dbus_type_get_alignment (element_type)"
770    *      failed in file dbus-message.c line 2344.
771    *      This is normally a bug in some application using the D-Bus library.
772    *      D-Bus not built with -rdynamic so unable to print a backtrace
773    *      Aborted (core dumped)
774    *
775    *  - message exceeding 128 MiB (2^27 bytes)
776    *
777    *  - endianness, message type, flags, protocol version
778    */
779
780   for (n = 0; n < 3; n++)
781     {
782       GDBusMessage *message;
783       GError *error;
784       DBusMessage *dbus_message;
785       char *blob;
786       int blob_len;
787       /* these are in pairs with matching length */
788       const gchar *valid_utf8_str = "this is valid...";
789       const gchar *invalid_utf8_str = "this is invalid\xff";
790       const gchar *valid_signature = "a{sv}a{sv}a{sv}aiai";
791       const gchar *invalid_signature = "not valid signature";
792       const gchar *valid_object_path = "/this/is/a/valid/dbus/object/path";
793       const gchar *invalid_object_path = "/this/is/not a valid object path!";
794
795       dbus_message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
796       dbus_message_set_serial (dbus_message, 0x41);
797       dbus_message_set_path (dbus_message, "/foo/bar");
798       dbus_message_set_member (dbus_message, "Member");
799       switch (n)
800         {
801         case 0:
802           /* invalid UTF-8 */
803           dbus_message_append_args (dbus_message,
804                                     DBUS_TYPE_STRING, &valid_utf8_str,
805                                     DBUS_TYPE_INVALID);
806           break;
807
808         case 1:
809           /* invalid object path */
810           dbus_message_append_args (dbus_message,
811                                     DBUS_TYPE_OBJECT_PATH, &valid_object_path,
812                                     DBUS_TYPE_INVALID);
813           break;
814
815         case 2:
816           /* invalid signature */
817           dbus_message_append_args (dbus_message,
818                                     DBUS_TYPE_SIGNATURE, &valid_signature,
819                                     DBUS_TYPE_INVALID);
820           break;
821
822         default:
823           g_assert_not_reached ();
824           break;
825         }
826       dbus_message_marshal (dbus_message, &blob, &blob_len);
827       /* hack up the message to be invalid by replacing each valid string
828        * with its invalid counterpart */
829       replace (blob, blob_len, valid_utf8_str, invalid_utf8_str);
830       replace (blob, blob_len, valid_object_path, invalid_object_path);
831       replace (blob, blob_len, valid_signature, invalid_signature);
832
833       error = NULL;
834       message = g_dbus_message_new_from_blob ((guchar *) blob,
835                                               blob_len,
836                                               G_DBUS_CAPABILITY_FLAGS_NONE,
837                                               &error);
838       g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
839       g_error_free (error);
840       g_assert (message == NULL);
841
842       dbus_free (blob);
843     }
844
845 }
846
847 /* ---------------------------------------------------------------------------------------------------- */
848
849 static void
850 message_serialize_header_checks (void)
851 {
852   GDBusMessage *message;
853   GDBusMessage *reply;
854   GError *error;
855   guchar *blob;
856   gsize blob_size;
857
858   /*
859    * check we can't serialize messages with INVALID type
860    */
861   message = g_dbus_message_new ();
862   error = NULL;
863   blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
864   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
865   g_assert_cmpstr (error->message, ==, "Cannot serialize message: type is INVALID");
866   g_error_free (error);
867   g_assert (blob == NULL);
868   g_object_unref (message);
869
870   /*
871    * check we can't serialize signal messages with INTERFACE, PATH or MEMBER unset / set to reserved value
872    */
873   message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember");
874   /* ----- */
875   /* interface NULL => error */
876   g_dbus_message_set_interface (message, NULL);
877   error = NULL;
878   blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
879   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
880   g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing");
881   g_error_free (error);
882   g_assert (blob == NULL);
883   /* interface reserved value => error */
884   g_dbus_message_set_interface (message, "org.freedesktop.DBus.Local");
885   error = NULL;
886   blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
887   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
888   g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: The INTERFACE header field is using the reserved value org.freedesktop.DBus.Local");
889   g_error_free (error);
890   g_assert (blob == NULL);
891   /* reset interface */
892   g_dbus_message_set_interface (message, "The.Interface");
893   /* ----- */
894   /* path NULL => error */
895   g_dbus_message_set_path (message, NULL);
896   error = NULL;
897   blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
898   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
899   g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing");
900   g_error_free (error);
901   g_assert (blob == NULL);
902   /* path reserved value => error */
903   g_dbus_message_set_path (message, "/org/freedesktop/DBus/Local");
904   error = NULL;
905   blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
906   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
907   g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: The PATH header field is using the reserved value /org/freedesktop/DBus/Local");
908   g_error_free (error);
909   g_assert (blob == NULL);
910   /* reset path */
911   g_dbus_message_set_path (message, "/the/path");
912   /* ----- */
913   /* member NULL => error */
914   g_dbus_message_set_member (message, NULL);
915   error = NULL;
916   blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
917   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
918   g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing");
919   g_error_free (error);
920   g_assert (blob == NULL);
921   /* reset member */
922   g_dbus_message_set_member (message, "TheMember");
923   /* ----- */
924   /* done */
925   g_object_unref (message);
926
927   /*
928    * check that we can't serialize method call messages with PATH or MEMBER unset
929    */
930   message = g_dbus_message_new_method_call (NULL, "/the/path", NULL, "TheMember");
931   /* ----- */
932   /* path NULL => error */
933   g_dbus_message_set_path (message, NULL);
934   error = NULL;
935   blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
936   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
937   g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing");
938   g_error_free (error);
939   g_assert (blob == NULL);
940   /* reset path */
941   g_dbus_message_set_path (message, "/the/path");
942   /* ----- */
943   /* member NULL => error */
944   g_dbus_message_set_member (message, NULL);
945   error = NULL;
946   blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
947   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
948   g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing");
949   g_error_free (error);
950   g_assert (blob == NULL);
951   /* reset member */
952   g_dbus_message_set_member (message, "TheMember");
953   /* ----- */
954   /* done */
955   g_object_unref (message);
956
957   /*
958    * check that we can't serialize method reply messages with REPLY_SERIAL unset
959    */
960   message = g_dbus_message_new_method_call (NULL, "/the/path", NULL, "TheMember");
961   g_dbus_message_set_serial (message, 42);
962   /* method reply */
963   reply = g_dbus_message_new_method_reply (message);
964   g_assert_cmpint (g_dbus_message_get_reply_serial (reply), ==, 42);
965   g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL);
966   error = NULL;
967   blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
968   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
969   g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_RETURN message: REPLY_SERIAL header field is missing");
970   g_error_free (error);
971   g_assert (blob == NULL);
972   g_object_unref (reply);
973   /* method error - first nuke ERROR_NAME, then REPLY_SERIAL */
974   reply = g_dbus_message_new_method_error (message, "Some.Error.Name", "the message");
975   g_assert_cmpint (g_dbus_message_get_reply_serial (reply), ==, 42);
976   /* nuke ERROR_NAME */
977   g_dbus_message_set_error_name (reply, NULL);
978   error = NULL;
979   blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
980   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
981   g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing");
982   g_error_free (error);
983   g_assert (blob == NULL);
984   /* reset ERROR_NAME */
985   g_dbus_message_set_error_name (reply, "Some.Error.Name");
986   /* nuke REPLY_SERIAL */
987   g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL);
988   error = NULL;
989   blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
990   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
991   g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing");
992   g_error_free (error);
993   g_assert (blob == NULL);
994   g_object_unref (reply);
995   g_object_unref (message);
996 }
997
998 /* ---------------------------------------------------------------------------------------------------- */
999
1000 int
1001 main (int   argc,
1002       char *argv[])
1003 {
1004   setlocale (LC_ALL, "C");
1005
1006   g_type_init ();
1007   g_test_init (&argc, &argv, NULL);
1008
1009   g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic);
1010   g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex);
1011   g_test_add_func ("/gdbus/message-serialize-invalid", message_serialize_invalid);
1012   g_test_add_func ("/gdbus/message-serialize-header-checks", message_serialize_header_checks);
1013   return g_test_run();
1014 }
1015