-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-message.c DBusMessage object
*
* Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
* Gets the data to be sent over the network for this message.
* The header and then the body should be written out.
* This function is guaranteed to always return the same
- * data once a message is locked (with _dbus_message_lock()).
+ * data once a message is locked (with dbus_message_lock()).
*
* @param message the message.
* @param header return location for message header data.
* Sets the serial number of a message.
* This can only be done once on a message.
*
+ * DBusConnection will automatically set the serial to an appropriate value
+ * when the message is sent; this function is only needed when encapsulating
+ * messages in another protocol, or otherwise bypassing DBusConnection.
+ *
* @param message the message
* @param serial the serial
*/
-void
-_dbus_message_set_serial (DBusMessage *message,
- dbus_uint32_t serial)
+void
+dbus_message_set_serial (DBusMessage *message,
+ dbus_uint32_t serial)
{
- _dbus_assert (message != NULL);
- _dbus_assert (!message->locked);
- _dbus_assert (dbus_message_get_serial (message) == 0);
+ _dbus_return_if_fail (message != NULL);
+ _dbus_return_if_fail (!message->locked);
_dbus_header_set_serial (&message->header, serial);
}
* reference to a message in the outgoing queue and change it
* underneath us. Messages are locked when they enter the outgoing
* queue (dbus_connection_send_message()), and the library complains
- * if the message is modified while locked.
+ * if the message is modified while locked. This function may also
+ * called externally, for applications wrapping D-Bus in another protocol.
*
* @param message the message to lock.
*/
void
-_dbus_message_lock (DBusMessage *message)
+dbus_message_lock (DBusMessage *message)
{
if (!message->locked)
{
_dbus_assert (i < MAX_MESSAGE_CACHE_SIZE);
_dbus_assert (message != NULL);
- _DBUS_UNLOCK (message_cache);
-
_dbus_assert (message->refcount.value == 0);
_dbus_assert (message->size_counters == NULL);
+
+ _DBUS_UNLOCK (message_cache);
return message;
}
#endif
out:
- _DBUS_UNLOCK (message_cache);
-
_dbus_assert (message->refcount.value == 0);
+ _DBUS_UNLOCK (message_cache);
+
if (!was_cached)
dbus_message_finalize (message);
}
* aside from the printf formatting.
*
* @todo add _DBUS_GNUC_PRINTF to this (requires moving _DBUS_GNUC_PRINTF to
- * public header, see DBUS_GNUC_DEPRECATED for an example)
+ * public header, see DBUS_DEPRECATED for an example)
*
* @param reply_to the original message
* @param error_name the error name
if (!dbus_message_iter_append_fixed_array (&array,
element_type,
value,
- n_elements))
+ n_elements)) {
+ dbus_message_iter_abandon_container (&iter, &array);
goto failed;
+ }
}
else if (element_type == DBUS_TYPE_STRING ||
element_type == DBUS_TYPE_SIGNATURE ||
{
if (!dbus_message_iter_append_basic (&array,
element_type,
- &value[i]))
+ &value[i])) {
+ dbus_message_iter_abandon_container (&iter, &array);
goto failed;
+ }
++i;
}
}
*
* The easiest way to iterate is like this:
* @code
- * dbus_message_iter_init (&iter);
+ * dbus_message_iter_init (message, &iter);
* while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
* dbus_message_iter_next (&iter);
* @endcode
* you won't be able to recurse further. There's no array of int32 to
* recurse into.
*
+ * If a container is an array of fixed-length types, it is much more
+ * efficient to use dbus_message_iter_get_fixed_array() to get the
+ * whole array in one shot, rather than individually walking over the
+ * array elements.
+ *
+ * Be sure you have somehow checked that
+ * dbus_message_iter_get_arg_type() matches the type you are expecting
+ * to recurse into. Results of this function are undefined if there is
+ * no container to recurse into at the current iterator position.
+ *
* @param iter the message iterator
* @param sub the sub-iterator to initialize
*/
* and for string a "const char**". The returned value is
* by reference and should not be freed.
*
- * All returned values are guaranteed to fit in 8 bytes. So you can
+ * Be sure you have somehow checked that
+ * dbus_message_iter_get_arg_type() matches the type you are
+ * expecting, or you'll crash when you try to use an integer as a
+ * string or something.
+ *
+ * To read any container type (array, struct, dict) you will need
+ * to recurse into the container with dbus_message_iter_recurse().
+ * If the container is an array of fixed-length values, you can
+ * get all the array elements at once with
+ * dbus_message_iter_get_fixed_array(). Otherwise, you have to
+ * iterate over the container's contents one value at a time.
+ *
+ * All basic-typed values are guaranteed to fit in 8 bytes. So you can
* write code like this:
*
* @code
- * #ifdef DBUS_HAVE_INT64
* dbus_uint64_t value;
* int type;
* dbus_message_iter_get_basic (&read_iter, &value);
* type = dbus_message_iter_get_arg_type (&read_iter);
* dbus_message_iter_append_basic (&write_iter, type, &value);
- * #endif
* @endcode
*
- * You can skip the #DBUS_HAVE_INT64 conditional unless you care about
- * some sort of really obscure platform. If you do know about such a
- * platform and want your code to work on it, create a struct
- * that occupies at least 8 bytes. dbus_uint64_t is just
+ * On some really obscure platforms dbus_uint64_t might not exist, if
+ * you need to worry about this you will know. dbus_uint64_t is just
* one example of a type that's large enough to hold any possible
- * value.
- *
- * Be sure you have somehow checked that
- * dbus_message_iter_get_arg_type() matches the type you are
- * expecting, or you'll crash when you try to use an integer as a
- * string or something.
+ * value, you could use a struct or char[8] instead if you like.
*
* @param iter the iterator
* @param value location to store the value
/**
* 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
+ * such as integers, bool, double. The returned block 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 message iter should be "in" the array (that is, you recurse into the
+ * array, and then you call dbus_message_iter_get_fixed_array() on the
+ * "sub-iterator" created by dbus_message_iter_recurse()).
*
* 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.
+ *
+ * This function should only be used if dbus_type_is_fixed() returns
+ * #TRUE for the element type.
*
- * Because the array is not copied, this function runs in
- * constant time and is fast; it's much preferred over walking the
- * entire array with an iterator.
+ * If an array's elements are not fixed in size, you have to recurse
+ * into the array with dbus_message_iter_recurse() and read the
+ * elements one by one.
+ *
+ * Because the array is not copied, this function runs in constant
+ * time and is fast; it's much preferred over walking the entire array
+ * with an iterator. (However, you can always use
+ * dbus_message_iter_recurse(), even for fixed-length types;
+ * dbus_message_iter_get_fixed_array() is just an optimization.)
*
* @param iter the iterator
* @param value location to store the block
return retval;
}
+/**
+ * Frees the signature string and marks the iterator as not having a
+ * type_str anymore. Since the new signature is not set, the message
+ * will generally be hosed after this is called.
+ *
+ * @param real an iterator without a type_str
+ */
+static void
+_dbus_message_iter_abandon_signature (DBusMessageRealIter *real)
+{
+ DBusString *str;
+
+ _dbus_assert (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER);
+ _dbus_assert (real->u.writer.type_str != NULL);
+ _dbus_assert (real->sig_refcount > 0);
+
+ real->sig_refcount -= 1;
+
+ if (real->sig_refcount > 0)
+ return;
+ _dbus_assert (real->sig_refcount == 0);
+
+ str = real->u.writer.type_str;
+
+ _dbus_type_writer_remove_types (&real->u.writer);
+ _dbus_string_free (str);
+ dbus_free (str);
+}
+
#ifndef DBUS_DISABLE_CHECKS
static dbus_bool_t
_dbus_message_iter_append_check (DBusMessageRealIter *iter)
contained_signature == NULL) ||
(type == DBUS_TYPE_DICT_ENTRY &&
contained_signature == NULL) ||
- contained_signature != NULL, FALSE);
+ (type == DBUS_TYPE_VARIANT &&
+ contained_signature != NULL) ||
+ (type == DBUS_TYPE_ARRAY &&
+ 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
+ /* 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
+ _dbus_return_val_if_fail ((type == DBUS_TYPE_ARRAY && contained_signature && *contained_signature == DBUS_DICT_ENTRY_BEGIN_CHAR) ||
+ (contained_signature == NULL ||
+ _dbus_check_is_valid_signature (contained_signature)),
+ FALSE);
if (!_dbus_message_iter_open_signature (real))
return FALSE;
}
/**
+ * Abandons creation of a contained-typed value and frees resources created
+ * by dbus_message_iter_open_container(). Once this returns, the message
+ * is hosed and you have to start over building the whole message.
+ *
+ * This should only be used to abandon creation of a message when you have
+ * open containers.
+ *
+ * @param iter the append iterator
+ * @param sub sub-iterator to close
+ */
+void
+dbus_message_iter_abandon_container (DBusMessageIter *iter,
+ DBusMessageIter *sub)
+{
+ DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+ DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub;
+
+ _dbus_return_if_fail (_dbus_message_iter_append_check (real));
+ _dbus_return_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER);
+ _dbus_return_if_fail (_dbus_message_iter_append_check (real_sub));
+ _dbus_return_if_fail (real_sub->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER);
+
+ _dbus_message_iter_abandon_signature (real);
+}
+
+/**
* Sets a flag indicating that the message does not want a reply; if
* this flag is set, the other end of the connection may (but is not
* required to) optimize by not sending method return or error
_dbus_string_delete (&loader->data, 0, header_len + body_len);
+ /* don't waste more than 2k of memory */
+ _dbus_string_compact (&loader->data, 2048);
+
_dbus_assert (_dbus_string_get_length (&message->header.data) == header_len);
_dbus_assert (_dbus_string_get_length (&message->body) == body_len);
}
/**
+ * Checks what kind of bad data confused the loader.
+ *
+ * @param loader the loader
+ * @returns why the loader is hosed, or DBUS_VALID if it isn't.
+ */
+DBusValidity
+_dbus_message_loader_get_corruption_reason (DBusMessageLoader *loader)
+{
+ _dbus_assert ((loader->corrupted && loader->corruption_reason != DBUS_VALID) ||
+ (!loader->corrupted && loader->corruption_reason == DBUS_VALID));
+
+ return loader->corruption_reason;
+}
+
+/**
* Sets the maximum size message we allow.
*
* @param loader the loader
int *len_p)
{
DBusString tmp;
+ dbus_bool_t was_locked;
_dbus_return_val_if_fail (msg != NULL, FALSE);
_dbus_return_val_if_fail (marshalled_data_p != NULL, FALSE);
_dbus_return_val_if_fail (len_p != NULL, FALSE);
-
+
if (!_dbus_string_init (&tmp))
return FALSE;
+ /* Ensure the message is locked, to ensure the length header is filled in. */
+ was_locked = msg->locked;
+
+ if (!was_locked)
+ dbus_message_lock (msg);
+
if (!_dbus_string_copy (&(msg->header.data), 0, &tmp, 0))
goto fail;
goto fail;
_dbus_string_free (&tmp);
+
+ if (!was_locked)
+ msg->locked = FALSE;
+
return TRUE;
fail:
_dbus_string_free (&tmp);
+
+ if (!was_locked)
+ msg->locked = FALSE;
+
return FALSE;
}
return msg;
fail_corrupt:
- dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Message is corrupted");
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Message is corrupted (%s)",
+ _dbus_validity_to_error_message (loader->corruption_reason));
_dbus_message_loader_unref (loader);
return NULL;
return NULL;
}
+/**
+ * Returns the number of bytes required to be in the buffer to demarshal a
+ * D-Bus message.
+ *
+ * Generally, this function is only useful for encapsulating D-Bus messages in
+ * a different protocol.
+ *
+ * @param str data to be marshalled
+ * @param len the length of str
+ * @param error the location to save errors to
+ * @returns -1 if there was no valid data to be demarshalled, 0 if there wasn't enough data to determine how much should be demarshalled. Otherwise returns the number of bytes to be demarshalled
+ *
+ */
+int
+dbus_message_demarshal_bytes_needed(const char *buf,
+ int len)
+{
+ DBusString str;
+ int byte_order, fields_array_len, header_len, body_len;
+ DBusValidity validity = DBUS_VALID;
+ int have_message;
+
+ if (!buf || len < DBUS_MINIMUM_HEADER_SIZE)
+ return 0;
+
+ if (len > DBUS_MAXIMUM_MESSAGE_LENGTH)
+ len = DBUS_MAXIMUM_MESSAGE_LENGTH;
+ _dbus_string_init_const_len (&str, buf, len);
+
+ validity = DBUS_VALID;
+ have_message
+ = _dbus_header_have_message_untrusted(DBUS_MAXIMUM_MESSAGE_LENGTH,
+ &validity, &byte_order,
+ &fields_array_len,
+ &header_len,
+ &body_len,
+ &str, 0,
+ len);
+ _dbus_string_free (&str);
+
+ if (validity == DBUS_VALID)
+ {
+ _dbus_assert(have_message);
+ return header_len + body_len;
+ }
+ else
+ {
+ return -1; /* broken! */
+ }
+}
+
/** @} */
/* tests in dbus-message-util.c */