+/*
+ * _g_dbus_message_new_from_kdbus_items:
+ *
+ * Single kdbus message may contain zero, one or more items
+ * (PAYLOAD_VEC or PAYLOAD_MEMFD), so we need this function
+ * (only for kdbus transport purposes) to parse them to GDBusMessage.
+ * kdbus_msg_items list contain list of pointer + data pair for each received item.
+ *
+ * TODO: Add support for two and more items
+ */
+
+GDBusMessage *
+_g_dbus_message_new_from_kdbus_items (GSList *kdbus_msg_items,
+ GError **error)
+{
+ gboolean ret;
+ GMemoryBuffer mbuf;
+ GDBusMessage *message;
+ guchar endianness;
+ guchar major_protocol_version;
+ guint32 message_body_len;
+ guint32 message_headers_len;
+ GVariant *headers;
+ GVariant *item;
+ GVariantIter iter;
+ GVariant *signature;
+
+ ret = FALSE;
+
+ g_return_val_if_fail (kdbus_msg_items != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ message = g_dbus_message_new ();
+ memset (&mbuf, 0, sizeof (mbuf));
+
+ /*
+ * MESSAGE HEADER
+ * message header in its entirety must be contained in a first single item
+ */
+ mbuf.data = ((msg_part*)kdbus_msg_items->data)->data;
+ mbuf.len = mbuf.valid_len = ((msg_part*)kdbus_msg_items->data)->size;
+
+ endianness = g_memory_buffer_read_byte (&mbuf);
+ switch (endianness)
+ {
+ case 'l':
+ mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN;
+ message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN;
+ break;
+ case 'B':
+ mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
+ message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
+ break;
+ default:
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Invalid endianness value. Expected 0x6c ('l') or 0x42 ('B') but found value 0x%02x"),
+ endianness);
+ goto out;
+ }
+
+ message->type = g_memory_buffer_read_byte (&mbuf);
+ message->flags = g_memory_buffer_read_byte (&mbuf);
+ major_protocol_version = g_memory_buffer_read_byte (&mbuf);
+
+ if (major_protocol_version != 2)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Invalid major protocol version. Expected 2 but found %d"),
+ major_protocol_version);
+ goto out;
+ }
+
+ message_body_len = g_memory_buffer_read_uint32 (&mbuf);
+ message->serial = g_memory_buffer_read_uint32 (&mbuf);
+
+ message_headers_len = g_memory_buffer_read_uint32 (&mbuf);
+ headers = g_variant_new_from_data (G_VARIANT_TYPE ("a{yv}"),
+ mbuf.data + mbuf.pos,
+ message_headers_len,
+ TRUE,
+ NULL,
+ NULL);
+ mbuf.pos += message_headers_len;
+
+ if (headers == NULL)
+ goto out;
+ g_variant_iter_init (&iter, headers);
+ while ((item = g_variant_iter_next_value (&iter)) != NULL)
+ {
+ guchar header_field;
+ GVariant *value;
+ g_variant_get (item,
+ "{yv}",
+ &header_field,
+ &value);
+ g_dbus_message_set_header (message, header_field, value);
+ g_variant_unref (value);
+ g_variant_unref (item);
+ }
+ g_variant_unref (headers);
+
+ signature = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE);
+ if (signature != NULL)
+ {
+ const gchar *signature_str;
+ gsize signature_str_len;
+
+ signature_str = g_variant_get_string (signature, &signature_str_len);
+
+ if (signature_str_len > 0)
+ {
+ GVariantType *variant_type;
+ gchar *tupled_signature_str;
+
+ gchar *data = NULL;
+ gsize size = NULL;
+
+ if (!g_variant_is_signature (signature_str))
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Parsed value '%s' is not a valid D-Bus signature (for body)"),
+ signature_str);
+ goto out;
+ }
+ tupled_signature_str = g_strdup_printf ("(%s)", signature_str);
+ variant_type = g_variant_type_new (tupled_signature_str);
+ g_free (tupled_signature_str);
+
+ /*
+ * MESSAGE BODY
+ */
+
+ if (g_slist_length(kdbus_msg_items) == 1)
+ {
+ /* if kdbus_msg_items has only one element, head and body are
+ contained in a single PAYLOAD_VEC item */
+ ensure_input_padding (&mbuf,8);
+ data = mbuf.data + mbuf.pos;
+ size = message_body_len;
+ }
+ else if (g_slist_length(kdbus_msg_items) > 1)
+ {
+ /* message consists two or more items
+ TODO: Add support for three and more items */
+ data = ((msg_part*)g_slist_next(kdbus_msg_items)->data)->data;
+ size = ((msg_part*)g_slist_next(kdbus_msg_items)->data)->size;
+ }
+ else
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("[KDBUS] Received message is not valid"));
+ goto out;
+ }
+
+ message->body = g_variant_new_from_data (variant_type,
+ data,
+ size,
+ TRUE,
+ NULL,
+ NULL);
+
+ g_variant_type_free (variant_type);
+ if (message->body == NULL)
+ goto out;
+ }
+ }
+ else
+ {
+ /* no signature, this is only OK if the body is empty */
+ if (message_body_len != 0)
+ {
+ /* G_GUINT32_FORMAT doesn't work with gettext, just use %u */
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ g_dngettext (GETTEXT_PACKAGE,
+ "No signature header in message but the message body is %u byte",
+ "No signature header in message but the message body is %u bytes",
+ message_body_len),
+ message_body_len);
+ goto out;
+ }
+ }
+
+ if (!validate_headers (message, error))
+ {
+ g_prefix_error (error, _("Cannot deserialize message: "));
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ if (ret)
+ {
+ return message;
+ }
+ else
+ {
+ if (message != NULL)
+ g_object_unref (message);
+ return NULL;
+ }
+}
+