Reduce size of message cache
[platform/upstream/dbus.git] / dbus / dbus-message.c
index 38ae317..880c25e 100644 (file)
 #include "dbus-marshal-validate.h"
 #include "dbus-marshal-byteswap.h"
 #include "dbus-marshal-header.h"
+#include "dbus-signature.h"
 #include "dbus-message-private.h"
 #include "dbus-object-tree.h"
 #include "dbus-memory.h"
 #include "dbus-list.h"
+#include "dbus-threads-internal.h"
 #include <string.h>
 
 /**
@@ -456,6 +458,8 @@ dbus_message_finalize (DBusMessage *message)
   _dbus_header_free (&message->header);
   _dbus_string_free (&message->body);
 
+  _dbus_assert (message->refcount.value == 0);
+  
   dbus_free (message);
 }
 
@@ -506,7 +510,7 @@ dbus_message_finalize (DBusMessage *message)
  */
 
 /** Avoid caching huge messages */
-#define MAX_MESSAGE_SIZE_TO_CACHE _DBUS_ONE_MEGABYTE
+#define MAX_MESSAGE_SIZE_TO_CACHE 10 * _DBUS_ONE_KILOBYTE
 
 /** Avoid caching too many messages */
 #define MAX_MESSAGE_CACHE_SIZE    5
@@ -657,10 +661,15 @@ dbus_message_cache_or_finalize (DBusMessage *message)
   message_cache[i] = message;
   message_cache_count += 1;
   was_cached = TRUE;
+#ifndef DBUS_DISABLE_CHECKS
+  message->in_cache = TRUE;
+#endif
 
  out:
   _DBUS_UNLOCK (message_cache);
 
+  _dbus_assert (message->refcount.value == 0);
+  
   if (!was_cached)
     dbus_message_finalize (message);
 }
@@ -691,6 +700,9 @@ dbus_message_new_empty_header (void)
   message->refcount.value = 1;
   message->byte_order = DBUS_COMPILER_BYTE_ORDER;
   message->locked = FALSE;
+#ifndef DBUS_DISABLE_CHECKS
+  message->in_cache = FALSE;
+#endif
   message->size_counters = NULL;
   message->size_counter_delta = 0;
   message->changed_stamp = 0;
@@ -1067,7 +1079,8 @@ dbus_message_ref (DBusMessage *message)
 
   _dbus_return_val_if_fail (message != NULL, NULL);
   _dbus_return_val_if_fail (message->generation == _dbus_current_generation, NULL);
-
+  _dbus_return_val_if_fail (!message->in_cache, NULL);
+  
   old_refcount = _dbus_atomic_inc (&message->refcount);
   _dbus_assert (old_refcount >= 1);
 
@@ -1087,6 +1100,7 @@ dbus_message_unref (DBusMessage *message)
 
   _dbus_return_if_fail (message != NULL);
   _dbus_return_if_fail (message->generation == _dbus_current_generation);
+  _dbus_return_if_fail (!message->in_cache);
 
   old_refcount = _dbus_atomic_dec (&message->refcount);
 
@@ -1128,25 +1142,15 @@ dbus_message_get_type (DBusMessage *message)
  * rather than this function.
  *
  * To append a basic type, specify its type code followed by the
- * value. For example:
+ * address of the value. For example:
  *
  * @code
- * DBUS_TYPE_INT32, 42,
- * DBUS_TYPE_STRING, "Hello World"
- * @endcode
- * or
- * @code
- * dbus_int32_t val = 42;
- * DBUS_TYPE_INT32, val
- * @endcode
  *
- * Be sure that your provided value is the right size. For example, this
- * won't work:
- * @code
- * DBUS_TYPE_INT64, 42
+ * dbus_int32_t v_INT32 = 42;
+ * const char *v_STRING = "Hello World";
+ * DBUS_TYPE_INT32, &v_INT32,
+ * DBUS_TYPE_STRING, &v_STRING,
  * @endcode
- * Because the "42" will be a 32-bit integer. You need to cast to
- * 64-bit.
  *
  * To append an array of fixed-length basic types, pass in the
  * DBUS_TYPE_ARRAY typecode, the element typecode, the address of
@@ -1168,6 +1172,9 @@ dbus_message_get_type (DBusMessage *message)
  * The last argument to this function must be #DBUS_TYPE_INVALID,
  * marking the end of the argument list.
  *
