| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
| /* dbus-message.c DBusMessage object |
| * |
| * Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc. |
| * Copyright (C) 2002, 2003 CodeFactory AB |
| * |
| * Licensed under the Academic Free License version 2.1 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #include <config.h> |
| #include "dbus-internals.h" |
| #include "dbus-marshal-recursive.h" |
| #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" |
| #ifdef HAVE_UNIX_FD_PASSING |
| #include "dbus-sysdeps-unix.h" |
| #endif |
| |
| #include <string.h> |
| |
| static void dbus_message_finalize (DBusMessage *message); |
| |
| /** |
| * @defgroup DBusMessageInternals DBusMessage implementation details |
| * @ingroup DBusInternals |
| * @brief DBusMessage private implementation details. |
| * |
| * The guts of DBusMessage and its methods. |
| * |
| * @{ |
| */ |
| |
| /* Not thread locked, but strictly const/read-only so should be OK |
| */ |
| /** An static string representing an empty signature */ |
| _DBUS_STRING_DEFINE_STATIC(_dbus_empty_signature_str, ""); |
| |
| /* these have wacky values to help trap uninitialized iterators; |
| * but has to fit in 3 bits |
| */ |
| enum { |
| DBUS_MESSAGE_ITER_TYPE_READER = 3, |
| DBUS_MESSAGE_ITER_TYPE_WRITER = 7 |
| }; |
| |
| /** typedef for internals of message iterator */ |
| typedef struct DBusMessageRealIter DBusMessageRealIter; |
| |
| /** |
| * @brief Internals of DBusMessageIter |
| * |
| * Object representing a position in a message. All fields are internal. |
| */ |
| struct DBusMessageRealIter |
| { |
| DBusMessage *message; /**< Message used */ |
| dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< stamp to detect invalid iters */ |
| dbus_uint32_t iter_type : 3; /**< whether this is a reader or writer iter */ |
| dbus_uint32_t sig_refcount : 8; /**< depth of open_signature() */ |
| union |
| { |
| DBusTypeWriter writer; /**< writer */ |
| DBusTypeReader reader; /**< reader */ |
| } u; /**< the type writer or reader that does all the work */ |
| }; |
| |
| static void |
| get_const_signature (DBusHeader *header, |
| 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)) |
| { |
| *type_pos_p += 1; /* skip the signature length which is 1 byte */ |
| } |
| else |
| { |
| *type_str_p = &_dbus_empty_signature_str; |
| *type_pos_p = 0; |
| } |
| } |
| |
| /** |
| * Swaps the message to compiler byte order if required |
| * |
| * @param message the message |
| */ |
| static void |
| _dbus_message_byteswap (DBusMessage *message) |
| { |
| const DBusString *type_str; |
| int type_pos; |
| |
| if (message->byte_order == DBUS_COMPILER_BYTE_ORDER) |
| return; |
| |
| _dbus_verbose ("Swapping message into compiler byte order\n"); |
| |
| get_const_signature (&message->header, &type_str, &type_pos); |
| |
| _dbus_marshal_byteswap (type_str, type_pos, |
| message->byte_order, |
| DBUS_COMPILER_BYTE_ORDER, |
| &message->body, 0); |
| |
| message->byte_order = DBUS_COMPILER_BYTE_ORDER; |
| |
| _dbus_header_byteswap (&message->header, DBUS_COMPILER_BYTE_ORDER); |
| } |
| |
| /** byte-swap the message if it doesn't match our byte order. |
| * Called only when we need the message in our own byte order, |
| * normally when reading arrays of integers or doubles. |
| * Otherwise should not be called since it would do needless |
| * work. |
| */ |
| #define ensure_byte_order(message) \ |
| if (message->byte_order != DBUS_COMPILER_BYTE_ORDER) \ |
| _dbus_message_byteswap (message) |
| |
| /** |
| * 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()). |
| * |
| * @param message the message. |
| * @param header return location for message header data. |
| * @param body return location for message body data. |
| */ |
| void |
| _dbus_message_get_network_data (DBusMessage *message, |
| const DBusString **header, |
| const DBusString **body) |
| { |
| _dbus_assert (message->locked); |
| |
| *header = &message->header.data; |
| *body = &message->body; |
| } |
| |
| /** |
| * 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 |
| * message is locked (with dbus_message_lock()). |
| * |
| * @param message the message. |
| * @param fds return location of unix fd array |
| * @param n_fds return number of entries in array |
| */ |
| void _dbus_message_get_unix_fds(DBusMessage *message, |
| const int **fds, |
| unsigned *n_fds) |
| { |
| _dbus_assert (message->locked); |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| *fds = message->unix_fds; |
| *n_fds = message->n_unix_fds; |
| #else |
| *fds = NULL; |
| *n_fds = 0; |
| #endif |
| } |
| |
| /** |
| * 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) |
| { |
| _dbus_return_if_fail (message != NULL); |
| _dbus_return_if_fail (!message->locked); |
| |
| _dbus_header_set_serial (&message->header, serial); |
| } |
| |
| /** |
| * Adds a counter to be incremented immediately with the size/unix fds |
| * of this message, and decremented by the size/unix fds of this |
| * message when this message if finalized. The link contains a |
| * counter with its refcount already incremented, but the counter |
| * itself not incremented. Ownership of link and counter refcount is |
| * passed to the message. |
| * |
| * @param message the message |
| * @param link link with counter as data |
| */ |
| void |
| _dbus_message_add_counter_link (DBusMessage *message, |
| DBusList *link) |
| { |
| /* right now we don't recompute the delta when message |
| * size changes, and that's OK for current purposes |
| * I think, but could be important to change later. |
| * Do recompute it whenever there are no outstanding counters, |
| * since it's basically free. |
| */ |
| if (message->counters == NULL) |
| { |
| message->size_counter_delta = |
| _dbus_string_get_length (&message->header.data) + |
| _dbus_string_get_length (&message->body); |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| message->unix_fd_counter_delta = message->n_unix_fds; |
| #endif |
| |
| #if 0 |
| _dbus_verbose ("message has size %ld\n", |
| message->size_counter_delta); |
| #endif |
| } |
| |
| _dbus_list_append_link (&message->counters, link); |
| |
| _dbus_counter_adjust_size (link->data, message->size_counter_delta); |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| _dbus_counter_adjust_unix_fd (link->data, message->unix_fd_counter_delta); |
| #endif |
| } |
| |
| /** |
| * Adds a counter to be incremented immediately with the size/unix fds |
| * of this message, and decremented by the size/unix fds of this |
| * message when this message if finalized. |
| * |
| * @param message the message |
| * @param counter the counter |
| * @returns #FALSE if no memory |
| */ |
| dbus_bool_t |
| _dbus_message_add_counter (DBusMessage *message, |
| DBusCounter *counter) |
| { |
| DBusList *link; |
| |
| link = _dbus_list_alloc_link (counter); |
| if (link == NULL) |
| return FALSE; |
| |
| _dbus_counter_ref (counter); |
| _dbus_message_add_counter_link (message, link); |
| |
| return TRUE; |
| } |
| |
| /** |
| * Removes a counter tracking the size/unix fds of this message, and |
| * decrements the counter by the size/unix fds of this message. |
| * |
| * @param message the message |
| * @param link_return return the link used |
| * @param counter the counter |
| */ |
| void |
| _dbus_message_remove_counter (DBusMessage *message, |
| DBusCounter *counter, |
| DBusList **link_return) |
| { |
| DBusList *link; |
| |
| link = _dbus_list_find_last (&message->counters, |
| counter); |
| _dbus_assert (link != NULL); |
| |
| _dbus_list_unlink (&message->counters, |
| link); |
| if (link_return) |
| *link_return = link; |
| else |
| _dbus_list_free_link (link); |
| |
| _dbus_counter_adjust_size (counter, - message->size_counter_delta); |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| _dbus_counter_adjust_unix_fd (counter, - message->unix_fd_counter_delta); |
| #endif |
| |
| _dbus_counter_unref (counter); |
| } |
| |
| /** |
| * Locks a message. Allows checking that applications don't keep a |
| * 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. 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) |
| { |
| if (!message->locked) |
| { |
| _dbus_header_update_lengths (&message->header, |
| _dbus_string_get_length (&message->body)); |
| |
| /* must have a signature if you have a body */ |
| _dbus_assert (_dbus_string_get_length (&message->body) == 0 || |
| dbus_message_get_signature (message) != NULL); |
| |
| message->locked = TRUE; |
| } |
| } |
| |
| static dbus_bool_t |
| set_or_delete_string_field (DBusMessage *message, |
| int field, |
| int typecode, |
| const char *value) |
| { |
| if (value == NULL) |
| return _dbus_header_delete_field (&message->header, field); |
| else |
| return _dbus_header_set_field_basic (&message->header, |
| field, |
| typecode, |
| &value); |
| } |
| |
| #if 0 |
| /* Probably we don't need to use this */ |
| /** |
| * Sets the signature of the message, i.e. the arguments in the |
| * message payload. The signature includes only "in" arguments for |
| * #DBUS_MESSAGE_TYPE_METHOD_CALL and only "out" arguments for |
| * #DBUS_MESSAGE_TYPE_METHOD_RETURN, so is slightly different from |
| * what you might expect (it does not include the signature of the |
| * entire C++-style method). |
| * |
| * The signature is a string made up of type codes such as |
| * #DBUS_TYPE_INT32. The string is terminated with nul (nul is also |
| * the value of #DBUS_TYPE_INVALID). The macros such as |
| * #DBUS_TYPE_INT32 evaluate to integers; to assemble a signature you |
| * may find it useful to use the string forms, such as |
| * #DBUS_TYPE_INT32_AS_STRING. |
| * |
| * An "unset" or #NULL signature is considered the same as an empty |
| * signature. In fact dbus_message_get_signature() will never return |
| * #NULL. |
| * |
| * @param message the message |
| * @param signature the type signature or #NULL to unset |
| * @returns #FALSE if no memory |
| */ |
| static dbus_bool_t |
| _dbus_message_set_signature (DBusMessage *message, |
| const char *signature) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (!message->locked, FALSE); |
| _dbus_return_val_if_fail (signature == NULL || |
| _dbus_check_is_valid_signature (signature)); |
| /* can't delete the signature if you have a message body */ |
| _dbus_return_val_if_fail (_dbus_string_get_length (&message->body) == 0 || |
| signature != NULL); |
| |
| return set_or_delete_string_field (message, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| DBUS_TYPE_SIGNATURE, |
| signature); |
| } |
| #endif |
| |
| /* Message Cache |
| * |
| * We cache some DBusMessage to reduce the overhead of allocating |
| * them. In my profiling this consistently made about an 8% |
| * difference. It avoids the malloc for the message, the malloc for |
| * the slot list, the malloc for the header string and body string, |
| * and the associated free() calls. It does introduce another global |
| * lock which could be a performance issue in certain cases. |
| * |
| * For the echo client/server the round trip time goes from around |
| * .000077 to .000069 with the message cache on my laptop. The sysprof |
| * change is as follows (numbers are cumulative percentage): |
| * |
| * with message cache implemented as array as it is now (0.000069 per): |
| * new_empty_header 1.46 |
| * mutex_lock 0.56 # i.e. _DBUS_LOCK(message_cache) |
| * mutex_unlock 0.25 |
| * self 0.41 |
| * unref 2.24 |
| * self 0.68 |
| * list_clear 0.43 |
| * mutex_lock 0.33 # i.e. _DBUS_LOCK(message_cache) |
| * mutex_unlock 0.25 |
| * |
| * with message cache implemented as list (0.000070 per roundtrip): |
| * new_empty_header 2.72 |
| * list_pop_first 1.88 |
| * unref 3.3 |
| * list_prepend 1.63 |
| * |
| * without cache (0.000077 per roundtrip): |
| * new_empty_header 6.7 |
| * string_init_preallocated 3.43 |
| * dbus_malloc 2.43 |
| * dbus_malloc0 2.59 |
| * |
| * unref 4.02 |
| * string_free 1.82 |
| * dbus_free 1.63 |
| * dbus_free 0.71 |
| * |
| * If you implement the message_cache with a list, the primary reason |
| * it's slower is that you add another thread lock (on the DBusList |
| * mempool). |
| */ |
| |
| /** Avoid caching huge messages */ |
| #define MAX_MESSAGE_SIZE_TO_CACHE 10 * _DBUS_ONE_KILOBYTE |
| |
| /** Avoid caching too many messages */ |
| #define MAX_MESSAGE_CACHE_SIZE 5 |
| |
| _DBUS_DEFINE_GLOBAL_LOCK (message_cache); |
| static DBusMessage *message_cache[MAX_MESSAGE_CACHE_SIZE]; |
| static int message_cache_count = 0; |
| static dbus_bool_t message_cache_shutdown_registered = FALSE; |
| |
| static void |
| dbus_message_cache_shutdown (void *data) |
| { |
| int i; |
| |
| _DBUS_LOCK (message_cache); |
| |
| i = 0; |
| while (i < MAX_MESSAGE_CACHE_SIZE) |
| { |
| if (message_cache[i]) |
| dbus_message_finalize (message_cache[i]); |
| |
| ++i; |
| } |
| |
| message_cache_count = 0; |
| message_cache_shutdown_registered = FALSE; |
| |
| _DBUS_UNLOCK (message_cache); |
| } |
| |
| /** |
| * Tries to get a message from the message cache. The retrieved |
| * message will have junk in it, so it still needs to be cleared out |
| * in dbus_message_new_empty_header() |
| * |
| * @returns the message, or #NULL if none cached |
| */ |
| static DBusMessage* |
| dbus_message_get_cached (void) |
| { |
| DBusMessage *message; |
| int i; |
| |
| message = NULL; |
| |
| _DBUS_LOCK (message_cache); |
| |
| _dbus_assert (message_cache_count >= 0); |
| |
| if (message_cache_count == 0) |
| { |
| _DBUS_UNLOCK (message_cache); |
| return NULL; |
| } |
| |
| /* This is not necessarily true unless count > 0, and |
| * message_cache is uninitialized until the shutdown is |
| * registered |
| */ |
| _dbus_assert (message_cache_shutdown_registered); |
| |
| i = 0; |
| while (i < MAX_MESSAGE_CACHE_SIZE) |
| { |
| if (message_cache[i]) |
| { |
| message = message_cache[i]; |
| message_cache[i] = NULL; |
| message_cache_count -= 1; |
| break; |
| } |
| ++i; |
| } |
| _dbus_assert (message_cache_count >= 0); |
| _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE); |
| _dbus_assert (message != NULL); |
| |
| _dbus_assert (message->refcount.value == 0); |
| _dbus_assert (message->counters == NULL); |
| |
| _DBUS_UNLOCK (message_cache); |
| |
| return message; |
| } |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| static void |
| close_unix_fds(int *fds, unsigned *n_fds) |
| { |
| DBusError e; |
| int i; |
| |
| if (*n_fds <= 0) |
| return; |
| |
| dbus_error_init(&e); |
| |
| for (i = 0; i < *n_fds; i++) |
| { |
| if (!_dbus_close(fds[i], &e)) |
| { |
| _dbus_warn("Failed to close file descriptor: %s\n", e.message); |
| dbus_error_free(&e); |
| } |
| } |
| |
| *n_fds = 0; |
| |
| /* We don't free the array here, in case we can recycle it later */ |
| } |
| #endif |
| |
| static void |
| free_counter (void *element, |
| void *data) |
| { |
| DBusCounter *counter = element; |
| DBusMessage *message = data; |
| |
| _dbus_counter_adjust_size (counter, - message->size_counter_delta); |
| #ifdef HAVE_UNIX_FD_PASSING |
| _dbus_counter_adjust_unix_fd (counter, - message->unix_fd_counter_delta); |
| #endif |
| |
| _dbus_counter_unref (counter); |
| } |
| |
| /** |
| * Tries to cache a message, otherwise finalize it. |
| * |
| * @param message the message |
| */ |
| static void |
| dbus_message_cache_or_finalize (DBusMessage *message) |
| { |
| dbus_bool_t was_cached; |
| int i; |
| |
| _dbus_assert (message->refcount.value == 0); |
| |
| /* This calls application code and has to be done first thing |
| * without holding the lock |
| */ |
| _dbus_data_slot_list_clear (&message->slot_list); |
| |
| _dbus_list_foreach (&message->counters, |
| free_counter, message); |
| _dbus_list_clear (&message->counters); |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| close_unix_fds(message->unix_fds, &message->n_unix_fds); |
| #endif |
| |
| was_cached = FALSE; |
| |
| _DBUS_LOCK (message_cache); |
| |
| if (!message_cache_shutdown_registered) |
| { |
| _dbus_assert (message_cache_count == 0); |
| |
| if (!_dbus_register_shutdown_func (dbus_message_cache_shutdown, NULL)) |
| goto out; |
| |
| i = 0; |
| while (i < MAX_MESSAGE_CACHE_SIZE) |
| { |
| message_cache[i] = NULL; |
| ++i; |
| } |
| |
| message_cache_shutdown_registered = TRUE; |
| } |
| |
| _dbus_assert (message_cache_count >= 0); |
| |
| if ((_dbus_string_get_length (&message->header.data) + |
| _dbus_string_get_length (&message->body)) > |
| MAX_MESSAGE_SIZE_TO_CACHE) |
| goto out; |
| |
| if (message_cache_count >= MAX_MESSAGE_CACHE_SIZE) |
| goto out; |
| |
| /* Find empty slot */ |
| i = 0; |
| while (message_cache[i] != NULL) |
| ++i; |
| |
| _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE); |
| |
| _dbus_assert (message_cache[i] == NULL); |
| message_cache[i] = message; |
| message_cache_count += 1; |
| was_cached = TRUE; |
| #ifndef DBUS_DISABLE_CHECKS |
| message->in_cache = TRUE; |
| #endif |
| |
| out: |
| _dbus_assert (message->refcount.value == 0); |
| |
| _DBUS_UNLOCK (message_cache); |
| |
| if (!was_cached) |
| dbus_message_finalize (message); |
| } |
| |
| #ifndef DBUS_DISABLE_CHECKS |
| static dbus_bool_t |
| _dbus_message_iter_check (DBusMessageRealIter *iter) |
| { |
| if (iter == NULL) |
| { |
| _dbus_warn_check_failed ("dbus message iterator is NULL\n"); |
| return FALSE; |
| } |
| |
| if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_READER) |
| { |
| if (iter->u.reader.byte_order != iter->message->byte_order) |
| { |
| _dbus_warn_check_failed ("dbus message changed byte order since iterator was created\n"); |
| return FALSE; |
| } |
| /* because we swap the message into compiler order when you init an iter */ |
| _dbus_assert (iter->u.reader.byte_order == DBUS_COMPILER_BYTE_ORDER); |
| } |
| else if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER) |
| { |
| if (iter->u.writer.byte_order != iter->message->byte_order) |
| { |
| _dbus_warn_check_failed ("dbus message changed byte order since append iterator was created\n"); |
| return FALSE; |
| } |
| /* because we swap the message into compiler order when you init an iter */ |
| _dbus_assert (iter->u.writer.byte_order == DBUS_COMPILER_BYTE_ORDER); |
| } |
| else |
| { |
| _dbus_warn_check_failed ("dbus message iterator looks uninitialized or corrupted\n"); |
| return FALSE; |
| } |
| |
| if (iter->changed_stamp != iter->message->changed_stamp) |
| { |
| _dbus_warn_check_failed ("dbus message iterator invalid because the message has been modified (or perhaps the iterator is just uninitialized)\n"); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| #endif /* DBUS_DISABLE_CHECKS */ |
| |
| /** |
| * Implementation of the varargs arg-getting functions. |
| * dbus_message_get_args() is the place to go for complete |
| * documentation. |
| * |
| * @todo This may leak memory and file descriptors if parsing fails. See #21259 |
| * |
| * @see dbus_message_get_args |
| * @param iter the message iter |
| * @param error error to be filled in |
| * @param first_arg_type type of the first argument |
| * @param var_args return location for first argument, followed by list of type/location pairs |
| * @returns #FALSE if error was set |
| */ |
| dbus_bool_t |
| _dbus_message_iter_get_args_valist (DBusMessageIter *iter, |
| DBusError *error, |
| int first_arg_type, |
| va_list var_args) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| int spec_type, msg_type, i; |
| dbus_bool_t retval; |
| |
| _dbus_assert (_dbus_message_iter_check (real)); |
| |
| retval = FALSE; |
| |
| spec_type = first_arg_type; |
| i = 0; |
| |
| while (spec_type != DBUS_TYPE_INVALID) |
| { |
| msg_type = dbus_message_iter_get_arg_type (iter); |
| |
| if (msg_type != spec_type) |
| { |
| dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, |
| "Argument %d is specified to be of type \"%s\", but " |
| "is actually of type \"%s\"\n", i, |
| _dbus_type_to_string (spec_type), |
| _dbus_type_to_string (msg_type)); |
| |
| goto out; |
| } |
| |
| if (spec_type == DBUS_TYPE_UNIX_FD) |
| { |
| #ifdef HAVE_UNIX_FD_PASSING |
| DBusBasicValue idx; |
| int *pfd, nfd; |
| |
| pfd = va_arg (var_args, int*); |
| _dbus_assert(pfd); |
| |
| _dbus_type_reader_read_basic(&real->u.reader, &idx); |
| |
| if (idx.u32 >= real->message->n_unix_fds) |
| { |
| dbus_set_error (error, DBUS_ERROR_INCONSISTENT_MESSAGE, |
| "Message refers to file descriptor at index %i," |
| "but has only %i descriptors attached.\n", |
| idx.u32, |
| real->message->n_unix_fds); |
| goto out; |
| } |
| |
| if ((nfd = _dbus_dup(real->message->unix_fds[idx.u32], error)) < 0) |
| goto out; |
| |
| *pfd = nfd; |
| #else |
| dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, |
| "Platform does not support file desciptor passing.\n"); |
| goto out; |
| #endif |
| } |
| else if (dbus_type_is_basic (spec_type)) |
| { |
| DBusBasicValue *ptr; |
| |
| ptr = va_arg (var_args, DBusBasicValue*); |
| |
| _dbus_assert (ptr != NULL); |
| |
| _dbus_type_reader_read_basic (&real->u.reader, |
| ptr); |
| } |
| else if (spec_type == DBUS_TYPE_ARRAY) |
| { |
| int element_type; |
| int spec_element_type; |
| const DBusBasicValue **ptr; |
| int *n_elements_p; |
| DBusTypeReader array; |
| |
| spec_element_type = va_arg (var_args, int); |
| element_type = _dbus_type_reader_get_element_type (&real->u.reader); |
| |
| if (spec_element_type != element_type) |
| { |
| dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, |
| "Argument %d is specified to be an array of \"%s\", but " |
| "is actually an array of \"%s\"\n", |
| i, |
| _dbus_type_to_string (spec_element_type), |
| _dbus_type_to_string (element_type)); |
| |
| goto out; |
| } |
| |
| if (dbus_type_is_fixed (spec_element_type) && |
| element_type != DBUS_TYPE_UNIX_FD) |
| { |
| ptr = va_arg (var_args, const DBusBasicValue**); |
| n_elements_p = va_arg (var_args, int*); |
| |
| _dbus_assert (ptr != NULL); |
| _dbus_assert (n_elements_p != NULL); |
| |
| _dbus_type_reader_recurse (&real->u.reader, &array); |
| |
| _dbus_type_reader_read_fixed_multi (&array, |
| (void *) ptr, n_elements_p); |
| } |
| else if (spec_element_type == DBUS_TYPE_STRING || |
| spec_element_type == DBUS_TYPE_SIGNATURE || |
| spec_element_type == DBUS_TYPE_OBJECT_PATH) |
| { |
| char ***str_array_p; |
| int n_elements; |
| char **str_array; |
| |
| str_array_p = va_arg (var_args, char***); |
| n_elements_p = va_arg (var_args, int*); |
| |
| _dbus_assert (str_array_p != NULL); |
| _dbus_assert (n_elements_p != NULL); |
| |
| /* Count elements in the array */ |
| _dbus_type_reader_recurse (&real->u.reader, &array); |
| |
| n_elements = 0; |
| while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) |
| { |
| ++n_elements; |
| _dbus_type_reader_next (&array); |
| } |
| |
| str_array = dbus_new0 (char*, n_elements + 1); |
| if (str_array == NULL) |
| { |
| _DBUS_SET_OOM (error); |
| goto out; |
| } |
| |
| /* Now go through and dup each string */ |
| _dbus_type_reader_recurse (&real->u.reader, &array); |
| |
| i = 0; |
| while (i < n_elements) |
| { |
| const char *s; |
| _dbus_type_reader_read_basic (&array, |
| (void *) &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; |
| |
| 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 = n_elements; |
| } |
| #ifndef DBUS_DISABLE_CHECKS |
| else |
| { |
| _dbus_warn ("you can't read arrays of container types (struct, variant, array) with %s for now\n", |
| _DBUS_FUNCTION_NAME); |
| goto out; |
| } |
| #endif |
| } |
| #ifndef DBUS_DISABLE_CHECKS |
| else |
| { |
| _dbus_warn ("you can only read arrays and basic types with %s for now\n", |
| _DBUS_FUNCTION_NAME); |
| goto out; |
| } |
| #endif |
| |
| spec_type = va_arg (var_args, int); |
| if (!_dbus_type_reader_next (&real->u.reader) && spec_type != DBUS_TYPE_INVALID) |
| { |
| dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, |
| "Message has only %d arguments, but more were expected", i); |
| goto out; |
| } |
| |
| i++; |
| } |
| |
| retval = TRUE; |
| |
| out: |
| |
| return retval; |
| } |
| |
| /** @} */ |
| |
| /** |
| * @defgroup DBusMessage DBusMessage |
| * @ingroup DBus |
| * @brief Message to be sent or received over a #DBusConnection. |
| * |
| * A DBusMessage is the most basic unit of communication over a |
| * DBusConnection. A DBusConnection represents a stream of messages |
| * received from a remote application, and a stream of messages |
| * sent to a remote application. |
| * |
| * A message has a message type, returned from |
| * dbus_message_get_type(). This indicates whether the message is a |
| * method call, a reply to a method call, a signal, or an error reply. |
| * |
| * A message has header fields such as the sender, destination, method |
| * or signal name, and so forth. DBusMessage has accessor functions for |
| * these, such as dbus_message_get_member(). |
| * |
| * Convenience functions dbus_message_is_method_call(), dbus_message_is_signal(), |
| * and dbus_message_is_error() check several header fields at once and are |
| * slightly more efficient than checking the header fields with individual |
| * accessor functions. |
| * |
| * Finally, a message has arguments. The number and types of arguments |
| * are in the message's signature header field (accessed with |
| * dbus_message_get_signature()). Simple argument values are usually |
| * retrieved with dbus_message_get_args() but more complex values such |
| * as structs may require the use of #DBusMessageIter. |
| * |
| * The D-Bus specification goes into some more detail about header fields and |
| * message types. |
| * |
| * @{ |
| */ |
| |
| /** |
| * @typedef DBusMessage |
| * |
| * Opaque data type representing a message received from or to be |
| * sent to another application. |
| */ |
| |
| /** |
| * Returns the serial of a message or 0 if none has been specified. |
| * The message's serial number is provided by the application sending |
| * the message and is used to identify replies to this message. |
| * |
| * All messages received on a connection will have a serial provided |
| * by the remote application. |
| * |
| * For messages you're sending, dbus_connection_send() will assign a |
| * serial and return it to you. |
| * |
| * @param message the message |
| * @returns the serial |
| */ |
| dbus_uint32_t |
| dbus_message_get_serial (DBusMessage *message) |
| { |
| _dbus_return_val_if_fail (message != NULL, 0); |
| |
| return _dbus_header_get_serial (&message->header); |
| } |
| |
| /** |
| * Sets the reply serial of a message (the serial of the message this |
| * is a reply to). |
| * |
| * @param message the message |
| * @param reply_serial the serial we're replying to |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_set_reply_serial (DBusMessage *message, |
| dbus_uint32_t reply_serial) |
| { |
| _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 */ |
| |
| return _dbus_header_set_field_basic (&message->header, |
| DBUS_HEADER_FIELD_REPLY_SERIAL, |
| DBUS_TYPE_UINT32, |
| &reply_serial); |
| } |
| |
| /** |
| * Returns the serial that the message is a reply to or 0 if none. |
| * |
| * @param message the message |
| * @returns the reply serial |
| */ |
| dbus_uint32_t |
| dbus_message_get_reply_serial (DBusMessage *message) |
| { |
| dbus_uint32_t v_UINT32; |
| |
| _dbus_return_val_if_fail (message != NULL, 0); |
| |
| if (_dbus_header_get_field_basic (&message->header, |
| DBUS_HEADER_FIELD_REPLY_SERIAL, |
| DBUS_TYPE_UINT32, |
| &v_UINT32)) |
| return v_UINT32; |
| else |
| return 0; |
| } |
| |
| static void |
| dbus_message_finalize (DBusMessage *message) |
| { |
| _dbus_assert (message->refcount.value == 0); |
| |
| /* This calls application callbacks! */ |
| _dbus_data_slot_list_free (&message->slot_list); |
| |
| _dbus_list_foreach (&message->counters, |
| free_counter, message); |
| _dbus_list_clear (&message->counters); |
| |
| _dbus_header_free (&message->header); |
| _dbus_string_free (&message->body); |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| close_unix_fds(message->unix_fds, &message->n_unix_fds); |
| dbus_free(message->unix_fds); |
| #endif |
| |
| _dbus_assert (message->refcount.value == 0); |
| |
| dbus_free (message); |
| } |
| |
| static DBusMessage* |
| dbus_message_new_empty_header (void) |
| { |
| DBusMessage *message; |
| dbus_bool_t from_cache; |
| |
| message = dbus_message_get_cached (); |
| |
| if (message != NULL) |
| { |
| from_cache = TRUE; |
| } |
| else |
| { |
| from_cache = FALSE; |
| message = dbus_new (DBusMessage, 1); |
| if (message == NULL) |
| return NULL; |
| #ifndef DBUS_DISABLE_CHECKS |
| message->generation = _dbus_current_generation; |
| #endif |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| message->unix_fds = NULL; |
| message->n_unix_fds_allocated = 0; |
| #endif |
| } |
| |
| message->refcount.value = 1; |
| message->byte_order = DBUS_COMPILER_BYTE_ORDER; |
| message->locked = FALSE; |
| #ifndef DBUS_DISABLE_CHECKS |
| message->in_cache = FALSE; |
| #endif |
| message->counters = NULL; |
| message->size_counter_delta = 0; |
| message->changed_stamp = 0; |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| message->n_unix_fds = 0; |
| message->n_unix_fds_allocated = 0; |
| message->unix_fd_counter_delta = 0; |
| #endif |
| |
| if (!from_cache) |
| _dbus_data_slot_list_init (&message->slot_list); |
| |
| if (from_cache) |
| { |
| _dbus_header_reinit (&message->header, message->byte_order); |
| _dbus_string_set_length (&message->body, 0); |
| } |
| else |
| { |
| if (!_dbus_header_init (&message->header, message->byte_order)) |
| { |
| dbus_free (message); |
| return NULL; |
| } |
| |
| if (!_dbus_string_init_preallocated (&message->body, 32)) |
| { |
| _dbus_header_free (&message->header); |
| dbus_free (message); |
| return NULL; |
| } |
| } |
| |
| 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) |
| { |
| DBusMessage *message; |
| |
| _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL); |
| |
| message = dbus_message_new_empty_header (); |
| if (message == NULL) |
| return NULL; |
| |
| if (!_dbus_header_create (&message->header, |
| message_type, |
| NULL, NULL, NULL, NULL, NULL)) |
| { |
| dbus_message_unref (message); |
| return NULL; |
| } |
| |
| return message; |
| } |
| |
| /** |
| * Constructs a new message to invoke a method on a remote |
| * object. Returns #NULL if memory can't be allocated for the |
| * message. The destination may be #NULL in which case no destination |
| * is set; this is appropriate when using D-Bus in a peer-to-peer |
| * context (no message bus). The interface may be #NULL, which means |
| * that if multiple methods with the given name exist it is undefined |
| * which one will be invoked. |
| * |
| * The path and method names may not be #NULL. |
| * |
| * Destination, path, interface, and method name can't contain |
| * any invalid characters (see the D-Bus specification). |
| * |
| * @param destination name that the message should be sent to or #NULL |
| * @param path object path the message should be sent to |
| * @param interface interface to invoke method on, or #NULL |
| * @param method method to invoke |
| * |
| * @returns a new DBusMessage, free with dbus_message_unref() |
| */ |
| DBusMessage* |
| dbus_message_new_method_call (const char *destination, |
| const char *path, |
| const char *interface, |
| 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 || |
| _dbus_check_is_valid_bus_name (destination), NULL); |
| _dbus_return_val_if_fail (_dbus_check_is_valid_path (path), NULL); |
| _dbus_return_val_if_fail (interface == NULL || |
| _dbus_check_is_valid_interface (interface), 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_MESSAGE_TYPE_METHOD_CALL, |
| destination, path, interface, method, NULL)) |
| { |
| dbus_message_unref (message); |
| return NULL; |
| } |
| |
| return message; |
| } |
| |
| /** |
| * Constructs a message that is a reply to a method call. Returns |
| * #NULL if memory can't be allocated for the message. |
| * |
| * @param method_call the message being replied to |
| * @returns a new DBusMessage, free with dbus_message_unref() |
| */ |
| DBusMessage* |
| dbus_message_new_method_return (DBusMessage *method_call) |
| { |
| DBusMessage *message; |
| const char *sender; |
| |
| _dbus_return_val_if_fail (method_call != NULL, NULL); |
| |
| sender = dbus_message_get_sender (method_call); |
| |
| /* sender is allowed to be null here in peer-to-peer case */ |
| |
| message = dbus_message_new_empty_header (); |
| if (message == NULL) |
| return NULL; |
| |
| if (!_dbus_header_create (&message->header, |
| 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, |
| dbus_message_get_serial (method_call))) |
| { |
| dbus_message_unref (message); |
| return NULL; |
| } |
| |
| return message; |
| } |
| |
| /** |
| * Constructs a new message representing a signal emission. Returns |
| * #NULL if memory can't be allocated for the message. A signal is |
| * identified by its originating object path, interface, and the name |
| * of the signal. |
| * |
| * Path, interface, and signal name must all be valid (the D-Bus |
| * specification defines the syntax of these fields). |
| * |
| * @param path the path to the object emitting the signal |
| * @param interface the interface the signal is emitted from |
| * @param name name of the signal |
| * @returns a new DBusMessage, free with dbus_message_unref() |
| */ |
| DBusMessage* |
| dbus_message_new_signal (const char *path, |
| const char *interface, |
| const char *name) |
| { |
| DBusMessage *message; |
| |
| _dbus_return_val_if_fail (path != NULL, NULL); |
| _dbus_return_val_if_fail (interface != NULL, NULL); |
| _dbus_return_val_if_fail (name != NULL, NULL); |
| _dbus_return_val_if_fail (_dbus_check_is_valid_path (path), NULL); |
| _dbus_return_val_if_fail (_dbus_check_is_valid_interface (interface), NULL); |
| _dbus_return_val_if_fail (_dbus_check_is_valid_member (name), NULL); |
| |
| message = dbus_message_new_empty_header (); |
| if (message == NULL) |
| return NULL; |
| |
| if (!_dbus_header_create (&message->header, |
| DBUS_MESSAGE_TYPE_SIGNAL, |
| NULL, path, interface, name, NULL)) |
| { |
| dbus_message_unref (message); |
| return NULL; |
| } |
| |
| dbus_message_set_no_reply (message, TRUE); |
| |
| return message; |
| } |
| |
| /** |
| * Creates a new message that is an error reply to another message. |
| * Error replies are most common in response to method calls, but |
| * can be returned in reply to any message. |
| * |
| * The error name must be a valid error name according to the syntax |
| * given in the D-Bus specification. If you don't want to make |
| * up an error name just use #DBUS_ERROR_FAILED. |
| * |
| * @param reply_to the message we're replying to |
| * @param error_name the error name |
| * @param error_message the error message string (or #NULL for none, but please give a message) |
| * @returns a new error message object, free with dbus_message_unref() |
| */ |
| DBusMessage* |
| dbus_message_new_error (DBusMessage *reply_to, |
| const char *error_name, |
| const char *error_message) |
| { |
| DBusMessage *message; |
| const char *sender; |
| DBusMessageIter iter; |
| |
| _dbus_return_val_if_fail (reply_to != NULL, NULL); |
| _dbus_return_val_if_fail (error_name != NULL, NULL); |
| _dbus_return_val_if_fail (_dbus_check_is_valid_error_name (error_name), NULL); |
| |
| sender = dbus_message_get_sender (reply_to); |
| |
| /* sender may be NULL for non-message-bus case or |
| * when the message bus is dealing with an unregistered |
| * connection. |
| */ |
| message = dbus_message_new_empty_header (); |
| if (message == NULL) |
| return NULL; |
| |
| if (!_dbus_header_create (&message->header, |
| 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, |
| dbus_message_get_serial (reply_to))) |
| { |
| dbus_message_unref (message); |
| return NULL; |
| } |
| |
| if (error_message != NULL) |
| { |
| dbus_message_iter_init_append (message, &iter); |
| if (!dbus_message_iter_append_basic (&iter, |
| DBUS_TYPE_STRING, |
| &error_message)) |
| { |
| dbus_message_unref (message); |
| return NULL; |
| } |
| } |
| |
| return message; |
| } |
| |
| /** |
| * Creates a new message that is an error reply to another message, allowing |
| * you to use printf formatting. |
| * |
| * See dbus_message_new_error() for details - this function is the same |
| * aside from the printf formatting. |
| * |
| * @todo add _DBUS_GNUC_PRINTF to this (requires moving _DBUS_GNUC_PRINTF to |
| * public header, see DBUS_DEPRECATED for an example) |
| * |
| * @param reply_to the original message |
| * @param error_name the error name |
| * @param error_format the error message format as with printf |
| * @param ... format string arguments |
| * @returns a new error message |
| */ |
| DBusMessage* |
| dbus_message_new_error_printf (DBusMessage *reply_to, |
| const char *error_name, |
| const char *error_format, |
| ...) |
| { |
| va_list args; |
| DBusString str; |
| DBusMessage *message; |
| |
| _dbus_return_val_if_fail (reply_to != NULL, NULL); |
| _dbus_return_val_if_fail (error_name != NULL, NULL); |
| _dbus_return_val_if_fail (_dbus_check_is_valid_error_name (error_name), NULL); |
| |
| if (!_dbus_string_init (&str)) |
| return NULL; |
| |
| va_start (args, error_format); |
| |
| if (_dbus_string_append_printf_valist (&str, error_format, args)) |
| message = dbus_message_new_error (reply_to, error_name, |
| _dbus_string_get_const_data (&str)); |
| else |
| message = NULL; |
| |
| _dbus_string_free (&str); |
| |
| va_end (args); |
| |
| return message; |
| } |
| |
| |
| /** |
| * Creates a new message that is an exact replica of the message |
| * specified, except that its refcount is set to 1, its message serial |
| * is reset to 0, and if the original message was "locked" (in the |
| * outgoing message queue and thus not modifiable) the new message |
| * will not be locked. |
| * |
| * @todo This function can't be used in programs that try to recover from OOM errors. |
| * |
| * @param message the message |
| * @returns the new message.or #NULL if not enough memory or Unix file descriptors (in case the message to copy includes Unix file descriptors) can be allocated. |
| */ |
| DBusMessage * |
| dbus_message_copy (const DBusMessage *message) |
| { |
| DBusMessage *retval; |
| |
| _dbus_return_val_if_fail (message != NULL, NULL); |
| |
| retval = dbus_new0 (DBusMessage, 1); |
| if (retval == NULL) |
| return NULL; |
| |
| retval->refcount.value = 1; |
| retval->byte_order = message->byte_order; |
| retval->locked = FALSE; |
| #ifndef DBUS_DISABLE_CHECKS |
| retval->generation = message->generation; |
| #endif |
| |
| if (!_dbus_header_copy (&message->header, &retval->header)) |
| { |
| dbus_free (retval); |
| return NULL; |
| } |
| |
| if (!_dbus_string_init_preallocated (&retval->body, |
| _dbus_string_get_length (&message->body))) |
| { |
| _dbus_header_free (&retval->header); |
| dbus_free (retval); |
| return NULL; |
| } |
| |
| if (!_dbus_string_copy (&message->body, 0, |
| &retval->body, 0)) |
| goto failed_copy; |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| retval->unix_fds = dbus_new(int, message->n_unix_fds); |
| if (retval->unix_fds == NULL && message->n_unix_fds > 0) |
| goto failed_copy; |
| |
| retval->n_unix_fds_allocated = message->n_unix_fds; |
| |
| for (retval->n_unix_fds = 0; |
| retval->n_unix_fds < message->n_unix_fds; |
| retval->n_unix_fds++) |
| { |
| retval->unix_fds[retval->n_unix_fds] = _dbus_dup(message->unix_fds[retval->n_unix_fds], NULL); |
| |
| if (retval->unix_fds[retval->n_unix_fds] < 0) |
| goto failed_copy; |
| } |
| |
| #endif |
| |
| return retval; |
| |
| failed_copy: |
| _dbus_header_free (&retval->header); |
| _dbus_string_free (&retval->body); |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| close_unix_fds(retval->unix_fds, &retval->n_unix_fds); |
| dbus_free(retval->unix_fds); |
| #endif |
| |
| dbus_free (retval); |
| |
| return NULL; |
| } |
| |
| |
| /** |
| * Increments the reference count of a DBusMessage. |
| * |
| * @param message the message |
| * @returns the message |
| * @see dbus_message_unref |
| */ |
| DBusMessage * |
| dbus_message_ref (DBusMessage *message) |
| { |
| dbus_int32_t old_refcount; |
| |
| _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); |
| |
| return message; |
| } |
| |
| /** |
| * Decrements the reference count of a DBusMessage, freeing the |
| * message if the count reaches 0. |
| * |
| * @param message the message |
| * @see dbus_message_ref |
| */ |
| void |
| dbus_message_unref (DBusMessage *message) |
| { |
| dbus_int32_t old_refcount; |
| |
| _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); |
| |
| _dbus_assert (old_refcount >= 0); |
| |
| if (old_refcount == 1) |
| { |
| /* Calls application callbacks! */ |
| dbus_message_cache_or_finalize (message); |
| } |
| } |
| |
| /** |
| * Gets the type of a message. Types include |
| * #DBUS_MESSAGE_TYPE_METHOD_CALL, #DBUS_MESSAGE_TYPE_METHOD_RETURN, |
| * #DBUS_MESSAGE_TYPE_ERROR, #DBUS_MESSAGE_TYPE_SIGNAL, but other |
| * types are allowed and all code must silently ignore messages of |
| * unknown type. #DBUS_MESSAGE_TYPE_INVALID will never be returned. |
| * |
| * @param message the message |
| * @returns the type of the message |
| */ |
| int |
| dbus_message_get_type (DBusMessage *message) |
| { |
| _dbus_return_val_if_fail (message != NULL, DBUS_MESSAGE_TYPE_INVALID); |
| |
| return _dbus_header_get_message_type (&message->header); |
| } |
| |
| /** |
| * Appends fields to a message given a variable argument list. The |
| * variable argument list should contain the type of each argument |
| * followed by the value to append. Appendable types are basic types, |
| * and arrays of fixed-length basic types (except arrays of Unix file |
| * descriptors). To append variable-length basic types, or any more |
| * complex value, you have to use an iterator rather than this |
| * function. |
| * |
| * To append a basic type, specify its type code followed by the |
| * address of the value. For example: |
| * |
| * @code |
| * |
| * dbus_int32_t v_INT32 = 42; |
| * const char *v_STRING = "Hello World"; |
| * dbus_message_append_args (message, |
| * DBUS_TYPE_INT32, &v_INT32, |
| * DBUS_TYPE_STRING, &v_STRING, |
| * DBUS_TYPE_INVALID); |
| * @endcode |
| * |
| * To append an array of fixed-length basic types (except Unix file |
| * descriptors), pass in the DBUS_TYPE_ARRAY typecode, the element |
| * typecode, the address of the array pointer, and a 32-bit integer |
| * giving the number of elements in the array. So for example: @code |
| * const dbus_int32_t array[] = { 1, 2, 3 }; const dbus_int32_t |
| * *v_ARRAY = array; dbus_message_append_args (message, |
| * DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3, DBUS_TYPE_INVALID); |
| * @endcode |
| * |
| * This function does not support arrays of Unix file descriptors. If |
| * you need those you need to manually recurse into the array. |
| * |
| * For Unix file descriptors this function will internally duplicate |
| * the descriptor you passed in. Hence you may close the descriptor |
| * immediately after this call. |
| * |
| * @warning in C, given "int array[]", "&array == array" (the |
| * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree). |
| * So if you're using an array instead of a pointer you have to create |
| * a pointer variable, assign the array to it, then take the address |
| * of the pointer variable. For strings it works to write |
| * const char *array = "Hello" and then use &array though. |
| * |
| * The last argument to this function must be #DBUS_TYPE_INVALID, |
| * marking the end of the argument list. If you don't do this |
| * then libdbus won't know to stop and will read invalid memory. |
| * |
| * 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 |
| * you have to start over building the whole message. |
| * |
| * @param message the message |
| * @param first_arg_type type of the first argument |
| * @param ... value of first argument, list of additional type-value pairs |
| * @returns #TRUE on success |
| */ |
| dbus_bool_t |
| dbus_message_append_args (DBusMessage *message, |
| int first_arg_type, |
| ...) |
| { |
| dbus_bool_t retval; |
| va_list var_args; |
| |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| |
| va_start (var_args, first_arg_type); |
| retval = dbus_message_append_args_valist (message, |
| first_arg_type, |
| var_args); |
| va_end (var_args); |
| |
| return retval; |
| } |
| |
| /** |
| * Like dbus_message_append_args() but takes a va_list for use by language bindings. |
| * |
| * @todo for now, if this function fails due to OOM it will leave |
| * the message half-written and you have to discard the message |
| * and start over. |
| * |
| * @see dbus_message_append_args. |
| * @param message the message |
| * @param first_arg_type type of first argument |
| * @param var_args value of first argument, then list of type/value pairs |
| * @returns #TRUE on success |
| */ |
| dbus_bool_t |
| dbus_message_append_args_valist (DBusMessage *message, |
| int first_arg_type, |
| va_list var_args) |
| { |
| int type; |
| DBusMessageIter iter; |
| |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| |
| type = first_arg_type; |
| |
| dbus_message_iter_init_append (message, &iter); |
| |
| while (type != DBUS_TYPE_INVALID) |
| { |
| if (dbus_type_is_basic (type)) |
| { |
| const DBusBasicValue *value; |
| value = va_arg (var_args, const DBusBasicValue*); |
| |
| if (!dbus_message_iter_append_basic (&iter, |
| type, |
| value)) |
| goto failed; |
| } |
| else if (type == DBUS_TYPE_ARRAY) |
| { |
| int element_type; |
| DBusMessageIter array; |
| char buf[2]; |
| |
| element_type = va_arg (var_args, int); |
| |
| buf[0] = element_type; |
| buf[1] = '\0'; |
| if (!dbus_message_iter_open_container (&iter, |
| DBUS_TYPE_ARRAY, |
| buf, |
| &array)) |
| goto failed; |
| |
| if (dbus_type_is_fixed (element_type) && |
| element_type != DBUS_TYPE_UNIX_FD) |
| { |
| 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)) { |
| dbus_message_iter_abandon_container (&iter, &array); |
| 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); |
| |
| value = *value_p; |
| |
| i = 0; |
| while (i < n_elements) |
| { |
| if (!dbus_message_iter_append_basic (&array, |
| element_type, |
| &value[i])) { |
| dbus_message_iter_abandon_container (&iter, &array); |
| 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; |
| } |
| #ifndef DBUS_DISABLE_CHECKS |
| else |
| { |
| _dbus_warn ("type %s isn't supported yet in %s\n", |
| _dbus_type_to_string (type), _DBUS_FUNCTION_NAME); |
| goto failed; |
| } |
| #endif |
| |
| type = va_arg (var_args, int); |
| } |
| |
| return TRUE; |
| |
| failed: |
| return FALSE; |
| } |
| |
| /** |
| * Gets arguments from a message given a variable argument list. The |
| * supported types include those supported by |
| * dbus_message_append_args(); that is, basic types and arrays of |
| * fixed-length basic types. The arguments are the same as they would |
| * be for dbus_message_iter_get_basic() or |
| * dbus_message_iter_get_fixed_array(). |
| * |
| * 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. To get a string array |
| * pass in "char ***array_location" and "int *n_elements". |
| * |
| * Similar to dbus_message_get_fixed_array() this function does not |
| * support arrays of type DBUS_TYPE_UNIX_FD. If you need to parse |
| * messages with arrays of Unix file descriptors you need to recurse |
| * into the array manually. |
| * |
| * Unix file descriptors that are read with this function will have |
| * the FD_CLOEXEC flag set. If you need them without this flag set, |
| * make sure to unset it with fcntl(). |
| * |
| * The variable argument list should contain the type of the argument |
| * followed by a pointer to where the value should be stored. The list |
| * is terminated with #DBUS_TYPE_INVALID. |
| * |
| * Except for string arrays, the returned values are constant; do not |
| * free them. They point into the #DBusMessage. |
| * |
| * If the requested arguments are not present, or do not have the |
| * requested types, then an error will be set. |
| * |
| * If more arguments than requested are present, the requested |
| * arguments are returned and the extra arguments are ignored. |
| * |
| * @todo support DBUS_TYPE_STRUCT and DBUS_TYPE_VARIANT and complex arrays |
| * |
| * @param message the message |
| * @param error error to be filled in on failure |
| * @param first_arg_type the first argument type |
| * @param ... location for first argument value, then list of type-location pairs |
| * @returns #FALSE if the error was set |
| */ |
| dbus_bool_t |
| dbus_message_get_args (DBusMessage *message, |
| DBusError *error, |
| int first_arg_type, |
| ...) |
| { |
| dbus_bool_t retval; |
| va_list var_args; |
| |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_error_is_set (error, FALSE); |
| |
| va_start (var_args, first_arg_type); |
| retval = dbus_message_get_args_valist (message, error, first_arg_type, var_args); |
| va_end (var_args); |
| |
| return retval; |
| } |
| |
| /** |
| * Like dbus_message_get_args but takes a va_list for use by language bindings. |
| * |
| * @see dbus_message_get_args |
| * @param message the message |
| * @param error error to be filled in |
| * @param first_arg_type type of the first argument |
| * @param var_args return location for first argument, followed by list of type/location pairs |
| * @returns #FALSE if error was set |
| */ |
| dbus_bool_t |
| dbus_message_get_args_valist (DBusMessage *message, |
| DBusError *error, |
| int first_arg_type, |
| va_list var_args) |
| { |
| DBusMessageIter iter; |
| |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_error_is_set (error, FALSE); |
| |
| dbus_message_iter_init (message, &iter); |
| return _dbus_message_iter_get_args_valist (&iter, error, first_arg_type, var_args); |
| } |
| |
| static void |
| _dbus_message_iter_init_common (DBusMessage *message, |
| DBusMessageRealIter *real, |
| int iter_type) |
| { |
| _dbus_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 |
| */ |
| ensure_byte_order (message); |
| |
| real->message = message; |
| real->changed_stamp = message->changed_stamp; |
| real->iter_type = iter_type; |
| real->sig_refcount = 0; |
| } |
| |
| /** |
| * Initializes a #DBusMessageIter for reading the arguments of the |
| * message passed in. |
| * |
| * When possible, dbus_message_get_args() is much more convenient. |
| * Some types of argument can only be read with #DBusMessageIter |
| * however. |
| * |
| * The easiest way to iterate is like this: |
| * @code |
| * 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 |
| * |
| * #DBusMessageIter contains no allocated memory; it need not be |
| * freed, and can be copied by assignment or memcpy(). |
| * |
| * @param message the message |
| * @param iter pointer to an iterator to initialize |
| * @returns #FALSE if the message has no arguments |
| */ |
| dbus_bool_t |
| dbus_message_iter_init (DBusMessage *message, |
| DBusMessageIter *iter) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| const DBusString *type_str; |
| int type_pos; |
| |
| _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); |
| |
| _dbus_message_iter_init_common (message, real, |
| DBUS_MESSAGE_ITER_TYPE_READER); |
| |
| _dbus_type_reader_init (&real->u.reader, |
| message->byte_order, |
| type_str, type_pos, |
| &message->body, |
| 0); |
| |
| return _dbus_type_reader_get_current_type (&real->u.reader) != DBUS_TYPE_INVALID; |
| } |
| |
| /** |
| * Checks if an iterator has any more fields. |
| * |
| * @param iter the message iter |
| * @returns #TRUE if there are more fields following |
| */ |
| dbus_bool_t |
| dbus_message_iter_has_next (DBusMessageIter *iter) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| |
| _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE); |
| _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); |
| |
| return _dbus_type_reader_has_next (&real->u.reader); |
| } |
| |
| /** |
| * Moves the iterator to the next field, if any. If there's no next |
| * field, returns #FALSE. If the iterator moves forward, returns |
| * #TRUE. |
| * |
| * @param iter the message iter |
| * @returns #TRUE if the iterator was moved to the next field |
| */ |
| dbus_bool_t |
| dbus_message_iter_next (DBusMessageIter *iter) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| |
| _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE); |
| _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); |
| |
| return _dbus_type_reader_next (&real->u.reader); |
| } |
| |
| /** |
| * Returns the argument type of the argument that the message iterator |
| * points to. If the iterator is at the end of the message, returns |
| * #DBUS_TYPE_INVALID. You can thus write a loop as follows: |
| * |
| * @code |
| * 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 |
| * |
| * @param iter the message iter |
| * @returns the argument type |
| */ |
| int |
| dbus_message_iter_get_arg_type (DBusMessageIter *iter) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| |
| _dbus_return_val_if_fail (_dbus_message_iter_check (real), DBUS_TYPE_INVALID); |
| _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); |
| |
| return _dbus_type_reader_get_current_type (&real->u.reader); |
| } |
| |
| /** |
| * Returns the element type of the array that the message iterator |
| * points to. Note that you need to check that the iterator points to |
| * an array prior to using this function. |
| * |
| * @param iter the message iter |
| * @returns the array element type |
| */ |
| int |
| dbus_message_iter_get_element_type (DBusMessageIter *iter) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| |
| _dbus_return_val_if_fail (_dbus_message_iter_check (real), DBUS_TYPE_INVALID); |
| _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, DBUS_TYPE_INVALID); |
| _dbus_return_val_if_fail (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_ARRAY, DBUS_TYPE_INVALID); |
| |
| return _dbus_type_reader_get_element_type (&real->u.reader); |
| } |
| |
| /** |
| * Recurses into a container value when reading values from a message, |
| * initializing a sub-iterator to use for traversing the child values |
| * of the container. |
| * |
| * Note that this recurses into a value, not a type, so you can only |
| * recurse if the value exists. The main implication of this is that |
| * if you have for example an empty array of array of int32, you can |
| * recurse into the outermost array, but it will have no values, so |
| * 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 (except Unix file |
| * descriptors), 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 |
| */ |
| void |
| dbus_message_iter_recurse (DBusMessageIter *iter, |
| DBusMessageIter *sub) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; |
| |
| _dbus_return_if_fail (_dbus_message_iter_check (real)); |
| _dbus_return_if_fail (sub != NULL); |
| |
| *real_sub = *real; |
| _dbus_type_reader_recurse (&real->u.reader, &real_sub->u.reader); |
| } |
| |
| /** |
| * 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. |
| * |
| * The returned string must be freed with dbus_free(). |
| * |
| * @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. |
| * |
| * The value argument should be the address of a location to store |
| * the returned value. So for int32 it should be a "dbus_int32_t*" |
| * and for string a "const char**". The returned value is |
| * by reference and should not be freed. |
| * |
| * This call duplicates Unix file descriptors when reading them. It is |
| * your job to close them when you don't need them anymore. |
| * |
| * Unix file descriptors that are read with this function will have |
| * the FD_CLOEXEC flag set. If you need them without this flag set, |
| * make sure to unset it with fcntl(). |
| * |
| * 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 (except Unix file |
| * descriptors), 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 |
| * 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); |
| * @endcode |
| * |
| * 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, you could use a struct or char[8] instead if you like. |
| * |
| * @param iter the iterator |
| * @param value location to store the value |
| */ |
| void |
| dbus_message_iter_get_basic (DBusMessageIter *iter, |
| void *value) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| |
| _dbus_return_if_fail (_dbus_message_iter_check (real)); |
| _dbus_return_if_fail (value != NULL); |
| |
| if (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_UNIX_FD) |
| { |
| #ifdef HAVE_UNIX_FD_PASSING |
| DBusBasicValue idx; |
| |
| _dbus_type_reader_read_basic(&real->u.reader, &idx); |
| |
| if (idx.u32 >= real->message->n_unix_fds) { |
| /* Hmm, we cannot really signal an error here, so let's make |
| sure to return an invalid fd. */ |
| *((int*) value) = -1; |
| return; |
| } |
| |
| *((int*) value) = _dbus_dup(real->message->unix_fds[idx.u32], NULL); |
| #else |
| *((int*) value) = -1; |
| #endif |
| } |
| else |
| { |
| _dbus_type_reader_read_basic (&real->u.reader, |
| value); |
| } |
| } |
| |
| /** |
| * 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. |
| * |
| * @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 |
| */ |
| 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 returned block will be from the |
| * current position in the array until the end of the array. |
| * |
| * There is one exception here: although DBUS_TYPE_UNIX_FD is |
| * considered a 'fixed' type arrays of this type may not be read with |
| * this function. |
| * |
| * 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. |
| * |
| * 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 |
| * @param n_elements number of elements in the block |
| */ |
| void |
| dbus_message_iter_get_fixed_array (DBusMessageIter *iter, |
| void *value, |
| int *n_elements) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| int subtype = _dbus_type_reader_get_current_type(&real->u.reader); |
| |
| _dbus_return_if_fail (_dbus_message_iter_check (real)); |
| _dbus_return_if_fail (value != NULL); |
| _dbus_return_if_fail ((subtype == DBUS_TYPE_INVALID) || |
| (dbus_type_is_fixed (subtype) && subtype != DBUS_TYPE_UNIX_FD)); |
| |
| _dbus_type_reader_read_fixed_multi (&real->u.reader, |
| value, n_elements); |
| } |
| |
| /** |
| * Initializes a #DBusMessageIter for appending arguments to the end |
| * of a message. |
| * |
| * @todo If appending any of the arguments fails due to lack of |
| * memory, the message is hosed and you have to start over building |
| * the whole message. |
| * |
| * @param message the message |
| * @param iter pointer to an iterator to initialize |
| */ |
| void |
| dbus_message_iter_init_append (DBusMessage *message, |
| DBusMessageIter *iter) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| |
| _dbus_return_if_fail (message != NULL); |
| _dbus_return_if_fail (iter != NULL); |
| |
| _dbus_message_iter_init_common (message, real, |
| DBUS_MESSAGE_ITER_TYPE_WRITER); |
| |
| /* We create the signature string and point iterators at it "on demand" |
| * when a value is actually appended. That means that init() never fails |
| * due to OOM. |
| */ |
| _dbus_type_writer_init_types_delayed (&real->u.writer, |
| message->byte_order, |
| &message->body, |
| _dbus_string_get_length (&message->body)); |
| } |
| |
| /** |
| * Creates a temporary signature string containing the current |
| * signature, stores it in the iterator, and points the iterator to |
| * the end of it. Used any time we write to the message. |
| * |
| * @param real an iterator without a type_str |
| * @returns #FALSE if no memory |
| */ |
| static dbus_bool_t |
| _dbus_message_iter_open_signature (DBusMessageRealIter *real) |
| { |
| DBusString *str; |
| const DBusString *current_sig; |
| int current_sig_pos; |
| |
| _dbus_assert (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER); |
| |
| if (real->u.writer.type_str != NULL) |
| { |
| _dbus_assert (real->sig_refcount > 0); |
| real->sig_refcount += 1; |
| return TRUE; |
| } |
| |
| str = dbus_new (DBusString, 1); |
| if (str == NULL) |
| return FALSE; |
| |
| if (!_dbus_header_get_field_raw (&real->message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| ¤t_sig, ¤t_sig_pos)) |
| current_sig = NULL; |
| |
| if (current_sig) |
| { |
| int current_len; |
| |
| current_len = _dbus_string_get_byte (current_sig, current_sig_pos); |
| current_sig_pos += 1; /* move on to sig data */ |
| |
| if (!_dbus_string_init_preallocated (str, current_len + 4)) |
| { |
| dbus_free (str); |
| return FALSE; |
| } |
| |
| if (!_dbus_string_copy_len (current_sig, current_sig_pos, current_len, |
| str, 0)) |
| { |
| _dbus_string_free (str); |
| dbus_free (str); |
| return FALSE; |
| } |
| } |
| else |
| { |
| if (!_dbus_string_init_preallocated (str, 4)) |
| { |
| dbus_free (str); |
| return FALSE; |
| } |
| } |
| |
| real->sig_refcount = 1; |
| |
| _dbus_type_writer_add_types (&real->u.writer, |
| str, _dbus_string_get_length (str)); |
| return TRUE; |
| } |
| |
| /** |
| * Sets the new signature as the message signature, frees the |
| * signature string, and marks the iterator as not having a type_str |
| * anymore. Frees the signature even if it fails, so you can't |
| * really recover from failure. Kinda busted. |
| * |
| * @param real an iterator without a type_str |
| * @returns #FALSE if no memory |
| */ |
| static dbus_bool_t |
| _dbus_message_iter_close_signature (DBusMessageRealIter *real) |
| { |
| DBusString *str; |
| const char *v_STRING; |
| dbus_bool_t retval; |
| |
| _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 TRUE; |
| _dbus_assert (real->sig_refcount == 0); |
| |
| retval = TRUE; |
| |
| str = real->u.writer.type_str; |
| |
| v_STRING = _dbus_string_get_const_data (str); |
| if (!_dbus_header_set_field_basic (&real->message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| DBUS_TYPE_SIGNATURE, |
| &v_STRING)) |
| retval = FALSE; |
| |
| _dbus_type_writer_remove_types (&real->u.writer); |
| _dbus_string_free (str); |
| dbus_free (str); |
| |
| 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) |
| { |
| if (!_dbus_message_iter_check (iter)) |
| return FALSE; |
| |
| if (iter->message->locked) |
| { |
| _dbus_warn_check_failed ("dbus append iterator can't be used: message is locked (has already been sent)\n"); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| #endif /* DBUS_DISABLE_CHECKS */ |
| |
| #ifdef HAVE_UNIX_FD_PASSING |
| static int * |
| expand_fd_array(DBusMessage *m, |
| unsigned n) |
| { |
| _dbus_assert(m); |
| |
| /* This makes space for adding n new fds to the array and returns a |
| pointer to the place were the first fd should be put. */ |
| |
| if (m->n_unix_fds + n > m->n_unix_fds_allocated) |
| { |
| unsigned k; |
| int *p; |
| |
| /* Make twice as much space as necessary */ |
| k = (m->n_unix_fds + n) * 2; |
| |
| /* Allocate at least four */ |
| if (k < 4) |
| k = 4; |
| |
| p = dbus_realloc(m->unix_fds, k * sizeof(int)); |
| if (p == NULL) |
| return NULL; |
| |
| m->unix_fds = p; |
| m->n_unix_fds_allocated = k; |
| } |
| |
| return m->unix_fds + m->n_unix_fds; |
| } |
| #endif |
| |
| /** |
| * Appends a basic-typed value to the message. The basic types are the |
| * non-container types such as integer and string. |
| * |
| * The "value" argument should be the address of a basic-typed value. |
| * So for string, const char**. For integer, dbus_int32_t*. |
| * |
| * For Unix file descriptors this function will internally duplicate |
| * the descriptor you passed in. Hence you may close the descriptor |
| * immediately after this call. |
| * |
| * @todo If this fails due to lack of memory, the message is hosed and |
| * you have to start over building the whole message. |
| * |
| * @param iter the append iterator |
| * @param type the type of the value |
| * @param value the address of the value |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_iter_append_basic (DBusMessageIter *iter, |
| int type, |
| const void *value) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| dbus_bool_t ret; |
| |
| _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 (value != NULL, FALSE); |
| |
| #ifndef DBUS_DISABLE_CHECKS |
| switch (type) |
| { |
| const char * const *string_p; |
| const dbus_bool_t *bool_p; |
| |
| case DBUS_TYPE_STRING: |
| string_p = value; |
| _dbus_return_val_if_fail (_dbus_check_is_valid_utf8 (*string_p), FALSE); |
| break; |
| |
| case DBUS_TYPE_OBJECT_PATH: |
| string_p = value; |
| _dbus_return_val_if_fail (_dbus_check_is_valid_path (*string_p), FALSE); |
| break; |
| |
| case DBUS_TYPE_SIGNATURE: |
| string_p = value; |
| _dbus_return_val_if_fail (_dbus_check_is_valid_signature (*string_p), FALSE); |
| break; |
| |
| case DBUS_TYPE_BOOLEAN: |
| bool_p = value; |
| _dbus_return_val_if_fail (*bool_p == 0 || *bool_p == 1, FALSE); |
| break; |
| |
| default: |
| { |
| /* nothing to check, all possible values are allowed */ |
| } |
| } |
| #endif |
| |
| if (!_dbus_message_iter_open_signature (real)) |
| return FALSE; |
| |
| if (type == DBUS_TYPE_UNIX_FD) |
| { |
| #ifdef HAVE_UNIX_FD_PASSING |
| int *fds; |
| dbus_uint32_t u; |
| |
| /* First step, include the fd in the fd list of this message */ |
| if (!(fds = expand_fd_array(real->message, 1))) |
| return FALSE; |
| |
| *fds = _dbus_dup(*(int*) value, NULL); |
| if (*fds < 0) |
| return FALSE; |
| |
| u = real->message->n_unix_fds; |
| |
| /* Second step, write the index to the fd */ |
| if (!(ret = _dbus_type_writer_write_basic (&real->u.writer, DBUS_TYPE_UNIX_FD, &u))) { |
| _dbus_close(*fds, NULL); |
| return FALSE; |
| } |
| |
| real->message->n_unix_fds += 1; |
| u += 1; |
| |
| /* Final step, update the header accordingly */ |
| ret = _dbus_header_set_field_basic (&real->message->header, |
| DBUS_HEADER_FIELD_UNIX_FDS, |
| DBUS_TYPE_UINT32, |
| &u); |
| |
| /* If any of these operations fail the message is |
| hosed. However, no memory or fds should be leaked since what |
| has been added to message has been added to the message, and |
| can hence be accounted for when the message is being |
| freed. */ |
| #else |
| ret = FALSE; |
| #endif |
| } |
| else |
| { |
| ret = _dbus_type_writer_write_basic (&real->u.writer, type, value); |
| } |
| |
| if (!_dbus_message_iter_close_signature (real)) |
| ret = FALSE; |
| |
| return ret; |
| } |
| |
| /** |
| * Appends a block of fixed-length values to an array. The |
| * fixed-length types are all basic types that are not string-like. So |
| * int32, double, bool, etc. (Unix file descriptors however are not |
| * supported.) You must call dbus_message_iter_open_container() to |
| * open an array of values before calling this function. You may call |
| * this function multiple times (and intermixed with calls to |
| * dbus_message_iter_append_basic()) for the same array. |
| * |
| * The "value" argument should be the address of the array. So for |
| * integer, "dbus_int32_t**" is expected for example. |
| * |
| * @warning in C, given "int array[]", "&array == array" (the |
| * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree). |
| * So if you're using an array instead of a pointer you have to create |
| * a pointer variable, assign the array to it, then take the address |
| * of the pointer variable. |
| * @code |
| * const dbus_int32_t array[] = { 1, 2, 3 }; |
| * const dbus_int32_t *v_ARRAY = array; |
| * if (!dbus_message_iter_append_fixed_array (&iter, DBUS_TYPE_INT32, &v_ARRAY, 3)) |
| * fprintf (stderr, "No memory!\n"); |
| * @endcode |
| * For strings it works to write const char *array = "Hello" and then |
| * use &array though. |
| * |
| * @todo If this fails due to lack of memory, the message is hosed and |
| * you have to start over building the whole message. |
| * |
| * @param iter the append iterator |
| * @param element_type the type of the array elements |
| * @param value the address of the array |
| * @param n_elements the number of elements to append |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_iter_append_fixed_array (DBusMessageIter *iter, |
| int element_type, |
| const void *value, |
| int n_elements) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| dbus_bool_t ret; |
| |
| _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) && element_type != DBUS_TYPE_UNIX_FD, 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); |
| _dbus_return_val_if_fail (n_elements <= |
| DBUS_MAXIMUM_ARRAY_LENGTH / _dbus_type_get_alignment (element_type), |
| FALSE); |
| |
| #ifndef DBUS_DISABLE_CHECKS |
| if (element_type == DBUS_TYPE_BOOLEAN) |
| { |
| const dbus_bool_t * const *bools = value; |
| int i; |
| |
| for (i = 0; i < n_elements; i++) |
| { |
| _dbus_return_val_if_fail ((*bools)[i] == 0 || (*bools)[i] == 1, FALSE); |
| } |
| } |
| #endif |
| |
| ret = _dbus_type_writer_write_fixed_multi (&real->u.writer, element_type, value, n_elements); |
| |
| return ret; |
| } |
| |
| /** |
| * Appends a container-typed value to the message; you are required to |
| * append the contents of the container using the returned |
| * sub-iterator, and then call |
| * 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 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. |
| * |
| * @param iter the append iterator |
| * @param type the type of the value |
| * @param contained_signature the type of container contents |
| * @param sub sub-iterator to initialize |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_iter_open_container (DBusMessageIter *iter, |
| int type, |
| const char *contained_signature, |
| DBusMessageIter *sub) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; |
| DBusString contained_str; |
| |
| _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 (sub != NULL, FALSE); |
| _dbus_return_val_if_fail ((type == DBUS_TYPE_STRUCT && |
| contained_signature == NULL) || |
| (type == DBUS_TYPE_DICT_ENTRY && |
| contained_signature == NULL) || |
| (type == DBUS_TYPE_VARIANT && |
| contained_signature != NULL) || |
| (type == DBUS_TYPE_ARRAY && |
| contained_signature != NULL), FALSE); |
| |
| /* 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 ((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; |
| |
| *real_sub = *real; |
| |
| 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); |
| } |
| } |
| |
| |
| /** |
| * Closes a container-typed value appended to the message; may write |
| * out more information to the message known only after the entire |
| * container is written, and may free resources created by |
| * dbus_message_iter_open_container(). |
| * |
| * @todo If this fails due to lack of memory, the message is hosed and |
| * you have to start over building the whole message. |
| * |
| * @param iter the append iterator |
| * @param sub sub-iterator to close |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_iter_close_container (DBusMessageIter *iter, |
| DBusMessageIter *sub) |
| { |
| DBusMessageRealIter *real = (DBusMessageRealIter *)iter; |
| DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; |
| dbus_bool_t ret; |
| |
| _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_message_iter_append_check (real_sub), FALSE); |
| _dbus_return_val_if_fail (real_sub->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); |
| |
| ret = _dbus_type_writer_unrecurse (&real->u.writer, |
| &real_sub->u.writer); |
| |
| if (!_dbus_message_iter_close_signature (real)) |
| ret = FALSE; |
| |
| return ret; |
| } |
| |
| /** |
| * 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 |
| * replies. If this flag is set, there is no way to know whether the |
| * message successfully arrived at the remote end. Normally you know a |
| * message was received when you receive the reply to it. |
| * |
| * The flag is #FALSE by default, that is by default the other end is |
| * required to reply. |
| * |
| * On the protocol level this toggles #DBUS_HEADER_FLAG_NO_REPLY_EXPECTED |
| * |
| * @param message the message |
| * @param no_reply #TRUE if no reply is desired |
| */ |
| void |
| dbus_message_set_no_reply (DBusMessage *message, |
| dbus_bool_t no_reply) |
| { |
| _dbus_return_if_fail (message != NULL); |
| _dbus_return_if_fail (!message->locked); |
| |
| _dbus_header_toggle_flag (&message->header, |
| DBUS_HEADER_FLAG_NO_REPLY_EXPECTED, |
| no_reply); |
| } |
| |
| /** |
| * Returns #TRUE if the message does not expect |
| * a reply. |
| * |
| * @param message the message |
| * @returns #TRUE if the message sender isn't waiting for a reply |
| */ |
| dbus_bool_t |
| dbus_message_get_no_reply (DBusMessage *message) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| |
| return _dbus_header_get_flag (&message->header, |
| DBUS_HEADER_FLAG_NO_REPLY_EXPECTED); |
| } |
| |
| /** |
| * Sets a flag indicating that an owner for the destination name will |
| * be automatically started before the message is delivered. When this |
| * flag is set, the message is held until a name owner finishes |
| * starting up, or fails to start up. In case of failure, the reply |
| * will be an error. |
| * |
| * The flag is set to #TRUE by default, i.e. auto starting is the default. |
| * |
| * On the protocol level this toggles #DBUS_HEADER_FLAG_NO_AUTO_START |
| * |
| * @param message the message |
| * @param auto_start #TRUE if auto-starting is desired |
| */ |
| void |
| dbus_message_set_auto_start (DBusMessage *message, |
| dbus_bool_t auto_start) |
| { |
| _dbus_return_if_fail (message != NULL); |
| _dbus_return_if_fail (!message->locked); |
| |
| _dbus_header_toggle_flag (&message->header, |
| DBUS_HEADER_FLAG_NO_AUTO_START, |
| !auto_start); |
| } |
| |
| /** |
| * Returns #TRUE if the message will cause an owner for |
| * destination name to be auto-started. |
| * |
| * @param message the message |
| * @returns #TRUE if the message will use auto-start |
| */ |
| dbus_bool_t |
| dbus_message_get_auto_start (DBusMessage *message) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| |
| return !_dbus_header_get_flag (&message->header, |
| DBUS_HEADER_FLAG_NO_AUTO_START); |
| } |
| |
| |
| /** |
| * Sets the object path this message is being sent to (for |
| * DBUS_MESSAGE_TYPE_METHOD_CALL) or the one a signal is being |
| * emitted from (for DBUS_MESSAGE_TYPE_SIGNAL). |
| * |
| * The path must contain only valid characters as defined |
| * in the D-Bus specification. |
| * |
| * @param message the message |
| * @param object_path the path or #NULL to unset |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_set_path (DBusMessage *message, |
| const char *object_path) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (!message->locked, FALSE); |
| _dbus_return_val_if_fail (object_path == NULL || |
| _dbus_check_is_valid_path (object_path), |
| FALSE); |
| |
| return set_or_delete_string_field (message, |
| DBUS_HEADER_FIELD_PATH, |
| DBUS_TYPE_OBJECT_PATH, |
| object_path); |
| } |
| |
| /** |
| * 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). Returns #NULL if none. |
| * |
| * See also dbus_message_get_path_decomposed(). |
| * |
| * The returned string becomes invalid if the message is |
| * modified, since it points into the wire-marshaled message data. |
| * |
| * @param message the message |
| * @returns the path (should not be freed) or #NULL |
| */ |
| const char* |
| dbus_message_get_path (DBusMessage *message) |
| { |
| const char *v; |
| |
| _dbus_return_val_if_fail (message != NULL, NULL); |
| |
| v = NULL; /* in case field doesn't exist */ |
| _dbus_header_get_field_basic (&message->header, |
| DBUS_HEADER_FIELD_PATH, |
| DBUS_TYPE_OBJECT_PATH, |
| (void *) &v); |
| return v; |
| } |
| |
| /** |
| * Checks if the message has a particular object path. The object |
| * path is the destination object for a method call or the emitting |
| * object for a signal. |
| * |
| * @param message the message |
| * @param path the path name |
| * @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 |
| * format (one array element per path component). |
| * Free the returned array with dbus_free_string_array(). |
| * |
| * An empty but non-NULL path array means the path "/". |
| * So the path "/foo/bar" becomes { "foo", "bar", NULL } |
| * and the path "/" becomes { NULL }. |
| * |
| * See also dbus_message_get_path(). |
| * |
| * @todo this could be optimized by using the len from the message |
| * instead of calling strlen() again |
| * |
| * @param message the message |
| * @param path place to store allocated array of path components; #NULL set here if no path field exists |
| * @returns #FALSE if no memory to allocate the array |
| */ |
| dbus_bool_t |
| dbus_message_get_path_decomposed (DBusMessage *message, |
| char ***path) |
| { |
| const char *v; |
| |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (path != NULL, FALSE); |
| |
| *path = NULL; |
| |
| v = dbus_message_get_path (message); |
| if (v != NULL) |
| { |
| if (!_dbus_decompose_path (v, strlen (v), |
| path, NULL)) |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| /** |
| * Sets the interface this message is being sent to |
| * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or |
| * the interface a signal is being emitted from |
| * (for DBUS_MESSAGE_TYPE_SIGNAL). |
| * |
| * The interface name must contain only valid characters as defined |
| * in the D-Bus specification. |
| * |
| * @param message the message |
| * @param interface the interface or #NULL to unset |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_set_interface (DBusMessage *message, |
| const char *interface) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (!message->locked, FALSE); |
| _dbus_return_val_if_fail (interface == NULL || |
| _dbus_check_is_valid_interface (interface), |
| FALSE); |
| |
| return set_or_delete_string_field (message, |
| DBUS_HEADER_FIELD_INTERFACE, |
| DBUS_TYPE_STRING, |
| interface); |
| } |
| |
| /** |
| * Gets the interface this message is being sent to |
| * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted |
| * from (for DBUS_MESSAGE_TYPE_SIGNAL). |
| * The interface name is fully-qualified (namespaced). |
| * Returns #NULL if none. |
| * |
| * The returned string becomes invalid if the message is |
| * modified, since it points into the wire-marshaled message data. |
| * |
| * @param message the message |
| * @returns the message interface (should not be freed) or #NULL |
| */ |
| const char* |
| dbus_message_get_interface (DBusMessage *message) |
| { |
| const char *v; |
| |
| _dbus_return_val_if_fail (message != NULL, NULL); |
| |
| v = NULL; /* in case field doesn't exist */ |
| _dbus_header_get_field_basic (&message->header, |
| DBUS_HEADER_FIELD_INTERFACE, |
| DBUS_TYPE_STRING, |
| (void *) &v); |
| return v; |
| } |
| |
| /** |
| * Checks if the message has an interface |
| * |
| * @param message the message |
| * @param interface the interface name |
| * @returns #TRUE if the interface field in the header matches |
| */ |
| 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). |
| * |
| * The member name must contain only valid characters as defined |
| * in the D-Bus specification. |
| * |
| * @param message the message |
| * @param member the member or #NULL to unset |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_set_member (DBusMessage *message, |
| const char *member) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (!message->locked, FALSE); |
| _dbus_return_val_if_fail (member == NULL || |
| _dbus_check_is_valid_member (member), |
| FALSE); |
| |
| return set_or_delete_string_field (message, |
| DBUS_HEADER_FIELD_MEMBER, |
| DBUS_TYPE_STRING, |
| member); |
| } |
| |
| /** |
| * Gets the interface member being invoked |
| * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted |
| * (DBUS_MESSAGE_TYPE_SIGNAL). Returns #NULL if none. |
| * |
| * The returned string becomes invalid if the message is |
| * modified, since it points into the wire-marshaled message data. |
| * |
| * @param message the message |
| * @returns the member name (should not be freed) or #NULL |
| */ |
| const char* |
| dbus_message_get_member (DBusMessage *message) |
| { |
| const char *v; |
| |
| _dbus_return_val_if_fail (message != NULL, NULL); |
| |
| v = NULL; /* in case field doesn't exist */ |
| _dbus_header_get_field_basic (&message->header, |
| DBUS_HEADER_FIELD_MEMBER, |
| DBUS_TYPE_STRING, |
| (void *) &v); |
| return v; |
| } |
| |
| /** |
| * Checks if the message has an interface member |
| * |
| * @param message the message |
| * @param member the member name |
| * @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). |
| * |
| * The error name must contain only valid characters as defined |
| * in the D-Bus specification. |
| * |
| * @param message the message |
| * @param error_name the name or #NULL to unset |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_set_error_name (DBusMessage *message, |
| const char *error_name) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (!message->locked, FALSE); |
| _dbus_return_val_if_fail (error_name == NULL || |
| _dbus_check_is_valid_error_name (error_name), |
| FALSE); |
| |
| return set_or_delete_string_field (message, |
| DBUS_HEADER_FIELD_ERROR_NAME, |
| DBUS_TYPE_STRING, |
| error_name); |
| } |
| |
| /** |
| * Gets the error name (DBUS_MESSAGE_TYPE_ERROR only) |
| * or #NULL if none. |
| * |
| * The returned string becomes invalid if the message is |
| * modified, since it points into the wire-marshaled message data. |
| * |
| * @param message the message |
| * @returns the error name (should not be freed) or #NULL |
| */ |
| const char* |
| dbus_message_get_error_name (DBusMessage *message) |
| { |
| const char *v; |
| |
| _dbus_return_val_if_fail (message != NULL, NULL); |
| |
| v = NULL; /* in case field doesn't exist */ |
| _dbus_header_get_field_basic (&message->header, |
| DBUS_HEADER_FIELD_ERROR_NAME, |
| DBUS_TYPE_STRING, |
| (void *) &v); |
| return v; |
| } |
| |
| /** |
| * Sets the message's destination. The destination is the name of |
| * another connection on the bus and may be either the unique name |
| * assigned by the bus to each connection, or a well-known name |
| * specified in advance. |
| * |
| * The destination name must contain only valid characters as defined |
| * in the D-Bus specification. |
| * |
| * @param message the message |
| * @param destination the destination name or #NULL to unset |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_set_destination (DBusMessage *message, |
| const char *destination) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (!message->locked, FALSE); |
| _dbus_return_val_if_fail (destination == NULL || |
| _dbus_check_is_valid_bus_name (destination), |
| FALSE); |
| |
| return set_or_delete_string_field (message, |
| DBUS_HEADER_FIELD_DESTINATION, |
| DBUS_TYPE_STRING, |
| destination); |
| } |
| |
| /** |
| * Gets the destination of a message or #NULL if there is none set. |
| * |
| * The returned string becomes invalid if the message is |
| * modified, since it points into the wire-marshaled message data. |
| * |
| * @param message the message |
| * @returns the message destination (should not be freed) or #NULL |
| */ |
| const char* |
| dbus_message_get_destination (DBusMessage *message) |
| { |
| const char *v; |
| |
| _dbus_return_val_if_fail (message != NULL, NULL); |
| |
| v = NULL; /* in case field doesn't exist */ |
| _dbus_header_get_field_basic (&message->header, |
| DBUS_HEADER_FIELD_DESTINATION, |
| DBUS_TYPE_STRING, |
| (void *) &v); |
| return v; |
| } |
| |
| /** |
| * Sets the message sender. |
| * |
| * The sender must be a valid bus name as defined in the D-Bus |
| * specification. |
| * |
| * Usually you don't want to call this. The message bus daemon will |
| * call it to set the origin of each message. If you aren't implementing |
| * a message bus daemon you shouldn't need to set the sender. |
| * |
| * @param message the message |
| * @param sender the sender or #NULL to unset |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| dbus_message_set_sender (DBusMessage *message, |
| const char *sender) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (!message->locked, FALSE); |
| _dbus_return_val_if_fail (sender == NULL || |
| _dbus_check_is_valid_bus_name (sender), |
| FALSE); |
| |
| return set_or_delete_string_field (message, |
| DBUS_HEADER_FIELD_SENDER, |
| DBUS_TYPE_STRING, |
| sender); |
| } |
| |
| /** |
| * Gets the unique name of the connection which originated this |
| * message, or #NULL if unknown or inapplicable. The sender is filled |
| * in by the message bus. |
| * |
| * Note, the returned sender is always the unique bus name. |
| * Connections may own multiple other bus names, but those |
| * are not found in the sender field. |
| * |
| * The returned string becomes invalid if the message is |
| * modified, since it points into the wire-marshaled message data. |
| * |
| * @param message the message |
| * @returns the unique name of the sender or #NULL |
| */ |
| const char* |
| dbus_message_get_sender (DBusMessage *message) |
| { |
| const char *v; |
| |
| _dbus_return_val_if_fail (message != NULL, NULL); |
| |
| v = NULL; /* in case field doesn't exist */ |
| _dbus_header_get_field_basic (&message->header, |
| DBUS_HEADER_FIELD_SENDER, |
| DBUS_TYPE_STRING, |
| (void *) &v); |
| return v; |
| } |
| |
| /** |
| * Gets the type signature of the message, i.e. the arguments in the |
| * message payload. The signature includes only "in" arguments for |
| * #DBUS_MESSAGE_TYPE_METHOD_CALL and only "out" arguments for |
| * #DBUS_MESSAGE_TYPE_METHOD_RETURN, so is slightly different from |
| * what you might expect (that is, it does not include the signature of the |
| * entire C++-style method). |
| * |
| * The signature is a string made up of type codes such as |
| * #DBUS_TYPE_INT32. The string is terminated with nul (nul is also |
| * the value of #DBUS_TYPE_INVALID). |
| * |
| * The returned string becomes invalid if the message is |
| * modified, since it points into the wire-marshaled message data. |
| * |
| * @param message the message |
| * @returns the type signature |
| */ |
| const char* |
| dbus_message_get_signature (DBusMessage *message) |
| { |
| const DBusString *type_str; |
| int type_pos; |
| |
| _dbus_return_val_if_fail (message != NULL, NULL); |
| |
| get_const_signature (&message->header, &type_str, &type_pos); |
| |
| return _dbus_string_get_const_data_len (type_str, type_pos, 0); |
| } |
| |
| static dbus_bool_t |
| _dbus_message_has_type_interface_member (DBusMessage *message, |
| int type, |
| const char *interface, |
| const char *member) |
| { |
| const char *n; |
| |
| _dbus_assert (message != NULL); |
| _dbus_assert (interface != NULL); |
| _dbus_assert (member != NULL); |
| |
| if (dbus_message_get_type (message) != type) |
| return FALSE; |
| |
| /* Optimize by checking the short member name first |
| * instead of the longer interface name |
| */ |
| |
| n = dbus_message_get_member (message); |
| |
| if (n && strcmp (n, member) == 0) |
| { |
| n = dbus_message_get_interface (message); |
| |
| if (n == NULL || strcmp (n, interface) == 0) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| /** |
| * Checks whether the message is a method call with the given |
| * interface and member fields. If the message is not |
| * #DBUS_MESSAGE_TYPE_METHOD_CALL, or has a different interface or |
| * member field, returns #FALSE. If the interface field is missing, |
| * then it will be assumed equal to the provided interface. The D-Bus |
| * protocol allows method callers to leave out the interface name. |
| * |
| * @param message the message |
| * @param interface the name to check (must not be #NULL) |
| * @param method the name to check (must not be #NULL) |
| * |
| * @returns #TRUE if the message is the specified method call |
| */ |
| dbus_bool_t |
| dbus_message_is_method_call (DBusMessage *message, |
| const char *interface, |
| const char *method) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (interface != NULL, FALSE); |
| _dbus_return_val_if_fail (method != NULL, FALSE); |
| /* don't check that interface/method are valid since it would be |
| * expensive, and not catch many common errors |
| */ |
| |
| return _dbus_message_has_type_interface_member (message, |
| DBUS_MESSAGE_TYPE_METHOD_CALL, |
| interface, method); |
| } |
| |
| /** |
| * Checks whether the message is a signal with the given interface and |
| * member fields. If the message is not #DBUS_MESSAGE_TYPE_SIGNAL, or |
| * has a different interface or member field, returns #FALSE. |
| * |
| * @param message the message |
| * @param interface the name to check (must not be #NULL) |
| * @param signal_name the name to check (must not be #NULL) |
| * |
| * @returns #TRUE if the message is the specified signal |
| */ |
| dbus_bool_t |
| dbus_message_is_signal (DBusMessage *message, |
| const char *interface, |
| const char *signal_name) |
| { |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (interface != NULL, FALSE); |
| _dbus_return_val_if_fail (signal_name != NULL, FALSE); |
| /* don't check that interface/name are valid since it would be |
| * expensive, and not catch many common errors |
| */ |
| |
| return _dbus_message_has_type_interface_member (message, |
| DBUS_MESSAGE_TYPE_SIGNAL, |
| interface, signal_name); |
| } |
| |
| /** |
| * Checks whether the message is an error reply with the given error |
| * name. If the message is not #DBUS_MESSAGE_TYPE_ERROR, or has a |
| * different name, returns #FALSE. |
| * |
| * @param message the message |
| * @param error_name the name to check (must not be #NULL) |
| * |
| * @returns #TRUE if the message is the specified error |
| */ |
| dbus_bool_t |
| dbus_message_is_error (DBusMessage *message, |
| const char *error_name) |
| { |
| const char *n; |
| |
| _dbus_return_val_if_fail (message != NULL, FALSE); |
| _dbus_return_val_if_fail (error_name != NULL, FALSE); |
| /* don't check that error_name is valid since it would be expensive, |
| * and not catch many common errors |
| */ |
| |
| if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR) |
| return FALSE; |
| |
| n = dbus_message_get_error_name (message); |
| |
| if (n && strcmp (n, error_name) == 0) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| /** |
| * Checks whether the message was sent to the given name. If the |
| * message has no destination specified or has a different |
| * destination, returns #FALSE. |
| * |
| * @param message the message |
| * @param name the name to check (must not be #NULL) |
| * |
| * @returns #TRUE if the message has the given destination name |
| */ |
|