GVariant: fix for not having offset for empty arrays
[platform/upstream/dbus.git] / dbus / dbus-message.c
index 32ac37a..322155a 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2002, 2003, 2004, 2005  Red Hat Inc.
  * Copyright (C) 2002, 2003  CodeFactory AB
+ * Copyright (C) 2015  Samsung Electronics
  *
  * Licensed under the Academic Free License version 2.1
  *
 #include "dbus-list.h"
 #include "dbus-threads-internal.h"
 #ifdef HAVE_UNIX_FD_PASSING
+#include "dbus-sysdeps.h"
 #include "dbus-sysdeps-unix.h"
 #endif
+#include "dbus-marshal-gvariant.h"
+#include "dbus-protocol-gvariant.h"
 
 #include <string.h>
 
   (type == DBUS_TYPE_STRING || type == DBUS_TYPE_SIGNATURE || \
    type == DBUS_TYPE_OBJECT_PATH)
 
+static unsigned char _dbus_default_protocol_version = DBUS_MAJOR_PROTOCOL_VERSION; /* DBUS_PROTOCOL_VERSION_GVARIANT; */
+static dbus_bool_t _dbus_first_bus_open = FALSE;
+
+static void protocol_strategy_last_type (int type);
+static void protocol_strategy_static (int type);
+
+typedef void (*DBusProtocolStrategyFunction)(int type);
+static DBusProtocolStrategyFunction _dbus_protocol_strategy_bus_function = protocol_strategy_last_type;
+static DBusProtocolStrategyFunction _dbus_protocol_strategy_message_function = protocol_strategy_static;
+
 static void dbus_message_finalize (DBusMessage *message);
 
 /**
@@ -117,6 +131,8 @@ enum {
 /** typedef for internals of message iterator */
 typedef struct DBusMessageRealIter DBusMessageRealIter;
 
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+
 /**
  * @brief Internals of DBusMessageIter
  *
@@ -135,19 +151,82 @@ struct DBusMessageRealIter
   } u; /**< the type writer or reader that does all the work */
 };
 
+static dbus_bool_t
+_dbus_header_is_gvariant (const DBusHeader *header)
+{
+  return (header->protocol_version == DBUS_PROTOCOL_VERSION_GVARIANT);
+}
+
+static dbus_bool_t
+_dbus_message_is_gvariant (const DBusMessage *message)
+{
+  return _dbus_header_is_gvariant (&message->header);
+}
+
+static void
+_dbus_message_toggle_gvariant (DBusMessage *message, dbus_bool_t gvariant)
+{
+  message->header.protocol_version = gvariant ? DBUS_PROTOCOL_VERSION_GVARIANT : DBUS_MAJOR_PROTOCOL_VERSION;
+}
+
 static void