+ * String/signature/path arrays should be passed in as "const char***
+ * address_of_array" and "int n_elements"
+ *
  * @todo support DBUS_TYPE_STRUCT and DBUS_TYPE_VARIANT and complex arrays
  *
  * @todo If this fails due to lack of memory, the message is hosed and
@@ -1227,7 +1234,7 @@ dbus_message_append_args_valist (DBusMessage *message,
 
   while (type != DBUS_TYPE_INVALID)
     {
-      if (_dbus_type_is_basic (type))
+      if (dbus_type_is_basic (type))
         {
           const DBusBasicValue *value;
           value = va_arg (var_args, const DBusBasicValue*);
@@ -1240,26 +1247,11 @@ dbus_message_append_args_valist (DBusMessage *message,
       else if (type == DBUS_TYPE_ARRAY)
         {
           int element_type;
-          const DBusBasicValue **value;
-          int n_elements;
           DBusMessageIter array;
           char buf[2];
 
           element_type = va_arg (var_args, int);
-
-#ifndef DBUS_DISABLE_CHECKS
-          if (!_dbus_type_is_fixed (element_type))
-            {
-              _dbus_warn ("arrays of %s can't be appended with %s for now\n",
-                          _dbus_type_to_string (element_type),
-                          _DBUS_FUNCTION_NAME);
-              goto failed;
-            }
-#endif
-
-          value = va_arg (var_args, const DBusBasicValue**);
-          n_elements = va_arg (var_args, int);
-
+              
           buf[0] = element_type;
           buf[1] = '\0';
           if (!dbus_message_iter_open_container (&iter,
@@ -1267,12 +1259,52 @@ dbus_message_append_args_valist (DBusMessage *message,
                                                  buf,
                                                  &array))
             goto failed;
+          
+          if (dbus_type_is_fixed (element_type))
+            {
+              const DBusBasicValue **value;
+              int n_elements;
+
+              value = va_arg (var_args, const DBusBasicValue**);
+              n_elements = va_arg (var_args, int);
+              
+              if (!dbus_message_iter_append_fixed_array (&array,
+                                                         element_type,
+                                                         value,
+                                                         n_elements))
+                goto failed;
+            }
+          else if (element_type == DBUS_TYPE_STRING ||
+                   element_type == DBUS_TYPE_SIGNATURE ||
+                   element_type == DBUS_TYPE_OBJECT_PATH)
+            {
+              const char ***value_p;
+              const char **value;
+              int n_elements;
+              int i;
+              
+              value_p = va_arg (var_args, const char***);
+              n_elements = va_arg (var_args, int);
 
-          if (!dbus_message_iter_append_fixed_array (&array,
-                                                     element_type,
-                                                     value,
-                                                     n_elements))
-            goto failed;
+              value = *value_p;
+              
+              i = 0;
+              while (i < n_elements)
+                {
+                  if (!dbus_message_iter_append_basic (&array,
+                                                       element_type,
+                                                       &value[i]))
+                    goto failed;
+                  ++i;
+                }
+            }
+          else
+            {
+              _dbus_warn ("arrays of %s can't be appended with %s for now\n",
+                          _dbus_type_to_string (element_type),
+                          _DBUS_FUNCTION_NAME);
+              goto failed;
+            }
 
           if (!dbus_message_iter_close_container (&iter, &array))
             goto failed;
@@ -1306,7 +1338,8 @@ dbus_message_append_args_valist (DBusMessage *message,
  * In addition to those types, arrays of string, object path, and
  * signature are supported; but these are returned as allocated memory
  * and must be freed with dbus_free_string_array(), while the other
- * types are returned as const references.
+ * types are returned as const references. To get a string array
+ * pass in "char ***array_location" and "int *n_elements"
  *
  * The variable argument list should contain the type of the argument
  * followed by a pointer to where the value should be stored. The list
@@ -1580,6 +1613,41 @@ dbus_message_iter_recurse (DBusMessageIter  *iter,
 }
 
 /**
+ * Returns the current signature of a message iterator.  This
+ * is useful primarily for dealing with variants; one can
+ * recurse into a variant and determine the signature of
+ * the variant's value.
+ *
+ * @param iter the message iterator
+ * @returns the contained signature, or NULL if out of memory
+ */
+char *
+dbus_message_iter_get_signature (DBusMessageIter *iter)
+{
+  const DBusString *sig;
+  DBusString retstr;
+  char *ret;
+  int start, len;
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), NULL);
+
+  if (!_dbus_string_init (&retstr))
+    return NULL;
+
+  _dbus_type_reader_get_signature (&real->u.reader, &sig,
+                                  &start, &len);
+  if (!_dbus_string_append_len (&retstr,
+                               _dbus_string_get_const_data (sig) + start,
+                               len))
+    return NULL;
+  if (!_dbus_string_steal_data (&retstr, &ret))
+    return NULL;
+  _dbus_string_free (&retstr);
+  return ret;
+}
+
+/**
  * Reads a basic-typed value from the message iterator.
  * Basic types are the non-containers such as integer and string.
  *
@@ -1628,11 +1696,30 @@ dbus_message_iter_get_basic (DBusMessageIter  *iter,
 }
 
 /**
+ * Returns the number of elements in the array;
+ *
+ * @param iter the iterator
+ * @returns the number of elements in the array
+ */
+int
+dbus_message_iter_get_array_len (DBusMessageIter *iter)
+{
+  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+
+  _dbus_return_val_if_fail (_dbus_message_iter_check (real), 0);
+
+  return _dbus_type_reader_get_array_length (&real->u.reader);
+}
+
+/**
  * Reads a block of fixed-length values from the message iterator.
  * Fixed-length values are those basic types that are not string-like,
  * such as integers, bool, double. The block read will be from the
  * current position in the array until the end of the array.
  *
+ * This function should only be used if #dbus_type_is_fixed returns
+ * #TRUE for the element type.
+ *
  * The value argument should be the address of a location to store the
  * returned array. So for int32 it should be a "const dbus_int32_t**"
  * The returned value is by reference and should not be freed.
@@ -1650,7 +1737,7 @@ dbus_message_iter_get_fixed_array (DBusMessageIter  *iter,
 
   _dbus_return_if_fail (_dbus_message_iter_check (real));
   _dbus_return_if_fail (value != NULL);
-  _dbus_return_if_fail (_dbus_type_is_fixed (_dbus_type_reader_get_element_type (&real->u.reader)));
+  _dbus_return_if_fail (dbus_type_is_fixed (_dbus_type_reader_get_current_type (&real->u.reader)));
 
   _dbus_type_reader_read_fixed_multi (&real->u.reader,
                                       value, n_elements);
@@ -1701,7 +1788,7 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
           goto out;
        }
 
-      if (_dbus_type_is_basic (spec_type))
+      if (dbus_type_is_basic (spec_type))
         {
           DBusBasicValue *ptr;
 
@@ -1735,7 +1822,7 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
               goto out;
             }
 
-          if (_dbus_type_is_fixed (spec_element_type))
+          if (dbus_type_is_fixed (spec_element_type))
             {
               ptr = va_arg (var_args, const DBusBasicValue**);
               n_elements_p = va_arg (var_args, int*);
@@ -1753,7 +1840,7 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
                    spec_element_type == DBUS_TYPE_OBJECT_PATH)
             {
               char ***str_array_p;
-              int i;
+              int n_elements;
               char **str_array;
 
               str_array_p = va_arg (var_args, char***);
@@ -1765,14 +1852,14 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
               /* Count elements in the array */
               _dbus_type_reader_recurse (&real->u.reader, &array);
 
-              i = 0;
-              if (_dbus_type_reader_has_next (&array))
+              n_elements = 0;
+              while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
                 {
-                  while (_dbus_type_reader_next (&array))
-                    ++i;
+                  ++n_elements;
+                  _dbus_type_reader_next (&array);
                 }
 
-              str_array = dbus_new0 (char*, i + 1);
+              str_array = dbus_new0 (char*, n_elements + 1);
               if (str_array == NULL)
                 {
                   _DBUS_SET_OOM (error);
@@ -1783,29 +1870,32 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
               _dbus_type_reader_recurse (&real->u.reader, &array);
 
               i = 0;
-              if (_dbus_type_reader_has_next (&array))
+              while (i < n_elements)
                 {
-                  do
+                  const char *s;
+                  _dbus_type_reader_read_basic (&array,
+                                                &s);
+                  
+                  str_array[i] = _dbus_strdup (s);
+                  if (str_array[i] == NULL)
                     {
-                      const char *s;
-                      _dbus_type_reader_read_basic (&array,
-                                                    &s);
-
-                      str_array[i] = _dbus_strdup (s);
-                      if (str_array[i] == NULL)
-                        {
-                          dbus_free_string_array (str_array);
-                          _DBUS_SET_OOM (error);
-                          goto out;
-                        }
-
-                      ++i;
+                      dbus_free_string_array (str_array);
+                      _DBUS_SET_OOM (error);
+                      goto out;
                     }
-                  while (_dbus_type_reader_next (&array));
+                  
+                  ++i;
+                  
+                  if (!_dbus_type_reader_next (&array))
+                    _dbus_assert (i == n_elements);
                 }
 
+              _dbus_assert (_dbus_type_reader_get_current_type (&array) == DBUS_TYPE_INVALID);
+              _dbus_assert (i == n_elements);
+              _dbus_assert (str_array[i] == NULL);
+
               *str_array_p = str_array;
-              *n_elements_p = i;
+              *n_elements_p = n_elements;
             }
 #ifndef DBUS_DISABLE_CHECKS
           else
@@ -2032,7 +2122,7 @@ dbus_message_iter_append_basic (DBusMessageIter *iter,
 
   _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE);
   _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE);
-  _dbus_return_val_if_fail (_dbus_type_is_basic (type), FALSE);
+  _dbus_return_val_if_fail (dbus_type_is_basic (type), FALSE);
   _dbus_return_val_if_fail (value != NULL, FALSE);
 
   if (!_dbus_message_iter_open_signature (real))
@@ -2092,7 +2182,7 @@ dbus_message_iter_append_fixed_array (DBusMessageIter *iter,
 
   _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE);
   _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE);
-  _dbus_return_val_if_fail (_dbus_type_is_fixed (element_type), FALSE);
+  _dbus_return_val_if_fail (dbus_type_is_fixed (element_type), FALSE);
   _dbus_return_val_if_fail (real->u.writer.container_type == DBUS_TYPE_ARRAY, FALSE);
   _dbus_return_val_if_fail (value != NULL, FALSE);
   _dbus_return_val_if_fail (n_elements >= 0, FALSE);
@@ -2112,10 +2202,10 @@ dbus_message_iter_append_fixed_array (DBusMessageIter *iter,
  * dbus_message_iter_close_container(). Container types are for
  * example struct, variant, and array. For variants, the
  * contained_signature should be the type of the single value inside
- * the variant. For structs, contained_signature should be #NULL; it
- * will be set to whatever types you write into the struct.  For
- * arrays, contained_signature should be the type of the array
- * elements.
+ * the variant. For structs and dict entries, contained_signature
+ * should be #NULL; it will be set to whatever types you write into
+ * the struct.  For arrays, contained_signature should be the type of
+ * the array elements.
  *
  * @todo If this fails due to lack of memory, the message is hosed and
  * you have to start over building the whole message.
@@ -2138,22 +2228,44 @@ dbus_message_iter_open_container (DBusMessageIter *iter,
 
   _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE);
   _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE);
-  _dbus_return_val_if_fail (_dbus_type_is_container (type), FALSE);
+  _dbus_return_val_if_fail (dbus_type_is_container (type), FALSE);
   _dbus_return_val_if_fail (sub != NULL, FALSE);
   _dbus_return_val_if_fail ((type == DBUS_TYPE_STRUCT &&
                              contained_signature == NULL) ||
+                            (type == DBUS_TYPE_DICT_ENTRY &&
+                             contained_signature == NULL) ||
                             contained_signature != NULL, FALSE);
+  
+#if 0
+  /* FIXME this would fail if the contained_signature is a dict entry,
+   * since dict entries are invalid signatures standalone (they must be in
+   * an array)
+   */
+  _dbus_return_val_if_fail (contained_signature == NULL ||
+                            _dbus_check_is_valid_signature (contained_signature));
+#endif
 
   if (!_dbus_message_iter_open_signature (real))
     return FALSE;
 
-  _dbus_string_init_const (&contained_str, contained_signature);
-
   *real_sub = *real;
-  return _dbus_type_writer_recurse (&real->u.writer,
-                                    type,
-                                    &contained_str, 0,
-                                    &real_sub->u.writer);
+
+  if (contained_signature != NULL)
+    {
+      _dbus_string_init_const (&contained_str, contained_signature);
+
+      return _dbus_type_writer_recurse (&real->u.writer,
+                                        type,
+                                        &contained_str, 0,
+                                        &real_sub->u.writer);
+    }
+  else
+    {
+      return _dbus_type_writer_recurse (&real->u.writer,
+                                        type,
+                                        NULL, 0,
+                                        &real_sub->u.writer);
+    } 
 }
 
 
@@ -2319,6 +2431,36 @@ dbus_message_get_path (DBusMessage   *message)
 }
 
 /**
+ * Checks if the message has a path
+ *
+ * @param message the message
+ * @returns #TRUE if there is a path field in the header
+ */
+dbus_bool_t
+dbus_message_has_path (DBusMessage   *message,
+                       const char    *path)
+{
+  const char *msg_path;
+  msg_path = dbus_message_get_path (message);
+  
+  if (msg_path == NULL)
+    {
+      if (path == NULL)
+        return TRUE;
+      else
+        return FALSE;
+    }
+
+  if (path == NULL)
+    return FALSE;
+   
+  if (strcmp (msg_path, path) == 0)
+    return TRUE;
+
+  return FALSE;
+}
+
+/**
  * Gets the object path this message is being sent to
  * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted
  * from (for DBUS_MESSAGE_TYPE_SIGNAL) in a decomposed
@@ -2409,6 +2551,37 @@ dbus_message_get_interface (DBusMessage *message)
 }
 
 /**
+ * Checks if the message has an interface
+ *
+ * @param message the message
+ * @returns #TRUE if there is a interface field in the header
+ */
+dbus_bool_t
+dbus_message_has_interface (DBusMessage   *message,
+                            const char    *interface)
+{
+  const char *msg_interface;
+  msg_interface = dbus_message_get_interface (message);
+   
+  if (msg_interface == NULL)
+    {
+      if (interface == NULL)
+        return TRUE;
+      else
+        return FALSE;
+    }
+
+  if (interface == NULL)
+    return FALSE;
+     
+  if (strcmp (msg_interface, interface) == 0)
+    return TRUE;
+
+  return FALSE;
+
+}
+
+/**
  * Sets the interface member being invoked
  * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted
  * (DBUS_MESSAGE_TYPE_SIGNAL).
@@ -2458,6 +2631,37 @@ dbus_message_get_member (DBusMessage *message)
 }
 
 /**
+ * Checks if the message has an interface member
+ *
+ * @param message the message
+ * @returns #TRUE if there is a member field in the header
+ */
+dbus_bool_t
+dbus_message_has_member (DBusMessage   *message,
+                         const char    *member)
+{
+  const char *msg_member;
+  msg_member = dbus_message_get_member (message);
+  if (msg_member == NULL)
+    {
+      if (member == NULL)
+        return TRUE;
+      else
+        return FALSE;
+    }
+
+  if (member == NULL)
+    return FALSE;
+    
+  if (strcmp (msg_member, member) == 0)
+    return TRUE;
+
+  return FALSE;
+
+}
+
+/**
  * Sets the name of the error (DBUS_MESSAGE_TYPE_ERROR).
  * The name is fully-qualified (namespaced).
  *
@@ -2787,7 +2991,7 @@ dbus_message_has_destination (DBusMessage  *message,
  * unique name of the connection as the sender. So you can't use this
  * function to see whether a sender owned a well-known name.
  *
- * Messages from the bus itself will have #DBUS_SERVICE_ORG_FREEDESKTOP_DBUS
+ * Messages from the bus itself will have #DBUS_SERVICE_DBUS
  * as the sender.
  *
  * @param message the message
@@ -2928,11 +3132,9 @@ _dbus_message_loader_new (void)
 
   loader->corrupted = FALSE;
   loader->corruption_reason = DBUS_VALID;
-  
-  /* Try to cap message size at something that won't *totally* hose
-   * the system if we have a couple of them.
-   */
-  loader->max_message_size = _DBUS_ONE_MEGABYTE * 32;
+
+  /* this can be configured by the app, but defaults to the protocol max */
+  loader->max_message_size = DBUS_MAXIMUM_MESSAGE_LENGTH;
 
   if (!_dbus_string_init (&loader->data))
     {
@@ -3095,7 +3297,12 @@ load_message (DBusMessageLoader *loader,
                           _dbus_string_get_length (&loader->data)))
     {
       _dbus_verbose ("Failed to load header for new message code %d\n", validity);
-      if (validity == DBUS_VALID)
+
+      /* assert here so we can catch any code that still uses DBUS_VALID to indicate
+         oom errors.  They should use DBUS_VALIDITY_UNKNOWN_OOM_ERROR instead */
+      _dbus_assert (validity != DBUS_VALID);
+
+      if (validity == DBUS_VALIDITY_UNKNOWN_OOM_ERROR)
         oom = TRUE;
       else
         {