+
+dbus_bool_t
+_dbus_message_gvariant_convert_to_unlocked (DBusMessage *message)
+{
+ const char *signature;
+ DBusTypeReader reader;
+ DBusString signature_str;
+ int count_variable_sized = 0;
+
+ _dbus_assert (_dbus_message_is_gvariant (message));
+ _dbus_assert (message->locked);
+
+ /* Differences between locked and unlocked GVariant messages:
+ * Locked:
+ * - body contains footer (zero byte, signature in parentheses, body offset);
+ * - there is no signature field in header;
+ * - 'signature' member is set if the signature was needed;
+ * - 'gvariant_body_last_'* members are unused.
+ * Unlocked:
+ * - body ends with root container offsets, if any;
+ * - header contains signature field;
+ * - 'signature' member is NULL;
+ * - 'gvariant_body_last_'* members are set to proper values.
+ *
+ * So, to convert to unlocked we need to:
+ * - check if the last type is a variable-size type; if so, we need
+ * to remember its end position for adding it as an offset to the body
+ * on first append (set 'gvariant_body_last_offset');
+ * - set 'gvariant_body_last_pos' to correct writing position (just before the offsets);
+ * - extract signature from the body and move it to the header field;
+ * - ensure the 'signature' member is NULL;
+ * - shrink body length to end just after the offsets.
+ */
+
+ /* Get the signature C-string, we'll need it to move it from the body
+ * to the header field, and for iterating over types.
+ * We need to clear message->signature later, as the function sets it.
+ */
+ signature = dbus_message_get_signature (message);
+ if (!signature)
+ {
+ _dbus_string_free (message->signature);
+ message->signature = NULL;
+ return FALSE;
+ }
+
+ /* Initialize 'reader' for iterating over types from the signature.
+ */
+ _dbus_string_init_const (&signature_str, signature);
+ reader.type_str = &signature_str;
+ reader.type_pos = 0;
+
+ _dbus_type_reader_gvariant_init (&reader, message);
+
+ /* If the last value is variable-sized, then the last offset is equal to the writing position
+ * The writing position is just before offsets. */
+ message->gvariant_body_last_pos = reader.value_end -
+ (reader.n_offsets * bus_gvariant_determine_word_size (reader.value_end, 0));
+
+ /* Count variable-sized types on the root container level. */
+ while (_dbus_string_get_byte (reader.type_str, reader.type_pos) != 0)
+ {
+ if (_dbus_reader_get_type_fixed_size (&reader, NULL) == 0)
+ count_variable_sized++;
+ _dbus_type_signature_next (_dbus_string_get_const_data(reader.type_str), &reader.type_pos);
+ }
+
+ /* Check if all the offsets are present in the body.
+ * If not, then the last type is of variable size, and will be a subject to add to the body
+ * on the first append to this message.
+ */
+ if (count_variable_sized == reader.n_offsets)
+ message->gvariant_body_last_offset = GVARIANT_LAST_OFFSET_NOT_SET;
+ else
+ message->gvariant_body_last_offset = message->gvariant_body_last_pos;
+
+ /* Convert to unlocked. We'll modify our message now. */
+ message->locked = FALSE;
+
+ /* Copy signature to the header */
+ if (!_dbus_header_set_field_basic (&message->header,
+ DBUS_HEADER_FIELD_SIGNATURE,
+ DBUS_TYPE_SIGNATURE,
+ &signature))
+ {
+ _dbus_string_free (message->signature);
+ message->signature = NULL;
+ return FALSE;
+ }
+
+ /* Unlocked DBusMessage needs to have NULL 'signature' member, since it has the above header field
+ * for signatures. Our 'message' has the member set, because dbus_message_get_signature() added it.
+ * Let's get rid of it.
+ */
+ _dbus_assert (message->signature);
+ _dbus_string_free (message->signature);
+ message->signature = NULL;
+
+ /* Unlocked messages have no signature, variant's zero byte, and final offset in the body.
+ * Cut them off. Luckily, _dbus_type_reader_gvariant_init() has already computed the correct
+ * length for us.
+ */
+ _dbus_string_set_length (&message->body, reader.value_end);
+
+ return TRUE;
+}