-get_const_signature (DBusHeader        *header,
+get_const_signature (DBusMessage       *message,
                      const DBusString **type_str_p,
                      int               *type_pos_p)
 {
-  if (_dbus_header_get_field_raw (header,
-                                  DBUS_HEADER_FIELD_SIGNATURE,
-                                  type_str_p,
-                                  type_pos_p))
+  dbus_bool_t got_signature = FALSE;
+  if (_dbus_message_is_gvariant (message) && message->locked)
     {
-      *type_pos_p += 1; /* skip the signature length which is 1 byte */
+      /* only locked GVariant messages have signatures in the body */
+      /*
+       * in case of received GVariant message, there may be no signature field in a header,
+       * but in the body. However, it is not nul-terminated.
+       * So, we need to allocate space and put it into message.
+       * It could also happen before, so check message->signature for already existing.
+       * FIXME: That may kinda break oom-safety.
+       *        For now - if oom, then return empty signature.
+       */
+      if (message->signature == NULL)
+        {
+          int type_str_len;
+          got_signature = _dbus_message_gvariant_get_signature (message,
+                                                                type_str_p,
+                                                                type_pos_p,
+                                                                &type_str_len);
+          if (got_signature && type_str_len > 1)
+            {
+              message->signature = dbus_new (DBusString, 1);
+              got_signature = got_signature &&
+                             _dbus_string_init_preallocated (message->signature, type_str_len - 1);
+
+              /* we need to copy the signature, but to ensure backward compatibility
+               * it must be stripped off outer parentheses - they are always there */
+              got_signature = got_signature &&
+                              _dbus_string_copy_len (*type_str_p, *type_pos_p + 1, type_str_len - 2,
+                                                     message->signature, 0);
+              got_signature = got_signature &&
+                              _dbus_string_append_byte (message->signature, 0);
+            }
+        }
+      else
+        got_signature = TRUE;
+
+      if (got_signature)
+        {
+          *type_str_p = message->signature;
+          *type_pos_p = 0;
+        }
     }
-  else
+  else if (_dbus_header_get_field_raw (&message->header,
+                                       DBUS_HEADER_FIELD_SIGNATURE,
+                                       type_str_p,
+                                       type_pos_p))
+    {
+      if (!_dbus_message_is_gvariant (message))
+        *type_pos_p += 1; /* skip the signature length which is 1 byte */
+      got_signature = TRUE;
+    }
+  if (!got_signature)
     {
       *type_str_p = &_dbus_empty_signature_str;
       *type_pos_p = 0;
@@ -173,7 +252,7 @@ _dbus_message_byteswap (DBusMessage *message)
 
   _dbus_verbose ("Swapping message into compiler byte order\n");
   
-  get_const_signature (&message->header, &type_str, &type_pos);
+  get_const_signature (message, &type_str, &type_pos);
   
   _dbus_marshal_byteswap (type_str, type_pos,
                           byte_order,
@@ -214,6 +293,13 @@ _dbus_message_get_network_data (DBusMessage          *message,
   *body = &message->body;
 }
 
+void _dbus_message_set_timeout_ms(DBusMessage *message, int timeout_ms) {
+  message->timeout_ms = timeout_ms;
+}
+int _dbus_message_get_timeout_ms(DBusMessage const *message) {
+  return message->timeout_ms;
+}
+
 /**
  * Gets the unix fds to be sent over the network for this message.
  * This function is guaranteed to always return the same data once a
@@ -384,8 +470,11 @@ dbus_message_lock (DBusMessage  *message)
 {
   if (!message->locked)
     {
-      _dbus_header_update_lengths (&message->header,
-                                   _dbus_string_get_length (&message->body));
+      if (!_dbus_message_is_gvariant (message))
+        _dbus_header_update_lengths (&message->header,
+                                     _dbus_string_get_length (&message->body));
+      else
+        _dbus_message_finalize_gvariant (message, TRUE);
 
       /* must have a signature if you have a body */
       _dbus_assert (_dbus_string_get_length (&message->body) == 0 ||
@@ -601,7 +690,7 @@ static void
 close_unix_fds(int *fds, unsigned *n_fds)
 {
   DBusError e;
-  int i;
+  unsigned int i;
 
   if (*n_fds <= 0)
     return;
@@ -665,6 +754,20 @@ dbus_message_cache_or_finalize (DBusMessage *message)
   close_unix_fds(message->unix_fds, &message->n_unix_fds);
 #endif
 
+  if (NULL != message->signature)
+  {
+    _dbus_string_free (message->signature);
+    dbus_free (message->signature);
+    message->signature = NULL;
+  }
+
+  if (NULL != message->unique_sender)
+  {
+    _dbus_string_free (message->unique_sender);
+    dbus_free (message->unique_sender);
+    message->unique_sender = NULL;
+  }
+
   was_cached = FALSE;
 
   if (!_DBUS_LOCK (message_cache))
@@ -812,7 +915,7 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
   /* copy var_args first, then we can do another iteration over it to
    * free memory and close unix fds if parse failed at some point.
    */
-  va_copy (copy_args, var_args);
+  DBUS_VA_COPY (copy_args, var_args);
 
   while (spec_type != DBUS_TYPE_INVALID)
     {
@@ -1142,13 +1245,25 @@ dbus_bool_t
 dbus_message_set_reply_serial (DBusMessage   *message,
                                dbus_uint32_t  reply_serial)
 {
+  int type = DBUS_TYPE_UINT32;
+
   _dbus_return_val_if_fail (message != NULL, FALSE);
   _dbus_return_val_if_fail (!message->locked, FALSE);
   _dbus_return_val_if_fail (reply_serial != 0, FALSE); /* 0 is invalid */
 
+  if (_dbus_message_is_gvariant (message))
+    {
+      dbus_uint64_t reply_serial_uint64 = reply_serial;
+      type = DBUS_TYPE_UINT64;
+      return _dbus_header_set_field_basic (&message->header,
+                                           DBUS_HEADER_FIELD_REPLY_SERIAL,
+                                           type,
+                                           &reply_serial_uint64);
+    }
+
   return _dbus_header_set_field_basic (&message->header,
                                        DBUS_HEADER_FIELD_REPLY_SERIAL,
-                                       DBUS_TYPE_UINT32,
+                                       type,
                                        &reply_serial);
 }
 
@@ -1162,14 +1277,28 @@ dbus_uint32_t
 dbus_message_get_reply_serial  (DBusMessage *message)
 {
   dbus_uint32_t v_UINT32;
+  dbus_uint64_t v_UINT64;
+  int type = DBUS_TYPE_UINT32;
+  void *value = &v_UINT32;
 
   _dbus_return_val_if_fail (message != NULL, 0);
 
+  if (_dbus_message_is_gvariant (message))
+    {
+      type = DBUS_TYPE_UINT64;
+      value = &v_UINT64;
+    }
+
   if (_dbus_header_get_field_basic (&message->header,
                                     DBUS_HEADER_FIELD_REPLY_SERIAL,
-                                    DBUS_TYPE_UINT32,
-                                    &v_UINT32))
-    return v_UINT32;
+                                    type,
+                                    value))
+    {
+      if (_dbus_message_is_gvariant (message))
+        return v_UINT64;
+      else
+        return v_UINT32;
+    }
   else
     return 0;
 }
@@ -1200,7 +1329,7 @@ dbus_message_finalize (DBusMessage *message)
 }
 
 static DBusMessage*
-dbus_message_new_empty_header (void)
+dbus_message_new_empty_header (dbus_bool_t gvariant)
 {
   DBusMessage *message;
   dbus_bool_t from_cache;
@@ -1237,6 +1366,7 @@ dbus_message_new_empty_header (void)
 #endif
   message->counters = NULL;
   message->size_counter_delta = 0;
+  message->timeout_ms = -1;
   message->changed_stamp = 0;
 
 #ifdef HAVE_UNIX_FD_PASSING
@@ -1245,6 +1375,8 @@ dbus_message_new_empty_header (void)
   message->unix_fd_counter_delta = 0;
 #endif
 
+  _dbus_message_toggle_gvariant (message, gvariant);  /* this works only if kdbus is enabled */
+
   if (!from_cache)
     _dbus_data_slot_list_init (&message->slot_list);
 
@@ -1269,36 +1401,35 @@ dbus_message_new_empty_header (void)
         }
     }
 
+  message->signature = NULL;
+  message->unique_sender = NULL;
+  message->gvariant_body_last_offset = GVARIANT_LAST_OFFSET_NOT_SET;
+  message->gvariant_body_last_pos = 0;
+
   return message;
 }
 
-/**
- * Constructs a new message of the given message type.
- * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL,
- * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth.
- *
- * Usually you want to use dbus_message_new_method_call(),
- * dbus_message_new_method_return(), dbus_message_new_signal(),
- * or dbus_message_new_error() instead.
- * 
- * @param message_type type of message
- * @returns new message or #NULL if no memory
- */
-DBusMessage*
-dbus_message_new (int message_type)
+static DBusMessage*
+_dbus_message_create_protocol_version (int         message_type,
+                            const char  *destination,
+                            const char  *path,
+                            const char  *interface,
+                            const char  *member,
+                            const char  *error_name,
+                           dbus_bool_t gvariant)
 {
   DBusMessage *message;
 
-  _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL);
+  _dbus_assert (message_type != DBUS_MESSAGE_TYPE_INVALID);
 
-  message = dbus_message_new_empty_header ();
+  message = dbus_message_new_empty_header (gvariant);
   if (message == NULL)
     return NULL;
 
-  if (!_dbus_header_create (&message->header,
+  if (!(_dbus_message_is_gvariant(message) ? _dbus_header_gvariant_create : _dbus_header_create) (&message->header,
                             DBUS_COMPILER_BYTE_ORDER,
                             message_type,
-                            NULL, NULL, NULL, NULL, NULL))
+                            destination, path, interface, member, error_name))
     {
       dbus_message_unref (message);
       return NULL;
@@ -1307,6 +1438,42 @@ dbus_message_new (int message_type)
   return message;
 }
 
+static DBusMessage*
+_dbus_message_create (int         message_type,
+                            const char  *destination,
+                            const char  *path,
+                            const char  *interface,
+                            const char  *member,
+                            const char  *error_name)
+{
+       return _dbus_message_create_protocol_version(message_type,
+                                            destination,
+                                            path,
+                                            interface,
+                                            member,
+                                             error_name,
+                                            _dbus_default_protocol_version == DBUS_PROTOCOL_VERSION_GVARIANT);
+}
+
+/**
+ * Constructs a new message of the given message type.
+ * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL,
+ * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth.
+ *
+ * Usually you want to use dbus_message_new_method_call(),
+ * dbus_message_new_method_return(), dbus_message_new_signal(),
+ * or dbus_message_new_error() instead.
+ *
+ * @param message_type type of message
+ * @returns new message or #NULL if no memory
+ */
+DBusMessage*
+dbus_message_new (int message_type)
+{
+  return _dbus_message_create(message_type,
+                                   NULL, NULL, NULL, NULL, NULL);
+}
+
 /**
  * Constructs a new message to invoke a method on a remote
  * object. Returns #NULL if memory can't be allocated for the
@@ -1334,8 +1501,6 @@ dbus_message_new_method_call (const char *destination,
                               const char *iface,
                               const char *method)
 {
-  DBusMessage *message;
-
   _dbus_return_val_if_fail (path != NULL, NULL);
   _dbus_return_val_if_fail (method != NULL, NULL);
   _dbus_return_val_if_fail (destination == NULL ||
@@ -1345,20 +1510,8 @@ dbus_message_new_method_call (const char *destination,
                             _dbus_check_is_valid_interface (iface), NULL);
   _dbus_return_val_if_fail (_dbus_check_is_valid_member (method), NULL);
 
-  message = dbus_message_new_empty_header ();
-  if (message == NULL)
-    return NULL;
-
-  if (!_dbus_header_create (&message->header,
-                            DBUS_COMPILER_BYTE_ORDER,
-                            DBUS_MESSAGE_TYPE_METHOD_CALL,
-                            destination, path, iface, method, NULL))
-    {
-      dbus_message_unref (message);
-      return NULL;
-    }
-
-  return message;
+  return _dbus_message_create(DBUS_MESSAGE_TYPE_METHOD_CALL,
+                                   destination, path, iface, method, NULL);
 }
 
 /**
@@ -1380,19 +1533,11 @@ dbus_message_new_method_return (DBusMessage *method_call)
 
   /* sender is allowed to be null here in peer-to-peer case */
 
-  message = dbus_message_new_empty_header ();
+  message = _dbus_message_create (DBUS_MESSAGE_TYPE_METHOD_RETURN,
+                                       sender, NULL, NULL, NULL, NULL);
   if (message == NULL)
     return NULL;
 
-  if (!_dbus_header_create (&message->header,
-                            DBUS_COMPILER_BYTE_ORDER,
-                            DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                            sender, NULL, NULL, NULL, NULL))
-    {
-      dbus_message_unref (message);
-      return NULL;
-    }
-
   dbus_message_set_no_reply (message, TRUE);
 
   if (!dbus_message_set_reply_serial (message,
@@ -1433,19 +1578,11 @@ dbus_message_new_signal (const char *path,
   _dbus_return_val_if_fail (_dbus_check_is_valid_interface (iface), NULL);
   _dbus_return_val_if_fail (_dbus_check_is_valid_member (name), NULL);
 
-  message = dbus_message_new_empty_header ();
+  message = _dbus_message_create (DBUS_MESSAGE_TYPE_SIGNAL,
+                                  NULL, path, iface, name, NULL);
   if (message == NULL)
     return NULL;
 
-  if (!_dbus_header_create (&message->header,
-                            DBUS_COMPILER_BYTE_ORDER,
-                            DBUS_MESSAGE_TYPE_SIGNAL,
-                            NULL, path, iface, name, NULL))
-    {
-      dbus_message_unref (message);
-      return NULL;
-    }
-
   dbus_message_set_no_reply (message, TRUE);
 
   return message;
@@ -1484,19 +1621,11 @@ dbus_message_new_error (DBusMessage *reply_to,
    * when the message bus is dealing with an unregistered
    * connection.
    */
-  message = dbus_message_new_empty_header ();
+  message = _dbus_message_create (DBUS_MESSAGE_TYPE_ERROR,
+                                  sender, NULL, NULL, NULL, error_name);
   if (message == NULL)
     return NULL;
 
-  if (!_dbus_header_create (&message->header,
-                            DBUS_COMPILER_BYTE_ORDER,
-                            DBUS_MESSAGE_TYPE_ERROR,
-                            sender, NULL, NULL, NULL, error_name))
-    {
-      dbus_message_unref (message);
-      return NULL;
-    }
-
   dbus_message_set_no_reply (message, TRUE);
 
   if (!dbus_message_set_reply_serial (message,
@@ -1599,6 +1728,7 @@ dbus_message_copy (const DBusMessage *message)
 #ifndef DBUS_DISABLE_CHECKS
   retval->generation = message->generation;
 #endif
+  _dbus_message_toggle_gvariant (retval, _dbus_message_is_gvariant (message));
 
   if (!_dbus_header_copy (&message->header, &retval->header))
     {
@@ -2024,7 +2154,7 @@ _dbus_message_iter_init_common (DBusMessage         *message,
                                 DBusMessageRealIter *real,
                                 int                  iter_type)
 {
-  _dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter));
+  _DBUS_STATIC_ASSERT (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter));
 
   /* Since the iterator will read or write who-knows-what from the
    * message, we need to get in the right byte order
@@ -2067,19 +2197,24 @@ dbus_message_iter_init (DBusMessage     *message,
   const DBusString *type_str;
   int type_pos;
 
+  BUILD_BUG_ON (sizeof(DBusMessageIter) != sizeof(DBusMessageRealIter));
+
   _dbus_return_val_if_fail (message != NULL, FALSE);
   _dbus_return_val_if_fail (iter != NULL, FALSE);
 
-  get_const_signature (&message->header, &type_str, &type_pos);
+  get_const_signature (message, &type_str, &type_pos);
 
   _dbus_message_iter_init_common (message, real,
                                   DBUS_MESSAGE_ITER_TYPE_READER);
 
   _dbus_type_reader_init (&real->u.reader,
-                          _dbus_header_get_byte_order (&message->header),
-                          type_str, type_pos,
-                          &message->body,
-                          0);
+                                   _dbus_header_get_byte_order (&message->header),
+                                   type_str, type_pos,
+                                   &message->body,
+                                   0);
+
+  if (_dbus_message_is_gvariant (message))
+    _dbus_type_reader_gvariant_init (&real->u.reader, message);
 
   return _dbus_type_reader_get_current_type (&real->u.reader) != DBUS_TYPE_INVALID;
 }
@@ -2324,20 +2459,56 @@ dbus_message_iter_get_basic (DBusMessageIter  *iter,
 }
 
 /**
+ * Returns the number of elements in the array-typed value pointed
+ * to by the iterator.
+ * Note that this function is O(1) for arrays of fixed-size types
+ * but O(n) for arrays of variable-length types such as strings,
+ * so it may be a bad idea to use it.
+ *
+ * @param iter the iterator
+ * @returns the number of elements in the array
+ */
+int
+dbus_message_iter_get_element_count (DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+  DBusTypeReader array;
+  int element_type;
+  int n_elements = 0;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), 0);
+  _dbus_return_val_if_fail (_dbus_type_reader_get_current_type (&real->u.reader)
+                            == DBUS_TYPE_ARRAY, 0);
+
+  element_type = _dbus_type_reader_get_element_type (&real->u.reader);
+  _dbus_type_reader_recurse (&real->u.reader, &array);
+  if (dbus_type_is_fixed (element_type))
+    {
+      int alignment = _dbus_type_get_alignment (element_type);
+      int total_len = _dbus_type_reader_get_array_length (&array);
+      n_elements = total_len / alignment;
+    }
+  else
+    {
+      while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+        {
+          ++n_elements;
+          _dbus_type_reader_next (&array);
+        }
+    }
+
+   return n_elements;
+}
+
+/**
  * Returns the number of bytes in the array as marshaled in the wire
  * protocol. The iterator must currently be inside an array-typed
  * value.
  *
  * This function is deprecated on the grounds that it is stupid.  Why
  * would you want to know how many bytes are in the array as marshaled
- * in the wire protocol?  For now, use the n_elements returned from
- * dbus_message_iter_get_fixed_array() instead, or iterate over the
- * array values and count them.
+ * in the wire protocol?  Use dbus_message_iter_get_element_count() instead.
  *
- * @todo introduce a variant of this get_n_elements that returns
- * the number of elements, though with a non-fixed array it will not
- * be very efficient, so maybe it's not good.
- * 
  * @param iter the iterator
  * @returns the number of bytes in the array
  */
@@ -2432,10 +2603,14 @@ dbus_message_iter_init_append (DBusMessage     *message,
    * when a value is actually appended. That means that init() never fails
    * due to OOM.
    */
-  _dbus_type_writer_init_types_delayed (&real->u.writer,
-                                        _dbus_header_get_byte_order (&message->header),
-                                        &message->body,
-                                        _dbus_string_get_length (&message->body));
+  _dbus_type_writer_gvariant_init_types_delayed (
+                              &real->u.writer,
+                              _dbus_header_get_byte_order (&message->header),
+                              &message->body,
+                              _dbus_string_get_length (&message->body),
+                              _dbus_message_is_gvariant (message),
+                              &message->gvariant_body_last_offset,
+                              &message->gvariant_body_last_pos);
 }
 
 /**
@@ -2474,11 +2649,21 @@ _dbus_message_iter_open_signature (DBusMessageRealIter *real)
   if (current_sig)
     {
       int current_len;
+      int additional_size_for_len = 0;
 
-      current_len = _dbus_string_get_byte (current_sig, current_sig_pos);
-      current_sig_pos += 1; /* move on to sig data */
+      if (!real->u.writer.gvariant)
+      {
+        current_len = _dbus_string_get_byte (current_sig, current_sig_pos);
+        current_sig_pos += 1; /* move on to sig data */
+        additional_size_for_len = 4;
+      }
+      else
+      {
+        /* GVariant has no length field, simply string */
+        current_len = strlen (_dbus_string_get_const_data (current_sig) + current_sig_pos);
+      }
 
-      if (!_dbus_string_init_preallocated (str, current_len + 4))
+      if (!_dbus_string_init_preallocated (str, current_len + additional_size_for_len))
         {
           dbus_free (str);
           return FALSE;
@@ -3512,6 +3697,9 @@ dbus_message_get_sender (DBusMessage *message)
 
   _dbus_return_val_if_fail (message != NULL, NULL);
 
+  if (NULL != message->unique_sender)
+    return _dbus_string_get_const_data (message->unique_sender);
+
   v = NULL; /* in case field doesn't exist */
   _dbus_header_get_field_basic (&message->header,
                                 DBUS_HEADER_FIELD_SENDER,
@@ -3546,7 +3734,7 @@ dbus_message_get_signature (DBusMessage *message)
 
   _dbus_return_val_if_fail (message != NULL, NULL);
 
-  get_const_signature (&message->header, &type_str, &type_pos);
+  get_const_signature (message, &type_str, &type_pos);
 
   return _dbus_string_get_const_data_len (type_str, type_pos, 0);
 }
@@ -4058,6 +4246,9 @@ _dbus_message_loader_return_unix_fds(DBusMessageLoader  *loader,
 
   loader->n_unix_fds += n_fds;
   loader->unix_fds_outstanding = FALSE;
+
+  if (n_fds && loader->unix_fds_change)
+    loader->unix_fds_change (loader->unix_fds_change_data);
 #else
   _dbus_assert_not_reached("Platform doesn't support unix fd passing");
 #endif
@@ -4147,18 +4338,35 @@ load_message (DBusMessageLoader *loader,
   /* 2. VALIDATE BODY */
   if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
     {
-      get_const_signature (&message->header, &type_str, &type_pos);
-      
-      /* Because the bytes_remaining arg is NULL, this validates that the
-       * body is the right length
-       */
-      validity = _dbus_validate_body_with_reason (type_str,
-                                                  type_pos,
-                                                  byte_order,
-                                                  NULL,
-                                                  &loader->data,
-                                                  header_len,
-                                                  body_len);
+      if (_dbus_message_is_gvariant (message))
+        {
+          type_str = NULL;
+          type_pos = 0;
+          validity = _dbus_validate_gvariant_body_with_reason (type_str,
+                                                               type_pos,
+                                                               byte_order,
+                                                               NULL,
+                                                               &loader->data,
+                                                               header_len,
+                                                               body_len);
+        }
+      else
+        {
+
+          get_const_signature (message, &type_str, &type_pos);
+
+          /* Because the bytes_remaining arg is NULL, this validates that the
+           * body is the right length
+           */
+
+          validity = _dbus_validate_body_with_reason (type_str,
+                                                      type_pos,
+                                                      byte_order,
+                                                      NULL,
+                                                      &loader->data,
+                                                      header_len,
+                                                      body_len);
+        }
       if (validity != DBUS_VALID)
         {
           _dbus_verbose ("Failed to validate message body code %d\n", validity);
@@ -4204,7 +4412,10 @@ load_message (DBusMessageLoader *loader,
 
       message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds;
       loader->n_unix_fds -= n_unix_fds;
-      memmove(loader->unix_fds + n_unix_fds, loader->unix_fds, loader->n_unix_fds);
+      memmove (loader->unix_fds, loader->unix_fds + n_unix_fds, loader->n_unix_fds * sizeof (loader->unix_fds[0]));
+
+      if (loader->unix_fds_change)
+        loader->unix_fds_change (loader->unix_fds_change_data);
     }
   else
     message->unix_fds = NULL;
@@ -4277,6 +4488,32 @@ load_message (DBusMessageLoader *loader,
   return FALSE;
 }
 
+static dbus_bool_t
+set_unique_sender (DBusMessage *message, uint64_t unique_sender_id)
+{
+  if (NULL == message->unique_sender)
+    {
+      message->unique_sender = dbus_new (DBusString, 1);
+      if (NULL == message->unique_sender)
+        return FALSE;
+
+      if (!_dbus_string_init (message->unique_sender))
+        return FALSE;
+    }
+
+  _dbus_string_set_length (message->unique_sender, 0);
+
+  if (!_dbus_string_append_printf (message->unique_sender, ":1.%llu", (unsigned long long)unique_sender_id))
+    {
+      _dbus_string_free (message->unique_sender);
+      dbus_free (message->unique_sender);
+      message->unique_sender = NULL;
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
 /**
  * Converts buffered data into messages, if we have enough data.  If
  * we don't have enough data, does nothing.
@@ -4299,6 +4536,7 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
     {
       DBusValidity validity;
       int byte_order, fields_array_len, header_len, body_len;
+      dbus_bool_t is_gvariant;
 
       if (_dbus_header_have_message_untrusted (loader->max_message_size,
                                                &validity,
@@ -4307,13 +4545,14 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
                                                &header_len,
                                                &body_len,
                                                &loader->data, 0,
-                                               _dbus_string_get_length (&loader->data)))
+                                               _dbus_string_get_length (&loader->data),
+                                               &is_gvariant))
         {
           DBusMessage *message;
 
           _dbus_assert (validity == DBUS_VALID);
 
-          message = dbus_message_new_empty_header ();
+          message = dbus_message_new_empty_header (is_gvariant);
           if (message == NULL)
             return FALSE;
 
@@ -4328,6 +4567,12 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
               return loader->corrupted;
             }
 
+          if (_dbus_message_is_gvariant (message))
+            {
+              set_unique_sender (message, _dbus_message_loader_get_unique_sender_id (loader));
+              message->locked = TRUE;
+            }
+
           _dbus_assert (loader->messages != NULL);
           _dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL);
        }
@@ -4347,6 +4592,172 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
   return TRUE;
 }
 
+DBusMessage *
+_dbus_decode_kmsg (DBusString  *data,
+                   uint64_t     sender_id,
+                   int         *fds,
+                   unsigned     n_fds)
+{
+  if (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE)
+    {
+
+      DBusValidity validity;
+      int byte_order, fields_array_len, header_len, body_len;
+      dbus_bool_t is_gvariant;
+
+      if (_dbus_header_have_message_untrusted (DBUS_MAXIMUM_MESSAGE_LENGTH,
+                                               &validity,
+                                               &byte_order,
+                                               &fields_array_len,
+                                               &header_len,
+                                               &body_len,
+                                               data, 0,
+                                               _dbus_string_get_length (data),
+                                               &is_gvariant))
+        {
+          DBusMessage *message;
+          dbus_uint32_t n_unix_fds = 0;
+          const DBusString *type_str = NULL;
+          int type_pos = 0;
+
+          _dbus_assert (validity == DBUS_VALID);
+
+          message = dbus_message_new_empty_header (is_gvariant);
+          if (message == NULL)
+            return NULL;
+
+          /*
+           * Validate and copy over header
+           */
+          _dbus_assert (_dbus_string_get_length (&message->header.data) == 0);
+          _dbus_assert ((header_len + body_len) <= _dbus_string_get_length (data));
+
+          if (!_dbus_header_load (&message->header,
+                                  DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED,
+                                  &validity,
+                                  byte_order,
+                                  fields_array_len,
+                                  header_len,
+                                  body_len,
+                                  data, 0,
+                                  _dbus_string_get_length (data)))
+            {
+              _dbus_verbose ("Failed to load header for new message code %d\n", validity);
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          _dbus_assert (validity == DBUS_VALID);
+
+          /*
+           * Validate body
+           */
+          if (_dbus_message_is_gvariant (message))
+            {
+              validity = _dbus_validate_gvariant_body_with_reason (type_str,
+                                                                   type_pos,
+                                                                   byte_order,
+                                                                   NULL,
+                                                                   data,
+                                                                   header_len,
+                                                                   body_len);
+            }
+          else
+            {
+              _dbus_verbose ("Not valid GVariant dbus message\n");
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          if (validity != DBUS_VALID)
+            {
+              _dbus_verbose ("Failed to validate message body code %d\n", validity);
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          /*
+           * Copy over Unix FDS
+           */
+          _dbus_header_get_field_basic(&message->header,
+                                       DBUS_HEADER_FIELD_UNIX_FDS,
+                                       DBUS_TYPE_UINT32,
+                                       &n_unix_fds);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+          if (n_unix_fds > n_fds)
+            {
+              _dbus_verbose("Message contains references to more unix fds than were sent %u != %u\n",
+                            n_unix_fds, message->n_unix_fds);
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          /* If this was a recycled message there might still be
+             some memory allocated for the fds */
+          dbus_free(message->unix_fds);
+
+          if (n_unix_fds > 0)
+            {
+              message->unix_fds = _dbus_memdup(fds, n_unix_fds * sizeof(message->unix_fds[0]));
+              if (message->unix_fds == NULL)
+                {
+                  _dbus_verbose ("Failed to allocate file descriptor array\n");
+                  dbus_message_unref (message);
+                  return NULL;
+                }
+
+              message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds;
+            }
+          else
+            message->unix_fds = NULL;
+#else
+          if (n_unix_fds > 0)
+            {
+              _dbus_verbose ("Hmm, message claims to come with file descriptors "
+                             "but that's not supported on our platform, disconnecting.\n");
+              dbus_message_unref (message);
+              return NULL;
+            }
+#endif
+
+          /*
+           * Copy over message body
+           */
+          _dbus_assert (_dbus_string_get_length (&message->body) == 0);
+          _dbus_assert (_dbus_string_get_length (data) >= (header_len + body_len));
+
+          if (!_dbus_string_copy_len (data, header_len, body_len, &message->body, 0))
+            {
+              _dbus_verbose ("Failed to move body into new message\n");
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          _dbus_assert (_dbus_string_get_length (&message->header.data) == header_len);
+          _dbus_assert (_dbus_string_get_length (&message->body) == body_len);
+
+           set_unique_sender (message, sender_id);
+           message->locked = TRUE;
+
+           /* Yupi, we have DBusMessage* */
+           return message;
+        }
+      else
+        {
+          _dbus_verbose ("Data broken with invalid code %d\n", validity);
+          return NULL;
+        } /* if _dbus_header_have_message_untrusted() */
+
+    }
+  else
+    {
+      _dbus_verbose ("message size < DBUS_MINIMUM_HEADER_SIZE\n");
+      return NULL;
+    } /* if DBUS_MINIMUM_HEADER_SIZE */
+}
+
 /**
  * Peeks at first loaded message, returns #NULL if no messages have
  * been queued.
@@ -4498,6 +4909,53 @@ _dbus_message_loader_get_max_message_unix_fds (DBusMessageLoader  *loader)
   return loader->max_message_unix_fds;
 }
 
+/**
+ * Return how many file descriptors are pending in the loader
+ *
+ * @param loader the loader
+ */
+int
+_dbus_message_loader_get_pending_fds_count (DBusMessageLoader *loader)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+  return loader->n_unix_fds;
+#else
+  return 0;
+#endif
+}
+
+/**
+ * Register a function to be called whenever the number of pending file
+ * descriptors in the loader change.
+ *
+ * @param loader the loader
+ * @param callback the callback
+ * @param data the data for the callback
+ */
+void
+_dbus_message_loader_set_pending_fds_function (DBusMessageLoader *loader,
+                                               void (* callback) (void *),
+                                               void *data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+  loader->unix_fds_change = callback;
+  loader->unix_fds_change_data = data;
+#endif
+}
+
+void
+_dbus_message_loader_set_unique_sender_id (DBusMessageLoader *loader,
+                                           uint64_t           id)
+{
+  loader->unique_sender_id = id;
+}
+
+uint64_t
+_dbus_message_loader_get_unique_sender_id (DBusMessageLoader *loader)
+{
+  return loader->unique_sender_id;
+}
+
 static DBusDataSlotAllocator slot_allocator =
   _DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (message_slots));
 
@@ -4755,7 +5213,10 @@ dbus_message_demarshal (const char *str,
     return NULL;
 
   _dbus_message_loader_get_buffer (loader, &buffer);
-  _dbus_string_append_len (buffer, str, len);
+
+  if (!_dbus_string_append_len (buffer, str, len))
+    goto fail_oom;
+
   _dbus_message_loader_return_buffer (loader, buffer);
 
   if (!_dbus_message_loader_queue_messages (loader))
@@ -4804,6 +5265,7 @@ dbus_message_demarshal_bytes_needed(const char *buf,
   int byte_order, fields_array_len, header_len, body_len;
   DBusValidity validity = DBUS_VALID;
   int have_message;
+  dbus_bool_t is_gvariant;
 
   if (!buf || len < DBUS_MINIMUM_HEADER_SIZE)
     return 0;
@@ -4820,7 +5282,8 @@ dbus_message_demarshal_bytes_needed(const char *buf,
                                           &header_len,
                                           &body_len,
                                           &str, 0,
-                                          len);
+                                          len,
+                                          &is_gvariant);
   _dbus_string_free (&str);
 
   if (validity == DBUS_VALID)
@@ -4835,6 +5298,301 @@ dbus_message_demarshal_bytes_needed(const char *buf,
     }
 }
 
+static dbus_bool_t
+_dbus_message_copy_recursive(DBusMessageIter *iter, DBusMessageIter *dest)
+{
+  dbus_bool_t res = TRUE;
+  int current_type;
+
+  while ((current_type = dbus_message_iter_get_arg_type (iter)) != DBUS_TYPE_INVALID) {
+    if (dbus_type_is_basic(current_type)) {
+      DBusBasicValue value;
+      dbus_message_iter_get_basic (iter, &value);
+      dbus_message_iter_append_basic (dest, current_type, &value);
+    }
+    else {
+      DBusMessageIter sub;
+      DBusMessageIter dest_sub;
+      char *sig = NULL;
+
+      dbus_message_iter_recurse (iter, &sub);
+      if (DBUS_TYPE_VARIANT == current_type)
+        sig = dbus_message_iter_get_signature (&sub);
+      else if (DBUS_TYPE_ARRAY == current_type)
+        sig = dbus_message_iter_get_signature (&sub);
+
+      res = res && dbus_message_iter_open_container (dest, current_type, sig, &dest_sub);
+      dbus_free(sig);
+      res = res && _dbus_message_copy_recursive (&sub, &dest_sub);
+      res = res && dbus_message_iter_close_container (dest, &dest_sub);
+
+      if (!res) {
+        return FALSE;
+      }
+   }
+
+   dbus_message_iter_next (iter);
+  }
+
+  return TRUE;
+}
+
+void
+_dbus_on_new_bus (int type)
+{
+  _dbus_assert (type == DBUS_MAJOR_PROTOCOL_VERSION || type == DBUS_PROTOCOL_VERSION_GVARIANT);
+  _dbus_protocol_strategy_bus_function (type);
+}
+
+static void
+_dbus_on_send_message (int type)
+{
+  _dbus_assert (type == DBUS_MAJOR_PROTOCOL_VERSION || type == DBUS_PROTOCOL_VERSION_GVARIANT);
+  _dbus_protocol_strategy_message_function (type);
+}
+
+DBusMessage *
+_dbus_message_remarshal (DBusMessage *message, dbus_bool_t gvariant)
+{
+  DBusMessage *ret;
+  DBusMessageIter iter, ret_iter;
+  size_t i;
+  dbus_uint32_t serial;
+  const char *sender;
+
+  _dbus_assert (message->locked);
+
+  _dbus_on_send_message (gvariant ? DBUS_PROTOCOL_VERSION_GVARIANT : DBUS_MAJOR_PROTOCOL_VERSION);
+
+  ret = _dbus_message_create_protocol_version (dbus_message_get_type(message),
+                                              dbus_message_get_destination(message),
+                                              dbus_message_get_path(message),
+                                              dbus_message_get_interface(message),
+                                              dbus_message_get_member(message),
+                                              dbus_message_get_error_name(message),
+                                              gvariant);
+
+  dbus_message_iter_init (message, &iter);
+  dbus_message_iter_init_append (ret, &ret_iter);
+  if (!_dbus_message_copy_recursive(&iter, &ret_iter))
+    return NULL;
+
+#ifdef HAVE_UNIX_FD_PASSING
+  ret->unix_fds = dbus_new(int, message->n_unix_fds);
+  if (ret->unix_fds == NULL && message->n_unix_fds > 0)
+    goto err;
+
+  ret->n_unix_fds_allocated = message->n_unix_fds;
+
+  for (i = 0; i < message->n_unix_fds; ++i) {
+    ret->unix_fds[i] = _dbus_dup(message->unix_fds[i], NULL);
+
+    if (ret->unix_fds[i] < 0)
+      goto err;
+  }
+
+  ret->n_unix_fds = message->n_unix_fds;
+#endif
+
+  /* Remarshal data in header:
+     byte order (already set)
+     type (already set)
+     flags - only those we understand
+     version (already set)
+     body length
+     serial
+     fields array (length)
+     fields:
+         path (already set)
+         interface (already set)
+         member (already set)
+         error name (already set)
+         reply serial
+         destination (already set)
+         sender
+         signature (set during copy, but an action needed for conversion to GVariant)
+         unix fds
+   */
+
+  /* FLAGS */
+  _dbus_header_toggle_flag (&ret->header, DBUS_HEADER_FLAG_NO_REPLY_EXPECTED,
+      _dbus_header_get_flag (&message->header, DBUS_HEADER_FLAG_NO_REPLY_EXPECTED));
+
+  _dbus_header_toggle_flag (&ret->header, DBUS_HEADER_FLAG_NO_AUTO_START,
+      _dbus_header_get_flag (&message->header, DBUS_HEADER_FLAG_NO_AUTO_START));
+
+  /* SERIAL / COOKIE */
+  serial = dbus_message_get_serial (message);
+
+  if (0 != serial)
+    dbus_message_set_serial (ret, serial);
+
+  /* Field: REPLY_SERIAL */
+  serial = dbus_message_get_reply_serial (message);
+
+  if (0 != serial && !dbus_message_set_reply_serial (ret, serial))
+    goto err;
+
+  /* Field: SENDER */
+  sender = dbus_message_get_sender (message);
+
+  if (NULL != sender && !dbus_message_set_sender (ret, sender))
+    goto err;
+
+  /* BODY LENGTH */
+  if (!gvariant)
+    _dbus_header_update_lengths (&ret->header,
+                                 _dbus_string_get_length (&ret->body));
+    /* For GVariant: */
+    /* Field: SIGNATURE to body; add body offset - this is done with dbus_message_lock() */
+  else
+    dbus_message_lock (ret);
+
+  return ret;
+
+err:
+  _dbus_header_free (&ret->header);
+  _dbus_string_free (&ret->body);
+
+#ifdef HAVE_UNIX_FD_PASSING
+  close_unix_fds(ret->unix_fds, &ret->n_unix_fds);
+  dbus_free(ret->unix_fds);
+#endif
+
+  return NULL;
+}
+
+void
+dbus_set_protocol_version (unsigned char version)
+{
+  _dbus_default_protocol_version = version;
+}
+
+static void
+protocol_strategy_first_type (int type)
+{
+  /* change protocol once */
+  if (!_dbus_first_bus_open)
+    {
+      _dbus_first_bus_open = TRUE;
+      _dbus_default_protocol_version = type;
+    }
+}
+
+static void
+protocol_strategy_last_type (int type)
+{
+  /* change protocol every time it is needed */
+  if (_dbus_default_protocol_version != type)
+    _dbus_default_protocol_version = type;
+}
+
+static void
+protocol_strategy_static (int type)
+{
+  /* do not change */
+}
+
+void
+dbus_set_default_protocol_strategy (const char *strategy_name)
+{
+  if (strcmp (strategy_name, "first-bus") == 0)
+    {
+      _dbus_protocol_strategy_bus_function = protocol_strategy_first_type;
+      _dbus_protocol_strategy_message_function = protocol_strategy_static;
+    }
+  else if (strcmp (strategy_name, "dbus1") == 0)
+    {
+      _dbus_default_protocol_version = DBUS_MAJOR_PROTOCOL_VERSION;
+      _dbus_protocol_strategy_bus_function = protocol_strategy_static;
+      _dbus_protocol_strategy_message_function = protocol_strategy_static;
+    }
+  else if (strcmp (strategy_name, "gvariant") == 0)
+    {
+      _dbus_default_protocol_version = DBUS_PROTOCOL_VERSION_GVARIANT;
+      _dbus_protocol_strategy_bus_function = protocol_strategy_static;
+      _dbus_protocol_strategy_message_function = protocol_strategy_static;
+    }
+  else if (strcmp (strategy_name, "last-message") == 0)
+    {
+      _dbus_protocol_strategy_bus_function = protocol_strategy_static;
+      _dbus_protocol_strategy_message_function = protocol_strategy_last_type;
+    }
+  else /* "last-bus" is default strategy */
+    {
+      _dbus_protocol_strategy_bus_function = protocol_strategy_last_type;
+      _dbus_protocol_strategy_message_function = protocol_strategy_static;
+    }
+}
+
+DBusMessage *
+_dbus_generate_local_error_message (dbus_uint32_t serial,
+                                    char *error_name,
+                                    char *error_msg)
+{
+  DBusMessage *message;
+  message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+  if (!message)
+    goto out;
+
+  if (!dbus_message_set_error_name (message, error_name))
+    {
+      dbus_message_unref (message);
+      message = NULL;
+      goto out;
+    }
+
+  dbus_message_set_no_reply (message, TRUE);
+
+  if (!dbus_message_set_reply_serial (message,
+                                      serial))
+    {
+      dbus_message_unref (message);
+      message = NULL;
+      goto out;
+    }
+
+  if (error_msg != NULL)
+    {
+      DBusMessageIter iter;
+
+      dbus_message_iter_init_append (message, &iter);
+      if (!dbus_message_iter_append_basic (&iter,
+                                           DBUS_TYPE_STRING,
+                                           &error_msg))
+        {
+          dbus_message_unref (message);
+          message = NULL;
+         goto out;
+        }
+    }
+
+ out:
+  return message;
+}
+
+dbus_bool_t
+_dbus_message_assure_dbus1 (DBusMessage **message)
+{
+  if ((*message)->header.protocol_version != DBUS_MAJOR_PROTOCOL_VERSION)
+    {
+      *message = _dbus_message_remarshal (*message, FALSE);
+      return TRUE;
+    }
+  return FALSE;
+}
+
+dbus_bool_t
+_dbus_message_assure_gvariant (DBusMessage **message)
+{
+  if ((*message)->header.protocol_version != DBUS_PROTOCOL_VERSION_GVARIANT)
+    {
+      *message = _dbus_message_remarshal (*message, TRUE);
+      return TRUE;
+    }
+  return FALSE;
+}
+
 /** @} */
 
 /* tests in dbus-message-util.c */